@1agh/maude 0.42.0 → 0.44.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/apps/studio/.ai/cache/_stats.json +7 -0
- package/apps/studio/acp/bootstrap-brief.ts +12 -2
- package/apps/studio/acp/bridge.ts +44 -10
- package/apps/studio/acp/env.ts +23 -0
- package/apps/studio/acp/login-state.ts +263 -0
- package/apps/studio/acp/plugin-bootstrap.ts +31 -25
- package/apps/studio/acp/probe.ts +152 -1
- package/apps/studio/annotations-layer.tsx +1451 -324
- package/apps/studio/annotations-model.ts +58 -0
- package/apps/studio/api.ts +568 -24
- package/apps/studio/bin/_audio-search.mjs +140 -0
- package/apps/studio/bin/_import-asset-pdf-worker.mjs +34 -0
- package/apps/studio/bin/_import-asset.mjs +815 -0
- package/apps/studio/bin/_import-brand.mjs +635 -0
- package/apps/studio/bin/_import-tokens-alias-resolver.mjs +95 -0
- package/apps/studio/bin/_import-tokens.mjs +1201 -0
- package/apps/studio/bin/_ingest-footage.mjs +386 -0
- package/apps/studio/bin/_probe-footage-playwright.mjs +269 -0
- package/apps/studio/bin/_transcribe.mjs +419 -0
- package/apps/studio/bin/_transcribe.test.mjs +80 -0
- package/apps/studio/bin/_video-playwright.mjs +141 -71
- package/apps/studio/bin/audio-search.sh +28 -0
- package/apps/studio/bin/generate.sh +154 -0
- package/apps/studio/bin/import-asset.sh +34 -0
- package/apps/studio/bin/import-brand.sh +29 -0
- package/apps/studio/bin/import-tokens.sh +33 -0
- package/apps/studio/bin/ingest-footage.sh +27 -0
- package/apps/studio/bin/photo-adjust.sh +163 -0
- package/apps/studio/bin/photo-bg-remove.sh +258 -0
- package/apps/studio/bin/probe-footage.sh +28 -0
- package/apps/studio/bin/read-annotations.mjs +125 -2
- package/apps/studio/bin/transcribe.sh +39 -0
- package/apps/studio/canvas-edit.ts +656 -12
- package/apps/studio/canvas-lib.tsx +637 -10
- package/apps/studio/canvas-pipeline.ts +65 -7
- package/apps/studio/canvas-shell.tsx +156 -16
- package/apps/studio/client/app.jsx +2385 -699
- package/apps/studio/client/export-center.jsx +35 -1
- package/apps/studio/client/generate-dialog.jsx +352 -0
- package/apps/studio/client/github.js +18 -0
- package/apps/studio/client/inspector-controls.jsx +781 -0
- package/apps/studio/client/panels/BrandUploadPanel.jsx +233 -0
- package/apps/studio/client/panels/ChatPanel.jsx +15 -33
- package/apps/studio/client/panels/GitPanel.jsx +8 -0
- package/apps/studio/client/panels/IntroVideoDialog.jsx +44 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +12 -0
- package/apps/studio/client/panels/ReadinessList.jsx +227 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +133 -21
- package/apps/studio/client/panels/SettingsPanel.jsx +828 -0
- package/apps/studio/client/panels/SetupChecklist.jsx +223 -0
- package/apps/studio/client/panels/StickerPicker.jsx +160 -0
- package/apps/studio/client/panels/TimelinePanel.jsx +15 -2
- package/apps/studio/client/photo-knobs.jsx +432 -0
- package/apps/studio/client/styles/3-shell-maude.css +376 -28
- package/apps/studio/client/styles/4-components.css +223 -0
- package/apps/studio/client/styles/6-acp-chat.css +86 -0
- package/apps/studio/client/tour/quick-setup-tour.js +39 -0
- package/apps/studio/commands/annotation-strokes-command.ts +13 -3
- package/apps/studio/commands/edit-source-command.ts +31 -4
- package/apps/studio/config.schema.json +70 -0
- package/apps/studio/context-menu.tsx +42 -8
- package/apps/studio/context.ts +16 -0
- package/apps/studio/design-setup-readiness.ts +115 -0
- package/apps/studio/dist/client.bundle.js +5572 -21
- package/apps/studio/dist/comment-mount.js +2 -2
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/@imgly_background-removal.js +5543 -0
- package/apps/studio/dist/runtime/pixi-js.js +519 -519
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/dom-selection.ts +25 -0
- package/apps/studio/draw/palette.ts +34 -0
- package/apps/studio/exporters/jobs.ts +47 -5
- package/apps/studio/exporters/video.ts +24 -4
- package/apps/studio/footage/schema.test.ts +287 -0
- package/apps/studio/footage/schema.ts +662 -0
- package/apps/studio/footage-store.ts +235 -0
- package/apps/studio/fs-watch.ts +10 -0
- package/apps/studio/generation/adapters/elevenlabs.ts +370 -0
- package/apps/studio/generation/adapters/gemini.ts +542 -0
- package/apps/studio/generation/adapters/groq.test.ts +80 -0
- package/apps/studio/generation/adapters/groq.ts +247 -0
- package/apps/studio/generation/audio-library.test.ts +95 -0
- package/apps/studio/generation/audio-library.ts +156 -0
- package/apps/studio/generation/captions.test.ts +78 -0
- package/apps/studio/generation/captions.ts +132 -0
- package/apps/studio/generation/download.ts +76 -0
- package/apps/studio/generation/elevenlabs.test.ts +223 -0
- package/apps/studio/generation/gemini.test.ts +350 -0
- package/apps/studio/generation/jobs.test.ts +128 -0
- package/apps/studio/generation/jobs.ts +243 -0
- package/apps/studio/generation/keys.test.ts +75 -0
- package/apps/studio/generation/keys.ts +162 -0
- package/apps/studio/generation/prefs.test.ts +95 -0
- package/apps/studio/generation/prefs.ts +82 -0
- package/apps/studio/generation/registry.ts +88 -0
- package/apps/studio/generation/types.test.ts +123 -0
- package/apps/studio/generation/types.ts +266 -0
- package/apps/studio/generation/whisper-models.test.ts +152 -0
- package/apps/studio/generation/whisper-models.ts +257 -0
- package/apps/studio/generation/whisper-spike-results.md +61 -0
- package/apps/studio/git/endpoints.ts +68 -7
- package/apps/studio/git/service.ts +199 -21
- package/apps/studio/github/service.ts +67 -0
- package/apps/studio/handoff.ts +3 -2
- package/apps/studio/http.ts +1039 -22
- package/apps/studio/input-router.tsx +10 -0
- package/apps/studio/inspect.ts +53 -1
- package/apps/studio/media/intro.mp4 +0 -0
- package/apps/studio/media-commit-chain.ts +119 -0
- package/apps/studio/paths.ts +20 -0
- package/apps/studio/photo/filters.ts +285 -0
- package/apps/studio/photo/pipeline.ts +607 -0
- package/apps/studio/photo/schema.ts +367 -0
- package/apps/studio/photo-store.ts +154 -0
- package/apps/studio/readiness.ts +108 -20
- package/apps/studio/runtime-bundle.ts +29 -6
- package/apps/studio/scaffold-design.ts +1145 -0
- package/apps/studio/server.ts +18 -2
- package/apps/studio/stickers/figjam-doodle/manifest.json +104 -0
- package/apps/studio/stickers/figjam-doodle/slice1.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice10.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice11.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice12.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice13.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice14.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice15.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice16.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice17.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice18.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice19.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice2.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice20.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice21.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice22.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice23.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice24.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice3.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice4.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice5.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice6.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice7.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice8.png +0 -0
- package/apps/studio/stickers/figjam-doodle/slice9.png +0 -0
- package/apps/studio/stickers/life-style/best.png +0 -0
- package/apps/studio/stickers/life-style/come-on.png +0 -0
- package/apps/studio/stickers/life-style/cut.png +0 -0
- package/apps/studio/stickers/life-style/detox.png +0 -0
- package/apps/studio/stickers/life-style/just.png +0 -0
- package/apps/studio/stickers/life-style/manifest.json +68 -0
- package/apps/studio/stickers/life-style/mirror.png +0 -0
- package/apps/studio/stickers/life-style/nice.png +0 -0
- package/apps/studio/stickers/life-style/objects.png +0 -0
- package/apps/studio/stickers/life-style/ok.png +0 -0
- package/apps/studio/stickers/life-style/queen.png +0 -0
- package/apps/studio/stickers/life-style/star.png +0 -0
- package/apps/studio/stickers/life-style/sun.png +0 -0
- package/apps/studio/stickers/life-style/water.png +0 -0
- package/apps/studio/stickers/life-style/yeah.png +0 -0
- package/apps/studio/stickers/life-style/you-can.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/check-the-deets.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/cut-it.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/dope.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/fresh.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/hot.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/idea.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/keep-exploring.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/killed-it.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/love-it.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/manifest.json +88 -0
- package/apps/studio/stickers/opposing-thoughts/not-sure.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/ok.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/pure-gold.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/save-for-later.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/steal-this.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/take-a-peek.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/this-or-that.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/thoughts.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/vibes.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/winner.png +0 -0
- package/apps/studio/stickers/opposing-thoughts/wip.png +0 -0
- package/apps/studio/stickers/project-status/group-100.png +0 -0
- package/apps/studio/stickers/project-status/group-101.png +0 -0
- package/apps/studio/stickers/project-status/group-102.png +0 -0
- package/apps/studio/stickers/project-status/group-103.png +0 -0
- package/apps/studio/stickers/project-status/group-104.png +0 -0
- package/apps/studio/stickers/project-status/group-105.png +0 -0
- package/apps/studio/stickers/project-status/group-106.png +0 -0
- package/apps/studio/stickers/project-status/group-107.png +0 -0
- package/apps/studio/stickers/project-status/group-108.png +0 -0
- package/apps/studio/stickers/project-status/group-109.png +0 -0
- package/apps/studio/stickers/project-status/group-110.png +0 -0
- package/apps/studio/stickers/project-status/group-111.png +0 -0
- package/apps/studio/stickers/project-status/group-112.png +0 -0
- package/apps/studio/stickers/project-status/group-113.png +0 -0
- package/apps/studio/stickers/project-status/group-114.png +0 -0
- package/apps/studio/stickers/project-status/group-115.png +0 -0
- package/apps/studio/stickers/project-status/group-117.png +0 -0
- package/apps/studio/stickers/project-status/group-118.png +0 -0
- package/apps/studio/stickers/project-status/group-119.png +0 -0
- package/apps/studio/stickers/project-status/group-120.png +0 -0
- package/apps/studio/stickers/project-status/group-121.png +0 -0
- package/apps/studio/stickers/project-status/group-122.png +0 -0
- package/apps/studio/stickers/project-status/group-41.png +0 -0
- package/apps/studio/stickers/project-status/group-42.png +0 -0
- package/apps/studio/stickers/project-status/group-43.png +0 -0
- package/apps/studio/stickers/project-status/group-44.png +0 -0
- package/apps/studio/stickers/project-status/group-45.png +0 -0
- package/apps/studio/stickers/project-status/group-46.png +0 -0
- package/apps/studio/stickers/project-status/group-47.png +0 -0
- package/apps/studio/stickers/project-status/group-48.png +0 -0
- package/apps/studio/stickers/project-status/group-49.png +0 -0
- package/apps/studio/stickers/project-status/group-50.png +0 -0
- package/apps/studio/stickers/project-status/group-51.png +0 -0
- package/apps/studio/stickers/project-status/group-52.png +0 -0
- package/apps/studio/stickers/project-status/group-53.png +0 -0
- package/apps/studio/stickers/project-status/group-54.png +0 -0
- package/apps/studio/stickers/project-status/group-55.png +0 -0
- package/apps/studio/stickers/project-status/group-56.png +0 -0
- package/apps/studio/stickers/project-status/group-57.png +0 -0
- package/apps/studio/stickers/project-status/group-58.png +0 -0
- package/apps/studio/stickers/project-status/group-59.png +0 -0
- package/apps/studio/stickers/project-status/group-60.png +0 -0
- package/apps/studio/stickers/project-status/group-61.png +0 -0
- package/apps/studio/stickers/project-status/group-62.png +0 -0
- package/apps/studio/stickers/project-status/group-63.png +0 -0
- package/apps/studio/stickers/project-status/group-64.png +0 -0
- package/apps/studio/stickers/project-status/group-65.png +0 -0
- package/apps/studio/stickers/project-status/group-66.png +0 -0
- package/apps/studio/stickers/project-status/group-67.png +0 -0
- package/apps/studio/stickers/project-status/group-68.png +0 -0
- package/apps/studio/stickers/project-status/group-69.png +0 -0
- package/apps/studio/stickers/project-status/group-70.png +0 -0
- package/apps/studio/stickers/project-status/group-71.png +0 -0
- package/apps/studio/stickers/project-status/group-72.png +0 -0
- package/apps/studio/stickers/project-status/group-73.png +0 -0
- package/apps/studio/stickers/project-status/group-74.png +0 -0
- package/apps/studio/stickers/project-status/group-75.png +0 -0
- package/apps/studio/stickers/project-status/group-76.png +0 -0
- package/apps/studio/stickers/project-status/group-77.png +0 -0
- package/apps/studio/stickers/project-status/group-78.png +0 -0
- package/apps/studio/stickers/project-status/group-79.png +0 -0
- package/apps/studio/stickers/project-status/group-80.png +0 -0
- package/apps/studio/stickers/project-status/group-81.png +0 -0
- package/apps/studio/stickers/project-status/group-82.png +0 -0
- package/apps/studio/stickers/project-status/group-83.png +0 -0
- package/apps/studio/stickers/project-status/group-84.png +0 -0
- package/apps/studio/stickers/project-status/group-85.png +0 -0
- package/apps/studio/stickers/project-status/group-86.png +0 -0
- package/apps/studio/stickers/project-status/group-87.png +0 -0
- package/apps/studio/stickers/project-status/group-88.png +0 -0
- package/apps/studio/stickers/project-status/group-89.png +0 -0
- package/apps/studio/stickers/project-status/group-90.png +0 -0
- package/apps/studio/stickers/project-status/group-91.png +0 -0
- package/apps/studio/stickers/project-status/group-92.png +0 -0
- package/apps/studio/stickers/project-status/group-93.png +0 -0
- package/apps/studio/stickers/project-status/group-94.png +0 -0
- package/apps/studio/stickers/project-status/group-96.png +0 -0
- package/apps/studio/stickers/project-status/group-97.png +0 -0
- package/apps/studio/stickers/project-status/group-98.png +0 -0
- package/apps/studio/stickers/project-status/group-99.png +0 -0
- package/apps/studio/stickers/project-status/manifest.json +328 -0
- package/apps/studio/test/_helpers.ts +15 -2
- package/apps/studio/test/acp-bootstrap-brief.test.ts +11 -0
- package/apps/studio/test/acp-bridge.test.ts +34 -1
- package/apps/studio/test/acp-commands.test.ts +2 -1
- package/apps/studio/test/acp-env.test.ts +30 -0
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +23 -57
- package/apps/studio/test/acp-session-plugins.test.ts +72 -1
- package/apps/studio/test/annotation-strokes-command.test.ts +17 -0
- package/apps/studio/test/annotations-layer.test.ts +174 -0
- package/apps/studio/test/asset-api.test.ts +3 -3
- package/apps/studio/test/canvas-edit.test.ts +17 -6
- package/apps/studio/test/canvas-origin-gate.test.ts +50 -0
- package/apps/studio/test/csp-canvas-shell.test.ts +11 -0
- package/apps/studio/test/csrf-write-guard.test.ts +23 -1
- package/apps/studio/test/design-setup-readiness.test.ts +155 -0
- package/apps/studio/test/dynamic-text-edit.test.ts +162 -0
- package/apps/studio/test/edit-source-command.test.ts +23 -0
- package/apps/studio/test/element-structural-edit.test.ts +154 -0
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +13 -0
- package/apps/studio/test/footage-store.test.ts +100 -0
- package/apps/studio/test/generate-route.test.ts +106 -0
- package/apps/studio/test/git-branches.test.ts +97 -0
- package/apps/studio/test/github-api.test.ts +56 -1
- package/apps/studio/test/import-asset-browser.test.ts +64 -0
- package/apps/studio/test/import-asset-route.test.ts +71 -0
- package/apps/studio/test/import-asset.test.ts +399 -0
- package/apps/studio/test/import-brand-browser.test.ts +88 -0
- package/apps/studio/test/import-brand-route.test.ts +120 -0
- package/apps/studio/test/import-brand.test.ts +206 -0
- package/apps/studio/test/import-tokens.test.ts +722 -0
- package/apps/studio/test/input-router.test.ts +33 -0
- package/apps/studio/test/inspect-script-syntax.test.ts +53 -0
- package/apps/studio/test/media-commit-chain.test.ts +210 -0
- package/apps/studio/test/photo-bg-remove-validation.test.ts +71 -0
- package/apps/studio/test/photo-canvas-bundle.test.ts +61 -0
- package/apps/studio/test/photo-edit-api.test.ts +201 -0
- package/apps/studio/test/photo-filters.test.ts +149 -0
- package/apps/studio/test/photo-pipeline.test.ts +106 -0
- package/apps/studio/test/photo-taxonomy.test.ts +96 -0
- package/apps/studio/test/read-annotations.test.ts +164 -0
- package/apps/studio/test/readiness.test.ts +13 -11
- package/apps/studio/test/scaffold-design.test.ts +122 -0
- package/apps/studio/test/shell-importmap.test.ts +49 -0
- package/apps/studio/test/text-editability-stamp.test.ts +131 -0
- package/apps/studio/test/timeline-parse.test.ts +57 -0
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +5 -3
- package/apps/studio/test/tour-overlay.test.tsx +16 -0
- package/apps/studio/test/undo-stack.test.ts +33 -0
- package/apps/studio/test/video-asset.test.ts +5 -5
- package/apps/studio/test/video-render-bridge.test.ts +23 -1
- package/apps/studio/text-caret.ts +239 -0
- package/apps/studio/tool-palette.tsx +49 -21
- package/apps/studio/ui-prefs.ts +130 -0
- package/apps/studio/undo-stack.ts +94 -2
- package/apps/studio/use-annotation-resize.tsx +4 -4
- package/apps/studio/use-canvas-media-drop.tsx +40 -1
- package/apps/studio/use-chrome-visibility.tsx +8 -2
- package/apps/studio/use-selection-set.tsx +21 -0
- package/apps/studio/video-comp.tsx +48 -7
- package/apps/studio/whats-new.json +149 -0
- package/apps/studio/ws.ts +5 -0
- package/cli/bin/maude.mjs +6 -6
- package/cli/commands/design.mjs +67 -3
- package/cli/lib/gitignore-block.mjs +2 -0
- package/cli/lib/pkg-root.mjs +107 -0
- package/cli/lib/pkg-root.test.mjs +79 -0
- package/cli/lib/reconstruct-toolset.test.mjs +194 -0
- package/cli/lib/update-check.mjs +8 -0
- package/package.json +13 -11
- package/plugins/design/dependencies.json +18 -0
- package/plugins/design/templates/_shell.html +1 -0
|
@@ -158,21 +158,65 @@ function jsxElementName(opening: AnyNode): string {
|
|
|
158
158
|
return '?';
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
function
|
|
161
|
+
function hasJsxAttr(opening: AnyNode, name: string): boolean {
|
|
162
162
|
const attrs = opening?.attributes;
|
|
163
163
|
if (!Array.isArray(attrs)) return false;
|
|
164
164
|
for (const a of attrs) {
|
|
165
|
-
if (
|
|
166
|
-
a?.type === 'JSXAttribute' &&
|
|
167
|
-
a.name?.type === 'JSXIdentifier' &&
|
|
168
|
-
a.name.name === 'data-cd-id'
|
|
169
|
-
) {
|
|
165
|
+
if (a?.type === 'JSXAttribute' && a.name?.type === 'JSXIdentifier' && a.name.name === name) {
|
|
170
166
|
return true;
|
|
171
167
|
}
|
|
172
168
|
}
|
|
173
169
|
return false;
|
|
174
170
|
}
|
|
175
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Phase 6 (unified-text-editing) — mirror of `applyTextEdit`'s editability
|
|
174
|
+
* test (canvas-edit.ts): the element's children are exactly ONE meaningful
|
|
175
|
+
* node, and it is either a static JSXText or a `{'string literal'}`
|
|
176
|
+
* expression container (DDR-150 P1). Elements that pass get a build-time
|
|
177
|
+
* `data-cd-editable="text"` marker so the shell only offers inline editing
|
|
178
|
+
* where a commit will actually save — the rendered DOM cannot distinguish
|
|
179
|
+
* `<p>Total: {1 + 1} items</p>` (multiple text nodes, mixed source → the
|
|
180
|
+
* engine refuses the edit) from honest leaf text, but the AST can. KEEP THE
|
|
181
|
+
* TWO PREDICATES IN LOCKSTEP — if applyTextEdit's acceptance changes, this
|
|
182
|
+
* must change with it, or the marker starts lying.
|
|
183
|
+
*/
|
|
184
|
+
function inlineEditableKind(node: AnyNode): 'text' | 'var' | null {
|
|
185
|
+
const children: AnyNode[] = Array.isArray(node?.children) ? node.children : [];
|
|
186
|
+
const meaningful = children.filter(
|
|
187
|
+
(c) => !(c?.type === 'JSXText' && typeof c.value === 'string' && c.value.trim() === '')
|
|
188
|
+
);
|
|
189
|
+
if (meaningful.length !== 1) return null;
|
|
190
|
+
const only = meaningful[0];
|
|
191
|
+
if (only?.type === 'JSXText') return 'text';
|
|
192
|
+
if (only?.type === 'JSXExpressionContainer') {
|
|
193
|
+
const expr = only.expression;
|
|
194
|
+
// A `{'string literal'}` rewrites in place (DDR-150 P1) — same as JSXText.
|
|
195
|
+
if (
|
|
196
|
+
expr &&
|
|
197
|
+
(expr.type === 'Literal' || expr.type === 'StringLiteral') &&
|
|
198
|
+
typeof expr.value === 'string'
|
|
199
|
+
) {
|
|
200
|
+
return 'text';
|
|
201
|
+
}
|
|
202
|
+
// A `{variable}` or `{item.prop}` — the string lives in a const or a
|
|
203
|
+
// `.map()`ed array; the edit engine traces it back at commit time
|
|
204
|
+
// (canvas-edit.ts resolveDynamicTextSpan). Computed forms (calls, template
|
|
205
|
+
// strings, `a + b`, ternaries) are NOT offered — no single source string.
|
|
206
|
+
if (expr?.type === 'Identifier') return 'var';
|
|
207
|
+
if (
|
|
208
|
+
expr?.type === 'MemberExpression' &&
|
|
209
|
+
!expr.computed &&
|
|
210
|
+
expr.object?.type === 'Identifier' &&
|
|
211
|
+
expr.property?.type === 'Identifier'
|
|
212
|
+
) {
|
|
213
|
+
return 'var';
|
|
214
|
+
}
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
176
220
|
function computeId(componentName: string, idx: number): string {
|
|
177
221
|
// 8 hex chars from Bun.hash — 32 bits of entropy. Per-component collision
|
|
178
222
|
// probability for the canvas-scale we ship (≤ ~300 JSX elements) is < 1e-5.
|
|
@@ -215,7 +259,7 @@ function walkInjectIds(
|
|
|
215
259
|
const idx = frame.jsxIndex;
|
|
216
260
|
frame.jsxIndex += 1;
|
|
217
261
|
|
|
218
|
-
if (!
|
|
262
|
+
if (!hasJsxAttr(opening, 'data-cd-id')) {
|
|
219
263
|
const id = computeId(frame.componentName, idx);
|
|
220
264
|
// Insert " data-cd-id=\"<id>\"" right after the tag name. magic-string's
|
|
221
265
|
// appendLeft puts the new text before any subsequent attribute. JSX
|
|
@@ -235,6 +279,20 @@ function walkInjectIds(
|
|
|
235
279
|
}
|
|
236
280
|
}
|
|
237
281
|
|
|
282
|
+
// Phase 6 (unified-text-editing) — build-time editability marker, so
|
|
283
|
+
// the shell's inline-edit entry and the edit engine can never disagree
|
|
284
|
+
// (inlineEditableKind mirrors applyTextEdit's acceptance). `text` = a
|
|
285
|
+
// literal rewritten in place; `var` = a `{variable}` traced back to its
|
|
286
|
+
// source string at commit (canvas-edit.ts). Independent of the id
|
|
287
|
+
// injection above: hand-authored data-cd-id elements still get it.
|
|
288
|
+
const editKind = inlineEditableKind(node);
|
|
289
|
+
if (editKind && !hasJsxAttr(opening, 'data-cd-editable')) {
|
|
290
|
+
const insertAt: number | undefined = opening?.name?.end;
|
|
291
|
+
if (typeof insertAt === 'number') {
|
|
292
|
+
s.appendLeft(insertAt, ` data-cd-editable="${editKind}"`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
238
296
|
// Recurse into opening attributes + children, with the jsxPath extended.
|
|
239
297
|
frame.jsxPath.push(elName);
|
|
240
298
|
if (opening) visit(opening.attributes);
|
|
@@ -41,6 +41,7 @@ import { AnnotationsLayer } from './annotations-layer.tsx';
|
|
|
41
41
|
import { ArtboardMarqueeOverlay } from './artboard-marquee.tsx';
|
|
42
42
|
import {
|
|
43
43
|
type ArtboardRect,
|
|
44
|
+
PhotoPreviewBridge,
|
|
44
45
|
SnapGuideOverlay,
|
|
45
46
|
useArtboardsContext,
|
|
46
47
|
useDragStateContext,
|
|
@@ -88,6 +89,7 @@ import {
|
|
|
88
89
|
import { ElementMarqueeOverlay } from './marquee-overlay.tsx';
|
|
89
90
|
import { MeasureOverlay } from './measure-overlay.tsx';
|
|
90
91
|
import { ParticipantsChrome } from './participants-chrome.tsx';
|
|
92
|
+
import { mountCaret, placeCaretAt } from './text-caret.ts';
|
|
91
93
|
import { ToolPalette } from './tool-palette.tsx';
|
|
92
94
|
import { UndoHud } from './undo-hud.tsx';
|
|
93
95
|
import {
|
|
@@ -327,6 +329,22 @@ const HALO_CSS = `
|
|
|
327
329
|
outline-offset: 1px;
|
|
328
330
|
border-radius: 1px;
|
|
329
331
|
cursor: text;
|
|
332
|
+
/* Unify the caret with the annotation editors (CARET_FIX_STYLE in
|
|
333
|
+
annotations-layer.tsx) — same accent, visible against any background.
|
|
334
|
+
No compositing trigger here (no translateZ/will-change) so WebKit keeps
|
|
335
|
+
blinking the native caret. */
|
|
336
|
+
caret-color: var(--maude-hud-accent, #4a63e7);
|
|
337
|
+
}
|
|
338
|
+
/* Dogfood fix — hover affordance for the same leaf-text elements the dblclick
|
|
339
|
+
handler above enters edit mode on. The :not(:empty):not(:has(> *)) selector
|
|
340
|
+
proxies "all children are text nodes" (the exact isLeafText check happens
|
|
341
|
+
in JS on dblclick — this only needs to be a good-enough visual hint). Loses
|
|
342
|
+
to an active draw/annotation tool's !important global cursor override
|
|
343
|
+
(use-tool-mode.tsx), so it only shows in Move mode, same as the edit itself.
|
|
344
|
+
NOTE: keep this comment backtick-free — it lives inside a JS template
|
|
345
|
+
literal and a stray backtick closes it early (bun parse fail). */
|
|
346
|
+
[data-cd-id]:not(:empty):not(:has(> *)):hover {
|
|
347
|
+
cursor: text;
|
|
330
348
|
}
|
|
331
349
|
/* Phase 12.1 (DDR-138) — in-canvas drag-to-reorder (Figma-style). Dragging a
|
|
332
350
|
SELECTED element floats it with the cursor (via transform, applied inline —
|
|
@@ -549,6 +567,18 @@ function getLastLayersArtboardId(): string | null {
|
|
|
549
567
|
}
|
|
550
568
|
}
|
|
551
569
|
|
|
570
|
+
// DDR-148 — an artboard hosting a <VideoComp> stamps a `[data-comp-id]`
|
|
571
|
+
// element inside it (see video-comp.tsx). Gates the artboard-chrome context
|
|
572
|
+
// menu's "Open Timeline" entry to artboards that actually have a sequence.
|
|
573
|
+
function artboardHasVideo(artboardId: string | null | undefined): boolean {
|
|
574
|
+
if (typeof document === 'undefined' || !artboardId) return false;
|
|
575
|
+
try {
|
|
576
|
+
return !!document.querySelector(`[data-dc-screen="${CSS.escape(artboardId)}"] [data-comp-id]`);
|
|
577
|
+
} catch {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
552
582
|
function postLayersTree(artboardId: string | null | undefined): void {
|
|
553
583
|
if (typeof document === 'undefined' || !artboardId) return;
|
|
554
584
|
const root = document.querySelector(`[data-dc-screen="${CSS.escape(artboardId)}"]`);
|
|
@@ -1440,6 +1470,34 @@ function buildRegistry(deps: {
|
|
|
1440
1470
|
}
|
|
1441
1471
|
},
|
|
1442
1472
|
},
|
|
1473
|
+
{
|
|
1474
|
+
// feature-photo-editor (Task 16) — only on a content-addressed `<img>`.
|
|
1475
|
+
// Selects it (so the Inspector derives the Photo target from the
|
|
1476
|
+
// selection's `photoAsset`) + opens the Inspector on the Photo tab.
|
|
1477
|
+
id: 'edit-photo',
|
|
1478
|
+
label: 'Edit Photo…',
|
|
1479
|
+
hidden: (target) => {
|
|
1480
|
+
const el = target.el as HTMLImageElement | null;
|
|
1481
|
+
if (el?.tagName?.toLowerCase() !== 'img') return true;
|
|
1482
|
+
return !/assets\/[0-9a-f]{8}\.[a-z0-9]+/i.test(el.getAttribute?.('src') || '');
|
|
1483
|
+
},
|
|
1484
|
+
onSelect: (target) => {
|
|
1485
|
+
if (target.el) {
|
|
1486
|
+
selSet.replace(
|
|
1487
|
+
hoverTargetToSelection({
|
|
1488
|
+
el: target.el,
|
|
1489
|
+
cdId: target.cdId ?? null,
|
|
1490
|
+
artboardId: target.artboardId ?? null,
|
|
1491
|
+
} as HoverTarget)
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
try {
|
|
1495
|
+
window.parent.postMessage({ dgn: 'open-inspector', tab: 'photo' }, '*');
|
|
1496
|
+
} catch {
|
|
1497
|
+
/* detached / cross-origin */
|
|
1498
|
+
}
|
|
1499
|
+
},
|
|
1500
|
+
},
|
|
1443
1501
|
{
|
|
1444
1502
|
id: 'select-layer',
|
|
1445
1503
|
label: 'Select layer',
|
|
@@ -1631,6 +1689,29 @@ function buildRegistry(deps: {
|
|
|
1631
1689
|
fitItem,
|
|
1632
1690
|
resetItem,
|
|
1633
1691
|
],
|
|
1692
|
+
[
|
|
1693
|
+
// DDR-148 — only meaningful (and only enabled) on a video-comp
|
|
1694
|
+
// artboard; scopes + opens the shell's Timeline dock.
|
|
1695
|
+
{
|
|
1696
|
+
id: 'open-timeline',
|
|
1697
|
+
label: 'Open Timeline',
|
|
1698
|
+
shortcut: '⌘⇧T',
|
|
1699
|
+
disabled: (target) => !artboardHasVideo(target.artboardId),
|
|
1700
|
+
disabledHint: 'This artboard has no video sequence',
|
|
1701
|
+
onSelect: (target) => {
|
|
1702
|
+
if (!target.artboardId) return;
|
|
1703
|
+
focusArtboard(target.artboardId);
|
|
1704
|
+
try {
|
|
1705
|
+
window.parent.postMessage(
|
|
1706
|
+
{ dgn: 'open-timeline-request', artboardId: target.artboardId },
|
|
1707
|
+
'*'
|
|
1708
|
+
);
|
|
1709
|
+
} catch {
|
|
1710
|
+
/* detached / cross-origin */
|
|
1711
|
+
}
|
|
1712
|
+
},
|
|
1713
|
+
},
|
|
1714
|
+
],
|
|
1634
1715
|
[
|
|
1635
1716
|
// G7 — align commands. Six modes; gated on ≥ 2 selected artboards
|
|
1636
1717
|
// (alignment is well-defined with 2). Primary surface is the
|
|
@@ -2134,6 +2215,9 @@ function CanvasRouter({
|
|
|
2134
2215
|
if (!host) return;
|
|
2135
2216
|
let editing: HTMLElement | null = null;
|
|
2136
2217
|
let original = '';
|
|
2218
|
+
// Unified custom blinking caret (text-caret.ts) — mounted for the whole
|
|
2219
|
+
// edit session, disposed in teardown. Hides the native caret while up.
|
|
2220
|
+
let caretDispose: (() => void) | null = null;
|
|
2137
2221
|
// DDR-150 P1 — the committed-but-unconfirmed edit, so a shell rejection
|
|
2138
2222
|
// (dynamic/mixed content the engine refuses) can revert the optimistic
|
|
2139
2223
|
// contenteditable text instead of letting it silently vanish on the next
|
|
@@ -2150,6 +2234,8 @@ function CanvasRouter({
|
|
|
2150
2234
|
}
|
|
2151
2235
|
};
|
|
2152
2236
|
function teardown(elx: HTMLElement): void {
|
|
2237
|
+
caretDispose?.();
|
|
2238
|
+
caretDispose = null;
|
|
2153
2239
|
elx.removeAttribute('contenteditable');
|
|
2154
2240
|
elx.classList.remove('dc-text-editing');
|
|
2155
2241
|
elx.removeEventListener('blur', onBlur, true);
|
|
@@ -2169,8 +2255,22 @@ function CanvasRouter({
|
|
|
2169
2255
|
const cdId = elx.getAttribute('data-cd-id');
|
|
2170
2256
|
if (!cdId) return;
|
|
2171
2257
|
const file = deriveFile();
|
|
2258
|
+
// For `{variable}` text the engine needs to know WHICH rendered instance
|
|
2259
|
+
// this is (a `.map()` renders one source element N×) — its index among
|
|
2260
|
+
// same-cd-id nodes — plus the pre-edit text to target the right array
|
|
2261
|
+
// item. Harmless for literal text (the engine ignores both there).
|
|
2262
|
+
let occurrence = 0;
|
|
2172
2263
|
try {
|
|
2173
|
-
|
|
2264
|
+
const sameId = Array.from(document.querySelectorAll(`[data-cd-id="${CSS.escape(cdId)}"]`));
|
|
2265
|
+
occurrence = Math.max(0, sameId.indexOf(elx));
|
|
2266
|
+
} catch {
|
|
2267
|
+
/* querySelectorAll/CSS.escape unavailable — occurrence stays 0 */
|
|
2268
|
+
}
|
|
2269
|
+
try {
|
|
2270
|
+
window.parent.postMessage(
|
|
2271
|
+
{ dgn: 'edit-text', id: cdId, file, text, occurrence, before: original.trim() },
|
|
2272
|
+
'*'
|
|
2273
|
+
);
|
|
2174
2274
|
} catch {
|
|
2175
2275
|
/* detached / cross-origin */
|
|
2176
2276
|
}
|
|
@@ -2187,9 +2287,36 @@ function CanvasRouter({
|
|
|
2187
2287
|
key: '',
|
|
2188
2288
|
before: original.trim(),
|
|
2189
2289
|
after: text,
|
|
2290
|
+
// Carry the occurrence so undo/redo can re-target a `{variable}` edit
|
|
2291
|
+
// (the .map()/component-prop resolution needs it — canvas-edit.ts).
|
|
2292
|
+
occurrence,
|
|
2190
2293
|
})
|
|
2191
2294
|
);
|
|
2192
2295
|
}
|
|
2296
|
+
// Enter contenteditable edit mode on a leaf-text `stamped` element, caret
|
|
2297
|
+
// placed at (clientX, clientY) rather than a full select-all (that used to
|
|
2298
|
+
// force a delete-and-retype for any edit that isn't a full replace).
|
|
2299
|
+
// `caretRangeFromPoint` (WebKit/Chromium) and its standardized successor
|
|
2300
|
+
// `caretPositionFromPoint` (Firefox + newer engines) both resolve the
|
|
2301
|
+
// exact character under the pointer; select-all-contents is the fallback
|
|
2302
|
+
// only when neither exists. Extracted so both the native dblclick path
|
|
2303
|
+
// AND the annotation Text-tool's click-through (maude:enter-text-edit,
|
|
2304
|
+
// dispatched from annotations-layer.tsx when its own click lands on an
|
|
2305
|
+
// existing editable element) share one entry point.
|
|
2306
|
+
function enterEditModeAt(stamped: HTMLElement, clientX: number, clientY: number): void {
|
|
2307
|
+
if (editing) return;
|
|
2308
|
+
editing = stamped;
|
|
2309
|
+
original = stamped.textContent ?? '';
|
|
2310
|
+
stamped.setAttribute('contenteditable', 'plaintext-only');
|
|
2311
|
+
stamped.classList.add('dc-text-editing');
|
|
2312
|
+
stamped.addEventListener('blur', onBlur, true);
|
|
2313
|
+
stamped.addEventListener('keydown', onKey, true);
|
|
2314
|
+
stamped.focus();
|
|
2315
|
+
// Unified placement chain (text-caret.ts) — the annotation editors use
|
|
2316
|
+
// the exact same helper, so caret-at-click can never diverge per surface.
|
|
2317
|
+
placeCaretAt(stamped, window, { x: clientX, y: clientY });
|
|
2318
|
+
caretDispose = mountCaret(stamped, window);
|
|
2319
|
+
}
|
|
2193
2320
|
const onDbl = (e: MouseEvent): void => {
|
|
2194
2321
|
if (editing) return;
|
|
2195
2322
|
const t = e.target as HTMLElement | null;
|
|
@@ -2229,22 +2356,32 @@ function CanvasRouter({
|
|
|
2229
2356
|
}
|
|
2230
2357
|
e.preventDefault();
|
|
2231
2358
|
e.stopPropagation();
|
|
2232
|
-
editing
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
stamped
|
|
2238
|
-
stamped.
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
sel?.removeAllRanges();
|
|
2244
|
-
sel?.addRange(range);
|
|
2245
|
-
} catch {
|
|
2246
|
-
/* selection API unavailable */
|
|
2359
|
+
// Phase 6 (unified-text-editing) — only offer inline edit where the
|
|
2360
|
+
// engine will actually save: the build-time data-cd-editable marker
|
|
2361
|
+
// (canvas-pipeline.ts) mirrors applyTextEdit's acceptance. The DOM-only
|
|
2362
|
+
// isLeafText heuristic above can't tell `<p>Total: {1 + 1} items</p>`
|
|
2363
|
+
// (mixed source, engine refuses, DDR-150 dead end) from honest static
|
|
2364
|
+
// text — the AST-stamped marker can.
|
|
2365
|
+
if (!stamped.hasAttribute('data-cd-editable')) {
|
|
2366
|
+
showCanvasToast(
|
|
2367
|
+
'This text is filled in from code (a variable) — edit it via chat or /design:edit.'
|
|
2368
|
+
);
|
|
2369
|
+
return;
|
|
2247
2370
|
}
|
|
2371
|
+
enterEditModeAt(stamped, e.clientX, e.clientY);
|
|
2372
|
+
};
|
|
2373
|
+
// Dogfood fix — the annotation Text tool's click-through: clicking an
|
|
2374
|
+
// existing editable element with the Text (T) tool active should edit
|
|
2375
|
+
// THAT text in place instead of dropping a new standalone annotation on
|
|
2376
|
+
// top of it. annotations-layer.tsx hit-tests past its own input-capture
|
|
2377
|
+
// overlay and dispatches this when the click resolves to a leaf-text
|
|
2378
|
+
// `[data-cd-id]` element; canvas-shell owns the DOM/contentEditable side
|
|
2379
|
+
// of editing, so it's the one that must act on it.
|
|
2380
|
+
const onEnterTextEdit = (e: Event): void => {
|
|
2381
|
+
const detail = (e as CustomEvent<{ el?: HTMLElement; clientX?: number; clientY?: number }>)
|
|
2382
|
+
.detail;
|
|
2383
|
+
if (!detail?.el || !host.contains(detail.el)) return;
|
|
2384
|
+
enterEditModeAt(detail.el, detail.clientX ?? 0, detail.clientY ?? 0);
|
|
2248
2385
|
};
|
|
2249
2386
|
// The shell rejected the last inline text edit — revert the optimistic
|
|
2250
2387
|
// contenteditable text so the canvas reflects the true (unsaved) source
|
|
@@ -2266,9 +2403,11 @@ function CanvasRouter({
|
|
|
2266
2403
|
// Capture phase so we beat the fit-to-view dblclick handler.
|
|
2267
2404
|
host.addEventListener('dblclick', onDbl, true);
|
|
2268
2405
|
window.addEventListener('message', onEditReverted);
|
|
2406
|
+
document.addEventListener('maude:enter-text-edit', onEnterTextEdit);
|
|
2269
2407
|
return () => {
|
|
2270
2408
|
host.removeEventListener('dblclick', onDbl, true);
|
|
2271
2409
|
window.removeEventListener('message', onEditReverted);
|
|
2410
|
+
document.removeEventListener('maude:enter-text-edit', onEnterTextEdit);
|
|
2272
2411
|
if (editing) teardown(editing);
|
|
2273
2412
|
};
|
|
2274
2413
|
}, [hostRef, selSet]);
|
|
@@ -2378,6 +2517,7 @@ function CanvasRouter({
|
|
|
2378
2517
|
alignArtboards={alignArtboards}
|
|
2379
2518
|
/>
|
|
2380
2519
|
<SnapGuideOverlay />
|
|
2520
|
+
<PhotoPreviewBridge />
|
|
2381
2521
|
<UndoHud />
|
|
2382
2522
|
<CursorsOverlay />
|
|
2383
2523
|
<AiBanner />
|