@1agh/maude 1.0.11 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +892 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -545
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -337
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Shell addresses use the same design-root-relative file identity as canvas URLs.
|
|
2
|
+
// RETURN_RULES twin: apps/hub/src/return-to.mjs; native: project_resolve.rs.
|
|
3
|
+
const PROJECT_RE = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
|
|
4
|
+
|
|
5
|
+
/** Validate an already decoded file identity. Never decode a filesystem path. */
|
|
6
|
+
export function normalizeOpenPath(raw, designRel = '.design') {
|
|
7
|
+
if (typeof raw !== 'string' || !raw || raw.length > 512) return null;
|
|
8
|
+
if (/^[a-z]:/i.test(raw) || raw.includes('\\')) return null;
|
|
9
|
+
if ([...raw].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127)) return null;
|
|
10
|
+
const prefix = `${designRel.replace(/^\.\//, '').replace(/\/$/, '')}/`;
|
|
11
|
+
const rel = raw.startsWith(prefix) ? raw.slice(prefix.length) : raw;
|
|
12
|
+
return rel.split('/').some((s) => s === '' || s === '.' || s === '..') ? null : rel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Decode a URL value exactly once, then validate. */
|
|
16
|
+
export function normalizeOpenParam(raw, designRel = '.design') {
|
|
17
|
+
if (typeof raw !== 'string') return null;
|
|
18
|
+
try {
|
|
19
|
+
return normalizeOpenPath(decodeURIComponent(raw), designRel);
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function readOpenParam(location, designRel = '.design') {
|
|
26
|
+
const values = new URLSearchParams(location.search).getAll('open');
|
|
27
|
+
// URLSearchParams already decoded the value; a second decode changes filenames.
|
|
28
|
+
return values.length === 1 ? normalizeOpenPath(values[0], designRel) : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const encodeOpenPath = (rel) => rel.split('/').map(encodeURIComponent).join('/');
|
|
32
|
+
|
|
33
|
+
export function withOpenParam(location, rel) {
|
|
34
|
+
const path = normalizeOpenPath(rel);
|
|
35
|
+
return path ? `?open=${encodeOpenPath(path)}` : location.pathname;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function httpUrl(raw) {
|
|
39
|
+
try {
|
|
40
|
+
const u = new URL(raw);
|
|
41
|
+
return /^https?:$/.test(u.protocol) && !u.username && !u.password ? u : null;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function projectIdFromHubUrl(raw) {
|
|
48
|
+
const host = httpUrl(raw)?.hostname;
|
|
49
|
+
if (!host || host.includes(':') || /^[\d.]+$/.test(host) || host.endsWith('.localhost')) return null;
|
|
50
|
+
const labels = host.split('.');
|
|
51
|
+
return labels.length >= 3 && PROJECT_RE.test(labels[0]) ? labels[0] : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function projectSlug(name) {
|
|
55
|
+
const slug = String(name ?? '').split('/').pop().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 40).replace(/-$/, '');
|
|
56
|
+
return PROJECT_RE.test(slug) ? slug : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A configured hub identity takes precedence over the folder's fallback name. */
|
|
60
|
+
export function localIdentityMatches(local, project) {
|
|
61
|
+
const linked = projectIdFromHubUrl(local.linkedHub?.url);
|
|
62
|
+
return linked ? linked === project : projectSlug(local.project) === project;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function buildShareLinks({ rel, shell, location, linkedHubUrl, project, localUrl }) {
|
|
66
|
+
const path = normalizeOpenPath(rel);
|
|
67
|
+
const result = { web: null, app: null, local: null, projectLabel: project ?? '', appIsLocalOnly: false };
|
|
68
|
+
if (!path) return result;
|
|
69
|
+
const query = `?open=${encodeOpenPath(path)}`;
|
|
70
|
+
const publicUrl = (value) => {
|
|
71
|
+
const url = httpUrl(value);
|
|
72
|
+
return url && !/^(localhost|127\.[\d.]+|\[::1\])$/.test(url.hostname) && !url.hostname.endsWith('.localhost') ? url : null;
|
|
73
|
+
};
|
|
74
|
+
const hub = publicUrl(linkedHubUrl);
|
|
75
|
+
const cloud = shell === 'cloud' ? publicUrl(location?.origin) : null;
|
|
76
|
+
const publicId = projectIdFromHubUrl(cloud?.origin ?? hub?.origin);
|
|
77
|
+
const id = publicId ?? projectSlug(project);
|
|
78
|
+
result.projectLabel = id ?? project ?? '';
|
|
79
|
+
result.web = cloud ? `${cloud.origin}${location.pathname}${query}` : hub ? `${hub.origin}/${query}` : null;
|
|
80
|
+
result.app = id ? `maude://open/${id}${query}` : null;
|
|
81
|
+
result.appIsLocalOnly = !!id && !publicId;
|
|
82
|
+
const local = shell !== 'cloud' && httpUrl(localUrl);
|
|
83
|
+
result.local = local ? `${local.origin}/${query}` : null;
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function parseFileDeepLink(raw) {
|
|
88
|
+
const m = /^maude:\/\/open\/([a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?)\/?\?([^#]*)$/.exec(String(raw ?? ''));
|
|
89
|
+
if (!m) return null;
|
|
90
|
+
const params = new URLSearchParams(m[2]);
|
|
91
|
+
if ([...params.keys()].some((key) => key !== 'open')) return null;
|
|
92
|
+
const rel = readOpenParam({ search: m[2] });
|
|
93
|
+
return rel ? { project: m[1], rel } : null;
|
|
94
|
+
}
|
|
@@ -584,25 +584,7 @@
|
|
|
584
584
|
.st-pal-icon { width: 26px; height: 26px; border-radius: var(--radius-sm); background: var(--bg-4); display: grid; place-items: center; color: var(--fg-1); flex: none; }
|
|
585
585
|
.st-pal-item.is-active .st-pal-icon { background: var(--accent); color: var(--accent-fg); }
|
|
586
586
|
|
|
587
|
-
/* ─── What's-new toast (Plan C P6) ─────────────────────────────────────────
|
|
588
|
-
* Lifted from Studio.css `.st-toast*`; repositioned fixed bottom-left for the
|
|
589
|
-
* real shell (the mockup floated it inside the stage). Mirrors ai-banner. */
|
|
590
|
-
.st-toast {
|
|
591
|
-
/* bottom raised above the in-canvas zoom pill (`.dc-zoom-tb`, bottom-left). */
|
|
592
|
-
position: fixed; left: 16px; bottom: 84px; width: 320px; z-index: 8000;
|
|
593
|
-
background: var(--bg-2); border: 1px solid var(--border-default); border-left: 3px solid var(--accent);
|
|
594
|
-
border-radius: var(--radius-md); box-shadow: var(--shadow-lg); padding: var(--space-4);
|
|
595
|
-
display: flex; flex-direction: column; gap: var(--space-2);
|
|
596
|
-
animation: st-toast-in var(--dur-route) var(--ease-out);
|
|
597
|
-
}
|
|
598
|
-
@keyframes st-toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
|
|
599
|
-
@media (prefers-reduced-motion: reduce) { .st-toast { animation-duration: 1ms; } }
|
|
600
|
-
.st-toast-hd { display: flex; align-items: center; gap: var(--space-2); color: var(--accent); font-family: var(--font-mono); font-size: var(--type-xs); letter-spacing: var(--tracking-wide); text-transform: uppercase; }
|
|
601
|
-
.st-toast-title { font-size: var(--type-md); font-weight: 600; color: var(--fg-0); }
|
|
602
|
-
.st-toast-txt { font-size: var(--type-sm); color: var(--fg-1); line-height: 1.5; }
|
|
603
587
|
.st-toast-actions { display: flex; gap: var(--space-2); margin-top: var(--space-1); }
|
|
604
|
-
.st-toast-close { position: absolute; top: var(--space-2); right: var(--space-2); appearance: none; border: 0; background: transparent; color: var(--fg-3); cursor: pointer; font-size: 16px; line-height: 1; padding: 2px 6px; border-radius: var(--radius-xs); }
|
|
605
|
-
.st-toast-close:hover { background: var(--bg-3); color: var(--fg-0); }
|
|
606
588
|
|
|
607
589
|
/* ─── Export & handoff dialog (Plan C P9/P10) ──────────────────────────────
|
|
608
590
|
* Lifted from Studio.css `.st-dialog*` / `.st-fmt*`. Rendered inside `.st-scrim`. */
|
|
@@ -1594,6 +1576,40 @@ body.st-scrubbing, body.st-scrubbing * { cursor: ew-resize !important; user-sele
|
|
|
1594
1576
|
.st-skel-thumb { height: 96px; border-radius: var(--radius-sm); }
|
|
1595
1577
|
.st-skel-line { height: 10px; }
|
|
1596
1578
|
|
|
1579
|
+
/* ─── Canvas failed to load (issue #115) ──────────────────────────────────────
|
|
1580
|
+
* Replaces the blank pane the 15s skeleton cap used to leave behind. Same card
|
|
1581
|
+
* geometry as the skeleton so the transition from "compiling" to "it didn't"
|
|
1582
|
+
* reads as one surface changing state, not a new thing appearing. Interactive
|
|
1583
|
+
* (the skeleton is `pointer-events: none`; this one has a button). */
|
|
1584
|
+
.st-canvas-error {
|
|
1585
|
+
position: absolute; inset: 0;
|
|
1586
|
+
display: grid; place-items: center;
|
|
1587
|
+
z-index: 6;
|
|
1588
|
+
background: var(--canvas-bg);
|
|
1589
|
+
}
|
|
1590
|
+
.st-canvas-error-card {
|
|
1591
|
+
width: 360px; max-width: calc(100% - var(--space-8));
|
|
1592
|
+
display: flex; flex-direction: column; gap: var(--space-3);
|
|
1593
|
+
padding: var(--space-5);
|
|
1594
|
+
background: var(--bg-1);
|
|
1595
|
+
border: 1px solid var(--border-default);
|
|
1596
|
+
border-radius: var(--radius-md);
|
|
1597
|
+
box-shadow: var(--shadow-md);
|
|
1598
|
+
animation: st-pop var(--dur-panel) var(--ease-out);
|
|
1599
|
+
}
|
|
1600
|
+
.st-canvas-error-title {
|
|
1601
|
+
font-family: var(--font-display); font-size: var(--type-base); font-weight: 600;
|
|
1602
|
+
color: var(--fg-0);
|
|
1603
|
+
}
|
|
1604
|
+
.st-canvas-error-body { font-size: var(--type-sm); line-height: 1.6; color: var(--fg-2); }
|
|
1605
|
+
.st-canvas-error-body code {
|
|
1606
|
+
font-family: var(--font-mono); font-size: var(--type-xs);
|
|
1607
|
+
background: var(--bg-2); color: var(--accent);
|
|
1608
|
+
padding: 1px 5px; border-radius: var(--radius-xs);
|
|
1609
|
+
}
|
|
1610
|
+
.st-canvas-error-card .btn { align-self: flex-start; }
|
|
1611
|
+
@media (prefers-reduced-motion: reduce) { .st-canvas-error-card { animation: none; } }
|
|
1612
|
+
|
|
1597
1613
|
/* ─── Micro-motion — hover/disclosure transitions on the dense surfaces ───── */
|
|
1598
1614
|
.st-row,
|
|
1599
1615
|
.st-comment,
|
|
@@ -2163,6 +2179,8 @@ body.st-scrubbing, body.st-scrubbing * { cursor: ew-resize !important; user-sele
|
|
|
2163
2179
|
.gp-version-msg { color: var(--fg-0); font-size: var(--type-sm); overflow: hidden; text-overflow: ellipsis; }
|
|
2164
2180
|
.gp-version-meta { color: var(--fg-2); font-family: var(--font-mono); font-size: var(--type-xs); margin-top: 2px; }
|
|
2165
2181
|
.gp-version-when { color: var(--fg-2); font-family: var(--font-mono); font-size: var(--type-xs); white-space: nowrap; transition: opacity var(--dur-soft) ease; }
|
|
2182
|
+
/* accepted revisions (T27/T28) — a row carries its own actions, so it is not a button */
|
|
2183
|
+
.gp-version-actions { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: calc(-1 * var(--space-1)); }
|
|
2166
2184
|
|
|
2167
2185
|
/* phase-27.1 — per-file History rows are click-to-preview when a canvas is open */
|
|
2168
2186
|
.gp-history-scope { color: var(--fg-1); font-size: var(--type-xs); padding: var(--space-1) var(--space-2) var(--space-2); margin: 0; }
|
|
@@ -2614,6 +2632,39 @@ body.st-scrubbing, body.st-scrubbing * { cursor: ew-resize !important; user-sele
|
|
|
2614
2632
|
.ob-spin { animation-duration: 1ms; }
|
|
2615
2633
|
}
|
|
2616
2634
|
|
|
2635
|
+
/* ── Team projects (plan T22) — the designer's way in: recent managed copies,
|
|
2636
|
+
Maude Cloud projects, a team server sign-in. Composes the ob-* door parts; the
|
|
2637
|
+
dialog frame reuses gi-modal/gi-dialog from the project switcher. ── */
|
|
2638
|
+
.gi-dl-open { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2); padding-bottom: var(--space-4); border-bottom: 1px solid var(--border-default); }
|
|
2639
|
+
.sp-ai-held { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2640
|
+
.sp-ai-actions { display: flex; gap: var(--space-2); }
|
|
2641
|
+
/* ── SourceConflictPanel (plan T28) — two versions, line by line. ── */
|
|
2642
|
+
.gi-dialog.scp-dialog { width: min(760px, 94vw); max-height: min(820px, 90vh); overflow: hidden; display: flex; flex-direction: column; padding: var(--space-6); gap: var(--space-4); }
|
|
2643
|
+
.scp-legend { display: flex; gap: var(--space-4); font-size: var(--type-xs); color: var(--fg-2); }
|
|
2644
|
+
.scp-key::before { content: ''; display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: var(--space-2); vertical-align: -1px; }
|
|
2645
|
+
.scp-key--mine::before { background: color-mix(in oklch, var(--status-warn) 45%, transparent); }
|
|
2646
|
+
.scp-key--theirs::before { background: color-mix(in oklch, var(--status-info) 45%, transparent); }
|
|
2647
|
+
.scp-diff { flex: 1; min-height: 120px; overflow: auto; border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--bg-1); font-family: var(--font-mono); font-size: var(--type-xs); line-height: 1.55; }
|
|
2648
|
+
.scp-diff:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
2649
|
+
.scp-line { display: flex; gap: var(--space-2); padding: 0 var(--space-3); white-space: pre; }
|
|
2650
|
+
.scp-line code { font: inherit; color: var(--fg-1); }
|
|
2651
|
+
.scp-mark { width: 1ch; color: var(--fg-3); flex: none; }
|
|
2652
|
+
.scp-line--mine { background: color-mix(in oklch, var(--status-warn) 14%, transparent); }
|
|
2653
|
+
.scp-line--theirs { background: color-mix(in oklch, var(--status-info) 14%, transparent); }
|
|
2654
|
+
.scp-line--skip { color: var(--fg-3); font-style: italic; }
|
|
2655
|
+
.scp-line--skip code { color: var(--fg-3); }
|
|
2656
|
+
.scp-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
|
|
2657
|
+
.scp-hint { font-size: var(--type-xs); color: var(--fg-2); }
|
|
2658
|
+
.scp-error { margin: 0; color: var(--status-error); font-size: var(--type-sm); }
|
|
2659
|
+
.tp { display: flex; flex-direction: column; gap: var(--space-5); max-width: 720px; }
|
|
2660
|
+
.tp-section { display: flex; flex-direction: column; gap: var(--space-3); }
|
|
2661
|
+
.tp-device { display: flex; flex-direction: column; gap: var(--space-3); max-width: 560px; }
|
|
2662
|
+
.tp-device .ob-form-actions { align-items: center; justify-content: space-between; }
|
|
2663
|
+
.gi-dialog.tp-dialog { width: min(620px, 94vw); max-height: min(760px, 90vh); overflow: auto; padding: var(--space-6); gap: var(--space-4); }
|
|
2664
|
+
.tp-dialog-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
|
|
2665
|
+
.tp-dialog-head h2 { margin: 0; font-size: var(--type-lg); color: var(--fg-0); }
|
|
2666
|
+
.tp-dialog-head p { margin: var(--space-1) 0 0; color: var(--fg-2); font-size: var(--type-sm); }
|
|
2667
|
+
|
|
2617
2668
|
/* ── Phase 29 (E4) — RepoBranchSwitcher. REDESIGNED to the approved mockup
|
|
2618
2669
|
(.design/ui/RepoBranchSwitcher.tsx): a compact ONE-LINE dock at the BOTTOM of the
|
|
2619
2670
|
sidebar (above the IdentityBar avatar) that opens ONE popup UPWARD. Mirrors the
|
|
@@ -1454,7 +1454,7 @@
|
|
|
1454
1454
|
animation-duration: 1ms;
|
|
1455
1455
|
}
|
|
1456
1456
|
}
|
|
1457
|
-
.
|
|
1457
|
+
.maude-notice .st-export-progress {
|
|
1458
1458
|
margin-top: var(--space-1, 4px);
|
|
1459
1459
|
}
|
|
1460
1460
|
/* Busy state for the Save…/Download button — the bytes fetch + (native) the
|
|
@@ -2157,3 +2157,46 @@
|
|
|
2157
2157
|
color: var(--fg-3);
|
|
2158
2158
|
overflow-wrap: anywhere;
|
|
2159
2159
|
}
|
|
2160
|
+
|
|
2161
|
+
/* Seed progress (feature-large-project-seed).
|
|
2162
|
+
A bar rather than a number alone: a large project's seed is minutes to hours
|
|
2163
|
+
of work, and "1 412" reads the same whether it is a quarter done or finished.
|
|
2164
|
+
Track and fill use existing tokens — no new colours enter the system here. */
|
|
2165
|
+
.sp-progress-track {
|
|
2166
|
+
height: 4px;
|
|
2167
|
+
border-radius: 2px;
|
|
2168
|
+
background: var(--bg-2);
|
|
2169
|
+
overflow: hidden;
|
|
2170
|
+
margin: 6px 0 4px;
|
|
2171
|
+
}
|
|
2172
|
+
.sp-progress-fill {
|
|
2173
|
+
height: 100%;
|
|
2174
|
+
background: var(--accent);
|
|
2175
|
+
border-radius: 2px;
|
|
2176
|
+
transition: width var(--dur-2, 240ms) ease;
|
|
2177
|
+
}
|
|
2178
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2179
|
+
.sp-progress-fill { transition-duration: 1ms; }
|
|
2180
|
+
}
|
|
2181
|
+
/* The falsifiable half (DDR-214) — present, quiet, never the headline. */
|
|
2182
|
+
.sp-raw-counters {
|
|
2183
|
+
opacity: 0.6;
|
|
2184
|
+
font-size: 0.9em;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
/* Issue #103: full export diagnostics are opt-in and stay inside the panel. */
|
|
2188
|
+
.st-export-item__error { min-width: 0; width: 100%; }
|
|
2189
|
+
.st-export-item__error summary { cursor: pointer; }
|
|
2190
|
+
.st-export-item__error pre { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 320px; overflow: auto; font: inherit; }
|
|
2191
|
+
.st-export-item:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
2192
|
+
|
|
2193
|
+
/* Share links reuse the shell dialog material. */
|
|
2194
|
+
.st-share-dialog { max-height: 90vh; overflow-y: auto; }
|
|
2195
|
+
.st-share-path, .st-share-hint { margin: 0; color: var(--fg-2); font-size: var(--type-xs); overflow-wrap: anywhere; }
|
|
2196
|
+
.st-share-link { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
2197
|
+
.st-share-link input { flex: 1; min-width: 0; padding: var(--space-2); border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--bg-2); color: var(--fg-0); font: inherit; }
|
|
2198
|
+
.st-share-link a { color: var(--fg-1); font-size: var(--type-xs); }
|
|
2199
|
+
.st-share-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--fg-2); cursor: pointer; }
|
|
2200
|
+
.st-share-btn:hover { background: var(--bg-2); color: var(--fg-0); }
|
|
2201
|
+
.st-share-btn:disabled { opacity: .4; cursor: default; }
|
|
2202
|
+
.st-share-btn:focus-visible, .st-share-link input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
@@ -9,7 +9,24 @@
|
|
|
9
9
|
// only `position` is overridden inline since this menu anchors to an
|
|
10
10
|
// arbitrary row, not the menubar's fixed offset.
|
|
11
11
|
|
|
12
|
-
import { useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
13
|
+
|
|
14
|
+
const EDGE = 8;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Where a menu of `size` goes so it stays on screen: below the anchor when it
|
|
18
|
+
* fits, else above it, else pinned to the viewport edge (scrolling inside).
|
|
19
|
+
* A row near the bottom of a long tree used to open its menu off-screen.
|
|
20
|
+
*/
|
|
21
|
+
export function placeMenu(anchor, size, viewport) {
|
|
22
|
+
let top = anchor.bottom + 4;
|
|
23
|
+
if (top + size.height > viewport.height - EDGE) {
|
|
24
|
+
const above = anchor.top - 4 - size.height;
|
|
25
|
+
top = above >= EDGE ? above : Math.max(EDGE, viewport.height - EDGE - size.height);
|
|
26
|
+
}
|
|
27
|
+
const left = Math.max(EDGE, Math.min(anchor.left, viewport.width - EDGE - size.width));
|
|
28
|
+
return { top, left, maxHeight: viewport.height - 2 * EDGE };
|
|
29
|
+
}
|
|
13
30
|
|
|
14
31
|
/**
|
|
15
32
|
* One shared menu instance per tree (mirrors useTreeDrag — only one row's
|
|
@@ -26,7 +43,14 @@ export function useRowMenu() {
|
|
|
26
43
|
e.stopPropagation();
|
|
27
44
|
const anchorEl = e.currentTarget;
|
|
28
45
|
const r = anchorEl.getBoundingClientRect();
|
|
29
|
-
setState({
|
|
46
|
+
setState({
|
|
47
|
+
x: r.left,
|
|
48
|
+
y: r.bottom + 4,
|
|
49
|
+
anchor: { top: r.top, bottom: r.bottom, left: r.left },
|
|
50
|
+
anchorEl,
|
|
51
|
+
view: 'root',
|
|
52
|
+
extra,
|
|
53
|
+
});
|
|
30
54
|
};
|
|
31
55
|
const close = () => {
|
|
32
56
|
setState((s) => {
|
|
@@ -46,6 +70,25 @@ export function useRowMenu() {
|
|
|
46
70
|
*/
|
|
47
71
|
export function TreeRowMenu({ state, onClose, rootItems, destinations, onPickDestination }) {
|
|
48
72
|
const ref = useRef(null);
|
|
73
|
+
const [placed, setPlaced] = useState(null);
|
|
74
|
+
const view = state?.view;
|
|
75
|
+
useLayoutEffect(() => {
|
|
76
|
+
const el = ref.current;
|
|
77
|
+
if (!state || !el) {
|
|
78
|
+
setPlaced(null);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const anchor = state.anchor ?? { top: state.y - 4, bottom: state.y - 4, left: state.x };
|
|
82
|
+
setPlaced(
|
|
83
|
+
placeMenu(
|
|
84
|
+
anchor,
|
|
85
|
+
{ width: el.offsetWidth, height: el.scrollHeight },
|
|
86
|
+
{ width: window.innerWidth, height: window.innerHeight }
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
// Re-place when the view switches (Move to… is a different height).
|
|
90
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
91
|
+
}, [state?.anchorEl, view]);
|
|
49
92
|
useEffect(() => {
|
|
50
93
|
if (!state) return;
|
|
51
94
|
const el = ref.current;
|
|
@@ -79,7 +122,9 @@ export function TreeRowMenu({ state, onClose, rootItems, destinations, onPickDes
|
|
|
79
122
|
}, [state, onClose]);
|
|
80
123
|
|
|
81
124
|
if (!state) return null;
|
|
82
|
-
const style =
|
|
125
|
+
const style = placed
|
|
126
|
+
? { position: 'fixed', left: placed.left, top: placed.top, maxHeight: placed.maxHeight, overflowY: 'auto' }
|
|
127
|
+
: { position: 'fixed', left: state.x, top: state.y };
|
|
83
128
|
|
|
84
129
|
if (state.view === 'move-to') {
|
|
85
130
|
return (
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// (entry.tour[]) land in Phase 3 — the panel/toast leave a hook for them.
|
|
10
10
|
|
|
11
11
|
import { useCallback, useEffect, useState } from 'react';
|
|
12
|
+
import { dismissNotice, notify } from '../notifications.tsx';
|
|
12
13
|
import { bestSeenVersion, computeUnseen } from './whats-new-seen.js';
|
|
13
14
|
|
|
14
15
|
const WN_SEEN = 'mdcc-whatsnew-seen';
|
|
@@ -143,37 +144,28 @@ export function WhatsNewBadge({ count = 0, onOpen }) {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
export function WhatsNewToast({ wn }) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
<button type="button" className="btn btn--primary btn--sm" onClick={wn.openPanel}>
|
|
169
|
-
{total > 1 ? `See all (${total})` : 'Details'}
|
|
170
|
-
</button>
|
|
171
|
-
<button type="button" className="btn btn--ghost btn--sm" onClick={wn.dismissToast}>
|
|
172
|
-
Dismiss
|
|
173
|
-
</button>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
);
|
|
147
|
+
const id = `whats-new-${wn.feedVersion}`;
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (!wn.showToast || !wn.toastEntry) {
|
|
150
|
+
dismissNotice(id);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
notify({
|
|
154
|
+
id,
|
|
155
|
+
group: 'whats-new',
|
|
156
|
+
kind: 'info',
|
|
157
|
+
duration: 10_000,
|
|
158
|
+
title: `What's new${wn.toastEntry.version ? ` · v${wn.toastEntry.version}` : ''} — ${wn.toastEntry.title}`,
|
|
159
|
+
description: wn.toastEntry.summary,
|
|
160
|
+
action: {
|
|
161
|
+
label: wn.unseen.length > 1 ? `See all (${wn.unseen.length})` : 'Details',
|
|
162
|
+
onClick: wn.openPanel,
|
|
163
|
+
},
|
|
164
|
+
onDismiss: wn.dismissToast,
|
|
165
|
+
});
|
|
166
|
+
}, [id, wn.showToast, wn.toastEntry, wn.unseen.length, wn.openPanel, wn.dismissToast]);
|
|
167
|
+
useEffect(() => () => dismissNotice(id), [id]);
|
|
168
|
+
return null;
|
|
177
169
|
}
|
|
178
170
|
|
|
179
171
|
export function WhatsNewPanel({ wn, onStartTour }) {
|
|
@@ -229,7 +221,9 @@ export function WhatsNewPanel({ wn, onStartTour }) {
|
|
|
229
221
|
<div className="mdcc-wn-item__hd">
|
|
230
222
|
<span className={'mdcc-wn-kind mdcc-wn-kind--' + e.kind}>{e.kind}</span>
|
|
231
223
|
<span className="mdcc-wn-item__title">{e.title}</span>
|
|
232
|
-
<span className="mdcc-wn-item__ver">
|
|
224
|
+
<span className="mdcc-wn-item__ver">
|
|
225
|
+
{e.version ? `v${e.version}` : 'next'}
|
|
226
|
+
</span>
|
|
233
227
|
</div>
|
|
234
228
|
<p className="mdcc-wn-item__summary">{e.summary}</p>
|
|
235
229
|
{isSafeHref(e.learnMore) && (
|
|
@@ -309,6 +309,75 @@ export function createCloudEndpoints(ctx: Ctx) {
|
|
|
309
309
|
});
|
|
310
310
|
},
|
|
311
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Open a project as a MANAGED local copy (T21): the same open → cell
|
|
314
|
+
* exchange → stored credential as `attach`, but nothing is written into
|
|
315
|
+
* the folder this studio happens to serve — the desktop creates the
|
|
316
|
+
* project's own copy and switches to it.
|
|
317
|
+
*/
|
|
318
|
+
async openManaged(projectId: string): Promise<CloudEndpointResult> {
|
|
319
|
+
const file = readCloudFile();
|
|
320
|
+
if (!file)
|
|
321
|
+
return { status: 401, json: { ok: false, error: 'Sign in to Maude Cloud first.' } };
|
|
322
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(String(projectId ?? ''))) {
|
|
323
|
+
return { status: 400, json: { ok: false, error: 'unknown project' } };
|
|
324
|
+
}
|
|
325
|
+
const opened = await cloudFetch('/projects/open', {
|
|
326
|
+
method: 'POST',
|
|
327
|
+
headers: { authorization: `Bearer ${file.token}`, 'content-type': 'application/json' },
|
|
328
|
+
body: JSON.stringify({ project: projectId }),
|
|
329
|
+
});
|
|
330
|
+
if (opened.status !== 200 || !opened.body?.token || !opened.body.url) {
|
|
331
|
+
return {
|
|
332
|
+
status: 502,
|
|
333
|
+
json: { ok: false, error: 'The project could not be opened with this account.' },
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return exchangeAtCell({
|
|
337
|
+
workspaceUrl: opened.body.url,
|
|
338
|
+
projectToken: opened.body.token,
|
|
339
|
+
role: opened.body.role,
|
|
340
|
+
project: projectId,
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
/** The maude:// handoff lane, as a managed copy (see `attachCode`). */
|
|
345
|
+
async openManagedCode(code: string, claimedProject?: string): Promise<CloudEndpointResult> {
|
|
346
|
+
if (typeof code !== 'string' || !/^mhc_[0-9a-f]{16,128}$/.test(code)) {
|
|
347
|
+
return { status: 400, json: { ok: false, error: 'That link is not valid.' } };
|
|
348
|
+
}
|
|
349
|
+
const exchanged = await cloudFetch('/auth/handoff/exchange', {
|
|
350
|
+
method: 'POST',
|
|
351
|
+
headers: { 'content-type': 'application/json' },
|
|
352
|
+
body: JSON.stringify({ code }),
|
|
353
|
+
});
|
|
354
|
+
if (exchanged.status !== 200 || !exchanged.body?.token || !exchanged.body.url) {
|
|
355
|
+
return {
|
|
356
|
+
status: 410,
|
|
357
|
+
json: {
|
|
358
|
+
ok: false,
|
|
359
|
+
error:
|
|
360
|
+
'That link expired — it works once, for two minutes. Press “Open in Maude” on the project page again.',
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
if (claimedProject && exchanged.body.project && claimedProject !== exchanged.body.project) {
|
|
365
|
+
return {
|
|
366
|
+
status: 409,
|
|
367
|
+
json: {
|
|
368
|
+
ok: false,
|
|
369
|
+
error: `That link said ${claimedProject} but it opens ${exchanged.body.project}. Nothing was opened — open the project from its own page instead.`,
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return exchangeAtCell({
|
|
374
|
+
workspaceUrl: exchanged.body.url,
|
|
375
|
+
projectToken: exchanged.body.token,
|
|
376
|
+
role: exchanged.body.role,
|
|
377
|
+
project: exchanged.body.project,
|
|
378
|
+
});
|
|
379
|
+
},
|
|
380
|
+
|
|
312
381
|
/**
|
|
313
382
|
* Attach via a one-time handoff code — the maude:// lane (Phase 17). The
|
|
314
383
|
* code came from an untrusted URL, so it is exchanged ONLY against the
|
|
@@ -664,6 +733,69 @@ export function createCloudEndpoints(ctx: Ctx) {
|
|
|
664
733
|
}
|
|
665
734
|
}
|
|
666
735
|
|
|
736
|
+
/**
|
|
737
|
+
* The cell exchange alone: project token → hub session → stored credential.
|
|
738
|
+
* Writes nothing into any project folder (a managed open creates its own).
|
|
739
|
+
*/
|
|
740
|
+
async function exchangeAtCell({
|
|
741
|
+
workspaceUrl,
|
|
742
|
+
projectToken,
|
|
743
|
+
role,
|
|
744
|
+
project,
|
|
745
|
+
}: {
|
|
746
|
+
workspaceUrl: string;
|
|
747
|
+
projectToken: string;
|
|
748
|
+
role?: string;
|
|
749
|
+
project?: string;
|
|
750
|
+
}): Promise<CloudEndpointResult> {
|
|
751
|
+
const r = await exchangeCredential(workspaceUrl, projectToken);
|
|
752
|
+
if (!r) {
|
|
753
|
+
return {
|
|
754
|
+
status: 502,
|
|
755
|
+
json: {
|
|
756
|
+
ok: false,
|
|
757
|
+
error: 'The workspace did not accept the sign-in. Try again in a minute.',
|
|
758
|
+
},
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
status: 200,
|
|
763
|
+
json: { ok: true, url: r.url, role: r.role ?? role ?? null, project: project ?? null },
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async function exchangeCredential(
|
|
768
|
+
workspaceUrl: string,
|
|
769
|
+
projectToken: string
|
|
770
|
+
): Promise<{ url: string; role?: string } | null> {
|
|
771
|
+
let hubToken: string | null = null;
|
|
772
|
+
let hubTokenExpiresAt: number | undefined;
|
|
773
|
+
let vouchedRole: string | undefined;
|
|
774
|
+
try {
|
|
775
|
+
const res = await fetch(`${workspaceUrl}/auth/login`, {
|
|
776
|
+
method: 'POST',
|
|
777
|
+
headers: { 'content-type': 'application/json' },
|
|
778
|
+
body: JSON.stringify({ token: projectToken }),
|
|
779
|
+
});
|
|
780
|
+
const body = (await res.json().catch(() => ({}))) as CloudBody & {
|
|
781
|
+
expiresAt?: unknown;
|
|
782
|
+
user?: { role?: unknown };
|
|
783
|
+
};
|
|
784
|
+
if (res.ok && body?.token) {
|
|
785
|
+
hubToken = body.token;
|
|
786
|
+
if (typeof body.expiresAt === 'number' && Number.isFinite(body.expiresAt))
|
|
787
|
+
hubTokenExpiresAt = body.expiresAt;
|
|
788
|
+
if (typeof body.user?.role === 'string' && body.user.role) vouchedRole = body.user.role;
|
|
789
|
+
}
|
|
790
|
+
} catch {
|
|
791
|
+
/* handled by the caller */
|
|
792
|
+
}
|
|
793
|
+
if (!hubToken) return null;
|
|
794
|
+
const norm = normalizeUrl(workspaceUrl);
|
|
795
|
+
saveHubCredential(norm, hubToken, vouchedRole, hubTokenExpiresAt);
|
|
796
|
+
return { url: norm, role: vouchedRole };
|
|
797
|
+
}
|
|
798
|
+
|
|
667
799
|
/** The shared tail of every attach: cell exchange → credential + linkedHub. */
|
|
668
800
|
async function linkToWorkspace({
|
|
669
801
|
workspaceUrl,
|
|
@@ -64,6 +64,10 @@ export function createCollab(ctx: Context, api: Api): Collab {
|
|
|
64
64
|
fileForSlug,
|
|
65
65
|
shouldSeed: (slug) => !(ctx.sharedDoc && registryRef?.isPinned(slug)),
|
|
66
66
|
});
|
|
67
|
+
// Accepted revisions: browser writes to a synced canvas's room are refused
|
|
68
|
+
// (the room doc is the hub's accepted replica). Asked per frame — the save
|
|
69
|
+
// mode can change while the studio runs.
|
|
70
|
+
persistence.acceptedMode = () => ctx.syncControl?.current?.()?.acceptedMode?.() === true;
|
|
67
71
|
const registry = createRegistry(persistence);
|
|
68
72
|
registryRef = registry;
|
|
69
73
|
|