@chengxianglibra/dsh-data-analysis 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.en.md +6 -3
  2. package/README.md +6 -3
  3. package/lib/client/credentials/create-datasource.js +1 -1
  4. package/lib/client/credentials/install.js +1 -1
  5. package/lib/client/input-context.js +1 -1
  6. package/lib/client/python-tool/card.js +2 -2
  7. package/lib/client/python-tool/model.js +1 -1
  8. package/lib/client/right-tabs/install.js +10 -7
  9. package/lib/client/settings/install.js +7 -3
  10. package/lib/client-composition.js +701 -0
  11. package/lib/client.js +82 -71
  12. package/lib/compatibility.js +1 -0
  13. package/lib/datasource/changes-contract.js +6 -2
  14. package/lib/datasource/defaults.js +26 -4
  15. package/lib/datasource/python.js +3 -1
  16. package/lib/datasource/rpc.js +3 -3
  17. package/lib/environment/runtime.js +2 -0
  18. package/lib/environment/workspace.js +13 -1
  19. package/lib/integration.js +5 -0
  20. package/lib/plugin.js +18 -8
  21. package/lib/presentation/execution-policy.js +5 -0
  22. package/lib/presentation/projection/index.js +27 -10
  23. package/lib/presentation/tool.js +2 -1
  24. package/lib/settings.js +11 -15
  25. package/lib/types/client/settings/card.d.ts +2 -2
  26. package/lib/types/client.d.ts +1 -0
  27. package/lib/types/compatibility.d.ts +1 -0
  28. package/lib/types/datasource/defaults.d.ts +2 -1
  29. package/lib/types/datasource/rpc.d.ts +2 -2
  30. package/lib/types/disclosure/execution-guidance.d.ts +8 -0
  31. package/lib/types/environment/errors.d.ts +1 -1
  32. package/lib/types/environment/runtime.d.ts +2 -0
  33. package/lib/types/environment/workspace.d.ts +2 -1
  34. package/lib/types/integration.d.ts +16 -0
  35. package/lib/types/plugin.d.ts +26 -3
  36. package/lib/types/presentation/execution-policy.d.ts +5 -0
  37. package/lib/types/rpc.d.ts +2 -2
  38. package/lib/types/semantic-reference/rpc.d.ts +2 -2
  39. package/lib/types/settings.d.ts +8 -8
  40. package/package.json +89 -70
  41. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  42. package/skills/dsh-data-analysis-files/SKILL.md +3 -3
  43. package/skills/dsh-data-analysis-files/references/delivery.md +2 -2
package/README.en.md CHANGED
@@ -26,7 +26,7 @@ This is an independently maintained community plugin. It is not an official Deep
26
26
  ### Prerequisites
27
27
 
28
28
  - Node.js `^22.19.0 || >=24.0.0`: version 22.19.0 or later in the 22.x series, or version 24.0.0 and above. Version 23.x is not supported.
29
- - DeepSeek Harness `>=0.1.5-rc.1`, installed and configured.
29
+ - DeepSeek Harness `>=0.1.7-rc.1`, launched with `npx @deepseek-ai/dsh web` and configured.
30
30
  - `pnpm` available on the command line for Harness to install the plugin.
31
31
  - Python 3.10+, available as `python3` (`python` on Windows), with `venv`/`ensurepip` support. Some Linux distributions require the additional `python3-venv` package.
32
32
 
@@ -35,16 +35,18 @@ An internet connection is required to download dependencies; wait for setup to f
35
35
 
36
36
  ### Install and launch
37
37
 
38
+ The commands below run Harness through `npx`; no global installation or `dsh` command on PATH is required.
39
+
38
40
  Install the plugin into the Web Profile:
39
41
 
40
42
  ```bash
41
- dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
43
+ npx @deepseek-ai/dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
42
44
  ```
43
45
 
44
46
  Start the Web Profile after installation. If it is already running, exit and restart it:
45
47
 
46
48
  ```bash
47
- dsh --profile web
49
+ npx @deepseek-ai/dsh web
48
50
  ```
49
51
 
50
52
  In the Web interface, select a Workspace, create a session, and send a message.
@@ -59,6 +61,7 @@ The examples below use the Web interface; Chinese labels are included to help yo
59
61
  No data source configuration or business definitions are required first.
60
62
  CSV, JSON, Parquet, and Excel `.xlsx` files are supported. Reading `.xlsx` for the first time requires an internet connection to download an extension.
61
63
  Convert older `.xls` files to `.xlsx`, CSV, or Parquet first.
64
+ To view or deliver a CSV, TSV, or XLSX file, ask the assistant to use native `present`, then open the read-only DSH spreadsheet preview on the right. Tables with report filtering and linked interactions remain in the report reader. The plugin does not write XLSX or analyze older `.xls` files.
62
65
 
63
66
  **Databases**: Open Data Sources (数据源) beside the session title to view connections and their configuration status in the current workspace:
64
67
 
package/README.md CHANGED
@@ -26,7 +26,7 @@
26
26
  ### 环境准备
27
27
 
28
28
  - Node.js `^22.19.0 || >=24.0.0`:22.x 需至少 22.19.0,或使用 24.0.0 及以上版本;不支持 23.x。
29
- - 已安装并配置好 DeepSeek Harness `>=0.1.5-rc.1`。
29
+ - 已通过 `npx @deepseek-ai/dsh web` 启动并配置好 DeepSeek Harness `>=0.1.7-rc.1`。
30
30
  - 命令行中可使用 `pnpm`,供 Harness 安装插件。
31
31
  - Python 3.10+,命令行可运行 `python3`(Windows 为 `python`),且支持 `venv`/`ensurepip`;部分 Linux 发行版需额外安装 `python3-venv`。
32
32
 
@@ -34,16 +34,18 @@
34
34
 
35
35
  ### 安装并启动
36
36
 
37
+ 以下命令均通过 `npx` 运行 Harness,无需全局安装或将 `dsh` 添加到 PATH。
38
+
37
39
  将插件安装到 Web Profile:
38
40
 
39
41
  ```bash
40
- dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
42
+ npx @deepseek-ai/dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
41
43
  ```
42
44
 
43
45
  安装后启动 Web Profile;如果已在运行,请先退出再重新启动:
44
46
 
45
47
  ```bash
46
- dsh --profile web
48
+ npx @deepseek-ai/dsh web
47
49
  ```
48
50
 
49
51
  在 Web 界面选择分析使用的工作区(Workspace),新建会话并发送一条消息。
@@ -56,6 +58,7 @@ dsh --profile web
56
58
  **本地文件**:上传文件后直接提问,或指定工作区中已有文件,无需先配置数据源或建立业务定义。
57
59
  支持 CSV、JSON、Parquet 和 Excel `.xlsx`;首次读取 `.xlsx` 需要联网下载扩展。
58
60
  旧版 `.xls` 请先转换为 `.xlsx`、CSV 或 Parquet。
61
+ 需要查看或交付 CSV、TSV、XLSX 文件时,可让助手通过原生 `present` 交付,随后在右侧打开 DSH 的只读表格预览;报告中可筛选联动的表格仍使用报告阅读器。插件不提供 XLSX 写入或旧 `.xls` 分析。
59
62
 
60
63
  **数据库**:在会话标题旁打开“数据源”,查看当前工作区的连接与配置状态:
61
64
 
@@ -14,7 +14,7 @@ function credentialLabel(field) {
14
14
  http_headers_env: 'marivo.credentials.header-credential-value',
15
15
  }[field] ?? field.replace(/_env$/, ''));
16
16
  }
17
- function credentialRows(schema, backend, values = {}) {
17
+ function credentialRows(schema, backend, values = schema.creationDefaults?.[backend] ?? {}) {
18
18
  return (schema.backends.find((item) => item.name === backend)?.fields ?? [])
19
19
  .filter((field) => field.name.endsWith('_env'))
20
20
  .flatMap((field) => field.type === 'json'
@@ -307,7 +307,7 @@ export function installCredentials(ctx, rpc, changes) {
307
307
  : t('marivo.credentials.awaiting-credentials'), type: "button", onClick: () => model.openRequest(request.id), children: t('marivo.credentials.configuration-needed') })) : null;
308
308
  })));
309
309
  ctx.slots.inject('shell.overlay', () => ctx.slots.register({ locale: 'marivo.navigation', name: 'shell.overlay', id: 'marivo-credential-observer' }, localized(ctx, function Observer({ useSessions, useWorkspaces }) {
310
- const sessionId = useSessions((state) => state.current) ?? '';
310
+ const sessionId = useSessions((state) => state.ids.find((id) => state.byId[id]?.retainedBy.mainView)) ?? '';
311
311
  const workspaces = useWorkspaces((state) => state.items);
312
312
  const currentWorkspace = workspaces.find((item) => item.sessionIds.includes(sessionId))?.workspaceId ?? '';
313
313
  useEffect(() => model.session(sessionId), [sessionId]);
@@ -3,7 +3,7 @@ export function ownedInput(host, sessionId, workspaceId) {
3
3
  const owners = workspaces.items.filter((item) => item.sessionIds.includes(sessionId));
4
4
  if (!sessionId ||
5
5
  !workspaceId ||
6
- host.sessions.list.getSnapshot().current !== sessionId ||
6
+ !host.sessions.list.getSnapshot().byId[sessionId]?.retainedBy.mainView ||
7
7
  workspaces.phase !== 'ready' ||
8
8
  workspaces.state === 'error' ||
9
9
  owners.length !== 1 ||
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { CodeBlock, DisclosureRow, IconCodeOutline16, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { CodeBlock, DisclosureRow, IconCodeOutlineRegular, writeClipboard, } from '@deepseek-ai/dsh-client-ui-primitives';
3
3
  import { useMemo, useState } from 'react';
4
4
  import { useCopy } from "../i18n/context.js";
5
5
  import { pythonToolModel } from "./model.js";
@@ -29,7 +29,7 @@ export function PythonToolCard({ block, inspect }) {
29
29
  const ok = await writeClipboard(code).catch(() => false);
30
30
  setCopy({ code, ok });
31
31
  };
32
- return (_jsxs("div", { className: "mp-tool", "data-state": model.state, children: [_jsx("style", { children: styles }), _jsx(DisclosureRow, { icon: _jsx(IconCodeOutline16, {}), title: "marivo_python", open: open, expandable: true, expandOnRowClick: true, keepContentWhenOpen: true, onToggle: () => setOpen((value) => !value), collapsedContent: _jsxs(_Fragment, { children: [_jsx("span", { className: "mp-summary", children: model.summary }), _jsx("span", { className: "mp-state", children: t(`marivo.python.${model.state}`) })] }), children: open && (_jsxs("div", { className: "mp-body", children: [_jsxs("section", { "aria-label": t('marivo.python.input'), children: [_jsxs("div", { className: "mp-input-heading", children: [_jsx("h4", { children: t('marivo.python.input') }), model.code !== undefined && (_jsx("button", { type: "button", className: "mp-inspect", onClick: () => void copySource(), children: t(copy?.code === model.code && copy.ok
32
+ return (_jsxs("div", { className: "mp-tool", "data-state": model.state, children: [_jsx("style", { children: styles }), _jsx(DisclosureRow, { icon: _jsx(IconCodeOutlineRegular, { size: 16 }), title: "marivo_python", open: open, expandable: true, expandOnRowClick: true, keepContentWhenOpen: true, onToggle: () => setOpen((value) => !value), collapsedContent: _jsxs(_Fragment, { children: [_jsx("span", { className: "mp-summary", children: model.summary }), _jsx("span", { className: "mp-state", children: t(`marivo.python.${model.state}`) })] }), children: open && (_jsxs("div", { className: "mp-body", children: [_jsxs("section", { "aria-label": t('marivo.python.input'), children: [_jsxs("div", { className: "mp-input-heading", children: [_jsx("h4", { children: t('marivo.python.input') }), model.code !== undefined && (_jsx("button", { type: "button", className: "mp-inspect", onClick: () => void copySource(), children: t(copy?.code === model.code && copy.ok
33
33
  ? 'marivo.python.copied'
34
34
  : 'marivo.python.copy') }))] }), copy && copy.code === model.code && !copy.ok && (_jsx("p", { role: "status", className: "mp-notice", children: t('marivo.python.copy-failed') })), _jsx("div", { className: "mp-scroll", children: model.code === undefined ? (_jsx("pre", { children: model.input })) : (_jsx(CodeBlock, { code: `${model.code}\n`, lang: "python", className: "mp-code", copyLabel: t('marivo.python.copy'), copiedLabel: t('marivo.python.copied') })) })] }), _jsxs("dl", { className: "mp-facts", children: [model.datasources !== undefined && (_jsxs("div", { children: [_jsx("dt", { children: "datasources" }), _jsx("dd", { children: JSON.stringify(model.datasources) })] })), model.timeoutMs !== undefined && (_jsxs("div", { children: [_jsx("dt", { children: "timeoutMs" }), _jsxs("dd", { children: [model.timeoutMs, " ms"] })] })), model.facts.map(({ name, value }) => (_jsxs("div", { children: [_jsx("dt", { children: name }), _jsx("dd", { children: value })] }, name)))] }), model.notices.map((notice) => (_jsx("p", { className: "mp-notice", children: notice }, notice))), model.facts.some((fact) => fact.name === 'truncated' && fact.value === 'true') && (_jsx("p", { className: "mp-notice", children: t('marivo.python.truncated') })), ['stdout', 'stderr'].map((name) => model[name] === undefined ? null : (_jsxs("section", { "aria-label": name, children: [_jsx("h4", { children: name }), _jsx("pre", { className: "mp-scroll", children: model[name] })] }, name))), model.fallback !== undefined && (_jsxs("section", { "aria-label": t('marivo.python.output'), children: [_jsx("h4", { children: t('marivo.python.output') }), _jsx("pre", { className: "mp-scroll", children: model.fallback })] })), _jsxs("details", { children: [_jsx("summary", { children: t('marivo.python.raw') }), _jsx("h4", { children: t('marivo.python.input') }), _jsx("pre", { className: "mp-scroll", children: model.input }), model.output !== undefined && (_jsxs(_Fragment, { children: [_jsx("h4", { children: t('marivo.python.output') }), _jsx("pre", { className: "mp-scroll", children: model.output })] }))] }), inspect && (_jsx("div", { children: _jsx("button", { type: "button", className: "mp-inspect", onClick: inspect, children: t('marivo.python.inspect') }) }))] })) })] }));
35
35
  }
@@ -15,7 +15,7 @@ function parse(text) {
15
15
  /** Read only the frozen Harness call; never fetch results or infer execution from settlement. */
16
16
  export function pythonToolModel(block) {
17
17
  const done = 'kind' in block;
18
- const input = (done ? block.call?.argsRaw : block.argsRaw) ?? '';
18
+ const input = (done ? block.call?.argsRaw : block.phase === 'start' ? block.argsRaw : undefined) ?? '';
19
19
  const args = parse(input);
20
20
  const code = typeof args?.code === 'string' ? args.code : undefined;
21
21
  const output = done
@@ -31,6 +31,7 @@ export const inject = [
31
31
  'conversation',
32
32
  'sidebarRight',
33
33
  'sidebarRightTabs',
34
+ 'uiWorkspace',
34
35
  ];
35
36
  const labels = workspaceDirectories;
36
37
  /** Default browser integration. Layout belongs to Harness; mutable content belongs to occurrences. */
@@ -78,7 +79,7 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
78
79
  state.phase !== 'ready' ||
79
80
  state.state === 'error' ||
80
81
  workspaceFor(sessionId) !== workspaceId ||
81
- (foreground && ctx.sessions.list.getSnapshot().current !== sessionId))
82
+ (foreground && !ctx.sessions.list.getSnapshot().byId[sessionId]?.retainedBy.mainView))
82
83
  throw new Error('marivo.navigation.the-owning-session-or-workspace-changed-return-to-its');
83
84
  };
84
85
  const changed = (workspaceId, reportId) => {
@@ -213,7 +214,7 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
213
214
  }),
214
215
  };
215
216
  return (_jsxs("section", { className: "rt-page", "data-rt-tab": tab.id, "data-rt-panel": panelId, "data-rt-kind": page.target.kind, "data-rt-workspace": page.target.workspaceId, "data-rt-build": report.document?.buildId, "data-rt-current": page.target.kind === 'report' && !page.target.buildId ? 'true' : undefined, children: [_jsx("style", { children: catalogStyles + credentialStyles + rightTabStyles }), state.error ? (_jsx("p", { role: "alert", children: t(state.error) })) : (_jsxs(_Fragment, { children: [page.target.kind === 'reports' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "rt-heading-row", children: [_jsx("h2", { className: "rt-heading", children: t(labels[page.target.kind]) }), page.target.kind === 'reports' && (_jsx("button", { type: "button", disabled: catalog.loading, onClick: () => void page.refresh(), children: t('marivo.presentation.refresh') }))] }), _jsx("p", { className: "rt-caption", children: workspaces.find((w) => w.workspaceId === page.target.workspaceId)?.title ??
216
- page.target.workspaceId })] })), state.newer && (_jsxs("p", { role: "status", className: "rt-notice", children: [t('marivo.navigation.a-newer-version-exists-the-current-view-is-unchanged'), _jsx("button", { type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.view-new-version') })] })), state.notice && _jsx("p", { role: "status", children: t(state.notice) }), page.target.kind === 'reports' && (_jsx(ReportCatalogView, { model: page.catalog, reader: catalogReader, sessions: sessions, onOpenSession: (id) => ctx.sessions.open(id) })), page.target.kind === 'semantic' && (_jsx(SemanticBrowserPanel, { model: page.semantic, onAsk: canAsk ? () => page.semantic.addToQuestion(ctx, page.sessionId) : undefined, workspaces: workspaces })), page.target.kind === 'datasources' && (_jsx(CredentialPanel, { model: page.datasources, workspaces: workspaces, onRefresh: () => void page.refresh() })), page.target.kind === 'report' && (_jsxs(_Fragment, { children: [report.editing && (_jsxs("div", { className: "rt-toolbar", role: "toolbar", "aria-label": t('marivo.presentation.report-editing-actions'), children: [_jsx("button", { type: "button", disabled: report.saving || !report.editing.undo.length, onClick: () => page.reader.undoEdit(), children: t('marivo.presentation.undo') }), _jsx("button", { type: "button", disabled: report.saving || !report.editing.redo.length, onClick: () => page.reader.redoEdit(), children: t('marivo.presentation.redo') }), _jsx("button", { type: "button", disabled: report.saving, onClick: () => act(page, () => save(page, tab)), children: report.saving
217
+ page.target.workspaceId })] })), state.newer && (_jsxs("p", { role: "status", className: "rt-notice", children: [t('marivo.navigation.a-newer-version-exists-the-current-view-is-unchanged'), _jsx("button", { type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.view-new-version') })] })), state.notice && _jsx("p", { role: "status", children: t(state.notice) }), page.target.kind === 'reports' && (_jsx(ReportCatalogView, { model: page.catalog, reader: catalogReader, sessions: sessions, onOpenSession: (id) => ctx.uiWorkspace.openSession(id) })), page.target.kind === 'semantic' && (_jsx(SemanticBrowserPanel, { model: page.semantic, onAsk: canAsk ? () => page.semantic.addToQuestion(ctx, page.sessionId) : undefined, workspaces: workspaces })), page.target.kind === 'datasources' && (_jsx(CredentialPanel, { model: page.datasources, workspaces: workspaces, onRefresh: () => void page.refresh() })), page.target.kind === 'report' && (_jsxs(_Fragment, { children: [report.editing && (_jsxs("div", { className: "rt-toolbar", role: "toolbar", "aria-label": t('marivo.presentation.report-editing-actions'), children: [_jsx("button", { type: "button", disabled: report.saving || !report.editing.undo.length, onClick: () => page.reader.undoEdit(), children: t('marivo.presentation.undo') }), _jsx("button", { type: "button", disabled: report.saving || !report.editing.redo.length, onClick: () => page.reader.redoEdit(), children: t('marivo.presentation.redo') }), _jsx("button", { type: "button", disabled: report.saving, onClick: () => act(page, () => save(page, tab)), children: report.saving
217
218
  ? t('marivo.presentation.saving')
218
219
  : t('marivo.presentation.save-edits') }), _jsx("button", { type: "button", disabled: report.saving, onClick: () => {
219
220
  if (!page.reader.dirty ||
@@ -221,7 +222,7 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
221
222
  page.reader.cancelEdit();
222
223
  }, children: t('marivo.presentation.cancel-edits') }), _jsx("span", { children: page.reader.dirty
223
224
  ? t('marivo.presentation.unsaved-edits')
224
- : t('marivo.presentation.edit-mode') })] })), report.editError && _jsx("p", { role: "alert", children: t(report.editError) }), report.notice && _jsx("p", { role: "status", children: t(report.notice) }), report.publicationUrl && (_jsx("p", { children: _jsx("a", { href: report.publicationUrl, target: "_blank", rel: "noopener noreferrer", children: t('marivo.presentation.open-published-report') }) })), report.loading && _jsx("p", { role: "status", children: t('marivo.navigation.loading-report') }), report.error && (_jsxs("div", { role: "alert", children: [_jsx("p", { children: t(report.error) }), _jsx("button", { type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.reload-report') })] })), report.historyOpen && (_jsx(ReportHistoryPanel, { state: report, model: historyModel, sessions: sessions, onOpenSession: (id) => ctx.sessions.open(id) })), report.document && (_jsx(HostPresentationReader, { document: report.document, closeSourceOnNavigate: true, viewMemory: page.viewMemory, exportActions: {
225
+ : t('marivo.presentation.edit-mode') })] })), report.editError && _jsx("p", { role: "alert", children: t(report.editError) }), report.notice && _jsx("p", { role: "status", children: t(report.notice) }), report.publicationUrl && (_jsx("p", { children: _jsx("a", { href: report.publicationUrl, target: "_blank", rel: "noopener noreferrer", children: t('marivo.presentation.open-published-report') }) })), report.loading && _jsx("p", { role: "status", children: t('marivo.navigation.loading-report') }), report.error && (_jsxs("div", { role: "alert", children: [_jsx("p", { children: t(report.error) }), _jsx("button", { type: "button", onClick: () => void page.refresh(), children: t('marivo.navigation.reload-report') })] })), report.historyOpen && (_jsx(ReportHistoryPanel, { state: report, model: historyModel, sessions: sessions, onOpenSession: (id) => ctx.uiWorkspace.openSession(id) })), report.document && (_jsx(HostPresentationReader, { document: report.document, closeSourceOnNavigate: true, viewMemory: page.viewMemory, exportActions: {
225
226
  downloadFullReport: () => void page.reader.downloadDisplayed(),
226
227
  publishing: report.publishingName
227
228
  ? {
@@ -257,7 +258,7 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
257
258
  function Body({ sessionId, useTabInfo, useWorkspaces, useSessions }) {
258
259
  const t = useCopy();
259
260
  const { tab, panel } = useTabInfo();
260
- const workspaces = useWorkspaces((s) => s.items), sessions = useSessions((s) => s.byId), currentSession = useSessions((s) => s.current), workspaceReady = useWorkspaces((s) => s.phase === 'ready' && s.state !== 'error');
261
+ const workspaces = useWorkspaces((s) => s.items), sessions = useSessions((s) => s.byId), currentSession = useSessions((s) => s.ids.find((id) => s.byId[id]?.retainedBy.mainView)), workspaceReady = useWorkspaces((s) => s.phase === 'ready' && s.state !== 'error');
261
262
  const [page, setPage] = useState(), [error, setError] = useState('');
262
263
  useEffect(() => {
263
264
  if (tab.signal.aborted)
@@ -383,7 +384,9 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
383
384
  const bind = () => {
384
385
  if (!ctx.connection.generation.getSnapshot())
385
386
  return stopFeeds();
386
- const current = ctx.sessions.list.getSnapshot().current;
387
+ const current = ctx.sessions.list
388
+ .getSnapshot()
389
+ .ids.find((id) => ctx.sessions.list.getSnapshot().byId[id]?.retainedBy.mainView);
387
390
  const workspaces = ctx.workspaces.list.getSnapshot();
388
391
  if (workspaces.phase !== 'ready' || workspaces.state === 'error')
389
392
  return stopFeeds();
@@ -414,14 +417,14 @@ export function installRightTabs(ctx, { diagnostics = false } = {}) {
414
417
  check(id, workspaceId);
415
418
  }
416
419
  catch (error) {
417
- if (ctx.sessions.list.getSnapshot().current === id)
420
+ if (ctx.sessions.list.getSnapshot().byId[id]?.retainedBy.mainView)
418
421
  fail(error);
419
422
  return;
420
423
  }
421
424
  changed(workspaceId, reportId);
422
425
  // Publication invalidation is Workspace-wide. Foreground navigation
423
426
  // alone is gated, and background deliveries are never queued.
424
- if (ctx.sessions.list.getSnapshot().current !== id)
427
+ if (!ctx.sessions.list.getSnapshot().byId[id]?.retainedBy.mainView)
425
428
  return;
426
429
  try {
427
430
  navigate(id, { kind: 'report', workspaceId, reportId, buildId }, ctx.sidebarRight, true);
@@ -2,9 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { localized } from "../i18n/host.js";
3
3
  import { PythonSettingsCard } from "./card.js";
4
4
  export function installSettings(ctx) {
5
- const scope = ctx.settingsScope.bind({ namespace: 'dsh-data-analysis' });
5
+ const scope = ctx.configForms.get('dsh-data-analysis');
6
6
  const Card = localized(ctx, PythonSettingsCard);
7
- ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({ name: 'settings.plugin.item', key: 'dsh-data-analysis', locale: 'marivo.settings' }, function SettingsCard() {
7
+ ctx.effect(() => ctx.configForms.whileServed(['dsh-data-analysis'], () => ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({
8
+ name: 'plugins.bundle.config',
9
+ key: '@chengxianglibra/dsh-data-analysis',
10
+ locale: 'marivo.settings',
11
+ }, function SettingsCard() {
8
12
  return _jsx(Card, { scope: scope });
9
- }));
13
+ }))));
10
14
  }