@rho-workspace/rho 0.5.3 → 0.5.5
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.
- package/README.md +24 -5
- package/dist/browser-access.js +3 -2
- package/dist/capabilities.d.ts +29 -2
- package/dist/capabilities.js +79 -2
- package/dist/cli.js +237 -99
- package/dist/contract.generated.d.ts +441 -16
- package/dist/contract.generated.js +1 -1
- package/dist/contracts/owners.generated.js +459 -10
- package/dist/conversation-presentation.d.ts +3 -1
- package/dist/conversation-presentation.js +16 -6
- package/dist/conversations.d.ts +10 -6
- package/dist/conversations.js +17 -13
- package/dist/discovery.d.ts +1 -0
- package/dist/discovery.js +3 -3
- package/dist/extension-contract.d.ts +99 -0
- package/dist/extension-contract.js +52 -0
- package/dist/extension-directory.d.ts +9 -0
- package/dist/extension-directory.js +11 -0
- package/dist/extension-worker.d.ts +1 -0
- package/dist/extension-worker.js +36 -0
- package/dist/extensions.d.ts +157 -0
- package/dist/extensions.js +349 -0
- package/dist/installs.d.ts +11 -0
- package/dist/installs.js +179 -0
- package/dist/plugin-cli.d.ts +1 -0
- package/dist/plugin-cli.js +139 -0
- package/dist/plugin-inventory.json +18 -0
- package/dist/plugins.d.ts +3 -3
- package/dist/project-notes.js +2 -0
- package/dist/project-work.d.ts +3 -1
- package/dist/project-work.js +15 -2
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.js +11 -7
- package/dist/screen-check-skill.d.ts +2 -0
- package/dist/screen-check-skill.js +39 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.js +105 -15
- package/dist/web/.vite/manifest.json +65 -65
- package/dist/web/assets/{canvas-BXhC8RDZ.js → canvas-D8rDdfDz.js} +1 -1
- package/dist/web/assets/choices-b0XhrGQV.js +1 -0
- package/dist/web/assets/{console-e1P5qYQ5.js → console-BSjLGe0T.js} +1 -1
- package/dist/web/assets/{dist-Dd3iAGFy.js → dist-Buk0lwzI.js} +2 -2
- package/dist/web/assets/dist-Cw8hRIlT.js +1 -0
- package/dist/web/assets/dist-Tzoe_zm-.js +3 -0
- package/dist/web/assets/{dist-DEorbOW0.js → dist-foH9IKc6.js} +1 -1
- package/dist/web/assets/explore-in-BVUy3ZW_.js +1 -0
- package/dist/web/assets/graph-1OdIJ9l7.js +2 -0
- package/dist/web/assets/index-CsC3sTRq.css +1 -0
- package/dist/web/assets/{index-BYuKpyoi.js → index-CxmItXqa.js} +220 -121
- package/dist/web/assets/{notes-meaning-B3HFbh84.js → notes-meaning-pVbbQUGP.js} +1 -1
- package/dist/web/assets/overview-B9UtVJ32.js +6 -0
- package/dist/web/assets/preview-CksD_5KS.js +1 -0
- package/dist/web/assets/{review-BVh9jWMB.js → review-CMxQlQ-C.js} +1 -1
- package/dist/web/assets/{skills-DPaEYkln.js → skills-BFNSyrnD.js} +1 -1
- package/dist/web/assets/{skills-work-dvjfy_tu.js → skills-work-BGPSnF_I.js} +1 -1
- package/dist/web/assets/{studio-process-BYm8mjRJ.js → studio-process-CsRoTHOh.js} +2 -2
- package/dist/web/assets/{timeline-events-CDZ7wiJX.js → timeline-events-BbuuFWHV.js} +1 -1
- package/dist/web/assets/{work-turns-BeCt303c.js → work-turns-BvJlIMmA.js} +1 -1
- package/dist/web/assets/workspace-CWlkjLNx.js +5 -0
- package/dist/web/composition.json +80 -80
- package/dist/web/index.html +3 -3
- package/dist/web/renderer.json +1 -1
- package/dist/web-assets.d.ts +2 -1
- package/dist/web-assets.js +3 -2
- package/dist/work-contract.js +2 -2
- package/docs/FRONTEND.md +30 -14
- package/docs/HEADLESS.md +60 -14
- package/docs/PLUGIN-AUTHORING.md +4 -0
- package/docs/THIRD-PARTY-PLUGINS.md +190 -0
- package/example/pa14-qs/.rho/rho/conversations/c-67f77cfebaba2cea.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-91aa2e46b1ac1a6c.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-fa24dfd80dfe4bcb.json +1 -1
- package/node_modules/@hono/node-server/dist/index.js +11 -0
- package/node_modules/@hono/node-server/dist/index.mjs +11 -0
- package/node_modules/@hono/node-server/dist/listener.js +11 -0
- package/node_modules/@hono/node-server/dist/listener.mjs +11 -0
- package/node_modules/@hono/node-server/dist/request.js +11 -0
- package/node_modules/@hono/node-server/dist/request.mjs +11 -0
- package/node_modules/@hono/node-server/dist/server.js +11 -0
- package/node_modules/@hono/node-server/dist/server.mjs +11 -0
- package/node_modules/@hono/node-server/dist/vercel.js +11 -0
- package/node_modules/@hono/node-server/dist/vercel.mjs +11 -0
- package/node_modules/@hono/node-server/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-agent/README.md +42 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +14 -3
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.js +2 -2
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.d.ts +6 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.js +174 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.d.ts +82 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.js +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +73 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.js +190 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.d.ts +11 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.js +7 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.d.ts +373 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +5 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.d.ts +33 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.js +3 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.d.ts +14 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.js +2 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.d.ts +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.js +37 -0
- package/node_modules/@rho-workspace/plugin-agent/package.json +5 -1
- package/node_modules/@rho-workspace/plugin-annotations/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-documents/README.md +4 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.js +1 -1
- package/node_modules/@rho-workspace/plugin-documents/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/README.md +71 -10
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.d.ts +2 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.js +4 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +5 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +7 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +55 -8
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.d.ts +9 -6
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.js +18 -41
- package/node_modules/@rho-workspace/plugin-kernel/dist/operations.js +5 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.d.ts +39 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.js +661 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.js +10 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.d.ts +38 -4
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.js +194 -97
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +10 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +79 -18
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +115 -45
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.d.ts +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.js +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.d.ts +63 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.js +344 -0
- package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +6 -3
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +6 -3
- package/node_modules/express-rate-limit/dist/index.cjs +1 -1
- package/node_modules/express-rate-limit/dist/index.mjs +1 -1
- package/node_modules/express-rate-limit/package.json +1 -1
- package/node_modules/ip-address/dist/ipv6.js +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/browsers.json +52 -0
- package/node_modules/playwright-core/cli.js +22 -0
- package/node_modules/playwright-core/index.js +17 -0
- package/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/playwright-core/lib/bootstrap.js +149 -0
- package/node_modules/playwright-core/lib/coreBundle.js +78041 -0
- package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/server/electron/loader.js +123 -0
- package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
- package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
- package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/playwright-core/lib/tools/cli-client/cli.js +7 -0
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +808 -0
- package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/playwright-core/lib/tools/cli-client/output.js +352 -0
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +422 -0
- package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +261 -0
- package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
- package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/playwright-core/lib/utilsBundle.js +86078 -0
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DK72OMRi.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Cn0mmDuv.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BEB2e8M3.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-D-g2vrvf.js +184 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-B794rD2Z.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.C8BeHuxd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.oNry3Hjv.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CYwC_gH1.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/playwright-core/package.json +34 -0
- package/package.json +13 -7
- package/protocol/index.json +75 -21
- package/protocol/operations/agent.sessions.list.json +100 -0
- package/protocol/operations/agent.sessions.read.json +266 -0
- package/protocol/operations/conversation.settings.check.json +147 -4
- package/protocol/operations/conversation.settings.read.json +145 -3
- package/protocol/operations/conversation.settings.update.json +166 -4
- package/protocol/operations/conversation.start.json +2 -1
- package/protocol/operations/extension.call.json +132 -0
- package/protocol/operations/extension.content.json +78 -0
- package/protocol/operations/extension.describe.json +195 -0
- package/protocol/operations/extension.install.json +198 -0
- package/protocol/operations/extension.list.json +123 -0
- package/protocol/operations/extension.manage.json +55 -0
- package/protocol/operations/extension.read.json +112 -0
- package/protocol/operations/kernel.interrupt.json +20 -0
- package/protocol/operations/kernel.read.json +30 -0
- package/protocol/operations/kernel.run.json +36 -1
- package/protocol/operations/kernel.stop.json +10 -0
- package/protocol/operations/project.work.read.json +3 -2
- package/protocol/operations/session.grant.json +9 -0
- package/scripts/browser-checks.mjs +53 -0
- package/scripts/screen-check.mjs +193 -0
- package/scripts/third-party-acceptance.mjs +77 -0
- package/vendor/composition.json +111 -111
- package/vendor/rho-workspace-plugin-agent-0.4.29.tgz +0 -0
- package/vendor/rho-workspace-plugin-annotations-0.5.2.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.11.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
- package/web/theme.css +17 -2
- package/dist/web/assets/choices-B-y2OYvE.js +0 -1
- package/dist/web/assets/dist-CBM2oq6g.js +0 -1
- package/dist/web/assets/dist-CH56yNDn.js +0 -3
- package/dist/web/assets/explore-in-muWXOGOY.js +0 -1
- package/dist/web/assets/graph-CK-df5lh.js +0 -2
- package/dist/web/assets/index-DBd77u4a.css +0 -1
- package/dist/web/assets/overview-D1Ev2OC_.js +0 -6
- package/dist/web/assets/preview-HbmQKUGY.js +0 -1
- package/dist/web/assets/workspace-d5Hw_ClZ.js +0 -5
- package/vendor/rho-workspace-plugin-agent-0.4.27.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.10.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.2.tgz +0 -0
|
@@ -10,6 +10,16 @@ const Shell = createContext({});
|
|
|
10
10
|
export const ShellProvider = Shell.Provider;
|
|
11
11
|
export const useShell = () => useContext(Shell);
|
|
12
12
|
export const withShell = (shell, node) => shell ? _jsx(ShellProvider, { value: shell, children: node }) : node;
|
|
13
|
+
export const environmentOf = (session) => {
|
|
14
|
+
const program = session.program;
|
|
15
|
+
if (!program || /^([A-Za-z]:)?[\\/]/.test(program))
|
|
16
|
+
return null;
|
|
17
|
+
return program.match(/^(.+?)[\\/](?:bin|Scripts)[\\/][^\\/]+$/)?.[1] ?? program;
|
|
18
|
+
};
|
|
19
|
+
export const versionOf = (session) => {
|
|
20
|
+
const version = session.version?.replace(/^R version (\S+).*$/, 'R $1') ?? (session.language === 'r' ? 'R' : 'Python'), environment = environmentOf(session);
|
|
21
|
+
return environment ? version + ' · ' + environment : version;
|
|
22
|
+
};
|
|
13
23
|
export const clock = (ms, seconds = false) => new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', ...(seconds ? { second: '2-digit' } : {}), hourCycle: 'h23' });
|
|
14
24
|
export function durationText(ms) {
|
|
15
25
|
if (ms < 10_000)
|
|
@@ -1,9 +1,37 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type GraphNode, type Making, type ValueGraph } from './value-graph.js';
|
|
1
3
|
import { type Cell } from './ui-notebook.js';
|
|
2
4
|
import { type KernelShell, type UiClient, type UiSnapshot } from './ui-base.js';
|
|
3
5
|
type Language = Cell['language'];
|
|
6
|
+
export declare const lineageViewLimits: {
|
|
7
|
+
nodes: number;
|
|
8
|
+
results: number;
|
|
9
|
+
wide: number;
|
|
10
|
+
};
|
|
11
|
+
type Facts = Map<string, {
|
|
12
|
+
type: string;
|
|
13
|
+
size?: string | null;
|
|
14
|
+
summary?: string | null;
|
|
15
|
+
}>;
|
|
16
|
+
interface LineageProps {
|
|
17
|
+
graph: ValueGraph;
|
|
18
|
+
language: Language;
|
|
19
|
+
selected?: string | null;
|
|
20
|
+
onSelect?: (id: string | null) => void;
|
|
21
|
+
onGo?: (making: Making) => void;
|
|
22
|
+
canGo?: (making: Making) => boolean;
|
|
23
|
+
onObject?: (name: string) => void;
|
|
24
|
+
onPath?: (path: string) => void;
|
|
25
|
+
folders?: boolean;
|
|
26
|
+
facts?: Facts;
|
|
27
|
+
code?: boolean;
|
|
28
|
+
more?: (node: GraphNode) => ReactNode;
|
|
29
|
+
hint?: string;
|
|
30
|
+
pinned?: boolean;
|
|
31
|
+
}
|
|
4
32
|
export declare function FlowPane({ shell, ...props }: Parameters<typeof FlowBody>[0] & {
|
|
5
33
|
shell?: KernelShell;
|
|
6
|
-
}): string | number | bigint | boolean | Iterable<
|
|
34
|
+
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
|
|
7
35
|
declare function FlowBody({ client, snapshot, onObject, onPath, onRemote }: {
|
|
8
36
|
client: UiClient;
|
|
9
37
|
snapshot: UiSnapshot;
|
|
@@ -11,8 +39,14 @@ declare function FlowBody({ client, snapshot, onObject, onPath, onRemote }: {
|
|
|
11
39
|
onPath?: (path: string) => void;
|
|
12
40
|
onRemote?: (placeId: string, path: string) => void;
|
|
13
41
|
}): import("react").JSX.Element;
|
|
14
|
-
export declare
|
|
15
|
-
|
|
16
|
-
|
|
42
|
+
export declare function ScriptFlowView({ shell, ...props }: Omit<LineageProps, 'code'> & {
|
|
43
|
+
shell?: KernelShell;
|
|
44
|
+
}): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react").JSX.Element | null | undefined;
|
|
45
|
+
export declare const innerFlowLimits: {
|
|
46
|
+
values: number;
|
|
17
47
|
};
|
|
48
|
+
export declare function CellScriptFlow({ code, language }: {
|
|
49
|
+
code: string;
|
|
50
|
+
language: Language;
|
|
51
|
+
}): import("react").JSX.Element | null;
|
|
18
52
|
export {};
|
|
@@ -1,17 +1,172 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { scriptFlow, scriptFlowLimits } from './script-flow.js';
|
|
4
|
+
import { graphPath, graphRows, scriptGraph, sessionGraph } from './value-graph.js';
|
|
4
5
|
import { Icon } from './ui-icons.js';
|
|
5
6
|
import { at, Code, languageName, languages, latestSessions, useAuthors, useKernel, usePlot, versionOf } from './ui-notebook.js';
|
|
6
7
|
import { whereOf } from './ui-servers.js';
|
|
7
8
|
import { withShell } from './ui-base.js';
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
// The protected resource is the reader's page: at most `nodes` values and files are drawn at once
|
|
10
|
+
// (those nearest the results first; the rest are counted), and the results bar names at most
|
|
11
|
+
// `results` results until the reader asks for the rest.
|
|
12
|
+
export const lineageViewLimits = { nodes: 80, results: 8, wide: 6 };
|
|
13
|
+
// A folder's path ends in /, and keeps it as its name.
|
|
14
|
+
const folder = (path) => path.endsWith('/');
|
|
15
|
+
const fileName = (path) => (path.replace(/\/$/, '').split('/').at(-1) ?? path) + (folder(path) ? '/' : '');
|
|
16
|
+
const isFile = (n) => n.id.startsWith('in:') || n.id.startsWith('out:');
|
|
17
|
+
// A long name keeps its first word and its end, where the files of one analysis differ.
|
|
18
|
+
const short = (name, most = 24) => {
|
|
19
|
+
if (name.length <= most)
|
|
20
|
+
return name;
|
|
21
|
+
const head = (name.match(/^[^_\-. ]+/)?.[0] ?? name).slice(0, 10);
|
|
22
|
+
let tail = name.slice(-(most - head.length - 1));
|
|
23
|
+
const cut = tail.search(/[_\-. ]/);
|
|
24
|
+
if (cut > 0 && cut < tail.length - 4)
|
|
25
|
+
tail = tail.slice(cut);
|
|
26
|
+
return head + '…' + tail;
|
|
27
|
+
};
|
|
28
|
+
const labelOf = (n) => short(isFile(n) ? fileName(n.name) : n.name);
|
|
29
|
+
// What a reader is told a node is: what the code says it is, else its name.
|
|
30
|
+
const titleOf = (n) => n.means ?? labelOf(n);
|
|
31
|
+
const plural = (count, one, many = one + 's') => `${count} ${count === 1 ? one : many}`;
|
|
32
|
+
const linesText = (m) => m.from === m.to ? 'line ' + m.from : `lines ${m.from}–${m.to}`;
|
|
33
|
+
// What is drawn: what leads to the chosen result, or to any result; past the limit, what is nearest
|
|
34
|
+
// the results.
|
|
35
|
+
function drawnOf(graph, result) {
|
|
36
|
+
const behind = result ? [...graphPath(graph, result).up] : [...graph.nodes.keys()].filter(id => !graph.others.includes(id));
|
|
37
|
+
if (behind.length <= lineageViewLimits.nodes)
|
|
38
|
+
return { ids: new Set(behind), cut: 0 };
|
|
39
|
+
const ids = new Set(), queue = result ? [result] : [...graph.results];
|
|
40
|
+
while (queue.length && ids.size < lineageViewLimits.nodes) {
|
|
41
|
+
const id = queue.shift();
|
|
42
|
+
if (ids.has(id))
|
|
43
|
+
continue;
|
|
44
|
+
ids.add(id);
|
|
45
|
+
queue.push(...graph.nodes.get(id).parents);
|
|
46
|
+
}
|
|
47
|
+
return { ids, cut: behind.length - ids.size };
|
|
48
|
+
}
|
|
49
|
+
function Lineage({ graph, language, selected: given, onSelect, onGo, canGo, onObject, onPath, folders = true, facts, code = true, more, hint, pinned = false }) {
|
|
50
|
+
const [own, setOwn] = useState(null);
|
|
51
|
+
const selected = (given !== undefined ? given : own) ?? null, chosen = selected && graph.nodes.has(selected) ? graph.nodes.get(selected) : null;
|
|
52
|
+
const select = (id) => { if (given === undefined)
|
|
53
|
+
setOwn(id); onSelect?.(id); };
|
|
54
|
+
const [result, setResult] = useState(undefined), [everyResult, setEveryResult] = useState(false);
|
|
55
|
+
const wide = useMemo(() => graph.results.length > 1 && graphRows(graph, drawnOf(graph, null).ids).some(row => row.length > lineageViewLimits.wide), [graph]);
|
|
56
|
+
const traced = result === undefined ? (wide ? graph.results[0] : null) : result && graph.nodes.has(result) ? result : null;
|
|
57
|
+
const drawn = useMemo(() => drawnOf(graph, traced), [graph, traced]);
|
|
58
|
+
const rows = useMemo(() => graphRows(graph, drawn.ids), [graph, drawn]);
|
|
59
|
+
const [pointed, setPointed] = useState(null);
|
|
60
|
+
const focus = pointed && graph.nodes.has(pointed) ? pointed : chosen?.id ?? null;
|
|
61
|
+
const path = useMemo(() => focus ? graphPath(graph, focus) : null, [graph, focus]);
|
|
62
|
+
const canvas = useRef(null);
|
|
63
|
+
const edges = useEdges(canvas, graph, drawn.ids, rows);
|
|
64
|
+
// A value chosen elsewhere (at the cursor) is drawn: the graph shows every result again if the
|
|
65
|
+
// one traced does not lead through it, and the value comes into view.
|
|
66
|
+
useEffect(() => { if (chosen && traced && !drawn.ids.has(chosen.id) && !graph.others.includes(chosen.id))
|
|
67
|
+
setResult(null); }, [chosen, traced, drawn, graph]);
|
|
68
|
+
useEffect(() => { if (chosen)
|
|
69
|
+
canvas.current?.querySelector(`[data-node="${CSS.escape(chosen.id)}"]`)?.scrollIntoView({ block: 'nearest' }); }, [chosen?.id]);
|
|
70
|
+
const choose = (id, again = false) => {
|
|
71
|
+
if (id === chosen?.id && !again) {
|
|
72
|
+
select(null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
select(id);
|
|
76
|
+
const made = graph.nodes.get(id)?.made[0];
|
|
77
|
+
if (made && onGo && !code && (!canGo || canGo(made)))
|
|
78
|
+
onGo(made);
|
|
79
|
+
};
|
|
80
|
+
const trace = (id) => { setResult(id); if (id)
|
|
81
|
+
choose(id, true); };
|
|
82
|
+
if (graph.tooLong)
|
|
83
|
+
return _jsxs("p", { className: "nb-empty-line", children: ["This script is larger than ", Math.round(scriptFlowLimits.bytes / 1024), " KB, so how its values are made is not read."] });
|
|
84
|
+
if (!graph.nodes.size)
|
|
85
|
+
return _jsx("p", { className: "nb-empty-line", children: "Nothing here makes a value or writes a file that can be read from the code." });
|
|
86
|
+
const results = everyResult ? graph.results : graph.results.slice(0, lineageViewLimits.results);
|
|
87
|
+
const state = (id) => !path ? '' : id === focus ? ' here' : path.up.has(id) ? ' from' : path.down.has(id) ? ' into' : ' dim';
|
|
88
|
+
return _jsxs("div", { className: "lin", children: [graph.results.length > 1 && _jsxs("div", { className: "lin-results", role: "group", "aria-label": "Which result to trace", children: [_jsx("span", { children: "Results" }), _jsx("button", { "aria-pressed": !traced, onClick: () => trace(null), children: "All" }), [...results, ...traced && !results.includes(traced) ? [traced] : []].map(id => { const n = graph.nodes.get(id); return _jsxs("button", { "aria-pressed": id === traced, className: n.kind, title: n.name, onClick: () => trace(id), children: [_jsx(Icon, { name: n.kind === 'plot' ? 'chart' : isFile(n) ? 'file' : 'braces', size: 11, stroke: 1.8 }), _jsx("span", { children: titleOf(n) })] }, id); }), results.length < graph.results.length && _jsxs("button", { className: "text-button", onClick: () => setEveryResult(true), children: [graph.results.length - results.length, " more"] })] }), _jsxs("div", { className: 'lin-main' + (pinned ? ' pinned' : ''), children: [_jsx(Detail, { graph: graph, node: chosen, language: language, facts: facts, code: code, onGo: onGo, canGo: canGo, onObject: onObject, onPath: onPath, folders: folders, onChoose: choose, more: more, hint: hint }), _jsxs("div", { className: "lin-canvas", ref: canvas, onPointerLeave: () => setPointed(null), children: [_jsx("svg", { className: "lin-edges", "aria-hidden": "true", children: edges.map(e => _jsx("path", { d: e.d, className: !path ? '' : path.up.has(e.from) && path.up.has(e.to) ? 'from' : path.down.has(e.from) && path.down.has(e.to) ? 'into' : 'off' }, e.key)) }), rows.map((row, i) => _jsx("div", { className: "lin-row", children: row.map(id => {
|
|
89
|
+
const n = graph.nodes.get(id), fact = facts?.get(n.name);
|
|
90
|
+
return _jsxs("button", { "data-node": id, className: 'lin-node ' + n.kind + state(id) + (id === chosen?.id ? ' chosen' : ''), "aria-pressed": id === chosen?.id, title: [n.means, n.name, fact && `${fact.type}${fact.size ? ' · ' + fact.size : ''}`].filter(Boolean).join('\n'), onClick: () => choose(id), onPointerEnter: () => setPointed(id), onFocus: () => setPointed(id), onBlur: () => setPointed(null), children: [_jsx(Icon, { name: n.kind === 'plot' ? 'chart' : isFile(n) ? 'file' : 'braces', size: 12, stroke: 1.8 }), _jsx("span", { className: "lin-text", children: n.means
|
|
91
|
+
? _jsxs(_Fragment, { children: [_jsx("span", { className: "lin-title", children: n.means }), _jsx("span", { className: "lin-name lin-sub", children: labelOf(n) })] })
|
|
92
|
+
: _jsx("span", { className: "lin-name", children: labelOf(n) }) })] }, id);
|
|
93
|
+
}) }, i))] })] }), drawn.cut > 0 && _jsxs("p", { className: "nb-note", children: [plural(drawn.cut, 'value'), " further from the results ", drawn.cut === 1 ? 'is' : 'are', " not drawn. Choose a result, or a value, to follow it."] }), graph.others.length > 0 && _jsxs("p", { className: "nb-note lin-others", children: ["Leading to no result: ", graph.others.map(id => _jsx("button", { className: 'lin-chip' + (id === traced ? ' chosen' : ''), title: 'Draw what leads to ' + graph.nodes.get(id).name, onClick: () => trace(id), children: _jsx("span", { children: titleOf(graph.nodes.get(id)) }) }, id))] }), graph.quiet > 0 && _jsxs("p", { className: "nb-note", children: [plural(graph.quiet, 'statement makes', 'statements make'), " nothing and ", graph.quiet === 1 ? 'is' : 'are', " not drawn: prints, checks and library calls."] }), graph.cut > 0 && _jsxs("p", { className: "nb-note", children: ["The last ", plural(graph.cut, 'step is', 'steps are'), " not read."] })] });
|
|
94
|
+
}
|
|
95
|
+
// The value chosen: what it is, how it was made and where, what it comes from and uses, what it
|
|
96
|
+
// leads to and which results it reaches. With nothing chosen, how much there is.
|
|
97
|
+
function Detail({ graph, node, language, facts, code, onGo, canGo, onObject, onPath, folders, onChoose, more, hint }) {
|
|
98
|
+
if (!node) {
|
|
99
|
+
const values = [...graph.nodes.values()].filter(n => n.kind === 'value').length, sources = graph.inputs.length;
|
|
100
|
+
const explained = [...graph.nodes.values()].filter(n => n.kind === 'value' && n.means).length;
|
|
101
|
+
return _jsxs("section", { className: "lin-detail empty", children: [_jsxs("p", { className: "lin-sum", children: [_jsx("b", { children: sources }), " ", sources === 1 ? 'source' : 'sources', _jsx("span", { "aria-hidden": "true", children: "\u2192" }), _jsx("b", { children: values }), " ", values === 1 ? 'value' : 'values', _jsx("span", { "aria-hidden": "true", children: "\u2192" }), _jsx("b", { children: graph.results.length }), " ", graph.results.length === 1 ? 'result' : 'results'] }), _jsxs("p", { children: [explained, " of ", values, " ", values === 1 ? 'value has' : 'values have', " a meaning written in the code."] }), _jsxs("p", { children: ["Choose a value to see what it comes from and what comes of it.", hint ? ' ' + hint : ''] })] });
|
|
102
|
+
}
|
|
103
|
+
const { down } = graphPath(graph, node.id), fact = facts?.get(node.name);
|
|
104
|
+
const reaches = graph.results.filter(r => r !== node.id && down.has(r) && !node.children.includes(r));
|
|
105
|
+
const chips = (ids, kind) => ids.map(id => { const n = graph.nodes.get(id); return _jsx("button", { className: 'lin-chip ' + kind, title: [n.means, n.name].filter(Boolean).join('\n'), onClick: () => onChoose(id), children: _jsx("span", { children: titleOf(n) }) }, id); });
|
|
106
|
+
const what = node.kind === 'input' ? (isFile(node) ? 'Data file' : 'Value already in the session') : node.kind === 'output' ? 'Written file' : node.kind === 'plot' ? 'Plot' : 'Value';
|
|
107
|
+
const file = isFile(node), openable = file && onPath && !(folder(node.name) && !folders);
|
|
108
|
+
// The statements of one cell that make it are named once, with that cell.
|
|
109
|
+
const groups = [];
|
|
110
|
+
for (const m of node.made) {
|
|
111
|
+
const last = groups.at(-1);
|
|
112
|
+
if (m.cell && last?.[0].cell?.cellId === m.cell.cellId)
|
|
113
|
+
last.push(m);
|
|
114
|
+
else
|
|
115
|
+
groups.push([m]);
|
|
116
|
+
}
|
|
117
|
+
const made = groups.map((group, i) => {
|
|
118
|
+
const m = group[0], where = m.cell ? `${languageName[language]} ${m.cell.number}` : linesText(m), go = onGo && (!canGo || canGo(m));
|
|
119
|
+
const whole = group.length === 1 && m.from === 1 && m.to === m.code.split('\n').length;
|
|
120
|
+
return _jsxs("span", { className: "lin-made", children: [i === 0 ? (node.kind === 'output' ? 'Written ' : 'Made ') + (m.cell ? 'by ' : 'at ') : 'changed ' + (m.cell ? 'by ' : 'at '), go ? _jsx("button", { className: "text-button", title: m.cell ? 'Show this cell in the Notebook' : 'Go to ' + linesText(m), onClick: () => onGo(m), children: where }) : _jsx("b", { children: where }), m.cell && !whole && _jsxs("span", { children: [", ", group.map(linesText).join(', ').replace(/, line /g, ', ').replace(/, lines /g, ', ')] }), m.cell && m.cell.label && i === groups.length - 1 && _jsxs("span", { className: "lin-purpose", children: [_jsx("span", { "aria-hidden": "true", children: " \u00B7 " }), m.cell.label] })] }, i);
|
|
121
|
+
});
|
|
122
|
+
return _jsxs("section", { className: "lin-detail", "aria-label": 'About ' + titleOf(node), children: [_jsxs("header", { children: [_jsx(Icon, { name: node.kind === 'plot' ? 'chart' : file ? 'file' : 'braces', size: 14, stroke: 1.8 }), _jsx("h3", { title: node.name, className: node.means ? undefined : 'lin-bare', children: titleOf(node) }), _jsxs("span", { className: "lin-kind", children: [what, fact ? ` · ${fact.type}${fact.size ? ' · ' + fact.size : ''}` : ''] }), !file && node.kind !== 'plot' && onObject && fact && _jsx("button", { className: "text-button", onClick: () => onObject(node.name), children: "Look at" }), openable && _jsx("button", { className: "text-button", onClick: () => onPath(node.name), children: "Open" })] }), _jsx("p", { className: "lin-said", children: node.means || file ? _jsxs(_Fragment, { children: [_jsx("code", { className: "lin-ident", children: node.name }), node.means ? ' · written in the code' : ' · no meaning written in the code'] }) : 'No meaning written in the code' }), made.length > 0 && _jsx("p", { className: "lin-made-line", children: made }), more?.(node), _jsxs("dl", { children: [_jsx("dt", { children: "From" }), _jsx("dd", { children: node.parents.length ? chips(node.parents, 'from') : _jsx("span", { children: node.kind === 'input' ? (file ? 'Read from the project' : 'Made before Rho kept a record') : 'Nothing read from the code' }) }), node.uses.length > 0 && _jsxs(_Fragment, { children: [_jsx("dt", { children: "Uses" }), _jsx("dd", { className: "lin-uses", children: node.uses.map(u => { const helper = graph.folded.get(u); return _jsxs("span", { className: "lin-use", children: [_jsx("code", { children: u }), helper?.means && _jsxs("span", { className: "lin-use-means", children: [" \u2014 ", helper.means] })] }, u); }) })] }), _jsx("dt", { children: "Leads to" }), _jsx("dd", { children: node.children.length ? chips(node.children, 'into') : _jsx("span", { children: graph.results.includes(node.id) ? 'Nothing yet: this is a result' : 'Nothing' }) }), !graph.results.includes(node.id) && (reaches.length > 0 || !node.children.some(c => graph.results.includes(c))) && _jsxs(_Fragment, { children: [_jsx("dt", { children: "Reaches" }), _jsx("dd", { children: reaches.length ? chips(reaches, 'into') : _jsx("span", { children: "No result" }) })] })] }), node.stale && _jsxs("p", { className: "lin-stale", children: ["Its cell also read ", node.stale.map(fileName).join(', '), ", made again since."] }), code && node.made.length > 0 && _jsxs("details", { className: "lin-code", children: [_jsx("summary", { children: "The code that made it" }), node.made.map((m, i) => _jsx(Code, { code: m.code, language: language }, i))] })] });
|
|
123
|
+
}
|
|
124
|
+
// The lines between values, measured from where the page put them, again as the view changes size:
|
|
125
|
+
// from the bottom of what a value comes from to its top.
|
|
126
|
+
function useEdges(canvas, graph, ids, rows) {
|
|
127
|
+
const [edges, setEdges] = useState([]);
|
|
128
|
+
const [size, setSize] = useState(0);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const element = canvas.current;
|
|
131
|
+
if (!element)
|
|
132
|
+
return;
|
|
133
|
+
const observer = new ResizeObserver(() => setSize(s => s + 1));
|
|
134
|
+
observer.observe(element);
|
|
135
|
+
for (const row of element.querySelectorAll('.lin-row'))
|
|
136
|
+
observer.observe(row);
|
|
137
|
+
return () => observer.disconnect();
|
|
138
|
+
}, [canvas, rows]);
|
|
139
|
+
useLayoutEffect(() => {
|
|
140
|
+
const element = canvas.current;
|
|
141
|
+
if (!element)
|
|
142
|
+
return;
|
|
143
|
+
const origin = element.getBoundingClientRect(), f = (n) => n.toFixed(1), box = new Map();
|
|
144
|
+
for (const node of element.querySelectorAll('[data-node]'))
|
|
145
|
+
box.set(node.dataset.node, node.getBoundingClientRect());
|
|
146
|
+
const next = [];
|
|
147
|
+
for (const id of ids) {
|
|
148
|
+
const to = box.get(id);
|
|
149
|
+
if (!to)
|
|
150
|
+
continue;
|
|
151
|
+
const parents = graph.nodes.get(id).parents.filter(p => box.has(p));
|
|
152
|
+
parents.forEach((p, i) => {
|
|
153
|
+
const from = box.get(p), x1 = from.left + from.width / 2 - origin.left, y1 = from.bottom - origin.top;
|
|
154
|
+
// Lines into one value arrive spread along its top.
|
|
155
|
+
const x2 = to.left + to.width * (parents.length > 1 ? 0.2 + 0.6 * i / (parents.length - 1) : 0.5) - origin.left, y2 = to.top - origin.top;
|
|
156
|
+
const bend = Math.max(14, Math.abs(y2 - y1) / 2);
|
|
157
|
+
next.push({ key: p + '>' + id, from: p, to: id, d: `M${f(x1)} ${f(y1)} C${f(x1)} ${f(y1 + bend)} ${f(x2)} ${f(y2 - bend)} ${f(x2)} ${f(y2)}` });
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
setEdges(now => JSON.stringify(now) === JSON.stringify(next) ? now : next);
|
|
161
|
+
}, [canvas, graph, ids, rows, size]);
|
|
162
|
+
return edges;
|
|
163
|
+
}
|
|
164
|
+
// How the values of a live session came about, as a view beside the Notebook and Variables: from
|
|
165
|
+
// what each cell recorded as it ran, the files and values it read and what it made of them. A
|
|
166
|
+
// value opens as its own view and a file in its viewer; a cell named on a value is shown in the
|
|
167
|
+
// Notebook when the page holds it. A file named in a session on a server is in that server's
|
|
168
|
+
// folder, and is shown there. The page that shows it may give it the application's code
|
|
169
|
+
// highlighting.
|
|
15
170
|
export function FlowPane({ shell, ...props }) { return withShell(shell, _jsx(FlowBody, { ...props })); }
|
|
16
171
|
function FlowBody({ client, snapshot, onObject, onPath, onRemote }) {
|
|
17
172
|
const projectId = snapshot.project.id;
|
|
@@ -24,65 +179,29 @@ function FlowBody({ client, snapshot, onObject, onPath, onRemote }) {
|
|
|
24
179
|
const shown = chosen && live.includes(chosen) ? chosen : recent && live.includes(recent) ? recent : live.at(-1) ?? null;
|
|
25
180
|
const session = shown ? latest[shown] : undefined;
|
|
26
181
|
const cells = useMemo(() => (read?.cells ?? []).filter(c => c.sessionId === session?.sessionId), [read, session?.sessionId]);
|
|
27
|
-
const graph = useMemo(() => session ?
|
|
182
|
+
const graph = useMemo(() => session ? sessionGraph(cells.map(c => ({ ...c, plots: c.outputs.filter(o => o.kind === 'plot').length })), session.variables, session.language) : null, [cells, session]);
|
|
183
|
+
const facts = useMemo(() => new Map(session?.variables.map(v => [v.name, v]) ?? []), [session?.variables]);
|
|
184
|
+
const byCell = useMemo(() => new Map(cells.map(c => [c.cellId, c])), [cells]);
|
|
185
|
+
const who = useAuthors(snapshot);
|
|
28
186
|
const id = useId();
|
|
29
187
|
if (off || (problem && !read))
|
|
30
188
|
return _jsx("div", { className: "nb-pane", children: _jsx("p", { className: "nb-empty-line", children: problem }) });
|
|
31
189
|
if (!read)
|
|
32
190
|
return _jsx("div", { className: "nb-pane", children: _jsx("p", { className: "nb-empty-line", children: "Reading the live sessions\u2026" }) });
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const focus = pointed ?? selected, path = useMemo(() => focus && graph.steps.some(s => s.id === focus) ? related(graph, focus) : null, [graph, focus]);
|
|
48
|
-
const stepOfNode = useMemo(() => new Map(graph.steps.flatMap(s => s.nodes.map(n => [n.id, s.id]))), [graph]);
|
|
49
|
-
const canvas = useRef(null);
|
|
50
|
-
const gutter = 12 + Math.min(laid.count, flowLimits.lanes) * lanePx;
|
|
51
|
-
const lines = useLinks(canvas, graph, laid.lane, gutter);
|
|
52
|
-
// The newest cell's box comes into view as it is made.
|
|
53
|
-
const newest = graph.steps.find(s => s.latest)?.id;
|
|
54
|
-
useEffect(() => { if (newest)
|
|
55
|
-
canvas.current?.querySelector(`[data-step="${CSS.escape(newest)}"]`)?.scrollIntoView({ block: 'nearest' }); }, [newest]);
|
|
56
|
-
const unknown = graph.steps.some(s => s.unknownInputs), omitted = graph.omitted;
|
|
57
|
-
return _jsxs(_Fragment, { children: [_jsx("p", { className: "flow-lede", children: "Each box is a cell, in the order they ran, with what it made. A line joins a value to each cell that read it." }), _jsxs("div", { className: "flow-canvas", ref: canvas, style: { paddingLeft: gutter }, onPointerLeave: () => setPointed(null), children: [_jsxs("svg", { className: "flow-links", "aria-hidden": "true", children: [_jsx("defs", { children: _jsx("marker", { id: "flow-arrow", viewBox: "0 0 8 8", refX: "7", refY: "4", markerWidth: "6", markerHeight: "6", orient: "auto", children: _jsx("path", { d: "M0 0 8 4 0 8z" }) }) }), lines.map(line => _jsx("path", { d: line.d, markerEnd: "url(#flow-arrow)", className: path ? (path.has(stepOfNode.get(line.from)) && path.has(line.to) ? 'on' : 'off') : '' }, line.key))] }), laid.order.map(s => {
|
|
58
|
-
const cell = s.cell ? byCell.get(s.cell.cellId) : undefined;
|
|
59
|
-
return _jsx(StepBox, { step: s, cell: cell, values: values, faded: !!path && !path.has(s.id), onPoint: setPointed, open: s.id === selected, onOpen: () => setSelected(selected === s.id ? null : s.id), onNode: node => node.kind === 'value' ? onObject?.(session.language, node.name) : node.kind === 'file' ? onPath?.(node.name) : setSelected(s.id), onFile: file => onPath?.(file), folders: !!session.place, children: s.id === selected && cell && _jsx(StepCell, { step: s, cell: cell, byCell: byCell, client: client, snapshot: snapshot }) }, s.id);
|
|
60
|
-
})] }), unknown && _jsx("p", { className: "nb-note", children: "Cells run before Rho kept what each cell read show what they made, without lines into them." }), omitted.values + omitted.plots + omitted.files > 0 && _jsxs("p", { className: "nb-note", children: ["Not drawn: ", [omitted.values && `${omitted.values} older ${omitted.values === 1 ? 'value' : 'values'}`, omitted.plots && `${omitted.plots} older ${omitted.plots === 1 ? 'plot' : 'plots'}`, omitted.files && `${omitted.files} older written ${omitted.files === 1 ? 'file' : 'files'}`].filter(Boolean).join(', '), "."] }), session.variablesOmitted > 0 && _jsxs("p", { className: "nb-note", children: [session.variablesOmitted, " more ", session.variablesOmitted === 1 ? 'value' : 'values', " in the session are not listed."] })] });
|
|
61
|
-
}
|
|
62
|
-
// A folder's path ends in /, and keeps it as its name.
|
|
63
|
-
const folder = (path) => path.endsWith('/');
|
|
64
|
-
const fileName = (path) => (path.replace(/\/$/, '').split('/').at(-1) ?? path) + (folder(path) ? '/' : '');
|
|
65
|
-
const and = (items) => items.length < 2 ? items.join('') : items.slice(0, -1).join(', ') + ' and ' + items.at(-1);
|
|
66
|
-
// A folder opens where it can be listed: in a session on a server, in the Servers view.
|
|
67
|
-
function StepBox({ step, cell, values, faded, open, onPoint, onOpen, onNode, onFile, folders = false, children }) {
|
|
68
|
-
const reads = step.reads.slice(0, flowLimits.reads), name = cell ? languageName[cell.language] : '';
|
|
69
|
-
return _jsxs("section", { className: 'flow-step ' + step.kind + (step.latest ? ' latest' : '') + (open ? ' open' : '') + (faded ? ' faded' : ''), "data-step": step.id, onPointerEnter: () => onPoint(step.id), children: [step.cell && cell
|
|
70
|
-
? _jsxs("button", { className: "flow-step-head", "aria-expanded": open, title: open ? 'Hide the code' : 'Show the code that made this', onClick: onOpen, children: [_jsxs("span", { className: 'nb-lang ' + cell.language, children: [name, " ", cell.number] }), _jsx("code", { children: cellLabel(step.cell) }), step.latest && _jsx("span", { className: "flow-newest", children: "Newest" }), _jsx(Icon, { name: "chevron", size: 12 })] })
|
|
71
|
-
: _jsx("p", { className: "flow-step-head", children: "Already in the session" }), reads.length > 0 && _jsxs("p", { className: "flow-reads", children: ["Reads ", reads.map(file => folder(file) && !folders
|
|
72
|
-
? _jsxs("span", { className: "flow-file", title: file, children: [_jsx(Icon, { name: "folder", size: 11, stroke: 1.8 }), fileName(file)] }, file)
|
|
73
|
-
: _jsxs("button", { className: "flow-file", title: 'Open ' + file, onClick: () => onFile(file), children: [_jsx(Icon, { name: folder(file) ? 'folder' : 'file', size: 11, stroke: 1.8 }), fileName(file)] }, file)), step.reads.length > reads.length && _jsxs("span", { children: ["and ", step.reads.length - reads.length, " more"] })] }), step.earlier.length > 0 && _jsxs("p", { className: "flow-step-note", children: ["Changes what ", and(step.earlier.map(c => `${name} ${c.number}`)), " made"] }), _jsx("ul", { className: "flow-outs", children: step.nodes.map(node => {
|
|
74
|
-
const value = node.kind === 'value' ? values.get(node.name) : undefined, wrote = node.id.startsWith('wrote:'), into = node.kind === 'file' && folder(node.name);
|
|
75
|
-
return _jsx("li", { children: _jsxs("button", { "data-node": node.id, className: 'flow-out ' + node.kind + (wrote ? ' wrote' : ''), disabled: into && !folders, title: node.kind === 'value' ? `Look at ${node.name}${value?.summary ? ' · ' + value.summary : ''}` : into ? `Wrote into ${node.name}` : node.kind === 'file' ? `Open ${node.name}` : 'Show the code that drew it', onClick: () => onNode(node), children: [_jsx(Icon, { name: node.kind === 'value' ? 'braces' : node.kind === 'plot' ? 'chart' : into ? 'folder' : 'file', size: 12, stroke: 1.8 }), _jsx("span", { children: node.kind === 'file' ? fileName(node.name) : node.name }), _jsx("small", { children: value ? `${value.type}${value.size ? ' · ' + value.size : ''}` : into ? 'written into' : wrote ? 'written' : '' })] }) }, node.id);
|
|
76
|
-
}) }), step.stale.length > 0 && _jsxs("p", { className: "flow-step-note stale", children: ["Read before ", step.stale.length === 1 ? 'it was' : 'they were', " made again: ", step.stale.map(fileName).join(', ')] }), step.unknownInputs && _jsx("p", { className: "flow-step-note", children: "What it read was not recorded" }), children] });
|
|
77
|
-
}
|
|
78
|
-
// The cell a box stands for, opened in place: the cells whose values it changed, its code, who ran
|
|
79
|
-
// it and when, and the plots it drew.
|
|
80
|
-
function StepCell({ step, cell, byCell, client, snapshot }) {
|
|
81
|
-
const who = useAuthors(snapshot);
|
|
82
|
-
const earlier = step.earlier.map(c => byCell.get(c.cellId)).filter((c) => !!c);
|
|
83
|
-
const inNotebook = document.getElementById('cell-' + cell.cellId) ? cell.cellId : null;
|
|
84
|
-
const plots = cell.outputs.filter(o => o.kind === 'plot');
|
|
85
|
-
return _jsxs("div", { className: "flow-cell", children: [earlier.map(c => _jsxs("div", { className: "flow-cell-earlier", children: [_jsxs("span", { className: 'nb-lang ' + c.language, children: [languageName[c.language], " ", c.number] }), _jsx(Code, { code: c.code, language: c.language })] }, c.cellId)), _jsx(Code, { code: cell.code, language: cell.language }), plots.length > 0 && _jsx("div", { className: "flow-cell-plots", children: plots.map(o => _jsx(CellPlot, { client: client, projectId: snapshot.project.id, cellId: cell.cellId, plot: o.plot }, o.plot)) }), _jsxs("footer", { children: [_jsxs("span", { children: [who(cell.by).name, " \u00B7 ", at(cell.queuedAt)] }), inNotebook && _jsx("button", { className: "text-button", onClick: () => document.getElementById('cell-' + inNotebook)?.scrollIntoView({ block: 'start', behavior: 'smooth' }), children: "Show in the Notebook" })] })] });
|
|
191
|
+
const inNotebook = (m) => !!m.cell && !!document.getElementById('cell-' + m.cell.cellId);
|
|
192
|
+
const toCell = (m) => { const element = m.cell && document.getElementById('cell-' + m.cell.cellId); element?.scrollIntoView({ block: 'start', behavior: 'smooth' }); };
|
|
193
|
+
// A plot shows with the cell that drew it, and a value with who ran that cell and when.
|
|
194
|
+
const more = (node) => {
|
|
195
|
+
const cell = node.made.at(-1)?.cell && byCell.get(node.made.at(-1).cell.cellId);
|
|
196
|
+
if (!cell)
|
|
197
|
+
return null;
|
|
198
|
+
const plots = node.kind === 'plot' ? cell.outputs.filter(o => o.kind === 'plot') : [];
|
|
199
|
+
return _jsxs(_Fragment, { children: [plots.length > 0 && _jsx("div", { className: "flow-cell-plots", children: plots.map(o => _jsx(CellPlot, { client: client, projectId: projectId, cellId: cell.cellId, plot: o.plot }, o.plot)) }), _jsxs("p", { className: "lin-who", children: ["Ran for ", who(cell.by).name, " at ", at(cell.queuedAt)] })] });
|
|
200
|
+
};
|
|
201
|
+
const unknown = cells.some(c => c.state !== 'queued' && c.state !== 'running' && c.used === undefined);
|
|
202
|
+
return _jsxs("div", { className: "nb-pane flow-pane", "aria-labelledby": id, children: [_jsxs("header", { className: "flow-head", children: [_jsx("h2", { id: id, className: "nb-pane-state", children: session ? _jsxs(_Fragment, { children: [_jsx("span", { className: 'nb-dot ' + session.state, "aria-hidden": "true" }), [versionOf(session), whereOf(session), session.state === 'busy' ? 'running a cell' : session.state === 'starting' ? 'starting' : 'idle'].filter(Boolean).join(' · ')] }) : 'Data flow' }), live.length > 1 && _jsx("div", { className: "nb-tabs", role: "group", "aria-label": "Session", children: live.map(l => _jsx("button", { "aria-pressed": l === shown, onClick: () => setChosen(l), children: languageName[l] }, l)) })] }), !session || !graph ? _jsx("p", { className: "nb-empty-line", children: "No live session. Once a cell runs in R or Python, this shows how each of its values came about: the files and values it comes from, and what comes of it." })
|
|
203
|
+
: !graph.nodes.size ? _jsxs("p", { className: "nb-empty-line", children: ["Nothing made in ", languageName[session.language], " yet."] })
|
|
204
|
+
: _jsxs(_Fragment, { children: [_jsx("p", { className: "flow-lede", children: "From the files the session read, at the top, to what it wrote and plotted, at the bottom." }), _jsx(Lineage, { graph: graph, language: session.language, facts: facts, pinned: true, onObject: onObject && (name => onObject(session.language, name)), onPath: session.place ? (onRemote && (file => onRemote(session.place.placeId, file))) : onPath, folders: !!session.place, onGo: toCell, canGo: inNotebook, more: more }, session.sessionId), unknown && _jsx("p", { className: "nb-note", children: "Cells run before Rho kept what each cell read show what they made, without what it came from." }), graph.omitted.values + graph.omitted.plots + graph.omitted.files > 0 && _jsxs("p", { className: "nb-note", children: ["Not drawn: ", [graph.omitted.values && `${graph.omitted.values} older ${graph.omitted.values === 1 ? 'value' : 'values'}`, graph.omitted.plots && `${graph.omitted.plots} older ${graph.omitted.plots === 1 ? 'plot' : 'plots'}`, graph.omitted.files && `${graph.omitted.files} older written ${graph.omitted.files === 1 ? 'file' : 'files'}`].filter(Boolean).join(', '), "."] }), session.variablesOmitted > 0 && _jsxs("p", { className: "nb-note", children: [session.variablesOmitted, " more ", session.variablesOmitted === 1 ? 'value' : 'values', " in the session are not listed."] })] })] });
|
|
86
205
|
}
|
|
87
206
|
function CellPlot({ client, projectId, cellId, plot }) {
|
|
88
207
|
const { src, problem } = usePlot(client, projectId, cellId, plot);
|
|
@@ -90,38 +209,16 @@ function CellPlot({ client, projectId, cellId, plot }) {
|
|
|
90
209
|
return _jsxs("p", { className: "nb-note", children: ["This plot could not be read. ", problem] });
|
|
91
210
|
return src ? _jsx("img", { src: src, alt: `Plot ${plot + 1}` }) : _jsx("span", { className: "nb-plot-wait", "aria-label": "Reading the plot" });
|
|
92
211
|
}
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
for (const child of element.querySelectorAll('.flow-step'))
|
|
106
|
-
observer.observe(child);
|
|
107
|
-
return () => observer.disconnect();
|
|
108
|
-
}, [canvas, graph]);
|
|
109
|
-
useLayoutEffect(() => {
|
|
110
|
-
const element = canvas.current;
|
|
111
|
-
if (!element)
|
|
112
|
-
return;
|
|
113
|
-
const origin = element.getBoundingClientRect(), r = 5, f = (n) => n.toFixed(1);
|
|
114
|
-
const next = graph.links.flatMap(link => {
|
|
115
|
-
const out = element.querySelector(`[data-node="${CSS.escape(link.from)}"]`), box = out?.closest('.flow-step');
|
|
116
|
-
const into = element.querySelector(`[data-step="${CSS.escape(link.to)}"]`), head = into?.querySelector('.flow-step-head');
|
|
117
|
-
if (!out || !box || !into || !head)
|
|
118
|
-
return [];
|
|
119
|
-
const sy = out.getBoundingClientRect().top + out.getBoundingClientRect().height / 2 - origin.top, sx = box.getBoundingClientRect().left - origin.left;
|
|
120
|
-
const ty = head.getBoundingClientRect().top + head.getBoundingClientRect().height / 2 - origin.top, tx = into.getBoundingClientRect().left - origin.left - 1;
|
|
121
|
-
const x = gutter - 8 - (lane.get(link.from) % flowLimits.lanes) * lanePx;
|
|
122
|
-
return [{ key: link.from + '>' + link.to, from: link.from, to: link.to, d: `M${f(sx)} ${f(sy)} H${f(x + r)} Q${f(x)} ${f(sy)} ${f(x)} ${f(sy + r)} V${f(ty - r)} Q${f(x)} ${f(ty)} ${f(x + r)} ${f(ty)} H${f(tx)}` }];
|
|
123
|
-
});
|
|
124
|
-
setLines(now => JSON.stringify(now) === JSON.stringify(next) ? now : next);
|
|
125
|
-
}, [canvas, graph, lane, gutter, size]);
|
|
126
|
-
return lines;
|
|
212
|
+
// A script's values, read from its text, for a page that shows the script: its editor follows
|
|
213
|
+
// the value chosen (marking the lines behind it) and goes to where a value is made.
|
|
214
|
+
export function ScriptFlowView({ shell, ...props }) {
|
|
215
|
+
return withShell(shell, _jsx(Lineage, { ...props, code: false, pinned: true, hint: "Read from the code; nothing runs. The code marks the same path." }));
|
|
216
|
+
}
|
|
217
|
+
// A cell that makes its values over several statements shows how, as its script would.
|
|
218
|
+
export const innerFlowLimits = { values: 3 };
|
|
219
|
+
export function CellScriptFlow({ code, language }) {
|
|
220
|
+
const graph = useMemo(() => scriptGraph(scriptFlow(code, language), language), [code, language]);
|
|
221
|
+
if ([...graph.nodes.values()].filter(n => n.kind !== 'input').length < innerFlowLimits.values)
|
|
222
|
+
return null;
|
|
223
|
+
return _jsxs("details", { className: "flow-inner", children: [_jsx("summary", { children: "How its values came about" }), _jsx(Lineage, { graph: graph, language: language })] });
|
|
127
224
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Icon } from './ui-icons.js';
|
|
4
4
|
import { askCell } from './ui-notebook-target.js';
|
|
5
|
-
import { call, agentName, when } from './ui-base.js';
|
|
5
|
+
import { call, agentName, when, versionOf } from './ui-base.js';
|
|
6
6
|
// What was done in the project's live sessions, for the Overview: where each session with cells
|
|
7
7
|
// runs and what it holds, and the newest cells by their purpose. Work done only in the Notebook,
|
|
8
8
|
// on this machine or on a server, is work. One small read follows the project's records; a read
|
|
@@ -23,7 +23,6 @@ export function useNotebookWork(client, projectId, cursor, enabled) {
|
|
|
23
23
|
}, [client, projectId, cursor, enabled]);
|
|
24
24
|
return { ...state, cells: state.read ? state.read.cells.length + state.read.omitted : 0 };
|
|
25
25
|
}
|
|
26
|
-
const versionOf = (s) => s.version?.replace(/^R version (\S+).*$/, 'R $1') ?? (s.language === 'r' ? 'R' : 'Python');
|
|
27
26
|
const firstLine = (code) => code.split('\n').map(line => line.trim()).find(line => line && !line.startsWith('#')) ?? 'A cell';
|
|
28
27
|
export function NotebookWorkSection({ work, snapshot, onOpen }) {
|
|
29
28
|
const { read, problem, cells } = work;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type CellRelations } from './lineage.js';
|
|
2
|
+
import { versionOf, type KernelShell, type SessionScope, type UiClient, type UiSnapshot, type KernelOutputs } from './ui-base.js';
|
|
3
|
+
export { versionOf };
|
|
2
4
|
type Read = KernelOutputs['kernel.read'];
|
|
3
5
|
export type Cell = Read['cells'][number];
|
|
4
6
|
export type Session = Read['sessions'][number];
|
|
@@ -13,7 +15,6 @@ export declare const notebookViewLimits: {
|
|
|
13
15
|
cachedPlots: number;
|
|
14
16
|
wideViewPx: number;
|
|
15
17
|
};
|
|
16
|
-
export declare const versionOf: (session: Session) => string;
|
|
17
18
|
export declare const at: (iso: string) => string;
|
|
18
19
|
interface Author {
|
|
19
20
|
name: string;
|
|
@@ -39,6 +40,13 @@ export declare function Code({ code, language }: {
|
|
|
39
40
|
code: string;
|
|
40
41
|
language: Language;
|
|
41
42
|
}): import("react").JSX.Element;
|
|
43
|
+
export declare const relationLimits: {
|
|
44
|
+
cells: number;
|
|
45
|
+
};
|
|
46
|
+
export declare function useRelations(cells: Cell[]): {
|
|
47
|
+
bySession: Map<string, CellRelations>;
|
|
48
|
+
all: CellRelations;
|
|
49
|
+
};
|
|
42
50
|
export { FormSwitch, type NotebookForm } from './ui-notebook-form.js';
|
|
43
51
|
import type { NotebookForm } from './ui-notebook-form.js';
|
|
44
52
|
export interface Prepared {
|