diff --git a/.circleci/config.yml b/.circleci/config.yml
index cef9aba..4257e6f 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -102,7 +102,7 @@ workflows:
arch: amd64
matrix:
parameters:
- tag: ['1.10.0b0', '1.9.0b0', '1.8.0b0', '1.7.0b0']
+ tag: ['1.11.0b1', '1.10.0b0', '1.9.0b0', '1.8.0b0']
# Publish for linux/arm64
- devcontainer-publish-arm64:
filters:
@@ -112,7 +112,7 @@ workflows:
arch: arm64
matrix:
parameters:
- tag: ['1.10.0b0', '1.9.0b0', '1.8.0b0', '1.7.0b0']
+ tag: ['1.11.0b1', '1.10.0b0', '1.9.0b0', '1.8.0b0']
# Manifest they two and push to registry
- devcontainer-manifest:
requires:
@@ -124,4 +124,4 @@ workflows:
- main
matrix:
parameters:
- tag: ['1.10.0b0', '1.9.0b0', '1.8.0b0', '1.7.0b0']
+ tag: ['1.11.0b1', '1.10.0b0', '1.9.0b0', '1.8.0b0']
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 0adf756..1c7392a 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -17,6 +17,7 @@ module.exports = {
rules: {
// let's bypass them
'promise/always-return': 'off',
+ '@typescript-eslint/ban-ts-comment': 'warn',
// common pitfalls
eqeqeq: 'error',
diff --git a/packages/secretnote-scql/package.json b/packages/secretnote-scql/package.json
index e6acb21..7e7699c 100644
--- a/packages/secretnote-scql/package.json
+++ b/packages/secretnote-scql/package.json
@@ -1,6 +1,6 @@
{
"name": "@alipay/secretnote-scql",
- "version": "0.0.50",
+ "version": "0.0.51",
"license": "Apache-2.0",
"author": "vectorse@126.com",
"repository": "https://github.com/secretflow/secretnote/tree/main/packages/secretnote",
diff --git a/packages/secretnote-scql/src/utils/error.ts b/packages/secretnote-scql/src/utils/error.ts
index 50f0271..6fdc544 100644
--- a/packages/secretnote-scql/src/utils/error.ts
+++ b/packages/secretnote-scql/src/utils/error.ts
@@ -14,6 +14,7 @@ export function genericErrorHandler(
reThrow?: boolean;
} = {},
) {
+ // eslint-disable-next-line no-console
console.error(e);
const { passthrough } = options;
let { silent, reThrow } = options;
@@ -23,7 +24,6 @@ export function genericErrorHandler(
if (!silent) {
message.error(e?.message || e.toString());
}
- // eslint-disable-next-line no-console
if (reThrow) {
throw e;
}
diff --git a/packages/secretnote-sf/package.json b/packages/secretnote-sf/package.json
index a714510..ac0f380 100644
--- a/packages/secretnote-sf/package.json
+++ b/packages/secretnote-sf/package.json
@@ -1,6 +1,6 @@
{
"name": "@alipay/secretnote-sf",
- "version": "0.0.50",
+ "version": "0.0.51",
"license": "Apache-2.0",
"author": "vectorse@126.com",
"repository": "https://github.com/secretflow/secretnote/tree/main/packages/secretnote",
@@ -14,8 +14,8 @@
"types": "dist/index.d.ts",
"scripts": {
"prepare": "pnpm run build:component",
- "build:component": "tsup src/index.tsx --inject react-shim.js",
- "dev": "tsup src/index.tsx --watch --inject react-shim.js",
+ "build:component": "tsup src/index.ts --inject react-shim.js",
+ "dev": "tsup src/index.ts --watch --inject react-shim.js",
"lint:eslint": "eslint . --ext ts,tsx",
"typecheck:tsc": "tsc -p tsconfig.json --noEmit",
"pb": "pnpm publish --no-git-checks --filter @secretflow/secretnote-sf"
diff --git a/packages/secretnote-sf/src/components/markdown-editor/index.tsx b/packages/secretnote-sf/src/components/markdown-editor/index.tsx
index b0554e2..bd8c25f 100644
--- a/packages/secretnote-sf/src/components/markdown-editor/index.tsx
+++ b/packages/secretnote-sf/src/components/markdown-editor/index.tsx
@@ -58,7 +58,6 @@ export function Editor({
onUpdate(e.editor);
debouncedUpdates(e);
},
- autofocus: 'end',
});
// Hydrate the editor with the content from localStorage.
diff --git a/packages/secretnote-sf/src/components/ribbon/index.less b/packages/secretnote-sf/src/components/ribbon/index.less
index dbb7a8b..86f0024 100644
--- a/packages/secretnote-sf/src/components/ribbon/index.less
+++ b/packages/secretnote-sf/src/components/ribbon/index.less
@@ -3,8 +3,12 @@
}
.secretnote-ribbon-popover {
- .title {
+ .mb-2 {
margin-bottom: 8px;
+ }
+
+ .title {
+ margin-bottom: 6px;
color: var(--mana-secretnote-text-color);
}
diff --git a/packages/secretnote-sf/src/components/ribbon/index.tsx b/packages/secretnote-sf/src/components/ribbon/index.tsx
index 6268a95..4536263 100644
--- a/packages/secretnote-sf/src/components/ribbon/index.tsx
+++ b/packages/secretnote-sf/src/components/ribbon/index.tsx
@@ -1,3 +1,5 @@
+// This is the ribbon on the top-right corner of each code cell used for selecting parties.
+
import { l10n } from '@difizen/mana-l10n';
import { Badge, Popover, Space, Tag } from 'antd';
import React from 'react';
@@ -5,8 +7,8 @@ import './index.less';
interface RibbonProps {
children: React.ReactNode;
- items: { label: string; key: string }[];
- value: string[];
+ items: { label: string; key: string }[]; // selectable parties that are already added nodes
+ value: string[]; // selected parties that is saved in the metadata of the cell execution
onChange?: (value: string[]) => void;
readonly?: boolean;
}
@@ -44,20 +46,23 @@ function Ribbon(props: RibbonProps) {
overlayClassName="secretnote-ribbon-popover"
content={
<>
-
{l10n.t('请选择要执行该代码的节点列表')}:
+ {l10n.t('请选择要执行该代码的节点列表')}
- {items.map((item) => (
- handleChange(item.key, checked)}
- >
- {item.label}
-
- ))}
+ {items.length
+ ? items.map((item) => (
+ handleChange(item.key, checked)}
+ >
+ {item.label}
+
+ ))
+ : l10n.t('还未拉起任何节点')}
>
}
diff --git a/packages/secretnote-sf/src/index.tsx b/packages/secretnote-sf/src/index.ts
similarity index 100%
rename from packages/secretnote-sf/src/index.tsx
rename to packages/secretnote-sf/src/index.ts
diff --git a/packages/secretnote-sf/src/modules/editor/cell/view.tsx b/packages/secretnote-sf/src/modules/editor/cell/view.tsx
index 870b354..5f81a5c 100644
--- a/packages/secretnote-sf/src/modules/editor/cell/view.tsx
+++ b/packages/secretnote-sf/src/modules/editor/cell/view.tsx
@@ -37,20 +37,20 @@ import { compareDateString, isReadonly } from '@/utils';
const SecretNoteCodeCellComponent = forwardRef((props, ref) => {
const instance = useInject(ViewInstance);
- const { partyList, parties } = instance;
+ const { allParties, cellParties } = instance;
const { readonly } = instance;
return (
({
+ items={(readonly ? cellParties : allParties).map((name) => ({
label: name,
key: name,
}))}
- value={parties}
+ value={cellParties}
onChange={(val) => {
!readonly && instance.onPartiesChange(val);
}}
@@ -72,10 +72,10 @@ export class SecretNoteCodeCellView extends JupyterCodeCellView {
view = SecretNoteCodeCellComponent;
- @prop() parties: string[] = [];
+ @prop() cellParties: string[] = [];
@prop() readonly = false;
- get partyList() {
+ get allParties() {
return this.serverManager.servers
.filter((s) => s.status === ServerStatus.Succeeded)
.map((server) => server.name);
@@ -94,7 +94,7 @@ export class SecretNoteCodeCellView extends JupyterCodeCellView {
this.serverManager = serverManager;
this.kernelManager = kernelManager;
this.readonly = isReadonly(configService);
- this.parties = this.getInitialParties();
+ this.cellParties = this.getInitialParties();
}
/**
@@ -114,7 +114,7 @@ export class SecretNoteCodeCellView extends JupyterCodeCellView {
return (
server &&
server.status === ServerStatus.Succeeded &&
- this.parties.includes(server.name)
+ this.cellParties.includes(server.name)
);
});
}
@@ -258,7 +258,7 @@ export class SecretNoteCodeCellView extends JupyterCodeCellView {
* Handle the change of parties user selected on the right-top corner of the cell.
*/
onPartiesChange(parties: string[]) {
- this.parties = parties;
+ this.cellParties = parties;
this.savePartiesToMeta(parties);
lastParties = parties;
}
@@ -275,23 +275,24 @@ export class SecretNoteCodeCellView extends JupyterCodeCellView {
if (execution && execution.parties) {
try {
const parties: string[] = JSON.parse(execution.parties as string);
- return this.readonly
- ? parties
- : // filter out parties that are not in the server list
- parties.filter((p) => this.partyList.includes(p));
+ if (this.readonly) {
+ return parties;
+ }
+ // filter out parties that are not in the server list
+ return parties.filter((p) => this.allParties.includes(p));
} catch (e) {
return [];
}
} else if (lastParties.length > 0) {
return lastParties; // load parties from previous cell settings
}
- return this.partyList;
+ return this.allParties;
}
/**
* Save the parties user selected to the cell model metadata.
*/
- savePartiesToMeta(parties = this.parties) {
+ savePartiesToMeta(parties = this.cellParties) {
const execution = this.model.metadata.execution as ExecutionMeta;
if (execution) {
execution.parties = JSON.stringify(parties);
diff --git a/packages/secretnote-sf/src/modules/editor/contents/contents-contrib.ts b/packages/secretnote-sf/src/modules/editor/contents/contents-contrib.ts
index b14189f..30adacc 100644
--- a/packages/secretnote-sf/src/modules/editor/contents/contents-contrib.ts
+++ b/packages/secretnote-sf/src/modules/editor/contents/contents-contrib.ts
@@ -1,42 +1,76 @@
-import type { NotebookModel, NotebookOption } from '@difizen/libro-jupyter';
-import { ContentContribution } from '@difizen/libro-jupyter';
+import type { ICell, NotebookModel, NotebookOption } from '@difizen/libro-jupyter';
+import { ContentContribution, isCode } from '@difizen/libro-jupyter';
import { URI, inject, singleton } from '@difizen/mana-app';
+import { l10n } from '@difizen/mana-l10n';
+import { message } from 'antd';
+import { uniq } from 'lodash-es';
+import { SecretNoteConfigService } from '@/modules/config';
import type { SecretNoteModel } from '@/modules/editor';
import { NotebookFileService } from '@/modules/notebook';
+import { genericErrorHandler, isReadonly, jsonParseSafe } from '@/utils';
@singleton({ contrib: ContentContribution })
export class SecretNoteContentContribution implements ContentContribution {
protected readonly notebookFileService: NotebookFileService;
+ protected readonly configService: SecretNoteConfigService;
- constructor(@inject(NotebookFileService) notebookFileService: NotebookFileService) {
+ constructor(
+ @inject(NotebookFileService) notebookFileService: NotebookFileService,
+ @inject(SecretNoteConfigService) configService: SecretNoteConfigService,
+ ) {
this.notebookFileService = notebookFileService;
+ this.configService = configService;
}
canHandle = () => {
return 3;
};
- async loadContent(options: NotebookOption, model: NotebookModel) {
- const secretNoteModel = model as SecretNoteModel;
+ async loadContent(options: NotebookOption, _model: NotebookModel) {
+ const model = _model as SecretNoteModel;
const fileUri = new URI(options.resource);
const filePath = fileUri.path.toString();
const currentFileContents = await this.notebookFileService.getFile(filePath);
if (currentFileContents) {
currentFileContents.content.nbformat_minor = 5;
- secretNoteModel.currentFileContents = currentFileContents;
+ model.currentFileContents = currentFileContents;
// use file path as id, will be passed to editor and lsp
// @see https://github.com/difizen/libro/commit/b91cd7588ba4adcb3ca83f241fe42471b30cdc26#diff-32d01fca78d40feed75dcc29437fae22f74ebe33ec16ee11fde7c6c220bedbdbR23
- secretNoteModel.id = secretNoteModel.filePath = currentFileContents.path;
+ model.id = model.filePath = currentFileContents.path;
- /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
// @ts-ignore
- if (!secretNoteModel.quickEditMode && !secretNoteModel.readOnly) {
- secretNoteModel.startKernelConnection();
+ if (!model.quickEditMode && !model.readOnly) {
+ model.startKernelConnection();
}
- return secretNoteModel.currentFileContents.content;
+ const { content } = model.currentFileContents;
+ // collect all parties in execution metadata and hint the user if possible
+ try {
+ if (!isReadonly(this.configService)) {
+ const requiredParties = uniq(
+ content?.cells
+ .map((v: ICell) =>
+ // @ts-ignore
+ isCode(v) ? jsonParseSafe(v?.metadata?.execution?.parties, []) : [],
+ )
+ .flat() as string[],
+ );
+ requiredParties.length &&
+ message.info(
+ l10n.t(
+ `当前 Notebook 需要参与方 {0} 执行,添加完成后请刷新页面`,
+ requiredParties.join(', '),
+ ),
+ 5,
+ );
+ }
+ } catch (e) {
+ genericErrorHandler(e, { silent: true });
+ }
+
+ return content;
}
}
}
diff --git a/packages/secretnote-sf/src/pages/sf-preview/index.less b/packages/secretnote-sf/src/pages/sf-preview/index.less
new file mode 100644
index 0000000..28efadf
--- /dev/null
+++ b/packages/secretnote-sf/src/pages/sf-preview/index.less
@@ -0,0 +1,9 @@
+.secretnote-sf-preview-container {
+ .mana-view-container {
+ > .libro-view {
+ > .libro-view-top {
+ display: none;
+ }
+ }
+ }
+}
diff --git a/packages/secretnote-sf/src/pages/sf-preview/index.tsx b/packages/secretnote-sf/src/pages/sf-preview/index.tsx
index bf1532b..49d2a2c 100644
--- a/packages/secretnote-sf/src/pages/sf-preview/index.tsx
+++ b/packages/secretnote-sf/src/pages/sf-preview/index.tsx
@@ -12,10 +12,11 @@ import {
} from '@/modules/preview';
import { ThemeModule } from '@/modules/theme';
import { ToolbarModule } from '@/modules/toolbar';
+import { useRunOnce } from '@/utils/hook';
import '@/lang';
-import { useRunOnce } from '@/utils/hook';
import '../../override.less';
+import './index.less';
export interface ISecretNotePreviewProps {
fileURL?: string; // file URL of the notebook to preview
@@ -23,9 +24,9 @@ export interface ISecretNotePreviewProps {
}
const App = (props: ISecretNotePreviewProps): JSX.Element => {
- useRunOnce(() =>
- localStorage.setItem(SecretNoteConfigLocalStorageKey, JSON.stringify(props)),
- );
+ useRunOnce(() => {
+ localStorage.setItem(SecretNoteConfigLocalStorageKey, JSON.stringify(props));
+ });
return (
(obj: T, keys: K[
Object.keys(obj).filter((k) => !keys.includes(k as K)),
) as Omit;
}
+
+/**
+ * JSON parse with fallback.
+ */
+export function jsonParseSafe(json: string, fallback: any = {}) {
+ try {
+ return JSON.parse(json);
+ } catch (e) {
+ return fallback;
+ }
+}
diff --git a/packages/secretnote-sf/src/utils/error.ts b/packages/secretnote-sf/src/utils/error.ts
index e05ee23..67673b3 100644
--- a/packages/secretnote-sf/src/utils/error.ts
+++ b/packages/secretnote-sf/src/utils/error.ts
@@ -14,11 +14,11 @@ export function genericErrorHandler(
reThrow?: boolean;
} = {},
) {
+ // eslint-disable-next-line no-console
+ console.error(e);
if (!options?.silent) {
message.error(getErrorString(e));
- console.error(e);
}
- // eslint-disable-next-line no-console
if (options?.reThrow) {
throw e;
}
diff --git a/pyprojects/secretnote/package.json b/pyprojects/secretnote/package.json
index d1fbc59..6bb4b89 100644
--- a/pyprojects/secretnote/package.json
+++ b/pyprojects/secretnote/package.json
@@ -1,7 +1,7 @@
{
"name": "secretnote",
"private": true,
- "version": "0.0.50",
+ "version": "0.0.51",
"type": "module",
"scripts": {
"dev:sf": "cd .. && NODE_ENV=development python -m secretnote sf --config=./secretnote/secretnote/sf/.jupyter/config_dev.py --no-browser",
diff --git a/pyprojects/secretnote/secretnote/__init__.py b/pyprojects/secretnote/secretnote/__init__.py
index bf41adb..a3ebe30 100644
--- a/pyprojects/secretnote/secretnote/__init__.py
+++ b/pyprojects/secretnote/secretnote/__init__.py
@@ -1 +1 @@
-__version__ = "0.0.50"
+__version__ = "0.0.51"