@theoremjs/studio 0.0.0-stage → 0.3.2
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/LICENSE +21 -0
- package/README.md +295 -2
- package/agreement.d.ts +14 -0
- package/agreement.js +80 -0
- package/asks.d.ts +47 -0
- package/asks.js +54 -0
- package/browser-connection.d.ts +49 -0
- package/browser-connection.js +215 -0
- package/browser-engine.d.ts +11 -0
- package/browser-engine.js +11 -0
- package/browser-execution.d.ts +6 -0
- package/browser-execution.js +18 -0
- package/browser-live.d.ts +4 -0
- package/browser-live.js +132 -0
- package/browser-transport.d.ts +33 -0
- package/browser-transport.js +229 -0
- package/browser.d.ts +4 -0
- package/browser.js +4 -0
- package/canonical.d.ts +10 -0
- package/canonical.js +25 -0
- package/compile-workspace.d.ts +28 -0
- package/compile-workspace.js +295 -0
- package/compile.d.ts +65 -0
- package/compile.js +1605 -0
- package/concierge-demo.d.ts +17 -0
- package/concierge-demo.js +1230 -0
- package/conversation-key.d.ts +5 -0
- package/conversation-key.js +10 -0
- package/decision-example-images.d.ts +30 -0
- package/decision-example-images.js +30 -0
- package/deno.json +3 -0
- package/draft.d.ts +407 -0
- package/draft.js +505 -0
- package/editors.d.ts +29 -0
- package/editors.js +51 -0
- package/example-embedding-files.d.ts +11 -0
- package/example-embedding-files.js +20 -0
- package/example.d.ts +36 -0
- package/example.js +357 -0
- package/guardrail-probe.d.ts +100 -0
- package/guardrail-probe.js +722 -0
- package/held-credentials.d.ts +28 -0
- package/held-credentials.js +87 -0
- package/installed.mjs +40 -0
- package/lent-keys.d.ts +122 -0
- package/lent-keys.js +47 -0
- package/line-diff.d.ts +15 -0
- package/line-diff.js +41 -0
- package/live-connection.d.ts +22 -0
- package/live-connection.js +27 -0
- package/live-session-bridge.d.ts +15 -0
- package/live-session-bridge.js +202 -0
- package/map-line.d.ts +19 -0
- package/map-line.js +42 -0
- package/map.d.ts +154 -0
- package/map.js +525 -0
- package/media-accept.d.ts +19 -0
- package/media-accept.js +65 -0
- package/mod.d.ts +39 -0
- package/mod.js +33 -0
- package/origins.d.ts +52 -0
- package/origins.js +165 -0
- package/package.json +85 -4
- package/page/assets/Banner-BISz3nXA.js +2 -0
- package/page/assets/BottomSheet-BwfBDtje.js +1 -0
- package/page/assets/Card-Lp-0P5eL.js +1 -0
- package/page/assets/CardWall-CBRLdDZW.js +1 -0
- package/page/assets/Center-nHS1JSux.js +1 -0
- package/page/assets/CheckboxInput-Cj5pk-KI.js +2 -0
- package/page/assets/CodeBlock-C7XJ4ioE.js +51 -0
- package/page/assets/CollapsibleGroup-N7_SOjbm.js +2 -0
- package/page/assets/ContextMenu-CBu9H-9H.js +2 -0
- package/page/assets/DateInput-BGrQ5tPC.js +1 -0
- package/page/assets/Dialog-DELWQo-v.js +1 -0
- package/page/assets/EmptyState-C8W4JKBZ.js +1 -0
- package/page/assets/HoverCard-D1Y1ltWQ.js +1 -0
- package/page/assets/Icon-JrysT8aU.js +3 -0
- package/page/assets/IconAlertTriangle-BqAEqqdU.js +1 -0
- package/page/assets/IconButton-DlOGzJIX.js +1 -0
- package/page/assets/IconEye-BB5aY-wQ.js +1 -0
- package/page/assets/IconFileText-6zOFKKrF.js +1 -0
- package/page/assets/IconSearch-BTx66a40.js +1 -0
- package/page/assets/IconShield-DDOh7Gr5.js +1 -0
- package/page/assets/ItemSwipeLayer-Ckn4X6h9.js +1 -0
- package/page/assets/PaneState-BvA2tLQg.js +1 -0
- package/page/assets/PaneState-eUzUkNE8.css +1 -0
- package/page/assets/ScrollableArea-Ckugdj1N.js +1 -0
- package/page/assets/Section-DJ97F7QE.js +1 -0
- package/page/assets/SegmentedControlItem-h-aaXzyS.js +1 -0
- package/page/assets/SidePanel-BL32abk8.js +1 -0
- package/page/assets/StackItem-CQviqAfM.js +1 -0
- package/page/assets/Text-bum6zWWx.js +2 -0
- package/page/assets/TimestampHoverCard-DQIG0RZF.js +1 -0
- package/page/assets/ToolResultCharts-CgshSv9Q.js +1 -0
- package/page/assets/Tooltip-BH9xiRMl.js +1 -0
- package/page/assets/TraceInspector-_yZiz05W.js +1 -0
- package/page/assets/TraceOverview-BPO-HIYK.js +57 -0
- package/page/assets/VStack-CkVMcNiK.js +1 -0
- package/page/assets/_virtual_studio-type-sources-ByxqQaOG.js +12936 -0
- package/page/assets/arrive-DvbqpeRS.js +1 -0
- package/page/assets/browser-engine-CYq9DQAM.js +9 -0
- package/page/assets/browser-transport-D-2jDYe7.js +47 -0
- package/page/assets/codicon-DF1abBS2.ttf +0 -0
- package/page/assets/compile-egress-BSxOdX4K.js +23 -0
- package/page/assets/container.stylex-BmOi-8DQ.js +1 -0
- package/page/assets/createReactComponent-CZwsv6DD.js +1 -0
- package/page/assets/decision-example-images-CZGr7YIT.js +1 -0
- package/page/assets/devWarning-DxuplmRl.js +1 -0
- package/page/assets/editor-DjpsE8to.css +1 -0
- package/page/assets/editor.api-Cd3ViPQa.js +908 -0
- package/page/assets/editor.worker-6GY8RhDc.js +1 -0
- package/page/assets/editor.worker-BmdWdrHZ.js +33 -0
- package/page/assets/editors-BJX_S59A.js +1 -0
- package/page/assets/egress-automata-B5ZGE1mT.js +1 -0
- package/page/assets/embedding-space-view-CfQBPDxD.js +140 -0
- package/page/assets/encode-files-Bc3HJyAh.js +1 -0
- package/page/assets/export-agent-C3yyzXH8.js +43 -0
- package/page/assets/figtree-latin-400-normal-BD4aNku5.woff +0 -0
- package/page/assets/figtree-latin-400-normal-g7Dtegnw.woff2 +0 -0
- package/page/assets/figtree-latin-500-normal-BWnGEVsr.woff2 +0 -0
- package/page/assets/figtree-latin-500-normal-B_yy1phn.woff +0 -0
- package/page/assets/figtree-latin-600-normal-BM_oTo4n.woff +0 -0
- package/page/assets/figtree-latin-600-normal-Cv_xCTDl.woff2 +0 -0
- package/page/assets/figtree-latin-700-normal-BCCt2T1q.woff +0 -0
- package/page/assets/figtree-latin-700-normal-th6qEP7c.woff2 +0 -0
- package/page/assets/focusOutline.stylex-BT0kfa4t.js +1 -0
- package/page/assets/focusableSelector-DXZeMU85.js +1 -0
- package/page/assets/groupStyles-C7Ter7zC.js +1 -0
- package/page/assets/guardrail-tester-Gut5xK4a.js +9 -0
- package/page/assets/host-patterns-C9RMP16Q.js +1 -0
- package/page/assets/index-CvvmFKgS.js +105 -0
- package/page/assets/index-DOaHlqBo.css +1 -0
- package/page/assets/isRtlElement-BG33-k_H.js +1 -0
- package/page/assets/labels-provider-4TIWDvsE.js +3 -0
- package/page/assets/lent-keys-C32ems-I.js +1 -0
- package/page/assets/live-ingress-lDUGPwth.js +1 -0
- package/page/assets/live-messages-CnkL4GWi.js +1 -0
- package/page/assets/mergeProps-ptS-gIMz.js +1 -0
- package/page/assets/padding.stylex-B76907Zf.js +1 -0
- package/page/assets/preload-helper-DpV6A1kQ.js +1 -0
- package/page/assets/reader-BOCCYaRp.js +7 -0
- package/page/assets/register-CDgivxvX.js +2 -0
- package/page/assets/rolldown-runtime-hePW80VL.js +1 -0
- package/page/assets/schemas-BQ9Jydzb.js +56 -0
- package/page/assets/shaped-data-BcarWqd5.js +16 -0
- package/page/assets/sharedResizeObserver-4U9Xq5Oq.js +1 -0
- package/page/assets/stub-DhdcU_Ew.js +471 -0
- package/page/assets/studio-code-pane-C_SAbcOa.js +8 -0
- package/page/assets/studio-console-DZwlEsvr.js +3 -0
- package/page/assets/studio-decision-B0FnCAXA.js +1 -0
- package/page/assets/studio-embedding-CmA51HVo.js +2 -0
- package/page/assets/studio-host-console-By7hqu2l.js +2 -0
- package/page/assets/studio-live-BrcVQcyV.js +1 -0
- package/page/assets/studio-map-B_xSia-z.js +1 -0
- package/page/assets/studio-map-Bb06Tw1F.css +1 -0
- package/page/assets/studio-monaco-CbKf90i6.css +1 -0
- package/page/assets/studio-monaco-xfyfezQS.js +34 -0
- package/page/assets/studio-ts.worker-B-EXG4Sv.js +67741 -0
- package/page/assets/studio-ts.worker-BXtW3z-O.js +1 -0
- package/page/assets/studio-type-checker-BOvxudki.js +23 -0
- package/page/assets/themeProps-3IqahpCr.js +1 -0
- package/page/assets/tokens.stylex-DkHf0P9C.js +1 -0
- package/page/assets/tool-schema-idaOe2nI.js +18 -0
- package/page/assets/trace-patterns-DnYJa0nI.js +1 -0
- package/page/assets/trace-record-C3zNksA_.js +1 -0
- package/page/assets/trace-schema-9CdEyG6Q.js +1 -0
- package/page/assets/tree-DqyAMsbI.js +3 -0
- package/page/assets/turn-client-Du3Um3Mg.js +2 -0
- package/page/assets/types-BhfXQCLx.js +1 -0
- package/page/assets/typescript-20GgZ8PJ.js +1 -0
- package/page/assets/use-one-call-Hcs8YBp1.js +1 -0
- package/page/assets/useAdaptivePresentation-BzmPCddb.js +1 -0
- package/page/assets/useClipboard-BgGRxRMJ.js +1 -0
- package/page/assets/useContainerReveal-BFy8N_QU.js +1 -0
- package/page/assets/useDevWarning-Z9SoixOP.js +1 -0
- package/page/assets/useLayerDismissal-B_G7IWpC.js +1 -0
- package/page/assets/useMediaQuery-BCT0NUeZ.js +1 -0
- package/page/assets/useScrollLock-Dyf6PHJt.js +1 -0
- package/page/assets/useScrollableArea-0NzdiBCq.js +1 -0
- package/page/assets/useTooltip-CJM_xhLB.js +1 -0
- package/page/assets/workspace-DZALqEDX.js +1 -0
- package/page/assets/zip-DTcEfps6.js +1 -0
- package/page/index.html +90 -0
- package/page.mjs +49 -0
- package/people.d.ts +87 -0
- package/people.js +110 -0
- package/policy.d.ts +43 -0
- package/policy.js +102 -0
- package/ports.d.mts +32 -0
- package/ports.mjs +85 -0
- package/probe-battery.d.ts +13 -0
- package/probe-battery.js +404 -0
- package/providers.d.ts +149 -0
- package/providers.js +306 -0
- package/read-source.d.ts +48 -0
- package/read-source.js +1301 -0
- package/read-steps.d.ts +23 -0
- package/read-steps.js +2 -0
- package/reader.d.ts +11 -0
- package/reader.js +11 -0
- package/registrations.d.ts +83 -0
- package/registrations.js +1 -0
- package/requirements.d.ts +22 -0
- package/requirements.js +38 -0
- package/run-payload-core.d.ts +28 -0
- package/run-payload-core.js +123 -0
- package/run-payload.d.ts +32 -0
- package/run-payload.js +26 -0
- package/runtime-scope.d.ts +84 -0
- package/runtime-scope.js +308 -0
- package/runtime.d.ts +84 -0
- package/runtime.js +387 -0
- package/section-notes.d.ts +33 -0
- package/section-notes.js +38 -0
- package/serve-page.mjs +96 -0
- package/server/draft-decide.d.ts +72 -0
- package/server/draft-decide.js +111 -0
- package/server/draft-embedding.d.ts +3 -0
- package/server/draft-embedding.js +35 -0
- package/server/draft-handler.d.ts +82 -0
- package/server/draft-handler.js +213 -0
- package/server/draft-probe.d.ts +14 -0
- package/server/draft-probe.js +61 -0
- package/server/draft-replies.d.ts +22 -0
- package/server/draft-replies.js +46 -0
- package/server/edited-session.d.ts +28 -0
- package/server/edited-session.js +83 -0
- package/server/edits.d.ts +38 -0
- package/server/edits.js +195 -0
- package/server/handler.d.ts +133 -0
- package/server/handler.js +598 -0
- package/server/lent-draft.d.ts +19 -0
- package/server/lent-draft.js +23 -0
- package/server/live.d.ts +15 -0
- package/server/live.js +96 -0
- package/server/open-editor.d.ts +63 -0
- package/server/open-editor.js +115 -0
- package/server/origins.d.ts +18 -0
- package/server/origins.js +330 -0
- package/server/project-source.d.ts +208 -0
- package/server/project-source.js +1042 -0
- package/server/project.d.ts +22 -0
- package/server/project.js +56 -0
- package/server/run-traces.d.ts +20 -0
- package/server/run-traces.js +96 -0
- package/server/save-new.d.ts +29 -0
- package/server/save-new.js +514 -0
- package/server/save-plan.d.ts +46 -0
- package/server/save-plan.js +1134 -0
- package/server/save-remove.d.ts +21 -0
- package/server/save-remove.js +528 -0
- package/server/save-session.d.ts +96 -0
- package/server/save-session.js +418 -0
- package/server/save-wire.d.ts +235 -0
- package/server/save-wire.js +8 -0
- package/server/save.d.ts +52 -0
- package/server/save.js +209 -0
- package/server/serve.d.ts +23 -0
- package/server/serve.js +413 -0
- package/server/test-connection.d.ts +17 -0
- package/server/test-connection.js +362 -0
- package/server/test-key.d.ts +2 -0
- package/server/test-key.js +83 -0
- package/server/watch.d.ts +28 -0
- package/server/watch.js +93 -0
- package/server/zod-edit.d.ts +74 -0
- package/server/zod-edit.js +558 -0
- package/server.d.ts +8 -0
- package/server.js +8 -0
- package/setups.mjs +170 -0
- package/shared-carry.d.ts +114 -0
- package/shared-carry.js +523 -0
- package/shared-settings.d.ts +34 -0
- package/shared-settings.js +70 -0
- package/source.d.ts +75 -0
- package/source.js +414 -0
- package/start.mjs +169 -0
- package/stream-lines.d.ts +9 -0
- package/stream-lines.js +27 -0
- package/stub.d.ts +6 -0
- package/stub.js +33 -0
- package/surface.d.ts +53 -0
- package/surface.js +631 -0
- package/system-markup.d.ts +20 -0
- package/system-markup.js +51 -0
- package/tool-schema.d.ts +23 -0
- package/tool-schema.js +69 -0
- package/tools.d.ts +16 -0
- package/tools.js +91 -0
- package/traces.d.ts +24 -0
- package/traces.js +29 -0
- package/transport.d.ts +57 -0
- package/transport.js +187 -0
- package/tree.d.ts +32 -0
- package/tree.js +83 -0
- package/types.d.ts +62 -0
- package/types.js +6 -0
- package/ui/acting-as-button.d.ts +2 -0
- package/ui/acting-as-button.js +16 -0
- package/ui/add-provider.d.ts +13 -0
- package/ui/add-provider.js +20 -0
- package/ui/boot-mark.css +53 -0
- package/ui/boot-mark.d.ts +19 -0
- package/ui/boot-mark.js +128 -0
- package/ui/boot-paint.d.ts +8 -0
- package/ui/boot-paint.js +8 -0
- package/ui/brand-icons.d.ts +57 -0
- package/ui/brand-icons.js +148 -0
- package/ui/built/theme.css +830 -0
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem-studio.d.ts +13 -0
- package/ui/built/theorem-studio.js +705 -0
- package/ui/built/theorem-studio.variants.d.ts +28 -0
- package/ui/code/monaco-contributions.d.ts +45 -0
- package/ui/code/studio-code.d.ts +7 -0
- package/ui/code/studio-code.js +253 -0
- package/ui/code/studio-monaco.d.ts +44 -0
- package/ui/code/studio-monaco.js +48 -0
- package/ui/code/studio-ts.worker.d.ts +1 -0
- package/ui/code/studio-ts.worker.js +16 -0
- package/ui/code/studio-type-checker.d.ts +12 -0
- package/ui/code/studio-type-checker.js +308 -0
- package/ui/code/studio-type-sources.d.ts +26 -0
- package/ui/confirm-write.d.ts +12 -0
- package/ui/confirm-write.js +45 -0
- package/ui/diff-block.d.ts +21 -0
- package/ui/diff-block.js +17 -0
- package/ui/figtree.css +59 -0
- package/ui/file-place.d.ts +4 -0
- package/ui/file-place.js +26 -0
- package/ui/guardrail-tester.d.ts +18 -0
- package/ui/guardrail-tester.js +248 -0
- package/ui/inspector-context.d.ts +119 -0
- package/ui/inspector-context.js +84 -0
- package/ui/inspector.d.ts +247 -0
- package/ui/inspector.js +284 -0
- package/ui/lib/acting-as.d.ts +25 -0
- package/ui/lib/acting-as.js +90 -0
- package/ui/lib/boot-sequence.d.ts +32 -0
- package/ui/lib/boot-sequence.js +57 -0
- package/ui/lib/export-agent.d.ts +11 -0
- package/ui/lib/export-agent.js +335 -0
- package/ui/lib/facet-icons.d.ts +20 -0
- package/ui/lib/facet-icons.js +21 -0
- package/ui/lib/panel-motion.d.ts +8 -0
- package/ui/lib/panel-motion.js +74 -0
- package/ui/lib/project-address.d.ts +14 -0
- package/ui/lib/project-address.js +24 -0
- package/ui/lib/project-starting.d.ts +13 -0
- package/ui/lib/project-starting.js +30 -0
- package/ui/lib/project-traces.d.ts +17 -0
- package/ui/lib/project-traces.js +54 -0
- package/ui/lib/read-map.d.ts +30 -0
- package/ui/lib/read-map.js +173 -0
- package/ui/lib/read-progress.d.ts +14 -0
- package/ui/lib/read-progress.js +12 -0
- package/ui/lib/refusal-lines.d.ts +3 -0
- package/ui/lib/refusal-lines.js +8 -0
- package/ui/lib/studio-activity.d.ts +5 -0
- package/ui/lib/studio-activity.js +17 -0
- package/ui/lib/studio-console.d.ts +27 -0
- package/ui/lib/studio-console.js +9 -0
- package/ui/lib/studio-conversation.d.ts +20 -0
- package/ui/lib/studio-conversation.js +130 -0
- package/ui/lib/studio-examples.d.ts +31 -0
- package/ui/lib/studio-examples.js +25 -0
- package/ui/lib/studio-labels.d.ts +6 -0
- package/ui/lib/studio-labels.js +13 -0
- package/ui/lib/studio-map-kept.d.ts +17 -0
- package/ui/lib/studio-map-kept.js +48 -0
- package/ui/lib/studio-open.d.ts +22 -0
- package/ui/lib/studio-open.js +54 -0
- package/ui/lib/studio-page.d.ts +33 -0
- package/ui/lib/studio-page.js +121 -0
- package/ui/lib/studio-prefs.d.ts +26 -0
- package/ui/lib/studio-prefs.js +87 -0
- package/ui/lib/studio-project.d.ts +106 -0
- package/ui/lib/studio-project.js +177 -0
- package/ui/lib/studio-restore.d.ts +49 -0
- package/ui/lib/studio-restore.js +251 -0
- package/ui/lib/studio-run-open.d.ts +15 -0
- package/ui/lib/studio-run-open.js +16 -0
- package/ui/lib/studio-session.d.ts +34 -0
- package/ui/lib/studio-session.js +17 -0
- package/ui/lib/studio-settings.d.ts +15 -0
- package/ui/lib/studio-settings.js +48 -0
- package/ui/lib/studio-store.d.ts +64 -0
- package/ui/lib/studio-store.js +391 -0
- package/ui/lib/studio-watch.d.ts +12 -0
- package/ui/lib/studio-watch.js +27 -0
- package/ui/lib/tab-fills.d.ts +6 -0
- package/ui/lib/tab-fills.js +14 -0
- package/ui/lib/tool-credentials.d.ts +3 -0
- package/ui/lib/tool-credentials.js +23 -0
- package/ui/lib/tool-probe.d.ts +19 -0
- package/ui/lib/tool-probe.js +104 -0
- package/ui/lib/type-icons.d.ts +35 -0
- package/ui/lib/type-icons.js +38 -0
- package/ui/lib/when-idle.d.ts +5 -0
- package/ui/lib/when-idle.js +14 -0
- package/ui/lib/zip.d.ts +5 -0
- package/ui/lib/zip.js +8 -0
- package/ui/map-token.css +11 -0
- package/ui/map-token.d.ts +17 -0
- package/ui/map-token.js +29 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/motion.css +113 -0
- package/ui/pan.d.ts +32 -0
- package/ui/pan.js +90 -0
- package/ui/profile-editor.d.ts +33 -0
- package/ui/profile-editor.js +2638 -0
- package/ui/provider-editor.d.ts +15 -0
- package/ui/provider-editor.js +92 -0
- package/ui/read-map.css +48 -0
- package/ui/read-map.d.ts +7 -0
- package/ui/read-map.js +163 -0
- package/ui/refusal-detail.d.ts +4 -0
- package/ui/refusal-detail.js +21 -0
- package/ui/shared-change.d.ts +9 -0
- package/ui/shared-change.js +28 -0
- package/ui/shell-motion.css +121 -0
- package/ui/shell-motion.d.ts +73 -0
- package/ui/shell-motion.js +194 -0
- package/ui/studio-code-pane.d.ts +19 -0
- package/ui/studio-code-pane.js +44 -0
- package/ui/studio-compare.d.ts +80 -0
- package/ui/studio-compare.js +326 -0
- package/ui/studio-conflicts.d.ts +17 -0
- package/ui/studio-conflicts.js +87 -0
- package/ui/studio-connection.d.ts +64 -0
- package/ui/studio-connection.js +286 -0
- package/ui/studio-decision.d.ts +6 -0
- package/ui/studio-decision.js +49 -0
- package/ui/studio-embedding.d.ts +6 -0
- package/ui/studio-embedding.js +30 -0
- package/ui/studio-host-console.d.ts +6 -0
- package/ui/studio-host-console.js +17 -0
- package/ui/studio-host.d.ts +73 -0
- package/ui/studio-host.js +12 -0
- package/ui/studio-live.d.ts +17 -0
- package/ui/studio-live.js +26 -0
- package/ui/studio-map.css +273 -0
- package/ui/studio-map.d.ts +17 -0
- package/ui/studio-map.js +397 -0
- package/ui/studio-nav.d.ts +24 -0
- package/ui/studio-nav.js +17 -0
- package/ui/studio-no-project.d.ts +5 -0
- package/ui/studio-no-project.js +9 -0
- package/ui/studio-run.css +76 -0
- package/ui/studio-run.d.ts +14 -0
- package/ui/studio-run.js +119 -0
- package/ui/studio-runner.d.ts +45 -0
- package/ui/studio-runner.js +187 -0
- package/ui/studio-save.d.ts +49 -0
- package/ui/studio-save.js +307 -0
- package/ui/studio-screen.d.ts +61 -0
- package/ui/studio-screen.js +2078 -0
- package/ui/studio-settings.d.ts +31 -0
- package/ui/studio-settings.js +288 -0
- package/ui/studio-shell.css +27 -0
- package/ui/studio-shell.d.ts +43 -0
- package/ui/studio-shell.js +87 -0
- package/ui/studio-theme.d.ts +7 -0
- package/ui/studio-theme.js +94 -0
- package/ui/studio.css +427 -0
- package/ui/theorem-mark.css +84 -0
- package/ui/theorem-mark.d.ts +22 -0
- package/ui/theorem-mark.js +21 -0
- package/vite.d.mts +30 -0
- package/vite.mjs +277 -0
- package/workspace.d.ts +230 -0
- package/workspace.js +1054 -0
package/ui/studio-map.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The map: the providers, models, shared settings, profiles, and tools as tokens in columns, with a
|
|
4
|
+
* line from each to what it feeds. It changes nothing; a node opens what it stands for. `Expand`
|
|
5
|
+
* shows each profile as a card of its sections and each tool with its description. A node can be
|
|
6
|
+
* dragged to where it reads best, and the field dragged to pan it; `Tidy` puts every node back. A
|
|
7
|
+
* right click on a node shows its family alone: what feeds it and what it feeds. A search shows the
|
|
8
|
+
* family of every node it finds.
|
|
9
|
+
*/
|
|
10
|
+
import { Button } from '@astryxdesign/core/Button';
|
|
11
|
+
import { Center } from '@astryxdesign/core/Center';
|
|
12
|
+
import { ContextMenu } from '@astryxdesign/core/ContextMenu';
|
|
13
|
+
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
|
14
|
+
import { Icon } from '@astryxdesign/core/Icon';
|
|
15
|
+
import { IconButton } from '@astryxdesign/core/IconButton';
|
|
16
|
+
import { ScrollableArea } from '@astryxdesign/core/ScrollableArea';
|
|
17
|
+
import { Text } from '@astryxdesign/core/Text';
|
|
18
|
+
import { TextInput } from '@astryxdesign/core/TextInput';
|
|
19
|
+
import { IconArrowsMaximize, IconArrowsMinimize, IconCloud, IconCode, IconExternalLink, IconEye, IconFocus2, IconLayoutGrid, IconSearch, IconX, } from '@tabler/icons-react';
|
|
20
|
+
import { useEffect, useMemo, useRef, useState, } from 'react';
|
|
21
|
+
import { mapFamily, mapFound, mapLayout, mapLinkWords, mapNoteLines, workspaceMap, } from "../mod.js";
|
|
22
|
+
import { IconTypeSafe, modelFamilyMark } from "./brand-icons.js";
|
|
23
|
+
import { FACET_ICON } from "./lib/facet-icons.js";
|
|
24
|
+
import { readKeptMap, writeKeptMap } from "./lib/studio-map-kept.js";
|
|
25
|
+
import { useProject } from "./lib/studio-project.js";
|
|
26
|
+
import { PROFILE_TYPE_ICON, profileTypeIcon, toolTypeIcon } from "./lib/type-icons.js";
|
|
27
|
+
import { MapToken, useTokenWidths } from "./map-token.js";
|
|
28
|
+
import { drags, fieldStart, follow, scrollOnce, usePan } from "./pan.js";
|
|
29
|
+
import './studio-map.css';
|
|
30
|
+
/** With detail a node holds its icon, its name, and two lines of its note; a card's head, one; a row, a section. */
|
|
31
|
+
/** A token's slot: as wide as the longest name it shows in full. */
|
|
32
|
+
const TOKEN_SIZES = {
|
|
33
|
+
node: { width: 240, height: 24 },
|
|
34
|
+
card: { head: 60, row: 28, foot: 4 },
|
|
35
|
+
columnGap: 96,
|
|
36
|
+
rowGap: 6,
|
|
37
|
+
heading: 24,
|
|
38
|
+
groupGap: 16,
|
|
39
|
+
padding: 24,
|
|
40
|
+
lineGap: 8,
|
|
41
|
+
};
|
|
42
|
+
/** With detail a node is its token grown to hold its note: the same slot and the same gaps. */
|
|
43
|
+
const DETAIL_SIZES = { ...TOKEN_SIZES, node: { width: 240, height: 84 } };
|
|
44
|
+
const TOKEN_LABEL = 28;
|
|
45
|
+
/** A long name keeps its start. */
|
|
46
|
+
function clipped(label) {
|
|
47
|
+
return label.length > TOKEN_LABEL ? `${label.slice(0, TOKEN_LABEL - 1)}…` : label;
|
|
48
|
+
}
|
|
49
|
+
function nodeIcon({ kind, type, note }) {
|
|
50
|
+
const agent = kind === 'agent' ? profileTypeIcon(type) : undefined;
|
|
51
|
+
if (agent)
|
|
52
|
+
return agent;
|
|
53
|
+
if (kind === 'tool')
|
|
54
|
+
return toolTypeIcon(type ?? '');
|
|
55
|
+
// A model's note is its API model id, which names the family.
|
|
56
|
+
if (kind === 'model' && type === 'typesafe')
|
|
57
|
+
return IconTypeSafe;
|
|
58
|
+
if (kind === 'model')
|
|
59
|
+
return modelFamilyMark(note) ?? FACET_ICON.modelBinding;
|
|
60
|
+
if (kind === 'provider')
|
|
61
|
+
return IconCloud;
|
|
62
|
+
if (kind === 'shared' && type && Object.hasOwn(FACET_ICON, type)) {
|
|
63
|
+
return FACET_ICON[type];
|
|
64
|
+
}
|
|
65
|
+
return kind === 'agent' ? PROFILE_TYPE_ICON.text : IconCode;
|
|
66
|
+
}
|
|
67
|
+
function rowIcon(type) {
|
|
68
|
+
return Object.hasOwn(FACET_ICON, type) ? FACET_ICON[type] : IconCode;
|
|
69
|
+
}
|
|
70
|
+
/** Whether `selected`, the open node, is `node` or a section under it. */
|
|
71
|
+
function isOpen(node, selected) {
|
|
72
|
+
if (node.kind === 'agent')
|
|
73
|
+
return selected === node.id || selected.startsWith(`${node.id}/`);
|
|
74
|
+
if (node.kind === 'model')
|
|
75
|
+
return node.holds?.includes(selected) ?? false;
|
|
76
|
+
return node.kind === 'tool' && selected === node.id;
|
|
77
|
+
}
|
|
78
|
+
/** How far the pointer goes before a press is a drag, and how far an arrow key moves a node. */
|
|
79
|
+
const DRAG_FROM = 4;
|
|
80
|
+
const NUDGE = 24;
|
|
81
|
+
const ARROWS = {
|
|
82
|
+
ArrowLeft: { x: -1, y: 0 },
|
|
83
|
+
ArrowRight: { x: 1, y: 0 },
|
|
84
|
+
ArrowUp: { x: 0, y: -1 },
|
|
85
|
+
ArrowDown: { x: 0, y: 1 },
|
|
86
|
+
};
|
|
87
|
+
/** How the map was left, and what it shows for it: every node, or one node's family. */
|
|
88
|
+
function useKept(whole) {
|
|
89
|
+
const project = useProject()?.name;
|
|
90
|
+
const [kept, setKept] = useState(() => readKeptMap(project));
|
|
91
|
+
const keep = (next) => {
|
|
92
|
+
setKept(next);
|
|
93
|
+
writeKeptMap(project, next);
|
|
94
|
+
};
|
|
95
|
+
const { expanded } = kept;
|
|
96
|
+
const known = whole.columns
|
|
97
|
+
.flat()
|
|
98
|
+
.some((group) => group.nodes.some((node) => node.id === kept.family));
|
|
99
|
+
const family = known ? kept.family : undefined;
|
|
100
|
+
const map = useMemo(() => (family === undefined ? whole : mapFamily(whole, family)), [whole, family]);
|
|
101
|
+
// A family is laid out afresh from the top. Where its nodes are dragged lasts while it shows.
|
|
102
|
+
const [lens, setLens] = useState({});
|
|
103
|
+
const places = expanded ? 'detail' : 'tokens';
|
|
104
|
+
return {
|
|
105
|
+
expanded,
|
|
106
|
+
family,
|
|
107
|
+
map,
|
|
108
|
+
moved: family === undefined ? kept[places] : lens,
|
|
109
|
+
/** Puts dragged nodes at `next`. They are kept when a drag ends, not at every step of it. */
|
|
110
|
+
put(next, ended) {
|
|
111
|
+
if (family !== undefined)
|
|
112
|
+
setLens(next);
|
|
113
|
+
else if (ended)
|
|
114
|
+
keep({ ...kept, [places]: next });
|
|
115
|
+
else
|
|
116
|
+
setKept({ ...kept, [places]: next });
|
|
117
|
+
},
|
|
118
|
+
showFamily(id) {
|
|
119
|
+
setLens({});
|
|
120
|
+
const { tokens, detail } = kept;
|
|
121
|
+
keep({ tokens, detail, expanded, ...(id === undefined ? {} : { family: id }) });
|
|
122
|
+
},
|
|
123
|
+
expand() {
|
|
124
|
+
keep({ ...kept, expanded: !expanded });
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** What makes a node draggable, and movable with Alt and the arrow keys. */
|
|
129
|
+
function useDrag(layout, moved, put) {
|
|
130
|
+
/** A drag just ended on this node: the click that follows it opens nothing. */
|
|
131
|
+
const dropped = useRef(false);
|
|
132
|
+
const [dragging, setDragging] = useState();
|
|
133
|
+
const draggable = (id) => ({
|
|
134
|
+
onPointerDown: (event) => {
|
|
135
|
+
dropped.current = false;
|
|
136
|
+
const from = layout.nodes[id];
|
|
137
|
+
if (!from || !drags(event))
|
|
138
|
+
return;
|
|
139
|
+
let next;
|
|
140
|
+
follow(event, (x, y) => {
|
|
141
|
+
// A press that barely moves is a click.
|
|
142
|
+
if (!next && Math.hypot(x, y) < DRAG_FROM)
|
|
143
|
+
return;
|
|
144
|
+
if (!next)
|
|
145
|
+
setDragging(id);
|
|
146
|
+
next = { ...moved, [id]: { x: Math.max(from.x + x, 0), y: Math.max(from.y + y, 0) } };
|
|
147
|
+
put(next, false);
|
|
148
|
+
}, (lifted) => {
|
|
149
|
+
if (!next)
|
|
150
|
+
return;
|
|
151
|
+
dropped.current = lifted;
|
|
152
|
+
// The click, if one follows, comes at once; a drop off the node leaves none to wait for.
|
|
153
|
+
globalThis.setTimeout(() => {
|
|
154
|
+
dropped.current = false;
|
|
155
|
+
}, 0);
|
|
156
|
+
setDragging(undefined);
|
|
157
|
+
put(next, true);
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
onClickCapture: (event) => {
|
|
161
|
+
if (!dropped.current)
|
|
162
|
+
return;
|
|
163
|
+
dropped.current = false;
|
|
164
|
+
event.stopPropagation();
|
|
165
|
+
event.preventDefault();
|
|
166
|
+
},
|
|
167
|
+
onKeyDown: (event) => {
|
|
168
|
+
const step = event.altKey ? ARROWS[event.key] : undefined;
|
|
169
|
+
const place = layout.nodes[id];
|
|
170
|
+
if (!step || !place)
|
|
171
|
+
return;
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
const x = Math.max(place.x + step.x * NUDGE, 0);
|
|
174
|
+
const y = Math.max(place.y + step.y * NUDGE, 0);
|
|
175
|
+
put({ ...moved, [id]: { x, y } }, true);
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
return { dragging, draggable };
|
|
179
|
+
}
|
|
180
|
+
/** The node or section under the pointer or holding focus: its lines, and what they reach, stay lit. */
|
|
181
|
+
function useLit(layout) {
|
|
182
|
+
const [lit, setLit] = useState();
|
|
183
|
+
const [on, onLines] = useMemo(() => {
|
|
184
|
+
const ids = new Set();
|
|
185
|
+
const lines = new Set();
|
|
186
|
+
if (lit === undefined)
|
|
187
|
+
return [ids, lines];
|
|
188
|
+
ids.add(lit);
|
|
189
|
+
for (const { link, from, to } of layout.lines) {
|
|
190
|
+
if (![link.from, link.to, from, to].includes(lit))
|
|
191
|
+
continue;
|
|
192
|
+
lines.add(link);
|
|
193
|
+
for (const id of [link.from, link.to, from, to])
|
|
194
|
+
ids.add(id);
|
|
195
|
+
}
|
|
196
|
+
return [ids, lines];
|
|
197
|
+
}, [layout, lit]);
|
|
198
|
+
const lights = (id) => ({
|
|
199
|
+
onPointerEnter: () => {
|
|
200
|
+
setLit(id);
|
|
201
|
+
},
|
|
202
|
+
onPointerLeave: () => {
|
|
203
|
+
setLit(undefined);
|
|
204
|
+
},
|
|
205
|
+
onFocus: () => {
|
|
206
|
+
setLit(id);
|
|
207
|
+
},
|
|
208
|
+
onBlur: () => {
|
|
209
|
+
setLit(undefined);
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return { lit, on, onLines, lights };
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* A family, and the map with or without detail, each start from the top left, or as near it as
|
|
216
|
+
* shows the node that is open.
|
|
217
|
+
*/
|
|
218
|
+
function useStart(field, family, open) {
|
|
219
|
+
const latest = useRef(open.place);
|
|
220
|
+
latest.current = open.place;
|
|
221
|
+
// invariant: the sizes are one of two constants, so they change exactly when the detail does.
|
|
222
|
+
const { sizes } = open;
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
const node = field.current;
|
|
225
|
+
if (!node)
|
|
226
|
+
return;
|
|
227
|
+
const place = latest.current;
|
|
228
|
+
const at = family === undefined ? place : undefined;
|
|
229
|
+
// The field starts past the room around it.
|
|
230
|
+
return scrollOnce(node, (scroller) => {
|
|
231
|
+
const hidden = at !== undefined &&
|
|
232
|
+
(at.x + sizes.node.width > scroller.clientWidth ||
|
|
233
|
+
at.y + at.height > scroller.clientHeight);
|
|
234
|
+
const { left, top } = fieldStart(node, scroller);
|
|
235
|
+
return {
|
|
236
|
+
left: left + (hidden ? at.x - sizes.padding : 0),
|
|
237
|
+
top: top + (hidden ? at.y - sizes.padding - sizes.heading : 0),
|
|
238
|
+
};
|
|
239
|
+
});
|
|
240
|
+
}, [field, family, sizes]);
|
|
241
|
+
}
|
|
242
|
+
/** What a right click offers: on a node, its family and what it opens; anywhere, the field's own. */
|
|
243
|
+
function menuOf(onNode, onOpen, actions) {
|
|
244
|
+
const { family, expanded, isMoved, showFamily, expand, tidy } = actions;
|
|
245
|
+
const forNode = [];
|
|
246
|
+
if (onNode && onNode.id !== family) {
|
|
247
|
+
forNode.push({
|
|
248
|
+
label: 'Show only its family',
|
|
249
|
+
icon: IconFocus2,
|
|
250
|
+
onClick: () => {
|
|
251
|
+
showFamily(onNode.id);
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
const opens = onNode?.opens;
|
|
256
|
+
if (opens !== undefined) {
|
|
257
|
+
forNode.push({
|
|
258
|
+
label: 'Open',
|
|
259
|
+
icon: IconExternalLink,
|
|
260
|
+
onClick: () => {
|
|
261
|
+
onOpen(opens);
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
if (onNode)
|
|
266
|
+
forNode.push({ type: 'divider' });
|
|
267
|
+
const everything = {
|
|
268
|
+
label: 'Show everything',
|
|
269
|
+
icon: IconEye,
|
|
270
|
+
onClick: () => {
|
|
271
|
+
showFamily(undefined);
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
return [
|
|
275
|
+
...forNode,
|
|
276
|
+
...(family === undefined ? [] : [everything]),
|
|
277
|
+
{
|
|
278
|
+
label: expanded ? 'Collapse' : 'Expand',
|
|
279
|
+
icon: expanded ? IconArrowsMinimize : IconArrowsMaximize,
|
|
280
|
+
onClick: expand,
|
|
281
|
+
},
|
|
282
|
+
{ label: 'Tidy', icon: IconLayoutGrid, isDisabled: !isMoved, onClick: tidy },
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
/** Over the field's corner: the search, what the right click offers, and Close. */
|
|
286
|
+
function MapTools({ actions, query, onQuery, onClose, }) {
|
|
287
|
+
const { family, expanded, isMoved, showFamily, expand, tidy } = actions;
|
|
288
|
+
return (_jsxs("div", { className: "studio-map-tools", children: [_jsx(TextInput, { label: "Search the map", isLabelHidden: true, size: "sm", width: 200, placeholder: "Search", startIcon: IconSearch, hasClear: true, value: query, onChange: onQuery }), family !== undefined && (_jsx(Button, { label: "Show everything", variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconEye, size: "sm" }), onClick: () => {
|
|
289
|
+
showFamily(undefined);
|
|
290
|
+
} })), isMoved && (_jsx(Button, { label: "Tidy", variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconLayoutGrid, size: "sm" }), onClick: tidy })), _jsx(IconButton, { label: expanded ? 'Collapse' : 'Expand', variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: expanded ? IconArrowsMinimize : IconArrowsMaximize, size: "sm" }), tooltip: expanded ? 'Show each node as a token' : 'Show sections and descriptions', onClick: expand }), _jsx(IconButton, { label: "Close map", variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconX, size: "sm" }), tooltip: "Close the map", onClick: onClose })] }));
|
|
291
|
+
}
|
|
292
|
+
/** What marks a node on the field, whichever way it is drawn. */
|
|
293
|
+
function marks({ node, on, isFound, isDragging }) {
|
|
294
|
+
const isLit = on.has(node.id) || (node.rows ?? []).some((row) => on.has(row.id));
|
|
295
|
+
return {
|
|
296
|
+
'data-map-node': node.id,
|
|
297
|
+
'data-on': isLit ? '' : undefined,
|
|
298
|
+
'data-found': isFound ? '' : undefined,
|
|
299
|
+
'data-dragging': isDragging ? '' : undefined,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function TokenNode(props) {
|
|
303
|
+
const { node, place, joins, selected, onOpen, lights, drag } = props;
|
|
304
|
+
const { opens } = node;
|
|
305
|
+
const open = isOpen(node, selected);
|
|
306
|
+
return (_jsx(MapToken, { id: node.id, label: clipped(node.long ?? node.label), icon: nodeIcon(node), x: place.x, y: place.y + place.height / 2, color: open ? 'blue' : undefined, description: [node.note, joins].filter(Boolean).join('. ') || undefined, onOpen: opens === undefined
|
|
307
|
+
? undefined
|
|
308
|
+
: () => {
|
|
309
|
+
onOpen(opens);
|
|
310
|
+
}, title: (node.long ?? node.label).length > TOKEN_LABEL ? (node.long ?? node.label) : undefined, "aria-current": open ? 'true' : undefined, ...marks(props), ...lights(node.id), ...drag }));
|
|
311
|
+
}
|
|
312
|
+
/** With detail: a node's icon, name and note, and under a profile's a row for each section. */
|
|
313
|
+
function DetailNode(props) {
|
|
314
|
+
const { node, place, joins, selected, on, onOpen, lights, drag } = props;
|
|
315
|
+
const { opens } = node;
|
|
316
|
+
const rows = node.rows ?? [];
|
|
317
|
+
const box = { left: place.x, top: place.y, height: place.height };
|
|
318
|
+
const head = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "studio-map-name", children: [_jsx(Icon, { icon: nodeIcon(node), size: "sm" }), _jsx(Text, { type: "supporting", color: "inherit", weight: "medium", maxLines: 1, hasTruncateTooltip: false, children: node.label })] }), node.note && (_jsx(Text, { type: "supporting", maxLines: rows.length > 0 ? 1 : mapNoteLines(node), hasTruncateTooltip: false, className: "studio-map-note", children: node.note }))] }));
|
|
319
|
+
const button = {
|
|
320
|
+
type: 'button',
|
|
321
|
+
title: node.note || undefined,
|
|
322
|
+
'aria-description': joins || undefined,
|
|
323
|
+
disabled: opens === undefined,
|
|
324
|
+
onClick: () => {
|
|
325
|
+
if (opens !== undefined)
|
|
326
|
+
onOpen(opens);
|
|
327
|
+
},
|
|
328
|
+
...lights(node.id),
|
|
329
|
+
};
|
|
330
|
+
if (rows.length === 0) {
|
|
331
|
+
return (_jsx("button", { ...button, ...drag, ...marks(props), className: "studio-map-node", "data-map-token": node.id, style: box, "aria-current": isOpen(node, selected) ? 'true' : undefined, children: head }));
|
|
332
|
+
}
|
|
333
|
+
return (_jsxs("div", { className: "studio-map-card", "data-map-token": node.id, style: box, "data-open": isOpen(node, selected) ? '' : undefined, ...marks(props), ...drag, children: [_jsx("button", { ...button, className: "studio-map-head", "aria-current": selected === node.id ? 'true' : undefined, children: head }), rows.map((row) => (_jsxs("button", { type: "button", className: "studio-map-row", "aria-current": selected === row.id ? 'true' : undefined, "data-on": on.has(row.id) ? '' : undefined, onClick: () => {
|
|
334
|
+
onOpen(row.opens);
|
|
335
|
+
}, ...lights(row.id), children: [_jsx(Icon, { icon: rowIcon(row.type), size: "sm" }), _jsx(Text, { type: "supporting", color: "inherit", weight: "medium", maxLines: 1, hasTruncateTooltip: false, children: row.label }), row.note && (_jsx(Text, { type: "supporting", maxLines: 1, justify: "end", hasTruncateTooltip: false, className: "studio-map-row-note", children: row.note }))] }, row.id)))] }));
|
|
336
|
+
}
|
|
337
|
+
export function StudioMap({ workspace, shared, selected, onOpen, onClose, }) {
|
|
338
|
+
const whole = useMemo(() => workspaceMap(workspace, shared), [workspace, shared]);
|
|
339
|
+
const { expanded, family, map: kept, moved, put, showFamily, expand } = useKept(whole);
|
|
340
|
+
const [query, setQuery] = useState('');
|
|
341
|
+
const found = useMemo(() => mapFound(kept, query), [kept, query]);
|
|
342
|
+
const map = useMemo(() => (query.trim() === '' ? kept : mapFamily(kept, found)), [kept, query, found]);
|
|
343
|
+
const isNothing = query.trim() !== '' && found.length === 0;
|
|
344
|
+
const field = useRef(null);
|
|
345
|
+
const sizes = expanded ? DETAIL_SIZES : TOKEN_SIZES;
|
|
346
|
+
const drawn = useMemo(() => ({ map, expanded }), [map, expanded]);
|
|
347
|
+
const widths = useTokenWidths(field, drawn);
|
|
348
|
+
const layout = useMemo(() => mapLayout(map, sizes, { moved, widths, detail: expanded }), [map, sizes, expanded, moved, widths]);
|
|
349
|
+
const nodes = map.columns.flat().flatMap((group) => group.nodes);
|
|
350
|
+
const opened = nodes.find((node) => isOpen(node, selected))?.id;
|
|
351
|
+
useStart(field, family, {
|
|
352
|
+
place: opened === undefined ? undefined : layout.nodes[opened],
|
|
353
|
+
sizes,
|
|
354
|
+
});
|
|
355
|
+
const { dragging, draggable } = useDrag(layout, moved, put);
|
|
356
|
+
const { panning, pan } = usePan('[data-map-node]');
|
|
357
|
+
const { lit, on, onLines, lights } = useLit(layout);
|
|
358
|
+
const actions = {
|
|
359
|
+
family,
|
|
360
|
+
expanded,
|
|
361
|
+
isMoved: nodes.some((node) => Object.hasOwn(moved, node.id)),
|
|
362
|
+
showFamily,
|
|
363
|
+
expand,
|
|
364
|
+
tidy: () => {
|
|
365
|
+
put({}, true);
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
/** The node the menu was opened on, if on one. */
|
|
369
|
+
const [menuOn, setMenuOn] = useState();
|
|
370
|
+
const menu = menuOf(nodes.find((node) => node.id === menuOn), onOpen, actions);
|
|
371
|
+
const Node = expanded ? DetailNode : TokenNode;
|
|
372
|
+
const open = nodes.find((node) => isOpen(node, selected))?.id;
|
|
373
|
+
// The open node's lines are drawn over the rest, and the lit ones over those.
|
|
374
|
+
const over = (line) => onLines.has(line.link) ? 2 : line.from === open || line.to === open ? 1 : 0;
|
|
375
|
+
const lines = [...layout.lines].sort((a, b) => over(a) - over(b));
|
|
376
|
+
return (_jsxs("div", { className: "studio-map-frame",
|
|
377
|
+
// why: the frame only wraps the map; Escape from anywhere inside it leaves the family.
|
|
378
|
+
role: "none", onKeyDown: (event) => {
|
|
379
|
+
if (event.key !== 'Escape' || family === undefined)
|
|
380
|
+
return;
|
|
381
|
+
// Escape closes an open menu first; that one is not for the family.
|
|
382
|
+
if (event.target.closest('[role="menu"]'))
|
|
383
|
+
return;
|
|
384
|
+
event.stopPropagation();
|
|
385
|
+
showFamily(undefined);
|
|
386
|
+
}, children: [_jsx(ScrollableArea, { className: "studio-map-scroll", axis: "both", role: "region", label: "Map", height: "100%", overscroll: "allow", children: _jsx(ContextMenu, { items: menu, label: "Map actions", "data-map-field": "", children: _jsxs("div", { ref: field, className: "studio-map", "data-lit": lit === undefined || dragging !== undefined ? undefined : '', "data-panning": panning ? '' : undefined, onPointerDown: pan, onContextMenuCapture: (event) => {
|
|
387
|
+
setMenuOn(event.target.closest('[data-map-node]')?.dataset.mapNode);
|
|
388
|
+
}, style: {
|
|
389
|
+
'--map-width': `${String(layout.width)}px`,
|
|
390
|
+
'--map-height': `${String(layout.height)}px`,
|
|
391
|
+
'--map-slot': `${String(sizes.node.width)}px`,
|
|
392
|
+
'--map-line': `${String(sizes.card.row)}px`,
|
|
393
|
+
}, children: [_jsx("svg", { className: "studio-map-lines", width: layout.width, height: layout.height, "aria-hidden": "true", children: lines.map(({ link, from, to, d }) => (_jsx("path", { className: "map-line", d: d, "data-kind": link.kind, "data-on": onLines.has(link) ? '' : undefined, "data-open": from === open || to === open ? '' : undefined }, `${link.kind}:${link.from}:${link.to}`))) }), layout.headings.map(({ label, x, y }) => (_jsx("div", { className: "studio-map-heading", style: { left: x, top: y }, children: _jsx(Text, { type: "supporting", color: "secondary", weight: "semibold", children: label }) }, label))), nodes.map((node) => {
|
|
394
|
+
const place = layout.nodes[node.id];
|
|
395
|
+
return (place && (_jsx(Node, { node: node, place: place, joins: mapLinkWords(map, node.id), selected: selected, on: on, isFound: found.includes(node.id), isDragging: dragging === node.id, onOpen: onOpen, lights: lights, drag: draggable(node.id) }, node.id)));
|
|
396
|
+
})] }) }) }), isNothing && (_jsx(Center, { className: "studio-map-nothing", children: _jsx(EmptyState, { headingLevel: 2, icon: _jsx(Icon, { icon: IconSearch, size: "lg" }), title: "Nothing matches" }) })), _jsx(MapTools, { actions: actions, query: query, onQuery: setQuery, onClose: onClose })] }));
|
|
397
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Where the website is. A studio running on its own sends its other pages there. */
|
|
2
|
+
export declare const THEOREM_SITE = "https://theorem.masudlewis.com";
|
|
3
|
+
/** The width of the studio's side nav: the tree's, and Settings' sections. */
|
|
4
|
+
export declare const NAV_WIDTH = 256;
|
|
5
|
+
/** The website's pages, in the rail's order. */
|
|
6
|
+
export declare const SITE_PAGES: readonly [{
|
|
7
|
+
readonly label: "Studio";
|
|
8
|
+
readonly href: "/studio";
|
|
9
|
+
}, {
|
|
10
|
+
readonly label: "Docs";
|
|
11
|
+
readonly href: "/docs";
|
|
12
|
+
}];
|
|
13
|
+
/** Where the package is published, in the rail's order. */
|
|
14
|
+
export declare function packageLinks(name: string, repository: string): readonly [{
|
|
15
|
+
readonly label: "GitHub";
|
|
16
|
+
readonly href: string;
|
|
17
|
+
}, {
|
|
18
|
+
readonly label: "JSR";
|
|
19
|
+
readonly href: `https://jsr.io/${string}`;
|
|
20
|
+
}, {
|
|
21
|
+
readonly label: "npm";
|
|
22
|
+
readonly href: `https://www.npmjs.com/package/${string}`;
|
|
23
|
+
}];
|
|
24
|
+
export type PackageLinks = ReturnType<typeof packageLinks>;
|
package/ui/studio-nav.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Where the website is. A studio running on its own sends its other pages there. */
|
|
2
|
+
export const THEOREM_SITE = 'https://theorem.masudlewis.com';
|
|
3
|
+
/** The width of the studio's side nav: the tree's, and Settings' sections. */
|
|
4
|
+
export const NAV_WIDTH = 256;
|
|
5
|
+
/** The website's pages, in the rail's order. */
|
|
6
|
+
export const SITE_PAGES = [
|
|
7
|
+
{ label: 'Studio', href: '/studio' },
|
|
8
|
+
{ label: 'Docs', href: '/docs' },
|
|
9
|
+
];
|
|
10
|
+
/** Where the package is published, in the rail's order. */
|
|
11
|
+
export function packageLinks(name, repository) {
|
|
12
|
+
return [
|
|
13
|
+
{ label: 'GitHub', href: repository },
|
|
14
|
+
{ label: 'JSR', href: `https://jsr.io/${name}` },
|
|
15
|
+
{ label: 'npm', href: `https://www.npmjs.com/package/${encodeURIComponent(name)}` },
|
|
16
|
+
];
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Center } from '@astryxdesign/core/Center';
|
|
3
|
+
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
|
4
|
+
import { Icon } from '@astryxdesign/core/Icon';
|
|
5
|
+
import { IconPlugConnectedX } from '@tabler/icons-react';
|
|
6
|
+
/** Shown in place of the studio when no local server has a project open. */
|
|
7
|
+
export function StudioNoProject({ title = 'No project is open', description = 'Start the studio server in your project (studio/server/serve.ts with your setup file), then reload.', }) {
|
|
8
|
+
return (_jsx(Center, { style: { height: '100%' }, children: _jsx(EmptyState, { headingLevel: 1, icon: _jsx(Icon, { icon: IconPlugConnectedX, size: "lg" }), title: title, description: description }) }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* Live voice still reads these from the root. */
|
|
2
|
+
:root {
|
|
3
|
+
--color-ink: #000000;
|
|
4
|
+
--color-paper: #f7f5f0;
|
|
5
|
+
--color-paper-bright: #ffffff;
|
|
6
|
+
--color-mute: #464646;
|
|
7
|
+
--ink-ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
8
|
+
--ink-duration: 0.3s;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* The runner fills the room the host gives the page. Keys and View trace sit above it, on the
|
|
12
|
+
page's inset. */
|
|
13
|
+
.run-page {
|
|
14
|
+
position: relative;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
height: 100%;
|
|
18
|
+
min-height: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
overscroll-behavior: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.run-controls {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex: none;
|
|
26
|
+
justify-content: flex-end;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: var(--spacing-2);
|
|
29
|
+
padding: var(--spacing-4);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.run-page > :not(.run-controls),
|
|
33
|
+
.run-page > .astryx-layout,
|
|
34
|
+
.run-page > div > .astryx-layout,
|
|
35
|
+
.run-chat {
|
|
36
|
+
flex: 1;
|
|
37
|
+
min-height: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* The page's own controls are the header row. An empty layout header would
|
|
41
|
+
only repeat that inset. */
|
|
42
|
+
.run-chat > div > .astryx-layout-header:not(:has(button)),
|
|
43
|
+
.run-page > .astryx-layout > div > .astryx-layout-header:not(:has(button)),
|
|
44
|
+
.run-page > div > .astryx-layout > div > .astryx-layout-header:not(:has(button)) {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* The split is already at its final widths, so the panels slide into place
|
|
49
|
+
and the fields inside the request do not reflow while they move. */
|
|
50
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
51
|
+
.run-page .theorem-console-request {
|
|
52
|
+
animation: theorem-console-request var(--duration-medium) var(--ease-standard);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.run-page .theorem-console-response {
|
|
56
|
+
animation: theorem-console-response var(--duration-medium) var(--ease-standard);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@keyframes theorem-console-request {
|
|
61
|
+
from {
|
|
62
|
+
/* Two fifths of the pair, shifted by three quarters of itself, starts centred. */
|
|
63
|
+
translate: 75% 0;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@keyframes theorem-console-response {
|
|
68
|
+
from {
|
|
69
|
+
translate: 100% 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* In the shell, the run page is clipped to the panel's corners. */
|
|
74
|
+
[role="main"]:has(.run-page) {
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type StudioRunPayload } from '../mod.ts';
|
|
2
|
+
import type { StudioRunOpened } from './lib/studio-run-open.ts';
|
|
3
|
+
import type { StudioSupplies } from './studio-host.ts';
|
|
4
|
+
/** The name the page and the document title use. A host has no handle: it's named by its id. */
|
|
5
|
+
export declare function studioRunTitle(payload: StudioRunPayload): string;
|
|
6
|
+
/**
|
|
7
|
+
* An agent opened in a tab of its own: the runner fills the page, with View trace and Keys above it.
|
|
8
|
+
* `opened` is what `openStudioRun` returned for this tab.
|
|
9
|
+
*/
|
|
10
|
+
export declare function StudioRunScreen({ opened, supplies, }: {
|
|
11
|
+
opened: StudioRunOpened;
|
|
12
|
+
/** What the page gives the run, as it gives the studio. */
|
|
13
|
+
supplies?: StudioSupplies;
|
|
14
|
+
}): import("react").JSX.Element;
|
package/ui/studio-run.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@astryxdesign/core/Button';
|
|
3
|
+
import { Icon } from '@astryxdesign/core/Icon';
|
|
4
|
+
import { Popover } from '@astryxdesign/core/Popover';
|
|
5
|
+
import { IconActivity, IconKey } from '@tabler/icons-react';
|
|
6
|
+
import { TheoremThemeProvider, TracePlacement } from '@theoremjs/react/ui';
|
|
7
|
+
import { useState, useSyncExternalStore } from 'react';
|
|
8
|
+
import { studioKeySlots } from "../browser.js";
|
|
9
|
+
import { modelRoute, modelSlots, onStudioLocal, STUDIO_PROVIDERS, } from "../mod.js";
|
|
10
|
+
import { recordsTraces } from "./lib/project-traces.js";
|
|
11
|
+
import { ProjectContext } from "./lib/studio-project.js";
|
|
12
|
+
import { StudioKeys, useStudioConnection } from "./studio-connection.js";
|
|
13
|
+
import { StudioRunner } from "./studio-runner.js";
|
|
14
|
+
import './studio-run.css';
|
|
15
|
+
/** The width below which these controls keep only their icon. */
|
|
16
|
+
const PHONE = '(width < 768px)';
|
|
17
|
+
function usePhone() {
|
|
18
|
+
return useSyncExternalStore((change) => {
|
|
19
|
+
const query = globalThis.matchMedia(PHONE);
|
|
20
|
+
query.addEventListener('change', change);
|
|
21
|
+
return () => {
|
|
22
|
+
query.removeEventListener('change', change);
|
|
23
|
+
};
|
|
24
|
+
}, () => globalThis.matchMedia(PHONE).matches, () => false);
|
|
25
|
+
}
|
|
26
|
+
/** The name the page and the document title use. A host has no handle: it's named by its id. */
|
|
27
|
+
export function studioRunTitle(payload) {
|
|
28
|
+
return 'identity' in payload.profile ? payload.profile.identity.handle : payload.agentId;
|
|
29
|
+
}
|
|
30
|
+
/** `payload` with each `from` slot, on its agent and the agents it calls, renamed to `to`. */
|
|
31
|
+
function withRenamedSlot(current, from, to) {
|
|
32
|
+
const rename = (value) => ({
|
|
33
|
+
...value,
|
|
34
|
+
...(value.keySlot === from ? { keySlot: to } : {}),
|
|
35
|
+
...(value.fallbackKeySlot === from ? { fallbackKeySlot: to } : {}),
|
|
36
|
+
});
|
|
37
|
+
const renamed = (profile) => profile.type === 'host'
|
|
38
|
+
? profile
|
|
39
|
+
: {
|
|
40
|
+
...profile,
|
|
41
|
+
models: Object.fromEntries(Object.entries(profile.models).map(([id, model]) => [id, rename(model)])),
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
...current,
|
|
45
|
+
profile: renamed(current.profile),
|
|
46
|
+
...(current.dependencies
|
|
47
|
+
? {
|
|
48
|
+
dependencies: current.dependencies.map((dependency) => ({
|
|
49
|
+
...dependency,
|
|
50
|
+
profile: renamed(dependency.profile),
|
|
51
|
+
})),
|
|
52
|
+
}
|
|
53
|
+
: {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** The providers a run's models name: the ones its agent compiled with, else the studio's own. */
|
|
57
|
+
function providersOf(payload) {
|
|
58
|
+
return payload.providers ?? STUDIO_PROVIDERS;
|
|
59
|
+
}
|
|
60
|
+
/** `payload` with `slot` as the key of each model that reads none, unless it is a host. */
|
|
61
|
+
function withAddedSlot(current, slot) {
|
|
62
|
+
if (current.profile.type === 'host')
|
|
63
|
+
return current;
|
|
64
|
+
return {
|
|
65
|
+
...current,
|
|
66
|
+
profile: {
|
|
67
|
+
...current.profile,
|
|
68
|
+
models: Object.fromEntries(Object.entries(current.profile.models).map(([id, binding]) => [
|
|
69
|
+
id,
|
|
70
|
+
modelSlots(providersOf(current), binding).keySlot
|
|
71
|
+
? binding
|
|
72
|
+
: { ...binding, keySlot: slot },
|
|
73
|
+
])),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** The Keys button and the popover it opens. */
|
|
78
|
+
function KeysPopover({ connection, isOpen, onOpenChange, onAddSlot, onRenameSlot, phone, }) {
|
|
79
|
+
return (_jsx(Popover, { label: "Keys", placement: "below", alignment: "end", width: 360, isOpen: isOpen, onOpenChange: onOpenChange, content: _jsx(StudioKeys, { connection: connection, onAddSlot: onAddSlot, onRenameSlot: onRenameSlot }), children: _jsx(Button, { label: "Keys", isIconOnly: phone, icon: _jsx(Icon, { icon: IconKey, size: "sm" }), "aria-pressed": isOpen }) }));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* An agent opened in a tab of its own: the runner fills the page, with View trace and Keys above it.
|
|
83
|
+
* `opened` is what `openStudioRun` returned for this tab.
|
|
84
|
+
*/
|
|
85
|
+
export function StudioRunScreen({ opened, supplies, }) {
|
|
86
|
+
const [payload, setPayload] = useState(opened.payload);
|
|
87
|
+
// The agent and the agents it calls each read their own key slots.
|
|
88
|
+
const profiles = [
|
|
89
|
+
payload.profile,
|
|
90
|
+
...(payload.dependencies ?? []).map((dependency) => dependency.profile),
|
|
91
|
+
];
|
|
92
|
+
const models = profiles.flatMap((profile) => profile.type === 'host'
|
|
93
|
+
? []
|
|
94
|
+
: Object.values(profile.models).map((binding) => ({
|
|
95
|
+
...modelRoute(providersOf(payload), profile.type, binding),
|
|
96
|
+
builtInTools: binding.builtInTools,
|
|
97
|
+
local: onStudioLocal(binding.provider, payload.providers !== undefined),
|
|
98
|
+
})));
|
|
99
|
+
const connection = useStudioConnection(models, payload.localBaseUrl, [
|
|
100
|
+
...profiles.flatMap((profile) => studioKeySlots(profile)),
|
|
101
|
+
...profiles.flatMap((profile) => profile.type === 'host'
|
|
102
|
+
? []
|
|
103
|
+
: Object.values(profile.models).flatMap((binding) => Object.values(modelSlots(providersOf(payload), binding)))),
|
|
104
|
+
], supplies);
|
|
105
|
+
const [keysOpen, setKeysOpen] = useState(payload.connectionMode === 'byok');
|
|
106
|
+
const traced = recordsTraces(payload);
|
|
107
|
+
const [traceOpen, setTraceOpen] = useState(false);
|
|
108
|
+
const phone = usePhone();
|
|
109
|
+
const { mode, runtime } = connection;
|
|
110
|
+
const title = studioRunTitle(payload);
|
|
111
|
+
const renameSlot = (from, to) => {
|
|
112
|
+
setPayload((current) => withRenamedSlot(current, from, to));
|
|
113
|
+
};
|
|
114
|
+
return (_jsxs(TheoremThemeProvider, { mode: "dark", children: [_jsx("title", { children: `${title} · theorem studio` }), _jsx(TracePlacement, { value: "panel", children: _jsxs("div", { className: "run-page", children: [_jsxs("div", { className: "run-controls", children: [traced ? (_jsx(Button, { label: traceOpen ? 'Hide trace' : 'View trace', isIconOnly: phone, icon: _jsx(Icon, { icon: IconActivity, size: "sm" }), "aria-pressed": traceOpen, onClick: () => {
|
|
115
|
+
setTraceOpen((open) => !open);
|
|
116
|
+
} })) : null, _jsx(KeysPopover, { connection: connection, isOpen: keysOpen, onOpenChange: setKeysOpen, onAddSlot: (slot) => {
|
|
117
|
+
setPayload((current) => withAddedSlot(current, slot));
|
|
118
|
+
}, onRenameSlot: renameSlot, phone: phone })] }), _jsx(ProjectContext.Provider, { value: opened.project, children: _jsx(StudioRunner, { payload: payload, mode: mode, runtime: runtime, trace: traced ? traceOpen : undefined, flush: true, columns: true, className: "run-chat" }, mode) })] }) })] }));
|
|
119
|
+
}
|