@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
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell is one surface. It starts as the whole viewport, where the mark draws, and moves to
|
|
3
|
+
* the shape of the page it opens. The website and the studio on its own both move it with this.
|
|
4
|
+
*/
|
|
5
|
+
/** This document has already drawn the mark. A full reload clears it; a route change does not. */
|
|
6
|
+
let played = false;
|
|
7
|
+
export function bootHasPlayed() {
|
|
8
|
+
return played;
|
|
9
|
+
}
|
|
10
|
+
/** Fired when the mark gives way, before the shell moves, so the cover attribute can drop. */
|
|
11
|
+
export const SHELL_REVEAL = 'theorem-shell-reveal';
|
|
12
|
+
/** Fired when the shell has settled and scroll may own it again. */
|
|
13
|
+
export const SHELL_INTRO_DONE = 'theorem-shell-intro-done';
|
|
14
|
+
export const OPEN_SHAPE = { kind: 'open' };
|
|
15
|
+
export const REGULAR_SHAPE = { kind: 'regular' };
|
|
16
|
+
const FRAME_WIDTH = '--shell-frame-width';
|
|
17
|
+
const MAIN_ID = 'astryx-app-shell-main';
|
|
18
|
+
const MOVE_CAP_MS = 1000;
|
|
19
|
+
/** How long content takes to fade out. Keep in step with `--shell-hold-ms` in shell-motion.css. */
|
|
20
|
+
export const HOLD_MS = 140;
|
|
21
|
+
/**
|
|
22
|
+
* The shape of a page whose shell is a surface inside the page area, measured from the page on
|
|
23
|
+
* screen. That surface carries `data-shell-frame`; a page without one rests in the regular shape.
|
|
24
|
+
*/
|
|
25
|
+
export function frameShape() {
|
|
26
|
+
const frame = document.querySelector('[data-shell-frame]');
|
|
27
|
+
return frame ? { kind: 'frame', width: frame.offsetWidth } : REGULAR_SHAPE;
|
|
28
|
+
}
|
|
29
|
+
/** Where the shell last came to rest, for a route change that gave no warning. */
|
|
30
|
+
let rest = OPEN_SHAPE;
|
|
31
|
+
/** The shape measured when a route change began, while the page it leaves was still on screen. */
|
|
32
|
+
let captured = null;
|
|
33
|
+
export function captureShape(shape) {
|
|
34
|
+
captured = shape;
|
|
35
|
+
}
|
|
36
|
+
export function hasCapturedShape() {
|
|
37
|
+
return captured !== null;
|
|
38
|
+
}
|
|
39
|
+
export function peekShape() {
|
|
40
|
+
return captured ?? rest;
|
|
41
|
+
}
|
|
42
|
+
/** The shape the shell is leaving, and the claim on it: the next route change starts afresh. */
|
|
43
|
+
export function takeShape() {
|
|
44
|
+
const shape = captured ?? rest;
|
|
45
|
+
captured = null;
|
|
46
|
+
return shape;
|
|
47
|
+
}
|
|
48
|
+
export function prefersReducedMotion() {
|
|
49
|
+
return globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
50
|
+
}
|
|
51
|
+
export function sleep(ms) {
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
globalThis.setTimeout(resolve, ms);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** What a host lays over the shell that pins it in place, such as a side panel. */
|
|
57
|
+
let pinned = () => false;
|
|
58
|
+
/** The host names when something of its own holds the shell still. */
|
|
59
|
+
export function pinShellWhile(check) {
|
|
60
|
+
pinned = check;
|
|
61
|
+
}
|
|
62
|
+
/** Whether the shell can move. A narrow screen, reduced motion, and a host's pin cannot. */
|
|
63
|
+
export function shellCanMove() {
|
|
64
|
+
return (globalThis.matchMedia('(min-width: 768px)').matches &&
|
|
65
|
+
!globalThis.matchMedia('(prefers-reduced-motion: reduce)').matches &&
|
|
66
|
+
!pinned());
|
|
67
|
+
}
|
|
68
|
+
/** The mark is still up, or the shell is on its way: scroll must not move the shell yet. */
|
|
69
|
+
export function shellIntroOwnsPull() {
|
|
70
|
+
const root = document.documentElement;
|
|
71
|
+
return root.hasAttribute('data-boot-pending') || root.hasAttribute('data-shell-move');
|
|
72
|
+
}
|
|
73
|
+
function showContent(root) {
|
|
74
|
+
root.dataset.shellIn = '';
|
|
75
|
+
globalThis.setTimeout(() => {
|
|
76
|
+
delete root.dataset.shellIn;
|
|
77
|
+
}, 320);
|
|
78
|
+
}
|
|
79
|
+
/** The page's content fades out and stays out until the shell settles. */
|
|
80
|
+
export function holdShell() {
|
|
81
|
+
document.documentElement.dataset.shellHold = '';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Put the shell into move mode at the shape it is in. A route that mounts under it then finds the
|
|
85
|
+
* shell already drawn as a clip and cannot snap it to its own shape.
|
|
86
|
+
*/
|
|
87
|
+
export function enterShell(from) {
|
|
88
|
+
const root = document.documentElement;
|
|
89
|
+
const main = document.getElementById(MAIN_ID);
|
|
90
|
+
if (!main || !shellCanMove() || root.hasAttribute('data-shell-move'))
|
|
91
|
+
return;
|
|
92
|
+
if (from.kind === 'frame')
|
|
93
|
+
root.style.setProperty(FRAME_WIDTH, `${String(from.width)}px`);
|
|
94
|
+
root.dataset.shellClip = from.kind;
|
|
95
|
+
root.dataset.shellMove = '';
|
|
96
|
+
getComputedStyle(main).getPropertyValue('clip-path');
|
|
97
|
+
}
|
|
98
|
+
/** Each move claims the shell; a claim that was overtaken must not settle it. */
|
|
99
|
+
let claim = 0;
|
|
100
|
+
/**
|
|
101
|
+
* Move the shell from one shape to another, content hidden. If it is already moving, it carries on
|
|
102
|
+
* from the shape it is in. Resolves true when it arrives, false when a newer move overtook it. It
|
|
103
|
+
* stays in its last shape until `settleShell`, so a page that is still loading can wait on it.
|
|
104
|
+
*/
|
|
105
|
+
export function moveShell(from, to) {
|
|
106
|
+
const root = document.documentElement;
|
|
107
|
+
const main = document.getElementById(MAIN_ID);
|
|
108
|
+
const mine = ++claim;
|
|
109
|
+
if (!main || !shellCanMove())
|
|
110
|
+
return Promise.resolve(true);
|
|
111
|
+
const moving = root.hasAttribute('data-shell-move');
|
|
112
|
+
if (moving && root.dataset.shellClip === to.kind)
|
|
113
|
+
return Promise.resolve(true);
|
|
114
|
+
const frame = from.kind === 'frame' ? from : to.kind === 'frame' ? to : null;
|
|
115
|
+
if (frame)
|
|
116
|
+
root.style.setProperty(FRAME_WIDTH, `${String(frame.width)}px`);
|
|
117
|
+
if (!moving) {
|
|
118
|
+
root.dataset.shellClip = from.kind;
|
|
119
|
+
root.dataset.shellMove = '';
|
|
120
|
+
}
|
|
121
|
+
/* The first shape has to be drawn once, so the second one is a change to ease and not a start. */
|
|
122
|
+
getComputedStyle(main).getPropertyValue('clip-path');
|
|
123
|
+
root.dataset.shellClip = to.kind;
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
const done = () => {
|
|
126
|
+
main.removeEventListener('transitionend', onEnd);
|
|
127
|
+
globalThis.clearTimeout(cap);
|
|
128
|
+
resolve(claim === mine);
|
|
129
|
+
};
|
|
130
|
+
const onEnd = (event) => {
|
|
131
|
+
if (event.target === main && event.propertyName === 'clip-path')
|
|
132
|
+
done();
|
|
133
|
+
};
|
|
134
|
+
main.addEventListener('transitionend', onEnd);
|
|
135
|
+
const cap = globalThis.setTimeout(done, MOVE_CAP_MS);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/** The shell rests. The page's content comes in at the size it will keep. */
|
|
139
|
+
export function settleShell(to) {
|
|
140
|
+
const root = document.documentElement;
|
|
141
|
+
const shown = root.hasAttribute('data-shell-move') || root.hasAttribute('data-shell-hold');
|
|
142
|
+
delete root.dataset.shellMove;
|
|
143
|
+
delete root.dataset.shellClip;
|
|
144
|
+
delete root.dataset.shellHold;
|
|
145
|
+
root.style.removeProperty(FRAME_WIDTH);
|
|
146
|
+
if (shown)
|
|
147
|
+
showContent(root);
|
|
148
|
+
rest = to;
|
|
149
|
+
globalThis.dispatchEvent(new Event(SHELL_INTRO_DONE));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The mark has finished but the page is still loading. The shell pulls in to the regular panel and
|
|
153
|
+
* waits there, page hidden, for what the loader shows on it. `revealShell` then takes it to rest.
|
|
154
|
+
* A shell that cannot move stays full-bleed.
|
|
155
|
+
*/
|
|
156
|
+
export function pullShellIn() {
|
|
157
|
+
const root = document.documentElement;
|
|
158
|
+
const main = document.getElementById(MAIN_ID);
|
|
159
|
+
if (!main || !shellCanMove())
|
|
160
|
+
return Promise.resolve(true);
|
|
161
|
+
root.dataset.shellClip = 'open';
|
|
162
|
+
root.dataset.shellMove = '';
|
|
163
|
+
delete root.dataset.bootPending;
|
|
164
|
+
globalThis.dispatchEvent(new Event(SHELL_REVEAL));
|
|
165
|
+
return moveShell(OPEN_SHAPE, REGULAR_SHAPE);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The loader has finished. The page stays hidden while the shell moves to the shape of the page it
|
|
169
|
+
* opens, then comes in. It moves from the full-bleed shell, or from the regular panel when
|
|
170
|
+
* `pullShellIn` took it there. A page that rests open stays open.
|
|
171
|
+
*/
|
|
172
|
+
export function revealShell(to) {
|
|
173
|
+
played = true;
|
|
174
|
+
const root = document.documentElement;
|
|
175
|
+
const main = document.getElementById(MAIN_ID);
|
|
176
|
+
const pulled = root.hasAttribute('data-shell-move');
|
|
177
|
+
const moves = Boolean(main) && (pulled || (shellCanMove() && to.kind !== 'open'));
|
|
178
|
+
if (moves && !pulled) {
|
|
179
|
+
root.dataset.shellClip = 'open';
|
|
180
|
+
root.dataset.shellMove = '';
|
|
181
|
+
}
|
|
182
|
+
delete root.dataset.bootPending;
|
|
183
|
+
globalThis.dispatchEvent(new Event(SHELL_REVEAL));
|
|
184
|
+
if (!moves) {
|
|
185
|
+
showContent(root);
|
|
186
|
+
rest = to;
|
|
187
|
+
globalThis.dispatchEvent(new Event(SHELL_INTRO_DONE));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
void moveShell(pulled ? REGULAR_SHAPE : OPEN_SHAPE, to).then((arrived) => {
|
|
191
|
+
if (arrived)
|
|
192
|
+
settleShell(to);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type StudioIssue, type StudioProvider } from '../mod.ts';
|
|
2
|
+
import type { StudioWorkspaceState } from './studio-screen.tsx';
|
|
3
|
+
/** The open agent's TypeScript. A failed compile keeps the text and marks the issues. */
|
|
4
|
+
export declare function CodeBody({ source, blocked, issues, state, }: {
|
|
5
|
+
source: string | null;
|
|
6
|
+
blocked: string | undefined;
|
|
7
|
+
issues: StudioIssue[];
|
|
8
|
+
state: StudioWorkspaceState;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* An open provider's definition. On a project it is edited here as in its editor; the studio's own,
|
|
12
|
+
* and one on an adapter the project made, only show.
|
|
13
|
+
*/
|
|
14
|
+
export declare function ProviderCode({ provider, issues, state, isEditable, }: {
|
|
15
|
+
provider: StudioProvider;
|
|
16
|
+
issues: StudioIssue[];
|
|
17
|
+
state: StudioWorkspaceState;
|
|
18
|
+
isEditable: boolean;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { PaneFailure } from '@theoremjs/react/ui';
|
|
3
|
+
import { useCallback, useRef } from 'react';
|
|
4
|
+
import { agentDraft, definitionFactory, providerSource, readProviderSource, readStudioSource, withAgentDraft, withProvider, } from "../mod.js";
|
|
5
|
+
import { StudioCode } from "./code/studio-code.js";
|
|
6
|
+
/** The open agent's TypeScript. A failed compile keeps the text and marks the issues. */
|
|
7
|
+
export function CodeBody({ source, blocked, issues, state, }) {
|
|
8
|
+
const seen = useRef(source);
|
|
9
|
+
if (source)
|
|
10
|
+
seen.current = source;
|
|
11
|
+
const apply = useCallback((text) => {
|
|
12
|
+
const current = agentDraft(state.workspace, state.focus);
|
|
13
|
+
if (!current) {
|
|
14
|
+
return { errors: [{ message: 'No agent is open.', line: 1, column: 1 }], spans: [] };
|
|
15
|
+
}
|
|
16
|
+
const read = readStudioSource(text, { ...current, toolSpecs: state.workspace.toolSpecs }, (agentId) => state.workspace.agents.find((agent) => agent.identity.agentId.trim() === agentId)?.key);
|
|
17
|
+
if (!read.ok)
|
|
18
|
+
return { errors: read.errors, spans: [] };
|
|
19
|
+
state.update((workspace) => withAgentDraft(workspace, state.focus, read.draft, read.registered));
|
|
20
|
+
return { errors: [], spans: read.spans };
|
|
21
|
+
}, [state]);
|
|
22
|
+
if (!seen.current) {
|
|
23
|
+
return (_jsx(PaneFailure, { title: "No code yet", description: `${blocked ?? ''} to generate the TypeScript.` }));
|
|
24
|
+
}
|
|
25
|
+
return (_jsx("div", { className: "studio-fill", children: _jsx(StudioCode, { text: source ?? seen.current, hold: source == null, issues: issues, onApply: apply }) }));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* An open provider's definition. On a project it is edited here as in its editor; the studio's own,
|
|
29
|
+
* and one on an adapter the project made, only show.
|
|
30
|
+
*/
|
|
31
|
+
export function ProviderCode({ provider, issues, state, isEditable, }) {
|
|
32
|
+
const apply = useCallback((text) => {
|
|
33
|
+
const current = state.workspace.providers.find((each) => each.id === provider.id);
|
|
34
|
+
if (!current) {
|
|
35
|
+
return { errors: [{ message: 'No provider is open.', line: 1, column: 1 }], spans: [] };
|
|
36
|
+
}
|
|
37
|
+
const read = readProviderSource(text, current);
|
|
38
|
+
if (!read.ok)
|
|
39
|
+
return { errors: read.errors, spans: [] };
|
|
40
|
+
state.update((workspace) => withProvider(workspace, read.provider));
|
|
41
|
+
return { errors: [], spans: read.spans };
|
|
42
|
+
}, [state, provider.id]);
|
|
43
|
+
return (_jsx("div", { className: "studio-fill", children: _jsx(StudioCode, { text: providerSource(provider), hold: false, isReadOnly: !isEditable || definitionFactory(provider.definition) === undefined, issues: issues, onApply: apply }, provider.id) }));
|
|
44
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type TraceFeed } from '@theoremjs/react/client';
|
|
2
|
+
import { type TheoremChatHandle } from '@theoremjs/react/ui';
|
|
3
|
+
import { type ReactNode, type Ref } from 'react';
|
|
4
|
+
import { type StudioWorkspace } from '../mod.ts';
|
|
5
|
+
import type { EditedAnswer, SaveRefusal } from '../server/save-wire.ts';
|
|
6
|
+
import { type ProjectSession } from './lib/studio-project.ts';
|
|
7
|
+
/**
|
|
8
|
+
* The load that runs a workspace's edits: asked for when there are edits, and again when they
|
|
9
|
+
* change. `answer` is the last one the server gave; `pending` while it is not for these edits yet.
|
|
10
|
+
* It waits while `hold` is set, so a reply that is streaming is not cut off by the next edit.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useEditedLoad(project: ProjectSession, edits: StudioWorkspace | undefined, hold: boolean): {
|
|
13
|
+
answer: EditedAnswer | undefined;
|
|
14
|
+
pending: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The stamp of the last load of the edits that ran. A refused load leaves the one before it
|
|
18
|
+
* standing under the refusal, so the edited side keeps what it showed; with no edits, none.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useLoadedStamp(answer: EditedAnswer | undefined): string | undefined;
|
|
21
|
+
/** Whether the builder put the files' side away: the edits then run alone. */
|
|
22
|
+
export declare function useSavedHidden(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Where the files' side stands in the row, with the line between it and the edits. Hiding it
|
|
25
|
+
* closes it toward its edge and showing it opens it again, on the motion the rest of the studio
|
|
26
|
+
* uses; it leaves the page once it has closed. It does not move when the page first opens.
|
|
27
|
+
*/
|
|
28
|
+
export declare function SavedSlot({ isShown, hasDivider, children, }: {
|
|
29
|
+
isShown: boolean;
|
|
30
|
+
/** Whether the edits sit beside it. */
|
|
31
|
+
hasDivider: boolean;
|
|
32
|
+
/** The side. With none there is nothing to close, and the slot is gone at once. */
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
}): import("react").JSX.Element | null;
|
|
35
|
+
/**
|
|
36
|
+
* The files' side: its run, and while the edits sit beside it, its title and the way to put it
|
|
37
|
+
* away.
|
|
38
|
+
*/
|
|
39
|
+
export declare function SavedSide({ isCompared, children, }: {
|
|
40
|
+
isCompared?: boolean;
|
|
41
|
+
children: ReactNode;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
43
|
+
export interface ProjectChatProps {
|
|
44
|
+
project: ProjectSession;
|
|
45
|
+
/** The id of the profile the page chats with, as its edits name it. */
|
|
46
|
+
profileId: string;
|
|
47
|
+
/** The workspace the preview runs, edits and all. Without one, the files alone are run. */
|
|
48
|
+
tested: StudioWorkspace | undefined;
|
|
49
|
+
note: () => void;
|
|
50
|
+
/** Where the files' side keeps its runs' traces. */
|
|
51
|
+
traces: TraceFeed;
|
|
52
|
+
trace?: boolean;
|
|
53
|
+
className?: string;
|
|
54
|
+
chatRef?: Ref<TheoremChatHandle>;
|
|
55
|
+
/** The value the page picked for each slot. */
|
|
56
|
+
slots?: Record<string, string>;
|
|
57
|
+
/** What the page tells the agent. */
|
|
58
|
+
context?: unknown;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The workspace when it holds edits the files do not: a changed setting, or an agent added or
|
|
62
|
+
* removed. Undefined when it is the files' own.
|
|
63
|
+
*/
|
|
64
|
+
export declare function unsavedEdits(tested: StudioWorkspace | undefined, profiles: readonly string[]): StudioWorkspace | undefined;
|
|
65
|
+
/** The edited side: its run, why it is not running, or that it is loading. */
|
|
66
|
+
export declare function EditedSide({ refused, isLoading, hasSaved, actions, children, }: {
|
|
67
|
+
refused: SaveRefusal | undefined;
|
|
68
|
+
isLoading: boolean;
|
|
69
|
+
/** The files hold this agent too, so their side can come back beside this one. */
|
|
70
|
+
hasSaved: boolean;
|
|
71
|
+
/** The ways out when the edits cannot run. */
|
|
72
|
+
actions?: ReactNode;
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
}): import("react").JSX.Element;
|
|
75
|
+
/**
|
|
76
|
+
* A project's text or image profile, run by the studio's local server. While the builder has
|
|
77
|
+
* edits the files do not hold, one message is answered twice, side by side: by the files, and by
|
|
78
|
+
* the files with the edits laid over them.
|
|
79
|
+
*/
|
|
80
|
+
export declare function ProjectChat(props: ProjectChatProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@astryxdesign/core/Button';
|
|
3
|
+
import { Divider } from '@astryxdesign/core/Divider';
|
|
4
|
+
import { HStack } from '@astryxdesign/core/HStack';
|
|
5
|
+
import { Icon } from '@astryxdesign/core/Icon';
|
|
6
|
+
import { IconButton } from '@astryxdesign/core/IconButton';
|
|
7
|
+
import { StackItem } from '@astryxdesign/core/Stack';
|
|
8
|
+
import { Text } from '@astryxdesign/core/Text';
|
|
9
|
+
import { VStack } from '@astryxdesign/core/VStack';
|
|
10
|
+
import { IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand } from '@tabler/icons-react';
|
|
11
|
+
import { createHttpTransport, createTraceFeed } from '@theoremjs/react/client';
|
|
12
|
+
import { ChatComposerBar, InPlace, PaneFailure, PaneLoading, TheoremChat, } from '@theoremjs/react/ui';
|
|
13
|
+
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, useSyncExternalStore, } from 'react';
|
|
14
|
+
import { atStart } from "../mod.js";
|
|
15
|
+
import { useActingAs } from "./lib/acting-as.js";
|
|
16
|
+
import { tracedTransport } from "./lib/project-traces.js";
|
|
17
|
+
import { noting } from "./lib/studio-activity.js";
|
|
18
|
+
import { STUDIO_LABELS } from "./lib/studio-labels.js";
|
|
19
|
+
import { editedProfileEndpoint, openEdited, projectProfileEndpoint, } from "./lib/studio-project.js";
|
|
20
|
+
import { RefusalDetail } from "./refusal-detail.js";
|
|
21
|
+
const SIDES = ['saved', 'edited'];
|
|
22
|
+
/** Why the edits are not running, as the builder reads it. */
|
|
23
|
+
const NOT_RUNNING = {
|
|
24
|
+
issues: 'Fix the issues in the editor to run your edits.',
|
|
25
|
+
load: 'The project did not start with your edits.',
|
|
26
|
+
differs: 'The studio cannot run these edits before they are saved.',
|
|
27
|
+
unanswered: 'The studio did not answer.',
|
|
28
|
+
};
|
|
29
|
+
/** A server that did not answer, as a refusal the pane can show. */
|
|
30
|
+
function unreachable(saved) {
|
|
31
|
+
return (error) => ({
|
|
32
|
+
ok: false,
|
|
33
|
+
reason: 'unanswered',
|
|
34
|
+
detail: [error instanceof Error ? error.message : String(error)],
|
|
35
|
+
saved,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The load that runs a workspace's edits: asked for when there are edits, and again when they
|
|
40
|
+
* change. `answer` is the last one the server gave; `pending` while it is not for these edits yet.
|
|
41
|
+
* It waits while `hold` is set, so a reply that is streaming is not cut off by the next edit.
|
|
42
|
+
*/
|
|
43
|
+
export function useEditedLoad(project, edits, hold) {
|
|
44
|
+
const [held, setHeld] = useState();
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!edits || hold || held?.of === edits)
|
|
47
|
+
return;
|
|
48
|
+
let stale = false;
|
|
49
|
+
openEdited(project, edits)
|
|
50
|
+
.catch(unreachable(held?.answer.saved ?? project.profiles))
|
|
51
|
+
.then((answer) => {
|
|
52
|
+
if (!stale)
|
|
53
|
+
setHeld({ of: edits, answer });
|
|
54
|
+
});
|
|
55
|
+
return () => {
|
|
56
|
+
stale = true;
|
|
57
|
+
};
|
|
58
|
+
}, [project, edits, hold, held]);
|
|
59
|
+
if (!edits)
|
|
60
|
+
return { answer: undefined, pending: false };
|
|
61
|
+
return { answer: held?.answer, pending: held?.of !== edits };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The stamp of the last load of the edits that ran. A refused load leaves the one before it
|
|
65
|
+
* standing under the refusal, so the edited side keeps what it showed; with no edits, none.
|
|
66
|
+
*/
|
|
67
|
+
export function useLoadedStamp(answer) {
|
|
68
|
+
const [loaded, setLoaded] = useState();
|
|
69
|
+
const now = answer === undefined ? undefined : answer.ok ? answer.stamp : loaded;
|
|
70
|
+
if (now !== loaded)
|
|
71
|
+
setLoaded(now);
|
|
72
|
+
return now;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* One message sent to both sides, and which of them still answers it. A side rests when its turn
|
|
76
|
+
* is done: the reply ended, and any question it stopped on was answered.
|
|
77
|
+
*/
|
|
78
|
+
function usePair() {
|
|
79
|
+
const chats = useRef({ saved: null, edited: null });
|
|
80
|
+
const [busy, setBusy] = useState([]);
|
|
81
|
+
const done = useCallback((side) => {
|
|
82
|
+
setBusy((sides) => sides.filter((other) => other !== side));
|
|
83
|
+
}, []);
|
|
84
|
+
const send = useCallback(async (text) => {
|
|
85
|
+
const sent = SIDES.filter((side) => chats.current[side]);
|
|
86
|
+
setBusy(sent);
|
|
87
|
+
const answers = sent.map(async (side) => {
|
|
88
|
+
// A side's send resolves once its reply is done, or at once when it could not take it.
|
|
89
|
+
const added = await chats.current[side]?.send(text).catch(() => null);
|
|
90
|
+
done(side);
|
|
91
|
+
return [side, added ?? null];
|
|
92
|
+
});
|
|
93
|
+
return new Map(await Promise.all(answers));
|
|
94
|
+
}, [done]);
|
|
95
|
+
const stop = useCallback(() => {
|
|
96
|
+
for (const side of SIDES)
|
|
97
|
+
chats.current[side]?.stop();
|
|
98
|
+
}, []);
|
|
99
|
+
const [refs] = useState(() => ({
|
|
100
|
+
saved: (handle) => {
|
|
101
|
+
chats.current.saved = handle;
|
|
102
|
+
},
|
|
103
|
+
edited: (handle) => {
|
|
104
|
+
chats.current.edited = handle;
|
|
105
|
+
},
|
|
106
|
+
}));
|
|
107
|
+
return useMemo(() => ({ busy, send, stop, refs, chats }), [busy, send, stop, refs]);
|
|
108
|
+
}
|
|
109
|
+
/** Whether the files' side is put away, for every comparison in this tab. */
|
|
110
|
+
const savedSide = { isHidden: false, listeners: new Set() };
|
|
111
|
+
function setSavedHidden(isHidden) {
|
|
112
|
+
savedSide.isHidden = isHidden;
|
|
113
|
+
for (const listener of savedSide.listeners)
|
|
114
|
+
listener();
|
|
115
|
+
}
|
|
116
|
+
function watchSavedSide(listener) {
|
|
117
|
+
savedSide.listeners.add(listener);
|
|
118
|
+
return () => {
|
|
119
|
+
savedSide.listeners.delete(listener);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** Whether the builder put the files' side away: the edits then run alone. */
|
|
123
|
+
export function useSavedHidden() {
|
|
124
|
+
return useSyncExternalStore(watchSavedSide, () => savedSide.isHidden, () => false);
|
|
125
|
+
}
|
|
126
|
+
/** One side of the comparison: what it runs, over its conversation. */
|
|
127
|
+
function Pane({ title, startContent, children, }) {
|
|
128
|
+
return (_jsxs(VStack, { height: "100%", children: [title && (_jsxs(HStack, { paddingInline: 3, paddingBlock: 2, gap: 1, vAlign: "center",
|
|
129
|
+
// As tall with the control as without, so the two titles sit on one line.
|
|
130
|
+
style: { minHeight: '2.75rem' }, children: [startContent, _jsx(Text, { type: "supporting", weight: "semibold", color: "secondary", children: title })] })), _jsx(StackItem, { size: "fill", children: children })] }));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Where the files' side stands in the row, with the line between it and the edits. Hiding it
|
|
134
|
+
* closes it toward its edge and showing it opens it again, on the motion the rest of the studio
|
|
135
|
+
* uses; it leaves the page once it has closed. It does not move when the page first opens.
|
|
136
|
+
*/
|
|
137
|
+
export function SavedSlot({ isShown, hasDivider, children, }) {
|
|
138
|
+
const [isMounted, setMounted] = useState(isShown);
|
|
139
|
+
const [hasMoved, setMoved] = useState(false);
|
|
140
|
+
const slot = useRef(null);
|
|
141
|
+
if (isShown && !isMounted)
|
|
142
|
+
setMounted(true);
|
|
143
|
+
if (isShown !== isMounted && !hasMoved)
|
|
144
|
+
setMoved(true);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (isShown || !slot.current)
|
|
147
|
+
return;
|
|
148
|
+
// It leaves once its own motion has run: at once where motion is reduced, and still on time
|
|
149
|
+
// in a tab that is not showing, where nothing reports a transition's end.
|
|
150
|
+
const seconds = Number.parseFloat(getComputedStyle(slot.current).transitionDuration) || 0;
|
|
151
|
+
const timer = setTimeout(() => {
|
|
152
|
+
setMounted(false);
|
|
153
|
+
}, seconds * 1000);
|
|
154
|
+
return () => {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
};
|
|
157
|
+
}, [isShown]);
|
|
158
|
+
if (!children || !(isShown || isMounted))
|
|
159
|
+
return null;
|
|
160
|
+
return (_jsxs("div", { ref: slot, className: "studio-saved-slot", "data-moved": hasMoved ? '' : undefined, "data-closed": isShown ? undefined : '', "aria-hidden": isShown ? undefined : true, children: [_jsx("div", { className: "studio-saved-slot-side", children: children }), hasDivider && _jsx(Divider, { orientation: "vertical" })] }));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The files' side: its run, and while the edits sit beside it, its title and the way to put it
|
|
164
|
+
* away.
|
|
165
|
+
*/
|
|
166
|
+
export function SavedSide({ isCompared = true, children, }) {
|
|
167
|
+
return (_jsx(Pane, { title: isCompared ? 'Saved (your files)' : undefined, startContent: _jsx(IconButton, { label: "Hide saved", variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconLayoutSidebarLeftCollapse, size: "sm" }), tooltip: "Hide saved: run your edits alone", onClick: () => {
|
|
168
|
+
setSavedHidden(true);
|
|
169
|
+
} }), children: children }));
|
|
170
|
+
}
|
|
171
|
+
function NotRunning({ refusal, actions }) {
|
|
172
|
+
return (_jsx(PaneFailure, { title: NOT_RUNNING[refusal.reason] ?? 'Your edits are not running.', actions: actions, children: _jsx(RefusalDetail, { refusal: refusal }) }));
|
|
173
|
+
}
|
|
174
|
+
/** The message both sides get, and the stop for both while either answers. */
|
|
175
|
+
function SharedComposer({ handle, why, isBusy, onSend, onStop, }) {
|
|
176
|
+
const [text, setText] = useState('');
|
|
177
|
+
return (_jsxs(HStack, { gap: 2, vAlign: "end", padding: 3, children: [_jsx(StackItem, { size: "fill", children: _jsx(ChatComposerBar, { handle: handle, draftText: text, onDraftTextChange: setText, onSubmit: onSend, isDisabled: isBusy || why !== undefined, placeholder: why ?? 'Message both' }) }), isBusy && _jsx(Button, { label: "Stop", variant: "ghost", onClick: onStop })] }));
|
|
178
|
+
}
|
|
179
|
+
/** The agent as the files hold it: an edit may have renamed it, or made it another type. */
|
|
180
|
+
function savedOf(tested, profileId) {
|
|
181
|
+
const agent = tested?.agents.find((held) => held.identity.agentId === profileId);
|
|
182
|
+
return agent && tested?.starts.agents[agent.key]?.identity;
|
|
183
|
+
}
|
|
184
|
+
/** The types a chat cannot talk to: the files' side of one of these has no conversation to show. */
|
|
185
|
+
const NOT_A_CHAT = new Set(['decision', 'host', 'live']);
|
|
186
|
+
/** Why a message cannot go to both sides now, when it cannot. */
|
|
187
|
+
function whyNot(failed, isReady) {
|
|
188
|
+
if (failed)
|
|
189
|
+
return 'Your edits are not running';
|
|
190
|
+
return isReady ? undefined : 'Loading your edits';
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Each side's latest conversation ref, so a side that comes back, or the edited side when its load
|
|
194
|
+
* is replaced, starts from what was said. Every load seals with the studio run's one key, so the
|
|
195
|
+
* edited side opens the files' side's ref and goes on from there on its own.
|
|
196
|
+
*/
|
|
197
|
+
function useConversations(isEditing) {
|
|
198
|
+
const conversations = useRef({});
|
|
199
|
+
if (!isEditing)
|
|
200
|
+
conversations.current.edited = undefined;
|
|
201
|
+
const [onChange] = useState(() => {
|
|
202
|
+
const change = (side) => (ref) => {
|
|
203
|
+
conversations.current[side] = ref;
|
|
204
|
+
};
|
|
205
|
+
return { saved: change('saved'), edited: change('edited') };
|
|
206
|
+
});
|
|
207
|
+
return { conversations, onChange };
|
|
208
|
+
}
|
|
209
|
+
/** Where each side sends: the files' profile, and the edited load's once there is one. */
|
|
210
|
+
function useTransports(project, ids, stamp, { note, traces }) {
|
|
211
|
+
const { actingAs } = useActingAs();
|
|
212
|
+
const saved = useMemo(() => {
|
|
213
|
+
const endpoint = projectProfileEndpoint(project, ids.saved);
|
|
214
|
+
return noting(tracedTransport(createHttpTransport, endpoint, traces, actingAs), note);
|
|
215
|
+
}, [project, ids.saved, traces, actingAs, note]);
|
|
216
|
+
// One feed for the edited side, so a new load keeps the traces of the turns it starts from.
|
|
217
|
+
const [editedTraces] = useState(createTraceFeed);
|
|
218
|
+
const edited = useMemo(() => {
|
|
219
|
+
if (stamp === undefined)
|
|
220
|
+
return null;
|
|
221
|
+
const endpoint = editedProfileEndpoint(project, ids.edited);
|
|
222
|
+
return noting(tracedTransport(createHttpTransport, endpoint, editedTraces, actingAs), note);
|
|
223
|
+
}, [project, ids.edited, stamp, editedTraces, actingAs, note]);
|
|
224
|
+
return { saved, edited };
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* The workspace when it holds edits the files do not: a changed setting, or an agent added or
|
|
228
|
+
* removed. Undefined when it is the files' own.
|
|
229
|
+
*/
|
|
230
|
+
export function unsavedEdits(tested, profiles) {
|
|
231
|
+
if (!tested)
|
|
232
|
+
return undefined;
|
|
233
|
+
// An agent added here is at its own start, and still one the files do not hold.
|
|
234
|
+
const isFiles = atStart(tested) &&
|
|
235
|
+
tested.agents.length === profiles.length &&
|
|
236
|
+
tested.agents.every((agent) => profiles.includes(agent.identity.agentId));
|
|
237
|
+
return isFiles ? undefined : tested;
|
|
238
|
+
}
|
|
239
|
+
/** The edited side: its run, why it is not running, or that it is loading. */
|
|
240
|
+
export function EditedSide({ refused, isLoading, hasSaved, actions, children, }) {
|
|
241
|
+
const isSavedHidden = useSavedHidden();
|
|
242
|
+
return (_jsxs(Pane, { title: "Edited (studio)", startContent: hasSaved &&
|
|
243
|
+
isSavedHidden && (_jsx(IconButton, { label: "Show saved", variant: "ghost", size: "sm", icon: _jsx(Icon, { icon: IconLayoutSidebarLeftExpand, size: "sm" }), tooltip: "Show saved: run your files beside your edits", onClick: () => {
|
|
244
|
+
setSavedHidden(false);
|
|
245
|
+
} })), children: [_jsx(InPlace, { view: refused ? _jsx(NotRunning, { refusal: refused, actions: actions }) : null, children: children }), isLoading && _jsx(PaneLoading, { label: "Loading your edits" })] }));
|
|
246
|
+
}
|
|
247
|
+
/** What the comparison shows now: which sides there are, and what each one runs. */
|
|
248
|
+
function useComparison({ project, profileId, tested, note, traces }) {
|
|
249
|
+
const edits = useMemo(() => unsavedEdits(tested, project.profiles), [tested, project.profiles]);
|
|
250
|
+
const pair = usePair();
|
|
251
|
+
const isBusy = pair.busy.length > 0;
|
|
252
|
+
const { answer, pending } = useEditedLoad(project, edits, isBusy);
|
|
253
|
+
const saved = savedOf(tested, profileId);
|
|
254
|
+
const ids = useMemo(() => ({ saved: saved?.agentId ?? profileId, edited: profileId }), [saved?.agentId, profileId]);
|
|
255
|
+
const isEditing = edits !== undefined;
|
|
256
|
+
// A profile the edits made a chat is another type in the files, which a chat cannot talk to.
|
|
257
|
+
const chatsInFiles = !saved || !NOT_A_CHAT.has(saved.profileType);
|
|
258
|
+
const inFiles = !isEditing || (chatsInFiles && (answer?.saved ?? project.profiles).includes(ids.saved));
|
|
259
|
+
const { conversations, onChange } = useConversations(isEditing);
|
|
260
|
+
const stamp = useLoadedStamp(answer);
|
|
261
|
+
const transports = useTransports(project, ids, stamp, { note, traces });
|
|
262
|
+
const refused = answer && !answer.ok && !pending ? answer : undefined;
|
|
263
|
+
return {
|
|
264
|
+
pair,
|
|
265
|
+
isBusy,
|
|
266
|
+
isEditing,
|
|
267
|
+
inFiles,
|
|
268
|
+
/** Both sides are there, so one message goes to both. */
|
|
269
|
+
isShared: isEditing && inFiles,
|
|
270
|
+
conversations,
|
|
271
|
+
onChange,
|
|
272
|
+
transports,
|
|
273
|
+
refused,
|
|
274
|
+
isReady: !pending && !refused && transports.edited !== null,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The chat the page holds: a message goes to both sides while both are there and can take it, and
|
|
279
|
+
* to the one side otherwise. It answers with what the edits said.
|
|
280
|
+
*/
|
|
281
|
+
function useChatHandle(chatRef, pair, only, canSendBoth) {
|
|
282
|
+
const { send, stop, chats } = pair;
|
|
283
|
+
const sendBoth = useCallback(async (text) => (await send(text)).get('edited') ?? null, [send]);
|
|
284
|
+
useImperativeHandle(chatRef, () => ({
|
|
285
|
+
send: (text) => {
|
|
286
|
+
if (only)
|
|
287
|
+
return chats.current[only]?.send(text) ?? Promise.resolve(null);
|
|
288
|
+
return canSendBoth ? sendBoth(text) : Promise.resolve(null);
|
|
289
|
+
},
|
|
290
|
+
stop,
|
|
291
|
+
}), [only, canSendBoth, sendBoth, stop, chats]);
|
|
292
|
+
return sendBoth;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* A project's text or image profile, run by the studio's local server. While the builder has
|
|
296
|
+
* edits the files do not hold, one message is answered twice, side by side: by the files, and by
|
|
297
|
+
* the files with the edits laid over them.
|
|
298
|
+
*/
|
|
299
|
+
export function ProjectChat(props) {
|
|
300
|
+
const { profileId, trace, className, chatRef, slots, context } = props;
|
|
301
|
+
const view = useComparison(props);
|
|
302
|
+
const { pair, isBusy, transports, refused, isReady } = view;
|
|
303
|
+
const isSavedHidden = useSavedHidden();
|
|
304
|
+
const showsSaved = view.inFiles && !(view.isEditing && isSavedHidden);
|
|
305
|
+
const isShared = view.isShared && showsSaved;
|
|
306
|
+
const only = isShared ? undefined : showsSaved ? 'saved' : 'edited';
|
|
307
|
+
const sendBoth = useChatHandle(chatRef, pair, only, !isBusy && isReady);
|
|
308
|
+
const common = {
|
|
309
|
+
detectCodeLanguage: true,
|
|
310
|
+
labels: STUDIO_LABELS,
|
|
311
|
+
trace,
|
|
312
|
+
className,
|
|
313
|
+
slots,
|
|
314
|
+
context,
|
|
315
|
+
composer: !isShared,
|
|
316
|
+
};
|
|
317
|
+
return (_jsxs(VStack, { height: "100%", children: [_jsx(StackItem, { size: "fill", children: _jsxs(HStack, { height: "100%", children: [_jsx(SavedSlot, { isShown: showsSaved, hasDivider: view.isShared, children: view.inFiles && (_jsx(SavedSide, { isCompared: view.isEditing, children: _jsx(TheoremChat, { ...common, transport: transports.saved,
|
|
318
|
+
// A side that comes back starts from what it had said.
|
|
319
|
+
conversation: view.conversations.current.saved, onConversationChange: view.onChange.saved, chatRef: pair.refs.saved }) })) }), view.isEditing && (_jsx("div", { className: "studio-edited-slot", children: _jsx(EditedSide, { refused: refused, isLoading: !transports.edited && !refused, hasSaved: view.inFiles, children: transports.edited && (_jsx(TheoremChat, { ...common,
|
|
320
|
+
// A new load is a new transport: the chat reads it in place and keeps its transcript.
|
|
321
|
+
transport: transports.edited, conversation: view.conversations.current.edited ?? view.conversations.current.saved, onConversationChange: view.onChange.edited,
|
|
322
|
+
// Under a refusal nothing reaches it: the edits it ran are not loaded.
|
|
323
|
+
chatRef: refused ? undefined : pair.refs.edited })) }) }, "edited"))] }) }), isShared && (_jsx(SharedComposer, { handle: profileId, why: whyNot(refused !== undefined, isReady), isBusy: isBusy, onSend: (text) => {
|
|
324
|
+
void sendBoth(text);
|
|
325
|
+
}, onStop: pair.stop }))] }));
|
|
326
|
+
}
|