openplanr 3.4.0 → 3.5.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/CHANGELOG.md +28 -0
- package/dist/cli/commands/design.d.ts +3 -0
- package/dist/cli/commands/design.d.ts.map +1 -0
- package/dist/cli/commands/design.js +137 -0
- package/dist/cli/commands/design.js.map +1 -0
- package/dist/cli/commands/groups/operations.d.ts.map +1 -1
- package/dist/cli/commands/groups/operations.js +2 -0
- package/dist/cli/commands/groups/operations.js.map +1 -1
- package/dist/dashboard/.vite/manifest.json +1 -1
- package/dist/dashboard/assets/{index-CvQAlBy3.js → index-B-l_941W.js} +2 -2
- package/dist/dashboard/assets/{index-CvQAlBy3.js.map → index-B-l_941W.js.map} +1 -1
- package/dist/dashboard/dashboard-manifest.json +13 -13
- package/dist/dashboard/index.html +1 -1
- package/dist/services/design-artifact-service.d.ts +22 -0
- package/dist/services/design-artifact-service.d.ts.map +1 -1
- package/dist/services/design-artifact-service.js +76 -16
- package/dist/services/design-artifact-service.js.map +1 -1
- package/dist/services/runtime-manager/runtime-package.js +1 -1
- package/dist/services/runtime-manager/runtime-package.js.map +1 -1
- package/docs/CLI.md +29 -0
- package/lib/host-packages/adapter-registry.json +5 -5
- package/lib/host-packages/capability-map.json +1 -1
- package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +1 -1
- package/lib/host-packages/claude/openplanr/.openplanr-content.json +24 -340
- package/lib/host-packages/claude/openplanr/README.md +8 -8
- package/lib/host-packages/claude/openplanr/skills/artifact/references/design-sharing.md +2 -2
- package/lib/host-packages/claude/openplanr/skills/design/SKILL.md +23 -12
- package/lib/host-packages/claude/openplanr/skills/design/references/design-system.md +2 -2
- package/lib/host-packages/claude/openplanr/skills/design/references/generation.md +3 -3
- package/lib/host-packages/claude/openplanr/skills/design/references/hosted-canvas.md +6 -3
- package/lib/host-packages/claude/openplanr/skills/design/references/review.md +6 -6
- package/lib/host-packages/claude/openplanr/skills/design/references/team-review.md +3 -4
- package/lib/host-packages/claude/openplanr/skills/design/references/utilities.md +78 -38
- package/lib/host-packages/claude/openplanr/skills/design-loop/SKILL.md +15 -7
- package/lib/host-packages/claude/openplanr/skills/design-loop/references/design-system.md +2 -2
- package/lib/host-packages/claude/openplanr/skills/design-loop/references/generation.md +3 -3
- package/lib/host-packages/claude/openplanr/skills/design-loop/references/review.md +6 -6
- package/lib/host-packages/claude/openplanr/skills/design-loop/references/team-review.md +3 -4
- package/lib/host-packages/claude/openplanr/skills/design-loop/references/utilities.md +78 -38
- package/lib/host-packages/claude/openplanr/skills/design-review/SKILL.md +25 -5
- package/lib/host-packages/claude/openplanr/skills/design-review/references/design-system.md +2 -2
- package/lib/host-packages/claude/openplanr/skills/design-review/references/generation.md +3 -3
- package/lib/host-packages/claude/openplanr/skills/design-review/references/review.md +6 -6
- package/lib/host-packages/claude/openplanr/skills/design-review/references/team-review.md +3 -4
- package/lib/host-packages/claude/openplanr/skills/design-review/references/utilities.md +78 -38
- package/lib/host-packages/claude/openplanr/skills/plan/SKILL.md +7 -3
- package/lib/host-packages/cursor/openplanr/.openplanr-content.json +23 -339
- package/lib/host-packages/cursor/openplanr/manifest.json +1 -1
- package/lib/host-packages/cursor/openplanr/rules/planr-artifact/references/design-sharing.md +2 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/design-system.md +2 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/generation.md +3 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/hosted-canvas.md +6 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/review.md +6 -6
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/team-review.md +3 -4
- package/lib/host-packages/cursor/openplanr/rules/planr-design/references/utilities.md +78 -38
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/design-system.md +2 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/generation.md +3 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/review.md +6 -6
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/team-review.md +3 -4
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/utilities.md +78 -38
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop.mdc +15 -7
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/design-system.md +2 -2
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/generation.md +3 -3
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/review.md +6 -6
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/team-review.md +3 -4
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/utilities.md +78 -38
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review.mdc +25 -5
- package/lib/host-packages/cursor/openplanr/rules/planr-design.mdc +23 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-plan.mdc +7 -3
- package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
- package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
- package/lib/host-packages/openai/openplanr/.openplanr-content.json +27 -343
- package/lib/host-packages/openai/openplanr/skills/artifact/references/design-sharing.md +2 -2
- package/lib/host-packages/openai/openplanr/skills/design/SKILL.md +23 -12
- package/lib/host-packages/openai/openplanr/skills/design/openplanr.skill.json +0 -11
- package/lib/host-packages/openai/openplanr/skills/design/references/design-system.md +2 -2
- package/lib/host-packages/openai/openplanr/skills/design/references/generation.md +3 -3
- package/lib/host-packages/openai/openplanr/skills/design/references/hosted-canvas.md +6 -3
- package/lib/host-packages/openai/openplanr/skills/design/references/review.md +6 -6
- package/lib/host-packages/openai/openplanr/skills/design/references/team-review.md +3 -4
- package/lib/host-packages/openai/openplanr/skills/design/references/utilities.md +78 -38
- package/lib/host-packages/openai/openplanr/skills/design-loop/SKILL.md +15 -7
- package/lib/host-packages/openai/openplanr/skills/design-loop/openplanr.skill.json +0 -11
- package/lib/host-packages/openai/openplanr/skills/design-loop/references/design-system.md +2 -2
- package/lib/host-packages/openai/openplanr/skills/design-loop/references/generation.md +3 -3
- package/lib/host-packages/openai/openplanr/skills/design-loop/references/review.md +6 -6
- package/lib/host-packages/openai/openplanr/skills/design-loop/references/team-review.md +3 -4
- package/lib/host-packages/openai/openplanr/skills/design-loop/references/utilities.md +78 -38
- package/lib/host-packages/openai/openplanr/skills/design-review/SKILL.md +25 -5
- package/lib/host-packages/openai/openplanr/skills/design-review/openplanr.skill.json +0 -11
- package/lib/host-packages/openai/openplanr/skills/design-review/references/design-system.md +2 -2
- package/lib/host-packages/openai/openplanr/skills/design-review/references/generation.md +3 -3
- package/lib/host-packages/openai/openplanr/skills/design-review/references/review.md +6 -6
- package/lib/host-packages/openai/openplanr/skills/design-review/references/team-review.md +3 -4
- package/lib/host-packages/openai/openplanr/skills/design-review/references/utilities.md +78 -38
- package/lib/host-packages/openai/openplanr/skills/plan/SKILL.md +7 -3
- package/lib/host-packages/openai/openplanr/skills/plan/openplanr.skill.json +0 -11
- package/package.json +2 -2
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/design.mjs +0 -1500
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
- package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design.mjs +0 -2611
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/claude/openplanr/skills/design/scripts/design.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/design.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/design-review/scripts/design.mjs +0 -12
- package/lib/host-packages/claude/openplanr/skills/plan/scripts/design.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/design.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/design.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/design.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/design.mjs +0 -12
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design.mjs +0 -1500
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
- package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design.mjs +0 -2611
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/design.mjs +0 -1500
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
- package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design.mjs +0 -2611
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
- package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
- package/lib/host-packages/openai/openplanr/skills/design/scripts/design.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/design.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/design-review/scripts/design.mjs +0 -12
- package/lib/host-packages/openai/openplanr/skills/plan/scripts/design.mjs +0 -12
|
@@ -1,2937 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DESIGN_DOCUMENT_SCHEMA,
|
|
3
|
-
assertDesignDocument,
|
|
4
|
-
bundleLocalDocument,
|
|
5
|
-
initialFrameHeight,
|
|
6
|
-
inspectLocalDocumentBody,
|
|
7
|
-
loadReviewContext,
|
|
8
|
-
parse,
|
|
9
|
-
parseFragment,
|
|
10
|
-
resolveLocalDocumentFile,
|
|
11
|
-
reviewDigest,
|
|
12
|
-
reviewFingerprints,
|
|
13
|
-
serialize,
|
|
14
|
-
validateDesignProps
|
|
15
|
-
} from "./design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs";
|
|
16
|
-
import {
|
|
17
|
-
AA_NORMAL,
|
|
18
|
-
contrastRatio,
|
|
19
|
-
embedJson,
|
|
20
|
-
escapeHtml,
|
|
21
|
-
renderArtifactShellDocument,
|
|
22
|
-
renderPlanrMark
|
|
23
|
-
} from "./design-artifact-shell.mjs";
|
|
24
|
-
import {
|
|
25
|
-
ARTIFACT_ERROR_CODES,
|
|
26
|
-
ARTIFACT_MAX_SOURCES,
|
|
27
|
-
ARTIFACT_MAX_VIEWS,
|
|
28
|
-
PipelineError,
|
|
29
|
-
createSharedArtifactEnvelope,
|
|
30
|
-
digestArtifactEnvelope,
|
|
31
|
-
resolveArtifactHtml
|
|
32
|
-
} from "./design-shared-artifact-support-protocol-contracts-9f6918b0.mjs";
|
|
33
|
-
import {
|
|
34
|
-
ARTIFACT_FRAME_GUARD_TEMPLATE,
|
|
35
|
-
ARTIFACT_HOST_GUARD_TEMPLATE,
|
|
36
|
-
ARTIFACT_WORKER_GUARD_SOURCE
|
|
37
|
-
} from "./design-sandbox-guards.mjs";
|
|
38
|
-
import {
|
|
39
|
-
atomicBytes,
|
|
40
|
-
atomicJson,
|
|
41
|
-
currentDesign,
|
|
42
|
-
designSpecPath,
|
|
43
|
-
hash,
|
|
44
|
-
json,
|
|
45
|
-
readJson,
|
|
46
|
-
recoverDesignPublication
|
|
47
|
-
} from "./design-shared-design-support-0bac378d.mjs";
|
|
48
|
-
import {
|
|
49
|
-
acquireStartLock
|
|
50
|
-
} from "./design-loopback-server.mjs";
|
|
51
|
-
|
|
52
|
-
// packages/design/lib/design/frame-size.mjs
|
|
53
|
-
function designFrameLabel(frame) {
|
|
54
|
-
const dimensions = `${frame.width} \xD7 ${frame.height}`;
|
|
55
|
-
return frame.label.includes(dimensions) ? frame.label : `${frame.label} \xB7 ${dimensions}`;
|
|
56
|
-
}
|
|
57
|
-
function designFrameViewport(document2, frame) {
|
|
58
|
-
return {
|
|
59
|
-
width: frame.width,
|
|
60
|
-
height: frame.height === "auto" ? initialFrameHeight(document2) : frame.height
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function matchesDesignFrame(frame, viewport) {
|
|
64
|
-
return Boolean(
|
|
65
|
-
frame && viewport?.width === frame.width && (frame.height === "auto" ? Number.isInteger(viewport.height) && viewport.height > 0 && viewport.height <= 16384 : viewport.height === frame.height)
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
function observeDesignFrameSize() {
|
|
69
|
-
let pending = false;
|
|
70
|
-
const measure = async () => {
|
|
71
|
-
if (pending) return;
|
|
72
|
-
pending = true;
|
|
73
|
-
try {
|
|
74
|
-
await document.fonts.ready;
|
|
75
|
-
await new Promise((resolve3) => requestAnimationFrame(() => requestAnimationFrame(resolve3)));
|
|
76
|
-
parent.postMessage(
|
|
77
|
-
{
|
|
78
|
-
type: "openplanr:design-size",
|
|
79
|
-
screenId: document.body.getAttribute("data-planr-screen"),
|
|
80
|
-
width: innerWidth,
|
|
81
|
-
height: Math.min(16384, Math.max(1, document.documentElement.scrollHeight))
|
|
82
|
-
},
|
|
83
|
-
"*"
|
|
84
|
-
);
|
|
85
|
-
} finally {
|
|
86
|
-
pending = false;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
const schedule = () => {
|
|
90
|
-
void measure().catch((error) => {
|
|
91
|
-
parent.postMessage({ type: "openplanr:design-size-error", message: error.message }, "*");
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
addEventListener("message", (event) => {
|
|
95
|
-
if (event.source === parent && event.data?.type === "openplanr:design-measure") schedule();
|
|
96
|
-
});
|
|
97
|
-
addEventListener(
|
|
98
|
-
"load",
|
|
99
|
-
() => {
|
|
100
|
-
new ResizeObserver(schedule).observe(document.body);
|
|
101
|
-
schedule();
|
|
102
|
-
},
|
|
103
|
-
{ once: true }
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// packages/design/lib/design/layout-save.mjs
|
|
108
|
-
import { readFileSync as readFileSync6, statSync } from "node:fs";
|
|
109
|
-
import { join as join3 } from "node:path";
|
|
110
|
-
|
|
111
|
-
// packages/design/lib/design/document.mjs
|
|
112
|
-
import { randomUUID } from "node:crypto";
|
|
113
|
-
import {
|
|
114
|
-
existsSync as existsSync2,
|
|
115
|
-
mkdirSync,
|
|
116
|
-
readFileSync as readFileSync5,
|
|
117
|
-
realpathSync,
|
|
118
|
-
renameSync,
|
|
119
|
-
rmSync,
|
|
120
|
-
writeFileSync
|
|
121
|
-
} from "node:fs";
|
|
122
|
-
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
123
|
-
|
|
124
|
-
// packages/artifact/lib/artifact/bridge.mjs
|
|
125
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
126
|
-
|
|
127
|
-
// packages/artifact/lib/artifact/internal/board-token.mjs
|
|
128
|
-
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
129
|
-
var BASE64URL_TOKEN_RE = /^[A-Za-z0-9_-]+$/;
|
|
130
|
-
function mintCapabilityToken({
|
|
131
|
-
bytes = 32,
|
|
132
|
-
encoding = "base64url",
|
|
133
|
-
randomBytesImpl = randomBytes
|
|
134
|
-
} = {}) {
|
|
135
|
-
if (!Number.isInteger(bytes) || bytes < 12 || bytes > 64) {
|
|
136
|
-
throw new RangeError("Capability tokens require 12 through 64 random bytes.");
|
|
137
|
-
}
|
|
138
|
-
if (!["base64url", "hex"].includes(encoding)) {
|
|
139
|
-
throw new TypeError(`Unsupported capability token encoding: ${String(encoding)}`);
|
|
140
|
-
}
|
|
141
|
-
const value = randomBytesImpl(bytes);
|
|
142
|
-
if (!Buffer.isBuffer(value) || value.byteLength !== bytes) {
|
|
143
|
-
throw new TypeError("Capability token random source returned the wrong byte length.");
|
|
144
|
-
}
|
|
145
|
-
return value.toString(encoding);
|
|
146
|
-
}
|
|
147
|
-
function isCapabilityToken(value, { bytes = 32, encoding = "base64url" } = {}) {
|
|
148
|
-
if (typeof value !== "string") return false;
|
|
149
|
-
if (encoding === "hex") return value.length === bytes * 2 && /^[a-f0-9]+$/.test(value);
|
|
150
|
-
if (encoding !== "base64url" || !BASE64URL_TOKEN_RE.test(value)) return false;
|
|
151
|
-
try {
|
|
152
|
-
return Buffer.from(value, "base64url").byteLength === bytes && Buffer.from(value, "base64url").toString("base64url") === value;
|
|
153
|
-
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function timingSafeTokenEqual(left, right) {
|
|
158
|
-
if (typeof left !== "string" || typeof right !== "string") return false;
|
|
159
|
-
const leftBytes = Buffer.from(left, "utf8");
|
|
160
|
-
const rightBytes = Buffer.from(right, "utf8");
|
|
161
|
-
return leftBytes.byteLength === rightBytes.byteLength && timingSafeEqual(leftBytes, rightBytes);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// packages/artifact/lib/artifact/ui/bridge-tools.mjs
|
|
165
|
-
var ARTIFACT_THUMBNAIL_MAX_EDGE = 320;
|
|
166
|
-
var ARTIFACT_THUMBNAIL_MAX_DATA_URL = 256 * 1024;
|
|
167
|
-
var ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA = 1e3;
|
|
168
|
-
var ARTIFACT_VIEWPORT_PAN_MAX_DELTA = 1e3;
|
|
169
|
-
function normalizeArtifactViewportZoom(value, viewport) {
|
|
170
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
171
|
-
return null;
|
|
172
|
-
const keys = ["x", "y", "deltaY"];
|
|
173
|
-
if (Object.keys(value).length !== keys.length || !Object.keys(value).every((key) => keys.includes(key)))
|
|
174
|
-
return null;
|
|
175
|
-
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
176
|
-
if (!keys.every(
|
|
177
|
-
(key) => Object.hasOwn(descriptors[key], "value") && typeof descriptors[key].value === "number" && Number.isFinite(descriptors[key].value)
|
|
178
|
-
))
|
|
179
|
-
return null;
|
|
180
|
-
const { x, y, deltaY } = value;
|
|
181
|
-
if (!Number.isFinite(viewport?.width) || !Number.isFinite(viewport?.height) || viewport.width < 1 || viewport.height < 1 || viewport.width > 16384 || viewport.height > 16384 || x < 0 || y < 0 || x > viewport.width || y > viewport.height || deltaY === 0 || Math.abs(deltaY) > ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA)
|
|
182
|
-
return null;
|
|
183
|
-
return Object.freeze({ x, y, deltaY });
|
|
184
|
-
}
|
|
185
|
-
function normalizeArtifactViewportPan(value) {
|
|
186
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value)))
|
|
187
|
-
return null;
|
|
188
|
-
const keys = ["deltaX", "deltaY"];
|
|
189
|
-
if (Object.keys(value).length !== keys.length || !Object.keys(value).every((key) => keys.includes(key)))
|
|
190
|
-
return null;
|
|
191
|
-
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
192
|
-
if (!keys.every(
|
|
193
|
-
(key) => Object.hasOwn(descriptors[key], "value") && typeof descriptors[key].value === "number" && Number.isFinite(descriptors[key].value)
|
|
194
|
-
))
|
|
195
|
-
return null;
|
|
196
|
-
const { deltaX, deltaY } = value;
|
|
197
|
-
if (!deltaX && !deltaY || Math.abs(deltaX) > ARTIFACT_VIEWPORT_PAN_MAX_DELTA || Math.abs(deltaY) > ARTIFACT_VIEWPORT_PAN_MAX_DELTA)
|
|
198
|
-
return null;
|
|
199
|
-
return Object.freeze({ deltaX, deltaY });
|
|
200
|
-
}
|
|
201
|
-
function createArtifactViewportGestures(window2, emit) {
|
|
202
|
-
const add = window2.addEventListener.bind(window2), remove = window2.removeEventListener.bind(window2);
|
|
203
|
-
const schedule = window2.setTimeout.bind(window2), cancel = window2.clearTimeout.bind(window2);
|
|
204
|
-
const prevent = window2.Event.prototype.preventDefault, stop = window2.Event.prototype.stopImmediatePropagation;
|
|
205
|
-
const getter = (prototype, name) => Object.getOwnPropertyDescriptor(prototype, name)?.get;
|
|
206
|
-
const native = {
|
|
207
|
-
x: getter(window2.MouseEvent.prototype, "clientX"),
|
|
208
|
-
y: getter(window2.MouseEvent.prototype, "clientY"),
|
|
209
|
-
ctrl: getter(window2.MouseEvent.prototype, "ctrlKey"),
|
|
210
|
-
meta: getter(window2.MouseEvent.prototype, "metaKey"),
|
|
211
|
-
shift: getter(window2.MouseEvent.prototype, "shiftKey"),
|
|
212
|
-
deltaX: getter(window2.WheelEvent.prototype, "deltaX"),
|
|
213
|
-
deltaY: getter(window2.WheelEvent.prototype, "deltaY"),
|
|
214
|
-
mode: getter(window2.WheelEvent.prototype, "deltaMode")
|
|
215
|
-
};
|
|
216
|
-
let enabled = false, disposed = false, timer = 0, pending = null, pendingType = "";
|
|
217
|
-
let gestureScale = 1;
|
|
218
|
-
const clear = () => {
|
|
219
|
-
gestureScale = 1;
|
|
220
|
-
if (timer) cancel(timer);
|
|
221
|
-
timer = 0;
|
|
222
|
-
pending = null;
|
|
223
|
-
pendingType = "";
|
|
224
|
-
};
|
|
225
|
-
const flush = () => {
|
|
226
|
-
timer = 0;
|
|
227
|
-
const value = pending;
|
|
228
|
-
const type = pendingType;
|
|
229
|
-
pending = null;
|
|
230
|
-
pendingType = "";
|
|
231
|
-
if (enabled && !disposed && value) emit(type, value);
|
|
232
|
-
};
|
|
233
|
-
const queue = (type, value) => {
|
|
234
|
-
const deltaX = value.deltaX ?? 0;
|
|
235
|
-
const deltaY = value.deltaY ?? 0;
|
|
236
|
-
if (pendingType && pendingType !== type) flush();
|
|
237
|
-
pendingType = type;
|
|
238
|
-
pending = type === "viewport.zoom" ? {
|
|
239
|
-
...value,
|
|
240
|
-
deltaY: Math.max(
|
|
241
|
-
-ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA,
|
|
242
|
-
Math.min(ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA, (pending?.deltaY || 0) + deltaY)
|
|
243
|
-
)
|
|
244
|
-
} : {
|
|
245
|
-
deltaX: Math.max(
|
|
246
|
-
-ARTIFACT_VIEWPORT_PAN_MAX_DELTA,
|
|
247
|
-
Math.min(ARTIFACT_VIEWPORT_PAN_MAX_DELTA, (pending?.deltaX || 0) + deltaX)
|
|
248
|
-
),
|
|
249
|
-
deltaY: Math.max(
|
|
250
|
-
-ARTIFACT_VIEWPORT_PAN_MAX_DELTA,
|
|
251
|
-
Math.min(ARTIFACT_VIEWPORT_PAN_MAX_DELTA, (pending?.deltaY || 0) + deltaY)
|
|
252
|
-
)
|
|
253
|
-
};
|
|
254
|
-
if (!timer) timer = schedule(flush, 16);
|
|
255
|
-
};
|
|
256
|
-
const wheel = (event) => {
|
|
257
|
-
if (!enabled || disposed || !event.isTrusted || !event.cancelable) return;
|
|
258
|
-
let x, y, deltaX, deltaY, mode, zooming, shift;
|
|
259
|
-
try {
|
|
260
|
-
x = native.x.call(event);
|
|
261
|
-
y = native.y.call(event);
|
|
262
|
-
mode = native.mode.call(event);
|
|
263
|
-
zooming = native.ctrl.call(event) || native.meta.call(event);
|
|
264
|
-
shift = native.shift.call(event);
|
|
265
|
-
deltaX = native.deltaX.call(event);
|
|
266
|
-
deltaY = native.deltaY.call(event);
|
|
267
|
-
} catch {
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (!Number.isFinite(deltaX) || !Number.isFinite(deltaY)) return;
|
|
271
|
-
const scaleX = mode === 1 ? 16 : mode === 2 ? window2.innerWidth : 1;
|
|
272
|
-
const scaleY = mode === 1 ? 16 : mode === 2 ? window2.innerHeight : 1;
|
|
273
|
-
deltaX *= scaleX;
|
|
274
|
-
deltaY *= scaleY;
|
|
275
|
-
let type;
|
|
276
|
-
let value;
|
|
277
|
-
if (zooming) {
|
|
278
|
-
type = "viewport.zoom";
|
|
279
|
-
deltaY = Math.max(
|
|
280
|
-
-ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA,
|
|
281
|
-
Math.min(ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA, deltaY)
|
|
282
|
-
);
|
|
283
|
-
value = normalizeArtifactViewportZoom(
|
|
284
|
-
{ x, y, deltaY },
|
|
285
|
-
{ width: window2.innerWidth, height: window2.innerHeight }
|
|
286
|
-
);
|
|
287
|
-
} else {
|
|
288
|
-
type = "viewport.pan";
|
|
289
|
-
if (shift && !deltaX) {
|
|
290
|
-
deltaX = deltaY;
|
|
291
|
-
deltaY = 0;
|
|
292
|
-
}
|
|
293
|
-
deltaX = Math.max(
|
|
294
|
-
-ARTIFACT_VIEWPORT_PAN_MAX_DELTA,
|
|
295
|
-
Math.min(ARTIFACT_VIEWPORT_PAN_MAX_DELTA, deltaX)
|
|
296
|
-
);
|
|
297
|
-
deltaY = Math.max(
|
|
298
|
-
-ARTIFACT_VIEWPORT_PAN_MAX_DELTA,
|
|
299
|
-
Math.min(ARTIFACT_VIEWPORT_PAN_MAX_DELTA, deltaY)
|
|
300
|
-
);
|
|
301
|
-
value = normalizeArtifactViewportPan({ deltaX, deltaY });
|
|
302
|
-
}
|
|
303
|
-
if (!value) return;
|
|
304
|
-
prevent.call(event);
|
|
305
|
-
stop.call(event);
|
|
306
|
-
queue(type, value);
|
|
307
|
-
};
|
|
308
|
-
const consumeGesture = (event) => {
|
|
309
|
-
if (!enabled || disposed || !event.isTrusted || !event.cancelable) return false;
|
|
310
|
-
prevent.call(event);
|
|
311
|
-
stop.call(event);
|
|
312
|
-
return true;
|
|
313
|
-
};
|
|
314
|
-
const gestureStart = (event) => {
|
|
315
|
-
if (consumeGesture(event)) gestureScale = 1;
|
|
316
|
-
};
|
|
317
|
-
const gestureChange = (raw) => {
|
|
318
|
-
if (!consumeGesture(raw)) return;
|
|
319
|
-
const event = raw;
|
|
320
|
-
if (!Number.isFinite(event.scale) || event.scale <= 0) return;
|
|
321
|
-
const deltaY = Math.max(
|
|
322
|
-
-ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA,
|
|
323
|
-
Math.min(ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA, -Math.log(event.scale / gestureScale) / 0.01)
|
|
324
|
-
);
|
|
325
|
-
gestureScale = event.scale;
|
|
326
|
-
const value = normalizeArtifactViewportZoom(
|
|
327
|
-
{ x: event.clientX, y: event.clientY, deltaY },
|
|
328
|
-
{ width: window2.innerWidth, height: window2.innerHeight }
|
|
329
|
-
);
|
|
330
|
-
if (value) queue("viewport.zoom", value);
|
|
331
|
-
};
|
|
332
|
-
const gestureEnd = (event) => {
|
|
333
|
-
if (consumeGesture(event)) gestureScale = 1;
|
|
334
|
-
};
|
|
335
|
-
const destroy = () => {
|
|
336
|
-
disposed = true;
|
|
337
|
-
enabled = false;
|
|
338
|
-
clear();
|
|
339
|
-
remove("wheel", wheel, true);
|
|
340
|
-
remove("gesturestart", gestureStart, true);
|
|
341
|
-
remove("gesturechange", gestureChange, true);
|
|
342
|
-
remove("gestureend", gestureEnd, true);
|
|
343
|
-
remove("pagehide", destroy);
|
|
344
|
-
};
|
|
345
|
-
add("wheel", wheel, { capture: true, passive: false });
|
|
346
|
-
add("gesturestart", gestureStart, { capture: true, passive: false });
|
|
347
|
-
add("gesturechange", gestureChange, { capture: true, passive: false });
|
|
348
|
-
add("gestureend", gestureEnd, { capture: true, passive: false });
|
|
349
|
-
add("pagehide", destroy, { once: true });
|
|
350
|
-
return Object.freeze({
|
|
351
|
-
setEnabled(value) {
|
|
352
|
-
if (disposed || typeof value !== "boolean") return false;
|
|
353
|
-
enabled = value;
|
|
354
|
-
if (!value) clear();
|
|
355
|
-
return true;
|
|
356
|
-
},
|
|
357
|
-
destroy
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
var ARTIFACT_BRIDGE_OPERATION_TIMEOUTS = Object.freeze({
|
|
361
|
-
"inspect.point": 1200,
|
|
362
|
-
"inspect.anchor": 1200,
|
|
363
|
-
"thumbnail.request": 3e3,
|
|
364
|
-
"export.request": 8e3
|
|
365
|
-
});
|
|
366
|
-
var ARTIFACT_INSPECTION_PROPERTIES = Object.freeze([
|
|
367
|
-
"font-family",
|
|
368
|
-
"font-size",
|
|
369
|
-
"font-weight",
|
|
370
|
-
"line-height",
|
|
371
|
-
"letter-spacing",
|
|
372
|
-
"color",
|
|
373
|
-
"background-color",
|
|
374
|
-
"display",
|
|
375
|
-
"position",
|
|
376
|
-
"gap",
|
|
377
|
-
"padding-top",
|
|
378
|
-
"padding-right",
|
|
379
|
-
"padding-bottom",
|
|
380
|
-
"padding-left",
|
|
381
|
-
"margin-top",
|
|
382
|
-
"margin-right",
|
|
383
|
-
"margin-bottom",
|
|
384
|
-
"margin-left",
|
|
385
|
-
"border-radius",
|
|
386
|
-
"border-width",
|
|
387
|
-
"border-color",
|
|
388
|
-
"box-sizing"
|
|
389
|
-
]);
|
|
390
|
-
function normalizeArtifactInspection(value, viewport) {
|
|
391
|
-
const plain = (entry) => entry && typeof entry === "object" && !Array.isArray(entry) && [Object.prototype, null].includes(Object.getPrototypeOf(entry));
|
|
392
|
-
const own = (entry, key) => {
|
|
393
|
-
const descriptor = plain(entry) ? Object.getOwnPropertyDescriptor(entry, key) : null;
|
|
394
|
-
return descriptor && Object.hasOwn(descriptor, "value") ? descriptor.value : void 0;
|
|
395
|
-
};
|
|
396
|
-
const exact2 = (entry, keys) => plain(entry) && Object.keys(entry).length === keys.length && Object.keys(entry).every((key) => keys.includes(key));
|
|
397
|
-
if (!exact2(value, ["tagName", "anchor", "rect", "viewport", "styles", "accessibility"]))
|
|
398
|
-
return null;
|
|
399
|
-
const tagName = own(value, "tagName");
|
|
400
|
-
const anchor = own(value, "anchor");
|
|
401
|
-
const rect = own(value, "rect");
|
|
402
|
-
const size = own(value, "viewport");
|
|
403
|
-
const styles = own(value, "styles");
|
|
404
|
-
const accessibility = own(value, "accessibility");
|
|
405
|
-
if (typeof tagName !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(tagName)) return null;
|
|
406
|
-
if (!exact2(size, ["width", "height"]) || !exact2(rect, ["x", "y", "width", "height"])) return null;
|
|
407
|
-
if (!["width", "height"].every(
|
|
408
|
-
(key) => Number.isInteger(own(size, key)) && own(size, key) >= 1 && own(size, key) <= 16384 && own(size, key) === viewport?.[key]
|
|
409
|
-
))
|
|
410
|
-
return null;
|
|
411
|
-
if (!["x", "y", "width", "height"].every(
|
|
412
|
-
(key) => typeof own(rect, key) === "number" && Number.isFinite(own(rect, key)) && own(rect, key) >= 0
|
|
413
|
-
))
|
|
414
|
-
return null;
|
|
415
|
-
if (rect.x + rect.width > size.width || rect.y + rect.height > size.height) return null;
|
|
416
|
-
if (anchor !== null && (!exact2(anchor, own(anchor, "screen") === void 0 ? ["planrId"] : ["planrId", "screen"]) || typeof own(anchor, "planrId") !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,511}$/.test(anchor.planrId) || own(anchor, "screen") !== void 0 && (typeof anchor.screen !== "string" || !/^[^\u0000-\u001f\u007f]{1,128}$/.test(anchor.screen))))
|
|
417
|
-
return null;
|
|
418
|
-
if (!exact2(styles, ARTIFACT_INSPECTION_PROPERTIES) || !ARTIFACT_INSPECTION_PROPERTIES.every((key) => {
|
|
419
|
-
const text = own(styles, key);
|
|
420
|
-
return typeof text === "string" && text.length <= 256 && !/[\u0000-\u001f\u007f]/.test(text) && !/(?:url\s*\(|https?:|file:)/i.test(text);
|
|
421
|
-
}))
|
|
422
|
-
return null;
|
|
423
|
-
if (!exact2(accessibility, ["role", "ariaLabel", "alt", "tabIndex", "disabled"]) || !["role", "ariaLabel", "alt"].every(
|
|
424
|
-
(key) => typeof own(accessibility, key) === "string" && own(accessibility, key).length <= 256 && !/[\u0000-\u001f\u007f]/.test(own(accessibility, key))
|
|
425
|
-
) || !Number.isInteger(own(accessibility, "tabIndex")) || accessibility.tabIndex < -1 || accessibility.tabIndex > 32767 || typeof own(accessibility, "disabled") !== "boolean")
|
|
426
|
-
return null;
|
|
427
|
-
return Object.freeze({
|
|
428
|
-
tagName,
|
|
429
|
-
anchor: anchor === null ? null : Object.freeze({ ...anchor }),
|
|
430
|
-
rect: Object.freeze({ ...rect }),
|
|
431
|
-
viewport: Object.freeze({ ...size }),
|
|
432
|
-
styles: Object.freeze({ ...styles }),
|
|
433
|
-
accessibility: Object.freeze({ ...accessibility })
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
function normalizeArtifactThumbnail(value) {
|
|
437
|
-
if (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).length !== 4 || !Object.keys(value).every((key) => ["dataUrl", "width", "height", "label"].includes(key)) || !Object.values(Object.getOwnPropertyDescriptors(value)).every(
|
|
438
|
-
(descriptor) => Object.hasOwn(descriptor, "value")
|
|
439
|
-
))
|
|
440
|
-
return null;
|
|
441
|
-
const { dataUrl, width, height, label } = value;
|
|
442
|
-
if (typeof dataUrl !== "string" || dataUrl.length > ARTIFACT_THUMBNAIL_MAX_DATA_URL || !/^data:image\/png;base64,[A-Za-z0-9+/]+=*$/.test(dataUrl) || !Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width > ARTIFACT_THUMBNAIL_MAX_EDGE || height > ARTIFACT_THUMBNAIL_MAX_EDGE || typeof label !== "string" || label.length < 1 || label.length > 128)
|
|
443
|
-
return null;
|
|
444
|
-
return Object.freeze({ dataUrl, width, height, label });
|
|
445
|
-
}
|
|
446
|
-
function normalizeArtifactBridgeToolResult(requestType, message, viewport) {
|
|
447
|
-
const base = ["channel", "schemaVersion", "type", "nonce", "artifactId", "requestId"];
|
|
448
|
-
const exact2 = (keys) => message && typeof message === "object" && !Array.isArray(message) && Object.keys(message).length === keys.length && Object.keys(message).every((key) => keys.includes(key)) && Object.values(Object.getOwnPropertyDescriptors(message)).every(
|
|
449
|
-
(descriptor) => Object.hasOwn(descriptor, "value")
|
|
450
|
-
);
|
|
451
|
-
if (requestType === "inspect.point" || requestType === "inspect.anchor") {
|
|
452
|
-
if (exact2(base) && message.type === "inspect.miss") return { valid: true, value: null };
|
|
453
|
-
if (!exact2([...base, "inspection"]) || message.type !== "inspect.result")
|
|
454
|
-
return { valid: false };
|
|
455
|
-
const value = normalizeArtifactInspection(message.inspection, viewport);
|
|
456
|
-
return value ? { valid: true, value } : { valid: false };
|
|
457
|
-
}
|
|
458
|
-
if (requestType === "thumbnail.request") {
|
|
459
|
-
if (exact2([...base, "reason"]) && message.type === "thumbnail.error" && typeof message.reason === "string" && message.reason.length <= 256)
|
|
460
|
-
return { valid: true, value: null };
|
|
461
|
-
if (!exact2([...base, "dataUrl", "width", "height", "label"]) || message.type !== "thumbnail.result")
|
|
462
|
-
return { valid: false };
|
|
463
|
-
const value = normalizeArtifactThumbnail({
|
|
464
|
-
dataUrl: message.dataUrl,
|
|
465
|
-
width: message.width,
|
|
466
|
-
height: message.height,
|
|
467
|
-
label: message.label
|
|
468
|
-
});
|
|
469
|
-
return value ? { valid: true, value } : { valid: false };
|
|
470
|
-
}
|
|
471
|
-
return { valid: false };
|
|
472
|
-
}
|
|
473
|
-
function createArtifactBridgeTools(document2, window2) {
|
|
474
|
-
const fromPoint = document2.elementFromPoint.bind(document2);
|
|
475
|
-
const query = document2.querySelectorAll.bind(document2);
|
|
476
|
-
const attr = window2.Element.prototype.getAttribute;
|
|
477
|
-
const closest = window2.Element.prototype.closest;
|
|
478
|
-
const bounds = window2.Element.prototype.getBoundingClientRect;
|
|
479
|
-
const computed = window2.getComputedStyle.bind(window2);
|
|
480
|
-
const cloneNode = window2.Node.prototype.cloneNode;
|
|
481
|
-
const append = window2.Node.prototype.appendChild;
|
|
482
|
-
const create = document2.createElement.bind(document2);
|
|
483
|
-
const setAttribute = window2.Element.prototype.setAttribute;
|
|
484
|
-
const serialize2 = window2.XMLSerializer.prototype.serializeToString;
|
|
485
|
-
const Image = window2.Image;
|
|
486
|
-
const Serializer = window2.XMLSerializer;
|
|
487
|
-
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
488
|
-
const get = (element, key) => attr.call(element, key);
|
|
489
|
-
const validId = (id) => typeof id === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,511}$/.test(id);
|
|
490
|
-
const screen = (element) => {
|
|
491
|
-
const owner = closest.call(element, "[data-planr-screen]");
|
|
492
|
-
const value = owner && get(owner, "data-planr-screen");
|
|
493
|
-
return typeof value === "string" && /^[^\u0000-\u001f\u007f]{1,128}$/.test(value) ? value : void 0;
|
|
494
|
-
};
|
|
495
|
-
const clean = (value) => String(value || "").replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 256);
|
|
496
|
-
const inspectElement = (element) => {
|
|
497
|
-
if (!(element instanceof window2.Element)) return null;
|
|
498
|
-
const tagName = element.localName;
|
|
499
|
-
if (typeof tagName !== "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(tagName)) return null;
|
|
500
|
-
const rect = bounds.call(element), width = window2.innerWidth, height = window2.innerHeight;
|
|
501
|
-
const x = clamp(rect.left, 0, width), y = clamp(rect.top, 0, height);
|
|
502
|
-
const owner = closest.call(element, "[data-planr-id]");
|
|
503
|
-
const planrId = owner && get(owner, "data-planr-id");
|
|
504
|
-
const anchorScreen = owner && screen(owner);
|
|
505
|
-
const style = computed(element);
|
|
506
|
-
const styles = Object.fromEntries(
|
|
507
|
-
ARTIFACT_INSPECTION_PROPERTIES.map((key) => {
|
|
508
|
-
const value = clean(style.getPropertyValue(key));
|
|
509
|
-
return [key, /(?:url\s*\(|https?:|file:)/i.test(value) ? "" : value];
|
|
510
|
-
})
|
|
511
|
-
);
|
|
512
|
-
return {
|
|
513
|
-
tagName,
|
|
514
|
-
anchor: validId(planrId) ? { planrId, ...anchorScreen ? { screen: anchorScreen } : {} } : null,
|
|
515
|
-
rect: {
|
|
516
|
-
x,
|
|
517
|
-
y,
|
|
518
|
-
width: Math.max(0, clamp(rect.right, 0, width) - x),
|
|
519
|
-
height: Math.max(0, clamp(rect.bottom, 0, height) - y)
|
|
520
|
-
},
|
|
521
|
-
viewport: { width, height },
|
|
522
|
-
styles,
|
|
523
|
-
accessibility: {
|
|
524
|
-
role: clean(get(element, "role")),
|
|
525
|
-
ariaLabel: clean(get(element, "aria-label")),
|
|
526
|
-
alt: clean(get(element, "alt")),
|
|
527
|
-
tabIndex: clamp(Number(element.tabIndex) || 0, -1, 32767),
|
|
528
|
-
disabled: get(element, "disabled") !== null || get(element, "aria-disabled") === "true"
|
|
529
|
-
}
|
|
530
|
-
};
|
|
531
|
-
};
|
|
532
|
-
let capturing = false;
|
|
533
|
-
return Object.freeze({
|
|
534
|
-
inspectAt(x, y) {
|
|
535
|
-
if (!Number.isFinite(x) || !Number.isFinite(y) || x < 0 || y < 0 || x > window2.innerWidth || y > window2.innerHeight)
|
|
536
|
-
return null;
|
|
537
|
-
return inspectElement(fromPoint(x, y));
|
|
538
|
-
},
|
|
539
|
-
inspect(anchor) {
|
|
540
|
-
if (!anchor || !validId(anchor.planrId) || Object.keys(anchor).some((key) => !["planrId", "screen"].includes(key)) || anchor.screen !== void 0 && (typeof anchor.screen !== "string" || !/^[^\u0000-\u001f\u007f]{1,128}$/.test(anchor.screen)))
|
|
541
|
-
return null;
|
|
542
|
-
let count = 0;
|
|
543
|
-
for (const element of query("[data-planr-id]")) {
|
|
544
|
-
if (++count > 1e4) return null;
|
|
545
|
-
if (get(element, "data-planr-id") === anchor.planrId && (anchor.screen === void 0 || screen(element) === anchor.screen))
|
|
546
|
-
return inspectElement(element);
|
|
547
|
-
}
|
|
548
|
-
return null;
|
|
549
|
-
},
|
|
550
|
-
async thumbnail() {
|
|
551
|
-
if (capturing) throw new Error("Thumbnail capture is busy.");
|
|
552
|
-
capturing = true;
|
|
553
|
-
let image;
|
|
554
|
-
try {
|
|
555
|
-
const deadline = Date.now() + 2400;
|
|
556
|
-
const width = window2.innerWidth, height = window2.innerHeight;
|
|
557
|
-
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1 || width > 16384 || height > 16384)
|
|
558
|
-
throw new Error("Thumbnail dimensions are unavailable.");
|
|
559
|
-
let count = 0, contentSize = 0;
|
|
560
|
-
const cloneStyled = (source) => {
|
|
561
|
-
if (++count > 4e3 || Date.now() > deadline)
|
|
562
|
-
throw new Error("Thumbnail capture limit exceeded.");
|
|
563
|
-
if (source.nodeType === 8 || source.nodeType === 1 && ["SCRIPT", "STYLE", "LINK", "META"].includes(source.tagName))
|
|
564
|
-
return document2.createTextNode("");
|
|
565
|
-
const target = cloneNode.call(source, false);
|
|
566
|
-
if (source.nodeType === 1) {
|
|
567
|
-
const styles = computed(source);
|
|
568
|
-
let css = "";
|
|
569
|
-
for (let index = 0; index < styles.length; index++) {
|
|
570
|
-
if (index >= 2048 || Date.now() > deadline)
|
|
571
|
-
throw new Error("Thumbnail style limit exceeded.");
|
|
572
|
-
const name = styles[index];
|
|
573
|
-
if (!name.startsWith("--")) css += name + ":" + styles.getPropertyValue(name) + ";";
|
|
574
|
-
}
|
|
575
|
-
contentSize += css.length + (source.textContent?.length || 0);
|
|
576
|
-
if (contentSize > 4 * 1024 * 1024) throw new Error("Thumbnail markup limit exceeded.");
|
|
577
|
-
setAttribute.call(target, "style", css + "animation:none;transition:none;");
|
|
578
|
-
for (const attribute of [...target.attributes])
|
|
579
|
-
if (/^on/i.test(attribute.name) || ["value", "srcdoc"].includes(attribute.name))
|
|
580
|
-
target.removeAttribute(attribute.name);
|
|
581
|
-
if (source.tagName === "INPUT" || source.tagName === "TEXTAREA") {
|
|
582
|
-
target.value = "";
|
|
583
|
-
target.textContent = "";
|
|
584
|
-
}
|
|
585
|
-
if (source.tagName === "CANVAS") {
|
|
586
|
-
try {
|
|
587
|
-
const replacement = create("img");
|
|
588
|
-
replacement.src = source.toDataURL("image/png");
|
|
589
|
-
setAttribute.call(replacement, "style", css);
|
|
590
|
-
return replacement;
|
|
591
|
-
} catch {
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
if (source.nodeType !== 1 || source.tagName !== "TEXTAREA")
|
|
596
|
-
for (let child = source.firstChild; child; child = child.nextSibling)
|
|
597
|
-
append.call(target, cloneStyled(child));
|
|
598
|
-
return target;
|
|
599
|
-
};
|
|
600
|
-
const clone = cloneStyled(document2.documentElement);
|
|
601
|
-
setAttribute.call(clone, "xmlns", "http://www.w3.org/1999/xhtml");
|
|
602
|
-
const markup = serialize2.call(new Serializer(), clone);
|
|
603
|
-
if (markup.length > 4 * 1024 * 1024) throw new Error("Thumbnail markup limit exceeded.");
|
|
604
|
-
const scale = Math.min(1, ARTIFACT_THUMBNAIL_MAX_EDGE / Math.max(width, height));
|
|
605
|
-
const outputWidth = Math.max(1, Math.round(width * scale)), outputHeight = Math.max(1, Math.round(height * scale));
|
|
606
|
-
const svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + outputWidth + '" height="' + outputHeight + '" viewBox="0 0 ' + width + " " + height + '"><foreignObject width="' + width + '" height="' + height + '">' + markup + "</foreignObject></svg>";
|
|
607
|
-
image = new Image();
|
|
608
|
-
await new Promise((resolve3, reject) => {
|
|
609
|
-
const timer = setTimeout(
|
|
610
|
-
() => reject(new Error("Thumbnail capture timed out.")),
|
|
611
|
-
Math.max(1, deadline - Date.now())
|
|
612
|
-
);
|
|
613
|
-
image.onload = () => {
|
|
614
|
-
clearTimeout(timer);
|
|
615
|
-
resolve3();
|
|
616
|
-
};
|
|
617
|
-
image.onerror = () => {
|
|
618
|
-
clearTimeout(timer);
|
|
619
|
-
reject(new Error("Thumbnail rendering unavailable."));
|
|
620
|
-
};
|
|
621
|
-
image.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
|
|
622
|
-
});
|
|
623
|
-
const canvas = create("canvas");
|
|
624
|
-
canvas.width = outputWidth;
|
|
625
|
-
canvas.height = outputHeight;
|
|
626
|
-
canvas.getContext("2d").drawImage(image, 0, 0, outputWidth, outputHeight);
|
|
627
|
-
const dataUrl = canvas.toDataURL("image/png");
|
|
628
|
-
if (dataUrl.length > ARTIFACT_THUMBNAIL_MAX_DATA_URL)
|
|
629
|
-
throw new Error("Thumbnail output limit exceeded.");
|
|
630
|
-
return { dataUrl, width: outputWidth, height: outputHeight, label: "screen" };
|
|
631
|
-
} finally {
|
|
632
|
-
if (image) {
|
|
633
|
-
image.onload = null;
|
|
634
|
-
image.onerror = null;
|
|
635
|
-
}
|
|
636
|
-
capturing = false;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
function renderArtifactBridgeToolsSource() {
|
|
642
|
-
return `const ARTIFACT_THUMBNAIL_MAX_EDGE=${ARTIFACT_THUMBNAIL_MAX_EDGE};
|
|
643
|
-
const ARTIFACT_THUMBNAIL_MAX_DATA_URL=${ARTIFACT_THUMBNAIL_MAX_DATA_URL};
|
|
644
|
-
const ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA=${ARTIFACT_VIEWPORT_ZOOM_MAX_DELTA};
|
|
645
|
-
const ARTIFACT_VIEWPORT_PAN_MAX_DELTA=${ARTIFACT_VIEWPORT_PAN_MAX_DELTA};
|
|
646
|
-
const ARTIFACT_INSPECTION_PROPERTIES=${JSON.stringify(ARTIFACT_INSPECTION_PROPERTIES)};
|
|
647
|
-
const ARTIFACT_BRIDGE_OPERATION_TIMEOUTS=${JSON.stringify(ARTIFACT_BRIDGE_OPERATION_TIMEOUTS)};
|
|
648
|
-
${normalizeArtifactInspection.toString()}
|
|
649
|
-
${normalizeArtifactThumbnail.toString()}
|
|
650
|
-
${normalizeArtifactBridgeToolResult.toString()}
|
|
651
|
-
${normalizeArtifactViewportZoom.toString()}
|
|
652
|
-
${normalizeArtifactViewportPan.toString()}
|
|
653
|
-
${createArtifactViewportGestures.toString()}
|
|
654
|
-
${createArtifactBridgeTools.toString()}`;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// packages/artifact/lib/artifact/ui/prototype-state.mjs
|
|
658
|
-
function installPrototypeState(screenId, viewId, parentOrigin, nonce) {
|
|
659
|
-
if (!/^[A-Za-z0-9_-]{43}$/.test(nonce)) return;
|
|
660
|
-
const win = window;
|
|
661
|
-
let snapshot = { session: {}, forms: {} }, restoring = false;
|
|
662
|
-
let aliasFields = [];
|
|
663
|
-
let aliasRestoreType = "";
|
|
664
|
-
let aliasesConfigured = false;
|
|
665
|
-
const earlyFields = /* @__PURE__ */ new Map();
|
|
666
|
-
let sequence = 0, hostRevision = -1, acknowledgedSequence = 0, pendingReset = 0;
|
|
667
|
-
const documentId = btoa(String.fromCharCode(...crypto.getRandomValues(new Uint8Array(32)))).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
|
|
668
|
-
let generation = null;
|
|
669
|
-
const pendingSession = /* @__PURE__ */ new Map();
|
|
670
|
-
let pendingForm = null;
|
|
671
|
-
function valid(value) {
|
|
672
|
-
let keys = 0;
|
|
673
|
-
function visit(node, depth) {
|
|
674
|
-
if (depth > 6) return false;
|
|
675
|
-
if (node === null || typeof node === "boolean") return true;
|
|
676
|
-
if (typeof node === "number") return Number.isFinite(node);
|
|
677
|
-
if (typeof node === "string") return node.length <= 8192;
|
|
678
|
-
if (Array.isArray(node))
|
|
679
|
-
return node.length <= 128 && node.every((item) => visit(item, depth + 1));
|
|
680
|
-
if (!node || typeof node !== "object" || Object.getPrototypeOf(node) !== Object.prototype)
|
|
681
|
-
return false;
|
|
682
|
-
return Object.entries(node).every(
|
|
683
|
-
([key, item]) => ++keys <= 128 && key.length <= 256 && !["__proto__", "constructor", "prototype"].includes(key) && visit(item, depth + 1)
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
try {
|
|
687
|
-
return !!value && Object.keys(value).every((key) => key === "session" || key === "forms") && !!value.session && !Array.isArray(value.session) && typeof value.session === "object" && !!value.forms && !Array.isArray(value.forms) && typeof value.forms === "object" && visit(value, 0) && new TextEncoder().encode(JSON.stringify(value)).byteLength <= 16384;
|
|
688
|
-
} catch {
|
|
689
|
-
return false;
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
const post = (type, update) => win.parent.postMessage(
|
|
693
|
-
{
|
|
694
|
-
type,
|
|
695
|
-
version: 1,
|
|
696
|
-
nonce,
|
|
697
|
-
screenId,
|
|
698
|
-
viewId,
|
|
699
|
-
documentId,
|
|
700
|
-
generation,
|
|
701
|
-
...type === "openplanr:prototype-state" ? { state: snapshot, sequence, ...update } : {}
|
|
702
|
-
},
|
|
703
|
-
parentOrigin
|
|
704
|
-
);
|
|
705
|
-
const fields = () => [
|
|
706
|
-
...document.querySelectorAll(
|
|
707
|
-
"input,textarea,select"
|
|
708
|
-
)
|
|
709
|
-
].filter(
|
|
710
|
-
(field) => !["password", "file", "hidden", "submit", "button", "reset"].includes(field.type) && !field.hasAttribute("data-planr-state-private")
|
|
711
|
-
);
|
|
712
|
-
const fieldKey = (field, index) => (field.id || field.name || `field-${index}`).slice(0, 256) + (field.type === "radio" ? `:${field.value}` : "");
|
|
713
|
-
function capture(sessionValue = snapshot.session) {
|
|
714
|
-
if (restoring) return;
|
|
715
|
-
const form = {};
|
|
716
|
-
fields().forEach((field, index) => {
|
|
717
|
-
const input = field;
|
|
718
|
-
form[fieldKey(field, index)] = ["checkbox", "radio"].includes(field.type) ? input.checked : field instanceof HTMLSelectElement && field.multiple ? [...field.selectedOptions].map((option) => option.value) : field.value;
|
|
719
|
-
});
|
|
720
|
-
const session = { ...sessionValue };
|
|
721
|
-
for (const field of fields()) {
|
|
722
|
-
if (aliasesConfigured && aliasFields.includes(field.id) && typeof field.value === "string" && field.value.length <= 4e3)
|
|
723
|
-
session[field.id] = field.value;
|
|
724
|
-
}
|
|
725
|
-
const next = { session, forms: { ...snapshot.forms, [viewId]: form } };
|
|
726
|
-
return commit(next);
|
|
727
|
-
}
|
|
728
|
-
function commit(next, reset = false) {
|
|
729
|
-
if (!valid(next) || sequence === Number.MAX_SAFE_INTEGER) return false;
|
|
730
|
-
const keys = [
|
|
731
|
-
.../* @__PURE__ */ new Set([...Object.keys(snapshot.session), ...Object.keys(next.session)])
|
|
732
|
-
].filter(
|
|
733
|
-
(key) => Object.hasOwn(snapshot.session, key) !== Object.hasOwn(next.session, key) || JSON.stringify(snapshot.session[key]) !== JSON.stringify(next.session[key])
|
|
734
|
-
);
|
|
735
|
-
const pending = new Map(pendingSession);
|
|
736
|
-
if (reset) pending.clear();
|
|
737
|
-
for (const key of keys)
|
|
738
|
-
pending.set(key, {
|
|
739
|
-
sequence: sequence + 1,
|
|
740
|
-
present: Object.hasOwn(next.session, key),
|
|
741
|
-
value: next.session[key]
|
|
742
|
-
});
|
|
743
|
-
if (pending.size > 256 || new TextEncoder().encode(JSON.stringify([...pending])).byteLength > 16384)
|
|
744
|
-
return false;
|
|
745
|
-
sequence++;
|
|
746
|
-
pendingSession.clear();
|
|
747
|
-
for (const [key, value] of pending) pendingSession.set(key, value);
|
|
748
|
-
if (reset) {
|
|
749
|
-
earlyFields.clear();
|
|
750
|
-
pendingReset = sequence;
|
|
751
|
-
pendingForm = null;
|
|
752
|
-
} else if (Object.hasOwn(next.forms, viewId))
|
|
753
|
-
pendingForm = { sequence, value: structuredClone(next.forms[viewId]) };
|
|
754
|
-
snapshot = structuredClone(next);
|
|
755
|
-
postPending();
|
|
756
|
-
return true;
|
|
757
|
-
}
|
|
758
|
-
function postPending() {
|
|
759
|
-
post("openplanr:prototype-state", {
|
|
760
|
-
sessionKeys: [...pendingSession.keys()],
|
|
761
|
-
sessionSequences: Object.fromEntries(
|
|
762
|
-
[...pendingSession].map(([key, update]) => [key, update.sequence])
|
|
763
|
-
),
|
|
764
|
-
...pendingReset ? { reset: true, resetSequence: pendingReset } : {}
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
function apply() {
|
|
768
|
-
const form = snapshot.forms[viewId];
|
|
769
|
-
if (!form) return;
|
|
770
|
-
restoring = true;
|
|
771
|
-
fields().forEach((field, index) => {
|
|
772
|
-
const value = form[fieldKey(field, index)];
|
|
773
|
-
if (value === void 0) return;
|
|
774
|
-
if (["checkbox", "radio"].includes(field.type) && typeof value === "boolean")
|
|
775
|
-
field.checked = value;
|
|
776
|
-
else if (field instanceof HTMLSelectElement && field.multiple && Array.isArray(value))
|
|
777
|
-
for (const option of field.options) option.selected = value.includes(option.value);
|
|
778
|
-
else if (typeof value === "string") field.value = value;
|
|
779
|
-
});
|
|
780
|
-
restoring = false;
|
|
781
|
-
}
|
|
782
|
-
win.__OPENPLANR_PROTOTYPE_STATE__ = Object.freeze({
|
|
783
|
-
get: () => structuredClone(snapshot.session),
|
|
784
|
-
set(value) {
|
|
785
|
-
const next = { session: value, forms: snapshot.forms };
|
|
786
|
-
if (!valid(next)) throw new TypeError("Prototype state exceeds its bounded JSON contract.");
|
|
787
|
-
if (!capture(structuredClone(value)))
|
|
788
|
-
throw new TypeError("Pending prototype edits exceed their bounded JSON contract.");
|
|
789
|
-
for (const key of Object.keys(value)) earlyFields.delete(key);
|
|
790
|
-
},
|
|
791
|
-
reset() {
|
|
792
|
-
if (!commit({ session: {}, forms: {} }, true))
|
|
793
|
-
throw new TypeError("Pending prototype edits exceed their bounded JSON contract.");
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
win.addEventListener("message", (event) => {
|
|
797
|
-
const data = event.data;
|
|
798
|
-
if (event.source === win.parent && (parentOrigin === "*" || event.origin === parentOrigin) && data?.nonce === nonce && aliasRestoreType && data.type === aliasRestoreType) {
|
|
799
|
-
data.state = Object.fromEntries(
|
|
800
|
-
aliasFields.flatMap((field) => {
|
|
801
|
-
const early = !aliasesConfigured ? earlyFields.get(field) : void 0;
|
|
802
|
-
const value = early?.value ?? snapshot.session[field];
|
|
803
|
-
return typeof value === "string" && value.length <= 4e3 ? [[field, value]] : [];
|
|
804
|
-
})
|
|
805
|
-
);
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
if (event.source !== win.parent || parentOrigin !== "*" && event.origin !== parentOrigin || data?.type !== "openplanr:prototype-state:restore" || data.version !== 1 || data.nonce !== nonce || data.screenId !== screenId || data.viewId !== viewId || !valid(data.state))
|
|
809
|
-
return;
|
|
810
|
-
const configuring = generation === null && data.generation !== void 0;
|
|
811
|
-
if (data.generation !== void 0) {
|
|
812
|
-
if (typeof data.generation !== "string" || !/^[A-Za-z0-9_-][A-Za-z0-9._:-]{7,127}$/.test(data.generation))
|
|
813
|
-
return;
|
|
814
|
-
if (data.documentId === void 0) {
|
|
815
|
-
post("openplanr:prototype-state:ready");
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
if (data.documentId !== documentId || generation !== null && data.generation !== generation)
|
|
819
|
-
return;
|
|
820
|
-
} else if (generation !== null) return;
|
|
821
|
-
if (data.revision !== void 0) {
|
|
822
|
-
if (!Number.isSafeInteger(data.revision) || data.revision < 0 || data.revision < hostRevision || !Number.isSafeInteger(data.acknowledgedSequence) || data.acknowledgedSequence < acknowledgedSequence || data.acknowledgedSequence > sequence)
|
|
823
|
-
return;
|
|
824
|
-
} else if (hostRevision >= 0) return;
|
|
825
|
-
const aliases = data.aliases;
|
|
826
|
-
aliasRestoreType = aliases?.version === 1 && typeof aliases.restoreType === "string" && /^[a-zA-Z][a-zA-Z0-9:_-]{0,127}$/.test(aliases.restoreType) && !aliases.restoreType.startsWith("openplanr:") ? aliases.restoreType : "";
|
|
827
|
-
aliasFields = aliases?.version === 1 && Array.isArray(aliases.fields) && aliases.fields.length <= 32 && aliases.fields.every(
|
|
828
|
-
(field) => typeof field === "string" && /^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(field) && !["constructor", "prototype", "__proto__"].includes(field)
|
|
829
|
-
) ? aliases.fields : [];
|
|
830
|
-
const ack = data.revision === void 0 ? sequence : data.acknowledgedSequence;
|
|
831
|
-
const next = structuredClone(data.state);
|
|
832
|
-
if (pendingReset > ack) {
|
|
833
|
-
next.session = {};
|
|
834
|
-
next.forms = {};
|
|
835
|
-
}
|
|
836
|
-
for (const [key, update] of pendingSession) {
|
|
837
|
-
if (update.sequence <= ack) continue;
|
|
838
|
-
if (update.present && update.value !== void 0) next.session[key] = update.value;
|
|
839
|
-
else delete next.session[key];
|
|
840
|
-
}
|
|
841
|
-
if (pendingForm && pendingForm.sequence > ack) next.forms[viewId] = pendingForm.value;
|
|
842
|
-
if (!valid(next)) return;
|
|
843
|
-
for (const [field, early] of earlyFields)
|
|
844
|
-
if (early.sequence <= ack && JSON.stringify(data.state.forms[viewId]?.[early.fieldKey]) !== JSON.stringify(early.formValue))
|
|
845
|
-
earlyFields.delete(field);
|
|
846
|
-
hostRevision = data.revision ?? hostRevision;
|
|
847
|
-
if (data.generation !== void 0) generation = data.generation;
|
|
848
|
-
acknowledgedSequence = data.revision === void 0 ? 0 : ack;
|
|
849
|
-
if (pendingReset <= ack) pendingReset = 0;
|
|
850
|
-
for (const [key, update] of pendingSession)
|
|
851
|
-
if (update.sequence <= ack) pendingSession.delete(key);
|
|
852
|
-
if (pendingForm && pendingForm.sequence <= ack) pendingForm = null;
|
|
853
|
-
snapshot = next;
|
|
854
|
-
if (!aliasesConfigured && aliasRestoreType) {
|
|
855
|
-
const promoted = structuredClone(snapshot);
|
|
856
|
-
for (const field of aliasFields) {
|
|
857
|
-
const early = earlyFields.get(field);
|
|
858
|
-
if (!early || (pendingSession.get(field)?.sequence ?? 0) > early.sequence) continue;
|
|
859
|
-
promoted.session[field] = early.value;
|
|
860
|
-
promoted.forms[viewId] ??= {};
|
|
861
|
-
promoted.forms[viewId][early.fieldKey] = early.formValue;
|
|
862
|
-
}
|
|
863
|
-
if (!aliasFields.some((field) => earlyFields.has(field)) || commit(promoted)) {
|
|
864
|
-
aliasesConfigured = true;
|
|
865
|
-
earlyFields.clear();
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
apply();
|
|
869
|
-
win.dispatchEvent(
|
|
870
|
-
new CustomEvent("openplanr:prototype-state-restored", {
|
|
871
|
-
detail: structuredClone(snapshot.session)
|
|
872
|
-
})
|
|
873
|
-
);
|
|
874
|
-
if (data.revision !== void 0 && (pendingSession.size || pendingReset || pendingForm))
|
|
875
|
-
postPending();
|
|
876
|
-
else if (configuring) post("openplanr:prototype-state:ready");
|
|
877
|
-
});
|
|
878
|
-
const captureField = (event) => {
|
|
879
|
-
if (!capture() || aliasesConfigured) return;
|
|
880
|
-
const field = fields().find((candidate) => candidate === event.target);
|
|
881
|
-
if (!field || !/^[a-zA-Z][a-zA-Z0-9_-]{0,127}$/.test(field.id) || ["constructor", "prototype", "__proto__"].includes(field.id) || field.value.length > 4e3)
|
|
882
|
-
return;
|
|
883
|
-
const key = fieldKey(field, fields().indexOf(field));
|
|
884
|
-
const next = new Map(earlyFields);
|
|
885
|
-
next.set(field.id, {
|
|
886
|
-
value: field.value,
|
|
887
|
-
fieldKey: key,
|
|
888
|
-
formValue: snapshot.forms[viewId][key],
|
|
889
|
-
sequence
|
|
890
|
-
});
|
|
891
|
-
if (next.size > 128 || new TextEncoder().encode(JSON.stringify([...next])).byteLength > 16384)
|
|
892
|
-
return;
|
|
893
|
-
earlyFields.clear();
|
|
894
|
-
for (const [id, value] of next) earlyFields.set(id, value);
|
|
895
|
-
};
|
|
896
|
-
document.addEventListener("input", captureField, true);
|
|
897
|
-
document.addEventListener("change", captureField, true);
|
|
898
|
-
document.addEventListener("submit", () => capture(), true);
|
|
899
|
-
if (document.readyState === "loading")
|
|
900
|
-
document.addEventListener(
|
|
901
|
-
"DOMContentLoaded",
|
|
902
|
-
() => {
|
|
903
|
-
apply();
|
|
904
|
-
post("openplanr:prototype-state:ready");
|
|
905
|
-
},
|
|
906
|
-
{ once: true }
|
|
907
|
-
);
|
|
908
|
-
else {
|
|
909
|
-
apply();
|
|
910
|
-
post("openplanr:prototype-state:ready");
|
|
911
|
-
}
|
|
912
|
-
post("openplanr:prototype-state:ready");
|
|
913
|
-
win.setTimeout(() => post("openplanr:prototype-state:ready"), 500);
|
|
914
|
-
}
|
|
915
|
-
function renderPrototypeStateInstaller() {
|
|
916
|
-
return installPrototypeState.toString();
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
// packages/artifact/lib/artifact/browser-sandbox.mjs
|
|
920
|
-
function pipelineError(code, message, details) {
|
|
921
|
-
return new PipelineError(code, message, "", details);
|
|
922
|
-
}
|
|
923
|
-
function browserCapabilityToken({ bytes = 32 } = {}) {
|
|
924
|
-
const data = crypto.getRandomValues(new Uint8Array(bytes));
|
|
925
|
-
return btoa(String.fromCharCode(...data)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "");
|
|
926
|
-
}
|
|
927
|
-
function isCapabilityToken2(value) {
|
|
928
|
-
if (typeof value !== "string" || !/^[A-Za-z0-9_-]{43}$/u.test(value)) return false;
|
|
929
|
-
try {
|
|
930
|
-
const decoded = atob(value.replaceAll("-", "+").replaceAll("_", "/") + "=");
|
|
931
|
-
return decoded.length === 32 && btoa(decoded).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/u, "") === value;
|
|
932
|
-
} catch {
|
|
933
|
-
return false;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
function validHostedOrigin(origin) {
|
|
937
|
-
if (typeof origin !== "string") return false;
|
|
938
|
-
try {
|
|
939
|
-
const parsed = new URL(origin);
|
|
940
|
-
return parsed.protocol === "https:" && parsed.origin === origin && !parsed.username && !parsed.password;
|
|
941
|
-
} catch {
|
|
942
|
-
return false;
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
var ARTIFACT_BRIDGE_CHANNEL = "openplanr.artifact-anchor";
|
|
946
|
-
var ARTIFACT_BRIDGE_VERSION = "1.0.0";
|
|
947
|
-
var ARTIFACT_BRIDGE_EVENT = "planr:artifact-anchor";
|
|
948
|
-
var ARTIFACT_BRIDGE_READY_EVENT = "planr:artifact-bridge-ready";
|
|
949
|
-
var ARTIFACT_BRIDGE_LAYOUT_EVENT = "planr:artifact-layout";
|
|
950
|
-
var ARTIFACT_VIEWPORT_ZOOM_EVENT = "planr:artifact-viewport-zoom";
|
|
951
|
-
var ARTIFACT_VIEWPORT_PAN_EVENT = "planr:artifact-viewport-pan";
|
|
952
|
-
var ARTIFACT_EXPORT_MAX_EDGE = 11e3;
|
|
953
|
-
var ARTIFACT_EXPORT_MAX_DATA_URL = 24 * 1024 * 1024;
|
|
954
|
-
var ARTIFACT_LAYOUT_MAX_WIDTH = 16384;
|
|
955
|
-
var ARTIFACT_LAYOUT_MAX_HEIGHT = 262144;
|
|
956
|
-
var ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,511}$/;
|
|
957
|
-
var SCRIPT_NONCE_RE = /^[A-Za-z0-9_-]{24}$/;
|
|
958
|
-
var FORBIDDEN_TAGS = /* @__PURE__ */ new Set([
|
|
959
|
-
"applet",
|
|
960
|
-
"base",
|
|
961
|
-
"embed",
|
|
962
|
-
"fencedframe",
|
|
963
|
-
"form",
|
|
964
|
-
"frame",
|
|
965
|
-
"frameset",
|
|
966
|
-
"iframe",
|
|
967
|
-
"noembed",
|
|
968
|
-
"noframes",
|
|
969
|
-
"noscript",
|
|
970
|
-
"object",
|
|
971
|
-
"portal"
|
|
972
|
-
]);
|
|
973
|
-
var URL_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
974
|
-
"action",
|
|
975
|
-
"archive",
|
|
976
|
-
"background",
|
|
977
|
-
"classid",
|
|
978
|
-
"codebase",
|
|
979
|
-
"formaction",
|
|
980
|
-
"href",
|
|
981
|
-
"longdesc",
|
|
982
|
-
"manifest",
|
|
983
|
-
"ping",
|
|
984
|
-
"profile",
|
|
985
|
-
"src",
|
|
986
|
-
"srcdoc",
|
|
987
|
-
"target",
|
|
988
|
-
"xlink:href"
|
|
989
|
-
]);
|
|
990
|
-
var REMOTE_URL_RE = /^(?:https?:|file:|ftp:|wss?:|\/\/)/i;
|
|
991
|
-
function getAttr(node, name) {
|
|
992
|
-
return node.attrs?.find((attribute) => attribute.name.toLowerCase() === name)?.value;
|
|
993
|
-
}
|
|
994
|
-
function setAttr(node, name, value) {
|
|
995
|
-
const existing = node.attrs?.find((attribute) => attribute.name.toLowerCase() === name);
|
|
996
|
-
if (existing) existing.value = value;
|
|
997
|
-
else {
|
|
998
|
-
node.attrs ??= [];
|
|
999
|
-
node.attrs.push({ name, value });
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
function createElement(tagName) {
|
|
1003
|
-
return parseFragment(`<${tagName}></${tagName}>`).childNodes[0];
|
|
1004
|
-
}
|
|
1005
|
-
function createText(value, parentNode) {
|
|
1006
|
-
return { nodeName: "#text", value, parentNode };
|
|
1007
|
-
}
|
|
1008
|
-
function descendants(node) {
|
|
1009
|
-
return [...node?.childNodes ?? [], ...node?.content?.childNodes ?? []];
|
|
1010
|
-
}
|
|
1011
|
-
function removeNode(node) {
|
|
1012
|
-
const parent2 = node.parentNode;
|
|
1013
|
-
const index = parent2?.childNodes?.indexOf(node) ?? -1;
|
|
1014
|
-
if (index >= 0) parent2.childNodes.splice(index, 1);
|
|
1015
|
-
}
|
|
1016
|
-
function findElement(document2, tagName) {
|
|
1017
|
-
const queue = descendants(document2);
|
|
1018
|
-
while (queue.length > 0) {
|
|
1019
|
-
const node = queue.shift();
|
|
1020
|
-
if (node?.tagName?.toLowerCase() === tagName) return node;
|
|
1021
|
-
queue.push(...descendants(node));
|
|
1022
|
-
}
|
|
1023
|
-
return null;
|
|
1024
|
-
}
|
|
1025
|
-
function artifactContentSecurityPolicy(scriptNonce) {
|
|
1026
|
-
if (typeof scriptNonce !== "string" || !SCRIPT_NONCE_RE.test(scriptNonce)) {
|
|
1027
|
-
throw pipelineError(ARTIFACT_ERROR_CODES.SANDBOX_POLICY, "Artifact script nonce is invalid.");
|
|
1028
|
-
}
|
|
1029
|
-
return [
|
|
1030
|
-
"default-src 'none'",
|
|
1031
|
-
`script-src 'nonce-${scriptNonce}' data: blob:`,
|
|
1032
|
-
`script-src-elem 'nonce-${scriptNonce}' data: blob:`,
|
|
1033
|
-
"script-src-attr 'unsafe-inline'",
|
|
1034
|
-
"style-src 'unsafe-inline' data: blob:",
|
|
1035
|
-
"img-src data: blob:",
|
|
1036
|
-
"media-src data: blob:",
|
|
1037
|
-
"font-src data:",
|
|
1038
|
-
"worker-src data: blob:",
|
|
1039
|
-
"connect-src 'none'",
|
|
1040
|
-
"frame-src 'none'",
|
|
1041
|
-
"child-src 'none'",
|
|
1042
|
-
"object-src 'none'",
|
|
1043
|
-
"manifest-src 'none'",
|
|
1044
|
-
"form-action 'none'",
|
|
1045
|
-
"base-uri 'none'"
|
|
1046
|
-
].join("; ");
|
|
1047
|
-
}
|
|
1048
|
-
function assertSandboxableTree(document2, { allowLocalForms = false } = {}) {
|
|
1049
|
-
const queue = descendants(document2);
|
|
1050
|
-
while (queue.length > 0) {
|
|
1051
|
-
const node = queue.shift();
|
|
1052
|
-
if (!node?.tagName) {
|
|
1053
|
-
queue.push(...descendants(node));
|
|
1054
|
-
continue;
|
|
1055
|
-
}
|
|
1056
|
-
const tag = node.tagName.toLowerCase();
|
|
1057
|
-
if (FORBIDDEN_TAGS.has(tag) && !(tag === "form" && allowLocalForms)) {
|
|
1058
|
-
throw pipelineError(
|
|
1059
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1060
|
-
`Unsafe <${tag}> cannot enter an artifact review sandbox.`
|
|
1061
|
-
);
|
|
1062
|
-
}
|
|
1063
|
-
if (tag === "meta" && getAttr(node, "http-equiv")?.trim().toLowerCase() === "refresh") {
|
|
1064
|
-
throw pipelineError(
|
|
1065
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1066
|
-
"Meta refresh navigation is forbidden."
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
for (const attribute of node.attrs ?? []) {
|
|
1070
|
-
const name = attribute.name.toLowerCase();
|
|
1071
|
-
const value = attribute.value.trim();
|
|
1072
|
-
if (["srcdoc", "target", "action", "formaction"].includes(name)) {
|
|
1073
|
-
throw pipelineError(
|
|
1074
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1075
|
-
`Artifact navigation attribute ${name} is forbidden.`
|
|
1076
|
-
);
|
|
1077
|
-
}
|
|
1078
|
-
if (URL_ATTRIBUTES.has(name) && REMOTE_URL_RE.test(value)) {
|
|
1079
|
-
throw pipelineError(
|
|
1080
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1081
|
-
`Remote artifact resource is forbidden: ${value.slice(0, 80)}`
|
|
1082
|
-
);
|
|
1083
|
-
}
|
|
1084
|
-
if (name === "style" && /(?:@import|url\s*\(\s*['"]?(?:https?:|file:|\/\/))/i.test(value)) {
|
|
1085
|
-
throw pipelineError(ARTIFACT_ERROR_CODES.SANDBOX_POLICY, "Remote inline CSS is forbidden.");
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
if (tag === "style") {
|
|
1089
|
-
const css = (node.childNodes ?? []).map((child) => child.value ?? "").join("");
|
|
1090
|
-
if (/(?:@import|url\s*\(\s*['"]?(?:https?:|file:|\/\/))/i.test(css)) {
|
|
1091
|
-
throw pipelineError(
|
|
1092
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1093
|
-
"Remote stylesheet resources are forbidden."
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
queue.push(...descendants(node));
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
var SANDBOX_GUARD_LIMITS = Object.freeze({
|
|
1101
|
-
__PLANR_SANDBOX_EXPORT_MAX_EDGE__: String(ARTIFACT_EXPORT_MAX_EDGE),
|
|
1102
|
-
__PLANR_SANDBOX_EXPORT_MAX_DATA_URL__: String(ARTIFACT_EXPORT_MAX_DATA_URL),
|
|
1103
|
-
__PLANR_SANDBOX_LAYOUT_MAX_WIDTH__: String(ARTIFACT_LAYOUT_MAX_WIDTH),
|
|
1104
|
-
__PLANR_SANDBOX_LAYOUT_MAX_HEIGHT__: String(ARTIFACT_LAYOUT_MAX_HEIGHT)
|
|
1105
|
-
});
|
|
1106
|
-
function sandboxGuardFiller(name, template, keys) {
|
|
1107
|
-
const pattern = new RegExp(keys.join("|"), "gu");
|
|
1108
|
-
const unfilled = [...new Set(template.replace(pattern, "").match(/__PLANR_SANDBOX_[A-Z_]+__/gu))];
|
|
1109
|
-
const missing = keys.filter((key) => !template.includes(key));
|
|
1110
|
-
if (unfilled.length > 0 || missing.length > 0) {
|
|
1111
|
-
throw pipelineError(
|
|
1112
|
-
ARTIFACT_ERROR_CODES.BRIDGE_INVALID,
|
|
1113
|
-
`The generated ${name} does not match bridge.mjs (unfilled: ${unfilled.join(", ") || "none"}; missing: ${missing.join(", ") || "none"}). Run npm run generate.`
|
|
1114
|
-
);
|
|
1115
|
-
}
|
|
1116
|
-
return (values) => template.replace(pattern, (key) => {
|
|
1117
|
-
if (typeof values[key] !== "string") {
|
|
1118
|
-
throw pipelineError(
|
|
1119
|
-
ARTIFACT_ERROR_CODES.BRIDGE_INVALID,
|
|
1120
|
-
`The ${name} needs a string for ${key}; received ${typeof values[key]}.`
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
return values[key];
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
var fillFrameGuard = sandboxGuardFiller("frame guard", ARTIFACT_FRAME_GUARD_TEMPLATE, [
|
|
1127
|
-
"__PLANR_SANDBOX_CONTRACT__",
|
|
1128
|
-
"__PLANR_SANDBOX_BRIDGE_TOOLS__;",
|
|
1129
|
-
"__PLANR_SANDBOX_WORKER_GUARD__",
|
|
1130
|
-
...Object.keys(SANDBOX_GUARD_LIMITS)
|
|
1131
|
-
]);
|
|
1132
|
-
function artifactGuardAndBridgeSource({
|
|
1133
|
-
artifactId,
|
|
1134
|
-
nonce,
|
|
1135
|
-
parentOrigin,
|
|
1136
|
-
prototypeState = false,
|
|
1137
|
-
reviewSelection = false,
|
|
1138
|
-
screenId = artifactId
|
|
1139
|
-
}) {
|
|
1140
|
-
const contract = JSON.stringify({
|
|
1141
|
-
channel: ARTIFACT_BRIDGE_CHANNEL,
|
|
1142
|
-
schemaVersion: ARTIFACT_BRIDGE_VERSION,
|
|
1143
|
-
artifactId,
|
|
1144
|
-
nonce,
|
|
1145
|
-
parentOrigin,
|
|
1146
|
-
...prototypeState ? { screenId } : {},
|
|
1147
|
-
...reviewSelection ? { reviewSelection: true } : {}
|
|
1148
|
-
}).replace(/</gu, "\\u003c");
|
|
1149
|
-
const guard = fillFrameGuard({
|
|
1150
|
-
__PLANR_SANDBOX_CONTRACT__: prototypeState || reviewSelection ? "__openplanrPrototypeContract" : contract,
|
|
1151
|
-
"__PLANR_SANDBOX_BRIDGE_TOOLS__;": renderArtifactBridgeToolsSource(),
|
|
1152
|
-
__PLANR_SANDBOX_WORKER_GUARD__: JSON.stringify(ARTIFACT_WORKER_GUARD_SOURCE),
|
|
1153
|
-
...SANDBOX_GUARD_LIMITS
|
|
1154
|
-
});
|
|
1155
|
-
if (!prototypeState && !reviewSelection) return guard;
|
|
1156
|
-
const prototypeInstaller = prototypeState ? `(${renderPrototypeStateInstaller()})(__openplanrPrototypeContract.screenId,__openplanrPrototypeContract.artifactId,__openplanrPrototypeContract.parentOrigin === 'null' ? '*' : __openplanrPrototypeContract.parentOrigin,__openplanrPrototypeContract.nonce);` : "";
|
|
1157
|
-
return `(function(__openplanrPrototypeContract){${guard};${prototypeInstaller}(${installPreviewNavigation.toString()})(__openplanrPrototypeContract);})(${contract});`;
|
|
1158
|
-
}
|
|
1159
|
-
function installPreviewNavigation(contract) {
|
|
1160
|
-
const trustedParent = parent, post = trustedParent.postMessage.bind(trustedParent), NativeElement = Element, svgRoots = [], svgClick = () => {
|
|
1161
|
-
}, addListener = EventTarget.prototype.addEventListener, removeListener = EventTarget.prototype.removeEventListener, queryAll = Document.prototype.querySelectorAll, query = NativeElement.prototype.querySelector, closest = NativeElement.prototype.closest, attribute = NativeElement.prototype.getAttribute, preventDefault = Event.prototype.preventDefault, stopImmediatePropagation = Event.prototype.stopImmediatePropagation, descriptors = Object.getOwnPropertyDescriptors, prototype = Object.getPrototypeOf, objectPrototype = Object.prototype, keys = Reflect.ownKeys;
|
|
1162
|
-
let selectionEnabled = false;
|
|
1163
|
-
if (contract.reviewSelection === true)
|
|
1164
|
-
addEventListener("message", (event) => {
|
|
1165
|
-
if (event.source !== trustedParent || event.origin !== contract.parentOrigin) return;
|
|
1166
|
-
const data = event.data;
|
|
1167
|
-
if (!data || typeof data !== "object") return;
|
|
1168
|
-
const dataPrototype = prototype(data);
|
|
1169
|
-
if (dataPrototype !== objectPrototype && dataPrototype !== null) return;
|
|
1170
|
-
const fields = descriptors(data), allowed = ["schemaVersion", "type", "channel", "viewId", "enabled"];
|
|
1171
|
-
if (keys(fields).length !== allowed.length || allowed.some((key) => !fields[key]?.enumerable || !("value" in fields[key])))
|
|
1172
|
-
return;
|
|
1173
|
-
if (fields.schemaVersion.value !== "1.0.0" || fields.type.value !== "openplanr:review-selection" || fields.channel.value !== contract.nonce || fields.viewId.value !== contract.artifactId || typeof fields.enabled.value !== "boolean")
|
|
1174
|
-
return;
|
|
1175
|
-
selectionEnabled = fields.enabled.value;
|
|
1176
|
-
for (const svg of svgRoots) removeListener.call(svg, "click", svgClick);
|
|
1177
|
-
svgRoots.length = 0;
|
|
1178
|
-
if (selectionEnabled)
|
|
1179
|
-
for (const svg of queryAll.call(document, "svg")) {
|
|
1180
|
-
if (!attribute.call(svg, "data-planr-id") && !attribute.call(svg, "data-element-id") && !query.call(svg, "[data-planr-id],[data-element-id]"))
|
|
1181
|
-
continue;
|
|
1182
|
-
addListener.call(svg, "click", svgClick);
|
|
1183
|
-
svgRoots.push(svg);
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
document.addEventListener(
|
|
1187
|
-
"click",
|
|
1188
|
-
(event) => {
|
|
1189
|
-
if (!(event.target instanceof NativeElement)) return;
|
|
1190
|
-
if (selectionEnabled) {
|
|
1191
|
-
const target2 = closest.call(event.target, "[data-planr-id],[data-element-id]"), elementId = target2 ? attribute.call(target2, "data-planr-id") ?? attribute.call(target2, "data-element-id") : null;
|
|
1192
|
-
if (!elementId || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(elementId)) return;
|
|
1193
|
-
preventDefault.call(event);
|
|
1194
|
-
stopImmediatePropagation.call(event);
|
|
1195
|
-
post(
|
|
1196
|
-
{
|
|
1197
|
-
schemaVersion: "1.0.0",
|
|
1198
|
-
channel: contract.nonce,
|
|
1199
|
-
type: "select",
|
|
1200
|
-
viewId: contract.artifactId,
|
|
1201
|
-
elementId
|
|
1202
|
-
},
|
|
1203
|
-
contract.parentOrigin === "null" ? "*" : contract.parentOrigin
|
|
1204
|
-
);
|
|
1205
|
-
return;
|
|
1206
|
-
}
|
|
1207
|
-
if (!contract.screenId) return;
|
|
1208
|
-
const target = closest.call(
|
|
1209
|
-
event.target,
|
|
1210
|
-
"[data-design-target],[data-planr-navigate],[data-design-navigate]"
|
|
1211
|
-
);
|
|
1212
|
-
if (!target) return;
|
|
1213
|
-
const screenId = target.getAttribute("data-design-target") || target.getAttribute("data-planr-navigate") || target.getAttribute("data-design-navigate");
|
|
1214
|
-
if (!screenId || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(screenId)) return;
|
|
1215
|
-
event.preventDefault();
|
|
1216
|
-
post(
|
|
1217
|
-
{
|
|
1218
|
-
schemaVersion: "1.0.0",
|
|
1219
|
-
channel: contract.nonce,
|
|
1220
|
-
type: "navigate",
|
|
1221
|
-
viewId: contract.artifactId,
|
|
1222
|
-
screenId
|
|
1223
|
-
},
|
|
1224
|
-
contract.parentOrigin === "null" ? "*" : contract.parentOrigin
|
|
1225
|
-
);
|
|
1226
|
-
},
|
|
1227
|
-
true
|
|
1228
|
-
);
|
|
1229
|
-
}
|
|
1230
|
-
function prepareArtifactDocument({
|
|
1231
|
-
html,
|
|
1232
|
-
artifactId,
|
|
1233
|
-
nonce,
|
|
1234
|
-
parentOrigin,
|
|
1235
|
-
scriptNonce = browserCapabilityToken({ bytes: 18 }),
|
|
1236
|
-
allowLocalForms = false,
|
|
1237
|
-
portable = false,
|
|
1238
|
-
trustedParentOrigin,
|
|
1239
|
-
screenId = artifactId,
|
|
1240
|
-
prototypeState = false,
|
|
1241
|
-
reviewSelection = false
|
|
1242
|
-
} = {}) {
|
|
1243
|
-
if (typeof html !== "string" || html.length === 0) {
|
|
1244
|
-
throw pipelineError(ARTIFACT_ERROR_CODES.SANDBOX_POLICY, "Artifact HTML is required.");
|
|
1245
|
-
}
|
|
1246
|
-
if (typeof artifactId !== "string" || !ID_RE.test(artifactId)) {
|
|
1247
|
-
throw pipelineError(ARTIFACT_ERROR_CODES.SANDBOX_POLICY, "Artifact id is invalid.");
|
|
1248
|
-
}
|
|
1249
|
-
if (!isCapabilityToken2(nonce)) {
|
|
1250
|
-
throw pipelineError(ARTIFACT_ERROR_CODES.SANDBOX_POLICY, "Artifact bridge nonce is invalid.");
|
|
1251
|
-
}
|
|
1252
|
-
if (typeof reviewSelection !== "boolean")
|
|
1253
|
-
throw pipelineError(
|
|
1254
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1255
|
-
"Artifact review selection option must be a boolean."
|
|
1256
|
-
);
|
|
1257
|
-
if (reviewSelection && artifactId.length > 128)
|
|
1258
|
-
throw pipelineError(
|
|
1259
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1260
|
-
"Review selection requires an artifact view id of at most 128 characters."
|
|
1261
|
-
);
|
|
1262
|
-
if (prototypeState && (typeof screenId !== "string" || !ID_RE.test(screenId)))
|
|
1263
|
-
throw pipelineError(
|
|
1264
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1265
|
-
"Prototype screen identity is invalid."
|
|
1266
|
-
);
|
|
1267
|
-
const originMatch = /^http:\/\/127\.0\.0\.1:(\d{1,5})$/.exec(parentOrigin ?? "");
|
|
1268
|
-
const originPort = Number(originMatch?.[1]);
|
|
1269
|
-
if (!(portable && parentOrigin === "null") && !(portable && parentOrigin === trustedParentOrigin && validHostedOrigin(parentOrigin)) && (!originMatch || !Number.isInteger(originPort) || originPort < 1 || originPort > 65535 || String(originPort) !== originMatch[1])) {
|
|
1270
|
-
throw pipelineError(
|
|
1271
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1272
|
-
"Artifact parent origin must be IPv4 loopback or an explicitly trusted HTTPS origin."
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
const document2 = parse(html, { sourceCodeLocationInfo: false });
|
|
1276
|
-
assertSandboxableTree(document2, { allowLocalForms });
|
|
1277
|
-
const head = findElement(document2, "head");
|
|
1278
|
-
if (!head)
|
|
1279
|
-
throw pipelineError(
|
|
1280
|
-
ARTIFACT_ERROR_CODES.SANDBOX_POLICY,
|
|
1281
|
-
"Artifact document has no head element."
|
|
1282
|
-
);
|
|
1283
|
-
for (const node of [...descendants(head)]) {
|
|
1284
|
-
if (node?.tagName?.toLowerCase() === "meta") {
|
|
1285
|
-
const httpEquiv = getAttr(node, "http-equiv")?.trim().toLowerCase();
|
|
1286
|
-
if (httpEquiv === "content-security-policy") removeNode(node);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
const csp = artifactContentSecurityPolicy(scriptNonce);
|
|
1290
|
-
const hasViewport = descendants(head).some(
|
|
1291
|
-
(node) => node?.tagName?.toLowerCase() === "meta" && getAttr(node, "name")?.trim().toLowerCase() === "viewport"
|
|
1292
|
-
);
|
|
1293
|
-
const viewportMeta = hasViewport ? null : createElement("meta");
|
|
1294
|
-
if (viewportMeta) {
|
|
1295
|
-
setAttr(viewportMeta, "name", "viewport");
|
|
1296
|
-
setAttr(viewportMeta, "content", "width=device-width,initial-scale=1,viewport-fit=cover");
|
|
1297
|
-
viewportMeta.parentNode = head;
|
|
1298
|
-
}
|
|
1299
|
-
const cspMeta = createElement("meta");
|
|
1300
|
-
setAttr(cspMeta, "http-equiv", "Content-Security-Policy");
|
|
1301
|
-
setAttr(cspMeta, "content", csp);
|
|
1302
|
-
cspMeta.parentNode = head;
|
|
1303
|
-
const referrerMeta = createElement("meta");
|
|
1304
|
-
setAttr(referrerMeta, "name", "referrer");
|
|
1305
|
-
setAttr(referrerMeta, "content", "no-referrer");
|
|
1306
|
-
referrerMeta.parentNode = head;
|
|
1307
|
-
const bridge = createElement("script");
|
|
1308
|
-
setAttr(bridge, "nonce", scriptNonce);
|
|
1309
|
-
bridge.childNodes = [
|
|
1310
|
-
createText(
|
|
1311
|
-
artifactGuardAndBridgeSource({
|
|
1312
|
-
artifactId,
|
|
1313
|
-
nonce,
|
|
1314
|
-
parentOrigin,
|
|
1315
|
-
prototypeState,
|
|
1316
|
-
reviewSelection,
|
|
1317
|
-
screenId
|
|
1318
|
-
}),
|
|
1319
|
-
bridge
|
|
1320
|
-
)
|
|
1321
|
-
];
|
|
1322
|
-
bridge.parentNode = head;
|
|
1323
|
-
const queue = descendants(document2);
|
|
1324
|
-
while (queue.length > 0) {
|
|
1325
|
-
const node = queue.shift();
|
|
1326
|
-
if (node?.tagName?.toLowerCase() === "script") setAttr(node, "nonce", scriptNonce);
|
|
1327
|
-
queue.push(...descendants(node));
|
|
1328
|
-
}
|
|
1329
|
-
head.childNodes = [
|
|
1330
|
-
cspMeta,
|
|
1331
|
-
referrerMeta,
|
|
1332
|
-
...viewportMeta ? [viewportMeta] : [],
|
|
1333
|
-
bridge,
|
|
1334
|
-
...head.childNodes ?? []
|
|
1335
|
-
];
|
|
1336
|
-
return Object.freeze({ html: serialize(document2), csp, scriptNonce });
|
|
1337
|
-
}
|
|
1338
|
-
function prepareArtifactSourceTemplate(options = {}) {
|
|
1339
|
-
const artifactId = `template-${browserCapabilityToken()}`;
|
|
1340
|
-
const prepared = prepareArtifactDocument({
|
|
1341
|
-
...options,
|
|
1342
|
-
artifactId,
|
|
1343
|
-
portable: true,
|
|
1344
|
-
parentOrigin: options.parentOrigin ?? "null"
|
|
1345
|
-
});
|
|
1346
|
-
const artifactIdToken = `"artifactId":${JSON.stringify(artifactId)}`;
|
|
1347
|
-
if (prepared.html.split(artifactIdToken).length !== 2)
|
|
1348
|
-
throw pipelineError(
|
|
1349
|
-
ARTIFACT_ERROR_CODES.BRIDGE_INVALID,
|
|
1350
|
-
"Shared source bridge identity is not unique."
|
|
1351
|
-
);
|
|
1352
|
-
return Object.freeze({ html: prepared.html, artifactIdToken });
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
// packages/artifact/lib/artifact/bridge.mjs
|
|
1356
|
-
var ID_RE2 = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,511}$/;
|
|
1357
|
-
function pipelineError2(code, message, details) {
|
|
1358
|
-
return new PipelineError(code, message, "", details);
|
|
1359
|
-
}
|
|
1360
|
-
function createArtifactBridgeNonce({ randomBytesImpl = randomBytes2 } = {}) {
|
|
1361
|
-
return mintCapabilityToken({ bytes: 32, randomBytesImpl });
|
|
1362
|
-
}
|
|
1363
|
-
var fillHostGuard = sandboxGuardFiller("host guard", ARTIFACT_HOST_GUARD_TEMPLATE, [
|
|
1364
|
-
"__PLANR_SANDBOX_CONFIG__",
|
|
1365
|
-
"__PLANR_SANDBOX_BRIDGE_TOOLS__;",
|
|
1366
|
-
...Object.keys(SANDBOX_GUARD_LIMITS)
|
|
1367
|
-
]);
|
|
1368
|
-
function validInlineSourcePool(sources, references) {
|
|
1369
|
-
if (!sources || typeof sources !== "object" || Array.isArray(sources) || !references || typeof references !== "object" || Array.isArray(references))
|
|
1370
|
-
return false;
|
|
1371
|
-
const entries = Object.entries(sources), views = Object.entries(references);
|
|
1372
|
-
if (!entries.length || entries.length > ARTIFACT_MAX_SOURCES || !views.length || views.length > ARTIFACT_MAX_VIEWS)
|
|
1373
|
-
return false;
|
|
1374
|
-
let bytes = 0;
|
|
1375
|
-
for (const [id, source] of entries) {
|
|
1376
|
-
if (!ID_RE2.test(id) || !source || Object.keys(source).length !== 2 || typeof source.html !== "string" || !/^"artifactId":"template-[A-Za-z0-9_-]{43}"$/u.test(source.artifactIdToken) || source.html.split(source.artifactIdToken).length !== 2)
|
|
1377
|
-
return false;
|
|
1378
|
-
bytes += Buffer.byteLength(source.html);
|
|
1379
|
-
if (bytes > 100 * 1024 * 1024) return false;
|
|
1380
|
-
}
|
|
1381
|
-
return views.every(
|
|
1382
|
-
([id, sourceId]) => ID_RE2.test(id) && typeof sourceId === "string" && Object.hasOwn(sources, sourceId)
|
|
1383
|
-
) && new Set(Object.values(references)).size === entries.length;
|
|
1384
|
-
}
|
|
1385
|
-
function renderArtifactParentRuntime({
|
|
1386
|
-
artifactBaseUrl,
|
|
1387
|
-
stageRuntimeUrl,
|
|
1388
|
-
adapterRuntimeUrl,
|
|
1389
|
-
nonce,
|
|
1390
|
-
inlineArtifacts,
|
|
1391
|
-
inlineSources,
|
|
1392
|
-
inlineArtifactSources,
|
|
1393
|
-
sourceTransport = "blob",
|
|
1394
|
-
frameBudget
|
|
1395
|
-
} = {}) {
|
|
1396
|
-
const canonicalPath = (value, { trailingSlash = false } = {}) => {
|
|
1397
|
-
if (typeof value !== "string" || !value.startsWith("/") || value.startsWith("//") || value.includes("\\") || value.includes("?") || value.includes("#") || /[\u0000-\u001f]/.test(value) || /%(?:00|2f|5c)/i.test(value) || (trailingSlash ? !value.endsWith("/") : value.endsWith("/")))
|
|
1398
|
-
return false;
|
|
1399
|
-
try {
|
|
1400
|
-
return value.split("/").filter(Boolean).every((segment) => {
|
|
1401
|
-
const decoded = decodeURIComponent(segment);
|
|
1402
|
-
return decoded !== "." && decoded !== ".." && !decoded.includes("/") && !decoded.includes("\\");
|
|
1403
|
-
});
|
|
1404
|
-
} catch {
|
|
1405
|
-
return false;
|
|
1406
|
-
}
|
|
1407
|
-
};
|
|
1408
|
-
const legacyPortable = inlineArtifacts && typeof inlineArtifacts === "object" && !Array.isArray(inlineArtifacts) && Object.values(inlineArtifacts).every((html) => typeof html === "string");
|
|
1409
|
-
const pooledPortable = validInlineSourcePool(inlineSources, inlineArtifactSources);
|
|
1410
|
-
const portable = legacyPortable || pooledPortable;
|
|
1411
|
-
if ((inlineSources !== void 0 || inlineArtifactSources !== void 0) && (!pooledPortable || inlineArtifacts !== void 0) || !portable && !canonicalPath(artifactBaseUrl, { trailingSlash: true }) || !(canonicalPath(stageRuntimeUrl) || portable && /^data:text\/javascript;base64,[A-Za-z0-9+/=]+$/u.test(stageRuntimeUrl)) || adapterRuntimeUrl !== void 0 && !canonicalPath(adapterRuntimeUrl) || !isCapabilityToken(nonce) || !["blob", "srcdoc"].includes(sourceTransport) || frameBudget !== void 0 && (!Number.isInteger(frameBudget) || frameBudget < 1 || frameBudget > 8)) {
|
|
1412
|
-
throw pipelineError2(
|
|
1413
|
-
ARTIFACT_ERROR_CODES.BRIDGE_INVALID,
|
|
1414
|
-
"Artifact parent runtime configuration is invalid."
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1417
|
-
const config = JSON.stringify({
|
|
1418
|
-
artifactBaseUrl,
|
|
1419
|
-
stageRuntimeUrl,
|
|
1420
|
-
adapterRuntimeUrl: adapterRuntimeUrl ?? null,
|
|
1421
|
-
nonce,
|
|
1422
|
-
inlineArtifacts: legacyPortable ? inlineArtifacts : null,
|
|
1423
|
-
...pooledPortable ? { inlineSources, inlineArtifactSources } : {},
|
|
1424
|
-
sourceTransport,
|
|
1425
|
-
...frameBudget === void 0 ? {} : { frameBudget },
|
|
1426
|
-
channel: ARTIFACT_BRIDGE_CHANNEL,
|
|
1427
|
-
schemaVersion: ARTIFACT_BRIDGE_VERSION,
|
|
1428
|
-
anchorEvent: ARTIFACT_BRIDGE_EVENT,
|
|
1429
|
-
readyEvent: ARTIFACT_BRIDGE_READY_EVENT,
|
|
1430
|
-
layoutEvent: ARTIFACT_BRIDGE_LAYOUT_EVENT,
|
|
1431
|
-
viewportZoomEvent: ARTIFACT_VIEWPORT_ZOOM_EVENT,
|
|
1432
|
-
viewportPanEvent: ARTIFACT_VIEWPORT_PAN_EVENT,
|
|
1433
|
-
navigationEvent: "planr:artifact-navigation-blocked",
|
|
1434
|
-
frameCsp: [
|
|
1435
|
-
"default-src 'none'",
|
|
1436
|
-
"script-src 'unsafe-inline' data: blob:",
|
|
1437
|
-
"script-src-attr 'unsafe-inline'",
|
|
1438
|
-
"style-src 'unsafe-inline' data: blob:",
|
|
1439
|
-
"img-src data: blob:",
|
|
1440
|
-
"media-src data: blob:",
|
|
1441
|
-
"font-src data:",
|
|
1442
|
-
"worker-src data: blob:",
|
|
1443
|
-
"connect-src 'none'",
|
|
1444
|
-
"frame-src 'none'",
|
|
1445
|
-
"child-src 'none'",
|
|
1446
|
-
"object-src 'none'",
|
|
1447
|
-
"manifest-src 'none'",
|
|
1448
|
-
"form-action 'none'",
|
|
1449
|
-
"base-uri 'none'"
|
|
1450
|
-
].join("; ")
|
|
1451
|
-
});
|
|
1452
|
-
return fillHostGuard({
|
|
1453
|
-
__PLANR_SANDBOX_CONFIG__: config,
|
|
1454
|
-
"__PLANR_SANDBOX_BRIDGE_TOOLS__;": renderArtifactBridgeToolsSource(),
|
|
1455
|
-
...SANDBOX_GUARD_LIMITS
|
|
1456
|
-
});
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
// packages/design/lib/design/lint.mjs
|
|
1460
|
-
import { readFileSync } from "node:fs";
|
|
1461
|
-
import { fileURLToPath } from "node:url";
|
|
1462
|
-
|
|
1463
|
-
// packages/design/lib/design/tokens.mjs
|
|
1464
|
-
var SPACING_STEP = 4;
|
|
1465
|
-
var FRAMES = {
|
|
1466
|
-
desktop: { w: 1440, h: 1024 },
|
|
1467
|
-
tablet: { w: 834, h: 1194 },
|
|
1468
|
-
mobile: { w: 390, h: 844 }
|
|
1469
|
-
};
|
|
1470
|
-
var DEFAULT_FRAME = FRAMES.desktop;
|
|
1471
|
-
var RESPONSIVE_FRAMES = [
|
|
1472
|
-
{ name: "desktop", ...FRAMES.desktop },
|
|
1473
|
-
{ name: "tablet", ...FRAMES.tablet },
|
|
1474
|
-
{ name: "mobile", ...FRAMES.mobile }
|
|
1475
|
-
];
|
|
1476
|
-
function isOnSpacingScale(px) {
|
|
1477
|
-
const n = Math.abs(Number(px));
|
|
1478
|
-
if (!Number.isInteger(n)) return false;
|
|
1479
|
-
return n === 0 || n === 2 || n % SPACING_STEP === 0;
|
|
1480
|
-
}
|
|
1481
|
-
function nearestSpacing(px) {
|
|
1482
|
-
const v = Number(px) || 0;
|
|
1483
|
-
const n = Math.abs(v);
|
|
1484
|
-
const sign = v < 0 ? -1 : 1;
|
|
1485
|
-
const candidates = [.../* @__PURE__ */ new Set([0, 2, Math.round(n / SPACING_STEP) * SPACING_STEP])].sort(
|
|
1486
|
-
(a, b) => a - b
|
|
1487
|
-
);
|
|
1488
|
-
const best = candidates.reduce(
|
|
1489
|
-
(b, c) => Math.abs(c - n) <= Math.abs(b - n) ? c : b,
|
|
1490
|
-
candidates[0]
|
|
1491
|
-
);
|
|
1492
|
-
return sign * best;
|
|
1493
|
-
}
|
|
1494
|
-
function isCanonicalFrame({ w, h } = {}) {
|
|
1495
|
-
return Object.values(FRAMES).some((f) => f.w === Number(w) && f.h === Number(h));
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
// packages/design/lib/design/lint.mjs
|
|
1499
|
-
var SPACING_PROP = /^(padding|margin|gap|row-gap|column-gap|inset|top|right|bottom|left)(-(top|right|bottom|left|block|inline)(-(start|end))?)?$/;
|
|
1500
|
-
var SIZING_PROP = /^(width|height|min-width|min-height|max-width|max-height|padding|margin|font-size|gap)/;
|
|
1501
|
-
var COLOR_PROP = /^(color|background|background-color|border(-(top|right|bottom|left))?-color|outline-color|fill|stroke|caret-color|text-decoration-color)$/;
|
|
1502
|
-
var COLOR_LITERAL = /#[0-9a-fA-F]{3,8}\b|rgba?\([^)]*\)|oklch\([^)]*\)/gi;
|
|
1503
|
-
var SYSTEM_FONTS = /* @__PURE__ */ new Set([
|
|
1504
|
-
"inherit",
|
|
1505
|
-
"initial",
|
|
1506
|
-
"sans-serif",
|
|
1507
|
-
"serif",
|
|
1508
|
-
"monospace",
|
|
1509
|
-
"system-ui",
|
|
1510
|
-
"ui-sans-serif",
|
|
1511
|
-
"ui-monospace",
|
|
1512
|
-
"ui-serif",
|
|
1513
|
-
"-apple-system",
|
|
1514
|
-
"blinkmacsystemfont",
|
|
1515
|
-
"segoe ui",
|
|
1516
|
-
"roboto",
|
|
1517
|
-
"helvetica neue",
|
|
1518
|
-
"helvetica",
|
|
1519
|
-
"arial",
|
|
1520
|
-
"sfmono-regular",
|
|
1521
|
-
"sf mono",
|
|
1522
|
-
"menlo",
|
|
1523
|
-
"consolas",
|
|
1524
|
-
"liberation mono",
|
|
1525
|
-
"cursive"
|
|
1526
|
-
]);
|
|
1527
|
-
function literalColors(value) {
|
|
1528
|
-
return /gradient/i.test(value) ? [] : value.match(COLOR_LITERAL) || [];
|
|
1529
|
-
}
|
|
1530
|
-
function firstColor(value) {
|
|
1531
|
-
if (/var\(|gradient/i.test(value)) return null;
|
|
1532
|
-
const m = value.match(/#[0-9a-fA-F]{3,8}\b|rgba?\([^)]*\)|oklch\([^)]*\)|\b(?:white|black)\b/i);
|
|
1533
|
-
return m ? m[0] : null;
|
|
1534
|
-
}
|
|
1535
|
-
function firstFontFamily(value) {
|
|
1536
|
-
const first = value.split(",")[0].trim().replace(/^["']|["']$/g, "");
|
|
1537
|
-
return first ? first.toLowerCase() : null;
|
|
1538
|
-
}
|
|
1539
|
-
function declBlocks(html) {
|
|
1540
|
-
const blocks = [];
|
|
1541
|
-
for (const m of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi))
|
|
1542
|
-
for (const b of cssDeclBlocks(m[1])) blocks.push(declsFromBlock(b));
|
|
1543
|
-
for (const m of html.matchAll(/style\s*=\s*(["'])([\s\S]*?)\1/gi))
|
|
1544
|
-
blocks.push(declsFromBlock(m[2]));
|
|
1545
|
-
return blocks;
|
|
1546
|
-
}
|
|
1547
|
-
function pxValues(value) {
|
|
1548
|
-
if (/\bvar\(|\bcalc\(/.test(value)) return [];
|
|
1549
|
-
const out = [];
|
|
1550
|
-
for (const m of value.matchAll(/-?\d+(?:\.\d+)?(px|rem|em)\b/g)) {
|
|
1551
|
-
const n = parseFloat(m[0]);
|
|
1552
|
-
out.push(/r?em/.test(m[1]) ? Math.round(n * 16 * 1e3) / 1e3 : n);
|
|
1553
|
-
}
|
|
1554
|
-
return out;
|
|
1555
|
-
}
|
|
1556
|
-
function declsFromBlock(block) {
|
|
1557
|
-
return block.split(";").map((s) => s.trim()).filter(Boolean).map((s) => {
|
|
1558
|
-
const i = s.indexOf(":");
|
|
1559
|
-
if (i < 0) return null;
|
|
1560
|
-
return { prop: s.slice(0, i).trim().toLowerCase(), value: s.slice(i + 1).trim() };
|
|
1561
|
-
}).filter(Boolean);
|
|
1562
|
-
}
|
|
1563
|
-
function cssDeclBlocks(css) {
|
|
1564
|
-
const clean = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1565
|
-
const blocks = [];
|
|
1566
|
-
let depth = 0;
|
|
1567
|
-
let buf = "";
|
|
1568
|
-
for (const ch of clean) {
|
|
1569
|
-
if (ch === "{") {
|
|
1570
|
-
depth += 1;
|
|
1571
|
-
buf = "";
|
|
1572
|
-
continue;
|
|
1573
|
-
}
|
|
1574
|
-
if (ch === "}") {
|
|
1575
|
-
if (depth >= 1) blocks.push(buf);
|
|
1576
|
-
depth -= 1;
|
|
1577
|
-
buf = "";
|
|
1578
|
-
continue;
|
|
1579
|
-
}
|
|
1580
|
-
if (depth >= 1) buf += ch;
|
|
1581
|
-
}
|
|
1582
|
-
return blocks;
|
|
1583
|
-
}
|
|
1584
|
-
function styleBlockDecls(html) {
|
|
1585
|
-
const out = [];
|
|
1586
|
-
for (const m of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi)) {
|
|
1587
|
-
for (const block of cssDeclBlocks(m[1])) {
|
|
1588
|
-
for (const d of declsFromBlock(block)) out.push({ ...d, where: "style" });
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
return out;
|
|
1592
|
-
}
|
|
1593
|
-
function inlineStyleDecls(html) {
|
|
1594
|
-
const out = [];
|
|
1595
|
-
for (const m of html.matchAll(/style\s*=\s*(["'])([\s\S]*?)\1/gi)) {
|
|
1596
|
-
for (const d of declsFromBlock(m[2])) out.push({ ...d, where: "inline" });
|
|
1597
|
-
}
|
|
1598
|
-
return out;
|
|
1599
|
-
}
|
|
1600
|
-
function inlineSizingDrift(html) {
|
|
1601
|
-
const out = [];
|
|
1602
|
-
for (const m of html.matchAll(/<([a-z][\w-]*)\b([^>]*)>/gi)) {
|
|
1603
|
-
const attrs = m[2];
|
|
1604
|
-
const cls = /\bclass\s*=\s*(["'])([\s\S]*?)\1/i.exec(attrs);
|
|
1605
|
-
const style = /\bstyle\s*=\s*(["'])([\s\S]*?)\1/i.exec(attrs);
|
|
1606
|
-
if (!cls || !style) continue;
|
|
1607
|
-
const sized = declsFromBlock(style[2]).filter((d) => SIZING_PROP.test(d.prop));
|
|
1608
|
-
if (sized.length) {
|
|
1609
|
-
out.push({
|
|
1610
|
-
rule: "inline-sizing-drift",
|
|
1611
|
-
level: "warn",
|
|
1612
|
-
tag: m[1],
|
|
1613
|
-
class: cls[2].trim(),
|
|
1614
|
-
props: sized.map((d) => d.prop).join(", "),
|
|
1615
|
-
message: `<${m[1]} class="${cls[2].trim()}"> carries inline sizing (${sized.map((d) => d.prop).join(", ")}) \u2014 move it to the shared class so siblings can't drift`
|
|
1616
|
-
});
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
return out;
|
|
1620
|
-
}
|
|
1621
|
-
function lintDesign(html, opts = {}) {
|
|
1622
|
-
const { designSystem = null } = opts;
|
|
1623
|
-
const errors = [];
|
|
1624
|
-
const warnings = [];
|
|
1625
|
-
if (opts.screen) {
|
|
1626
|
-
const values = Object.hasOwn(opts.variant?.props ?? {}, opts.screen.id) ? opts.variant.props[opts.screen.id] : {};
|
|
1627
|
-
errors.push(
|
|
1628
|
-
...validateDesignProps({
|
|
1629
|
-
screens: [opts.screen],
|
|
1630
|
-
variants: [{ props: { [opts.screen.id]: values } }]
|
|
1631
|
-
}).map((message) => ({ rule: "invalid-instance-prop", level: "error", message }))
|
|
1632
|
-
);
|
|
1633
|
-
}
|
|
1634
|
-
const decls = [...styleBlockDecls(html), ...inlineStyleDecls(html)];
|
|
1635
|
-
for (const d of decls) {
|
|
1636
|
-
if (SPACING_PROP.test(d.prop)) {
|
|
1637
|
-
for (const px of pxValues(d.value)) {
|
|
1638
|
-
const scale = opts.spacing ?? designSystem?.spacing;
|
|
1639
|
-
const onScale = scale?.length ? scale.includes(Math.abs(px)) : isOnSpacingScale(px);
|
|
1640
|
-
if (!onScale) {
|
|
1641
|
-
errors.push({
|
|
1642
|
-
rule: "spacing-off-grid",
|
|
1643
|
-
level: "error",
|
|
1644
|
-
prop: d.prop,
|
|
1645
|
-
value: `${px}px`,
|
|
1646
|
-
suggestion: `${nearestSpacing(px)}px`,
|
|
1647
|
-
where: d.where,
|
|
1648
|
-
message: `${d.prop}: ${px}px is off the 4-point grid (${d.where}) \u2192 use ${nearestSpacing(px)}px`
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
if (COLOR_PROP.test(d.prop)) {
|
|
1654
|
-
for (const lit of literalColors(d.value)) {
|
|
1655
|
-
warnings.push({
|
|
1656
|
-
rule: "color-not-token",
|
|
1657
|
-
level: "warn",
|
|
1658
|
-
prop: d.prop,
|
|
1659
|
-
value: lit,
|
|
1660
|
-
where: d.where,
|
|
1661
|
-
message: `${d.prop}: ${lit} is a raw color \u2014 use a design-system token via var(--\u2026)`
|
|
1662
|
-
});
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
if ((d.prop === "font-family" || d.prop === "font") && designSystem?.fonts?.length) {
|
|
1666
|
-
const fam = firstFontFamily(d.value);
|
|
1667
|
-
const allowed = new Set(designSystem.fonts.map((f) => String(f.family || "").toLowerCase()));
|
|
1668
|
-
if (fam && !allowed.has(fam) && !SYSTEM_FONTS.has(fam)) {
|
|
1669
|
-
warnings.push({
|
|
1670
|
-
rule: "font-not-token",
|
|
1671
|
-
level: "warn",
|
|
1672
|
-
value: fam,
|
|
1673
|
-
where: d.where,
|
|
1674
|
-
message: `font "${fam}" is not in the design system (${[...allowed].join(", ") || "none"})`
|
|
1675
|
-
});
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
for (const block of declBlocks(html)) {
|
|
1680
|
-
const c = block.find((d) => d.prop === "color");
|
|
1681
|
-
const bg = block.find((d) => d.prop === "background" || d.prop === "background-color");
|
|
1682
|
-
if (!c || !bg) continue;
|
|
1683
|
-
const fg = firstColor(c.value);
|
|
1684
|
-
const back = firstColor(bg.value);
|
|
1685
|
-
if (!fg || !back) continue;
|
|
1686
|
-
const ratio = contrastRatio(fg, back);
|
|
1687
|
-
if (ratio != null && ratio < AA_NORMAL) {
|
|
1688
|
-
errors.push({
|
|
1689
|
-
rule: "contrast-below-aa",
|
|
1690
|
-
level: "error",
|
|
1691
|
-
value: `${ratio.toFixed(1)}:1`,
|
|
1692
|
-
fg,
|
|
1693
|
-
bg: back,
|
|
1694
|
-
message: `text/background contrast ${ratio.toFixed(1)}:1 is below AA 4.5:1 (${fg} on ${back})`
|
|
1695
|
-
});
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
warnings.push(...inlineSizingDrift(html));
|
|
1699
|
-
return {
|
|
1700
|
-
ok: errors.length === 0,
|
|
1701
|
-
errors,
|
|
1702
|
-
warnings,
|
|
1703
|
-
declarations: decls.length,
|
|
1704
|
-
summary: `${errors.length} error(s), ${warnings.length} warning(s)`
|
|
1705
|
-
};
|
|
1706
|
-
}
|
|
1707
|
-
function withoutRootTheme(html) {
|
|
1708
|
-
if (!/theme-(?:light|dark)|data-theme/iu.test(html)) return null;
|
|
1709
|
-
const body = inspectLocalDocumentBody(html);
|
|
1710
|
-
if (!body) return null;
|
|
1711
|
-
const edits = [];
|
|
1712
|
-
for (const { name, value } of body.attrs) {
|
|
1713
|
-
const location = body.location.attrs?.[name];
|
|
1714
|
-
if (!location) continue;
|
|
1715
|
-
let replacement;
|
|
1716
|
-
if (name === "data-theme" && ["light", "dark"].includes(value)) replacement = "";
|
|
1717
|
-
if (name === "class") {
|
|
1718
|
-
const raw = html.slice(location.startOffset, location.endOffset);
|
|
1719
|
-
const quoted = /^(class\s*=\s*)(["'])([\s\S]*)\2$/iu.exec(raw);
|
|
1720
|
-
if (quoted) {
|
|
1721
|
-
const kept = quoted[3].replace(/(^|\s)theme-(?:light|dark)(?=\s|$)/gu, "$1");
|
|
1722
|
-
if (kept !== quoted[3]) replacement = `${quoted[1]}${quoted[2]}${kept}${quoted[2]}`;
|
|
1723
|
-
} else if (/^class\s*=\s*theme-(?:light|dark)$/iu.test(raw)) replacement = "";
|
|
1724
|
-
}
|
|
1725
|
-
if (replacement !== void 0) edits.push({ ...location, replacement });
|
|
1726
|
-
}
|
|
1727
|
-
if (!edits.length) return null;
|
|
1728
|
-
let normalized = html;
|
|
1729
|
-
for (const { startOffset, endOffset, replacement } of edits.sort(
|
|
1730
|
-
(a, b) => b.startOffset - a.startOffset
|
|
1731
|
-
))
|
|
1732
|
-
normalized = normalized.slice(0, startOffset) + replacement + normalized.slice(endOffset);
|
|
1733
|
-
return normalized;
|
|
1734
|
-
}
|
|
1735
|
-
function lintThemeOnlySources(sources) {
|
|
1736
|
-
const warnings = [];
|
|
1737
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1738
|
-
for (const { screenId, source, html } of sources) {
|
|
1739
|
-
const normalized = withoutRootTheme(html);
|
|
1740
|
-
if (normalized === null) continue;
|
|
1741
|
-
const key = JSON.stringify({
|
|
1742
|
-
html: normalized,
|
|
1743
|
-
styles: source.styles ?? [],
|
|
1744
|
-
scripts: source.scripts ?? []
|
|
1745
|
-
});
|
|
1746
|
-
const previous = seen.get(key);
|
|
1747
|
-
if (previous && previous.screenId !== screenId && previous.source !== source.html && previous.html !== html)
|
|
1748
|
-
warnings.push({
|
|
1749
|
-
rule: "theme-only-source",
|
|
1750
|
-
level: "warn",
|
|
1751
|
-
screenId,
|
|
1752
|
-
message: `Screens ${previous.screenId} (${previous.source}) and ${screenId} (${source.html}) differ only by a root theme marker; declare a theme prop and share one source.`
|
|
1753
|
-
});
|
|
1754
|
-
else seen.set(key, { screenId, source: source.html, html });
|
|
1755
|
-
}
|
|
1756
|
-
return warnings;
|
|
1757
|
-
}
|
|
1758
|
-
function lintCanvasData(data, { frames } = {}) {
|
|
1759
|
-
const errors = [];
|
|
1760
|
-
for (const section of data?.sections || []) {
|
|
1761
|
-
for (const a of section.artboards || []) {
|
|
1762
|
-
const accepted = frames?.length ? frames.some(
|
|
1763
|
-
(frame) => frame.width === a.width && (frame.height === "auto" ? a.height === "auto" || Number.isInteger(a.height) && a.height > 0 && a.height <= 16384 : frame.height === a.height)
|
|
1764
|
-
) : isCanonicalFrame({ w: a.width, h: a.height });
|
|
1765
|
-
if (!accepted) {
|
|
1766
|
-
errors.push({
|
|
1767
|
-
rule: "frame-not-canonical",
|
|
1768
|
-
level: "error",
|
|
1769
|
-
artboard: a.id || a.label || "(unnamed)",
|
|
1770
|
-
value: `${a.width}\xD7${a.height}`,
|
|
1771
|
-
suggestion: `${FRAMES.desktop.w}\xD7${FRAMES.desktop.h}`,
|
|
1772
|
-
message: `artboard "${a.id || a.label || "?"}" is ${a.width}\xD7${a.height} \u2014 use the canonical ${FRAMES.desktop.w}\xD7${FRAMES.desktop.h} desktop frame`
|
|
1773
|
-
});
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
return { ok: errors.length === 0, errors };
|
|
1778
|
-
}
|
|
1779
|
-
function extractCanvasData(html) {
|
|
1780
|
-
const m = /(?:var\s+DATA\s*=|__CANVAS_DATA\s*=)\s*(\{[\s\S]*?\})\s*;/.exec(html);
|
|
1781
|
-
if (!m) return null;
|
|
1782
|
-
try {
|
|
1783
|
-
return JSON.parse(m[1]);
|
|
1784
|
-
} catch {
|
|
1785
|
-
return null;
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
var isMain = process.argv[1] && fileURLToPath(new URL("./runtime/packages/design/lib/design/lint.mjs", import.meta.url).href) === process.argv[1];
|
|
1789
|
-
if (isMain) {
|
|
1790
|
-
const expectStyles = process.argv.includes("--expect-styles");
|
|
1791
|
-
const files = process.argv.slice(2).filter((a) => a !== "--expect-styles");
|
|
1792
|
-
if (!files.length) {
|
|
1793
|
-
console.error("usage: node lib/design/lint.mjs [--expect-styles] <file.html> [<file2.html> \u2026]");
|
|
1794
|
-
process.exit(2);
|
|
1795
|
-
}
|
|
1796
|
-
let totalErrors = 0;
|
|
1797
|
-
let emptyParsed = false;
|
|
1798
|
-
for (const file of files) {
|
|
1799
|
-
let html;
|
|
1800
|
-
try {
|
|
1801
|
-
html = readFileSync(file, "utf-8");
|
|
1802
|
-
} catch (e) {
|
|
1803
|
-
console.error(`\u2717 cannot read ${file}: ${e.message}`);
|
|
1804
|
-
totalErrors += 1;
|
|
1805
|
-
continue;
|
|
1806
|
-
}
|
|
1807
|
-
if (/\.s?css$/i.test(file) || !/<style[\s>]/i.test(html) && /\{[^}]*:[^}]*\}/.test(html)) {
|
|
1808
|
-
html = `<style>${html}</style>`;
|
|
1809
|
-
}
|
|
1810
|
-
const res = lintDesign(html);
|
|
1811
|
-
const data = extractCanvasData(html);
|
|
1812
|
-
const frame = data ? lintCanvasData(data) : { ok: true, errors: [] };
|
|
1813
|
-
const allErrors = [...res.errors, ...frame.errors];
|
|
1814
|
-
totalErrors += allErrors.length;
|
|
1815
|
-
console.log(
|
|
1816
|
-
`
|
|
1817
|
-
${file} \u2014 ${res.declarations} declaration(s), ${allErrors.length} error(s), ${res.warnings.length} warning(s)`
|
|
1818
|
-
);
|
|
1819
|
-
for (const e of allErrors) console.log(` \u2717 [${e.rule}] ${e.message}`);
|
|
1820
|
-
for (const w of res.warnings) console.log(` \u26A0 [${w.rule}] ${w.message}`);
|
|
1821
|
-
if (res.declarations === 0) {
|
|
1822
|
-
emptyParsed = true;
|
|
1823
|
-
console.log(
|
|
1824
|
-
` \u26A0 [no-styles-parsed] 0 CSS declarations found \u2014 point at COMPILED CSS, or wrap raw CSS as <style>\u2026</style>${expectStyles ? " (fails --expect-styles)" : ""}`
|
|
1825
|
-
);
|
|
1826
|
-
} else if (!allErrors.length && !res.warnings.length) {
|
|
1827
|
-
console.log(" \u2713 clean");
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
process.exit(totalErrors ? 1 : expectStyles && emptyParsed ? 3 : 0);
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
// packages/design/lib/design/recommend-format.mjs
|
|
1834
|
-
var DESIGN_FORMATS = Object.freeze(["prototype", "walkthrough", "canvas"]);
|
|
1835
|
-
var EXPLORATORY_KEYWORDS = Object.freeze([
|
|
1836
|
-
"option",
|
|
1837
|
-
"options",
|
|
1838
|
-
"concept",
|
|
1839
|
-
"concepts",
|
|
1840
|
-
"explore",
|
|
1841
|
-
"exploration",
|
|
1842
|
-
"variant",
|
|
1843
|
-
"variants",
|
|
1844
|
-
"moodboard",
|
|
1845
|
-
"brainstorm",
|
|
1846
|
-
"compare",
|
|
1847
|
-
"comparison"
|
|
1848
|
-
]);
|
|
1849
|
-
|
|
1850
|
-
// packages/design/lib/design/manifest.mjs
|
|
1851
|
-
var DESIGN_SOURCES = Object.freeze(["spec", "png", "describe"]);
|
|
1852
|
-
var CONTENT_PROVENANCE = Object.freeze(["spec", "inferred"]);
|
|
1853
|
-
var FRAMEWORKS = Object.freeze(["vanilla", "react"]);
|
|
1854
|
-
var NAV_MODES = Object.freeze(["anchor", "lazy"]);
|
|
1855
|
-
var SCHEMA_VERSION = "1.0.0";
|
|
1856
|
-
function defaultFramework(designFormat) {
|
|
1857
|
-
return designFormat === "canvas" ? "react" : "vanilla";
|
|
1858
|
-
}
|
|
1859
|
-
function buildManifest({
|
|
1860
|
-
designFormat,
|
|
1861
|
-
source,
|
|
1862
|
-
generatedAt,
|
|
1863
|
-
screens = [],
|
|
1864
|
-
contentProvenance = "spec",
|
|
1865
|
-
framework,
|
|
1866
|
-
navMode = null,
|
|
1867
|
-
screenName = "",
|
|
1868
|
-
iterations = 0,
|
|
1869
|
-
branch = "",
|
|
1870
|
-
specId = "",
|
|
1871
|
-
htmlFile = "",
|
|
1872
|
-
pretextTier = ""
|
|
1873
|
-
} = {}) {
|
|
1874
|
-
if (!designFormat) throw new Error("buildManifest: designFormat is required");
|
|
1875
|
-
if (!source) throw new Error("buildManifest: source is required");
|
|
1876
|
-
if (!generatedAt) throw new Error("buildManifest: generatedAt is required");
|
|
1877
|
-
const manifest = {
|
|
1878
|
-
schema_version: SCHEMA_VERSION,
|
|
1879
|
-
design_format: designFormat,
|
|
1880
|
-
source,
|
|
1881
|
-
content_provenance: contentProvenance,
|
|
1882
|
-
framework: framework ?? defaultFramework(designFormat),
|
|
1883
|
-
screens: [...screens],
|
|
1884
|
-
screen_count: screens.length,
|
|
1885
|
-
iterations,
|
|
1886
|
-
generated_at: generatedAt
|
|
1887
|
-
};
|
|
1888
|
-
if (navMode) manifest.nav_mode = navMode;
|
|
1889
|
-
if (screenName) manifest.screen_name = screenName;
|
|
1890
|
-
if (branch) manifest.branch = branch;
|
|
1891
|
-
if (specId) manifest.spec_id = specId;
|
|
1892
|
-
if (htmlFile) manifest.html_file = htmlFile;
|
|
1893
|
-
if (pretextTier) manifest.pretext_tier = pretextTier;
|
|
1894
|
-
return manifest;
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
// packages/design/lib/design/studio.mjs
|
|
1898
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
1899
|
-
import { readFileSync as readFileSync3 } from "node:fs";
|
|
1900
|
-
|
|
1901
|
-
// packages/artifact/lib/artifact/internal/runtime-asset.mjs
|
|
1902
|
-
import { createHash } from "node:crypto";
|
|
1903
|
-
import {
|
|
1904
|
-
closeSync,
|
|
1905
|
-
constants,
|
|
1906
|
-
existsSync,
|
|
1907
|
-
fstatSync,
|
|
1908
|
-
lstatSync,
|
|
1909
|
-
openSync,
|
|
1910
|
-
readFileSync as readFileSync2,
|
|
1911
|
-
readSync
|
|
1912
|
-
} from "node:fs";
|
|
1913
|
-
import { resolve } from "node:path";
|
|
1914
|
-
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "node:url";
|
|
1915
|
-
var DIGEST = /^[a-f0-9]{64}$/u;
|
|
1916
|
-
var MAX_FRAGMENT_BYTES = 128 * 1024;
|
|
1917
|
-
var MAX_SOURCE_BYTES = 256 * 1024;
|
|
1918
|
-
var MAX_ASSET_BYTES = 16 * 1024 * 1024;
|
|
1919
|
-
var MAX_SOURCES = 256;
|
|
1920
|
-
var MAX_SCOPES = 16;
|
|
1921
|
-
var MAX_SCOPE_TEXT_BYTES = 4096;
|
|
1922
|
-
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
1923
|
-
var SCOPE_ID = /^[a-z][a-z0-9-]{0,63}$/u;
|
|
1924
|
-
function isRecord(value) {
|
|
1925
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1926
|
-
}
|
|
1927
|
-
var digest = (bytes) => createHash("sha256").update(bytes).digest("hex");
|
|
1928
|
-
function boundedRead(url, label, { limit, exact: exact2, links = false }) {
|
|
1929
|
-
if (!links) {
|
|
1930
|
-
const stat = lstatSync(url, { throwIfNoEntry: false });
|
|
1931
|
-
if (!stat?.isFile()) throw new Error(`${label} must be a regular file, not a link.`);
|
|
1932
|
-
}
|
|
1933
|
-
const descriptor = openSync(
|
|
1934
|
-
fileURLToPath2(url),
|
|
1935
|
-
constants.O_RDONLY | (links ? 0 : constants.O_NOFOLLOW ?? 0)
|
|
1936
|
-
);
|
|
1937
|
-
try {
|
|
1938
|
-
const stat = fstatSync(descriptor);
|
|
1939
|
-
if (!stat.isFile()) throw new Error(`${label} must be a regular file, not a link.`);
|
|
1940
|
-
if (exact2 !== void 0 ? stat.size !== exact2 : stat.size > limit)
|
|
1941
|
-
throw new Error(`${label} is ${stat.size} bytes; expected ${exact2 ?? `at most ${limit}`}.`);
|
|
1942
|
-
const capacity = (exact2 ?? Math.min(stat.size, limit)) + 1;
|
|
1943
|
-
const buffer = Buffer.alloc(capacity);
|
|
1944
|
-
let length = 0;
|
|
1945
|
-
for (let read = -1; read !== 0 && length < capacity; length += read)
|
|
1946
|
-
read = readSync(descriptor, buffer, length, capacity - length, null);
|
|
1947
|
-
if (length === capacity) throw new Error(`${label} changed size while it was read.`);
|
|
1948
|
-
if (exact2 !== void 0 && length !== exact2)
|
|
1949
|
-
throw new Error(`${label} changed size while it was read.`);
|
|
1950
|
-
return buffer.subarray(0, length);
|
|
1951
|
-
} finally {
|
|
1952
|
-
closeSync(descriptor);
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
function sourceManifest(value, basename) {
|
|
1956
|
-
const path = new RegExp(
|
|
1957
|
-
`^${basename.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}\\.sources/\\d{2,4}-[a-z0-9-]+\\.js$`,
|
|
1958
|
-
"u"
|
|
1959
|
-
);
|
|
1960
|
-
if (!isRecord(value) || value.kind !== "openplanr-runtime-asset-sources" || value.schemaVersion !== "1.0.0" || value.asset !== basename || typeof value.sha256 !== "string" || !DIGEST.test(value.sha256) || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || value.bytes > MAX_ASSET_BYTES || !Array.isArray(value.scopes) || !value.scopes.length || value.scopes.length > MAX_SCOPES || !Array.isArray(value.sources) || !value.sources.length || value.sources.length > MAX_SOURCES)
|
|
1961
|
-
throw new Error("Runtime source manifest is invalid.");
|
|
1962
|
-
const ids = /* @__PURE__ */ new Set();
|
|
1963
|
-
const scopes = value.scopes.map((scope, index) => {
|
|
1964
|
-
if (!isRecord(scope) || typeof scope.id !== "string" || !SCOPE_ID.test(scope.id) || ids.has(scope.id) || (index === 0 ? scope.parent !== null : typeof scope.parent !== "string" || !ids.has(scope.parent)) || typeof scope.open !== "string" || typeof scope.close !== "string" || Buffer.byteLength(scope.open) > MAX_SCOPE_TEXT_BYTES || Buffer.byteLength(scope.close) > MAX_SCOPE_TEXT_BYTES)
|
|
1965
|
-
throw new Error("Runtime source scope is invalid.");
|
|
1966
|
-
ids.add(scope.id);
|
|
1967
|
-
return scope;
|
|
1968
|
-
});
|
|
1969
|
-
const paths = /* @__PURE__ */ new Set();
|
|
1970
|
-
const sources = value.sources.map((source) => {
|
|
1971
|
-
if (!isRecord(source) || typeof source.path !== "string" || !path.test(source.path) || paths.has(source.path) || typeof source.scope !== "string" || !ids.has(source.scope) || !Number.isSafeInteger(source.bytes) || source.bytes <= 0 || source.bytes >= MAX_SOURCE_BYTES || typeof source.sha256 !== "string" || !DIGEST.test(source.sha256))
|
|
1972
|
-
throw new Error("Runtime source identity is invalid.");
|
|
1973
|
-
paths.add(source.path);
|
|
1974
|
-
return source;
|
|
1975
|
-
});
|
|
1976
|
-
return { bytes: value.bytes, sha256: value.sha256, scopes, sources };
|
|
1977
|
-
}
|
|
1978
|
-
function readSourceUnits(url, basename) {
|
|
1979
|
-
const manifestUrl = new URL(`${basename}.sources.json`, url);
|
|
1980
|
-
const manifest = boundedRead(manifestUrl, "Runtime source manifest", {
|
|
1981
|
-
limit: MAX_MANIFEST_BYTES
|
|
1982
|
-
});
|
|
1983
|
-
const expected = sourceManifest(JSON.parse(manifest.toString("utf8")), basename);
|
|
1984
|
-
const directory = lstatSync(new URL(`${basename}.sources`, url), { throwIfNoEntry: false });
|
|
1985
|
-
if (!directory?.isDirectory()) throw new Error("Runtime source directory must not be a link.");
|
|
1986
|
-
const byId = new Map(expected.scopes.map((scope) => [scope.id, scope]));
|
|
1987
|
-
const parts = [];
|
|
1988
|
-
const open = [];
|
|
1989
|
-
const closed = /* @__PURE__ */ new Set();
|
|
1990
|
-
let total = 0;
|
|
1991
|
-
const append = (bytes2) => {
|
|
1992
|
-
total += bytes2.length;
|
|
1993
|
-
if (total > expected.bytes) throw new Error("Runtime sources exceed their declared size.");
|
|
1994
|
-
parts.push(bytes2);
|
|
1995
|
-
};
|
|
1996
|
-
const close = () => {
|
|
1997
|
-
const scope = open.pop();
|
|
1998
|
-
if (!scope) return;
|
|
1999
|
-
closed.add(scope.id);
|
|
2000
|
-
append(Buffer.from(scope.close, "utf8"));
|
|
2001
|
-
};
|
|
2002
|
-
for (const source of expected.sources) {
|
|
2003
|
-
const chain = [];
|
|
2004
|
-
for (let scope = byId.get(source.scope); scope; scope = byId.get(scope.parent ?? ""))
|
|
2005
|
-
chain.unshift(scope);
|
|
2006
|
-
while (open.length && !chain.includes(open[open.length - 1])) close();
|
|
2007
|
-
for (const scope of chain.slice(open.length)) {
|
|
2008
|
-
if (closed.has(scope.id)) throw new Error("Runtime source scopes are not contiguous.");
|
|
2009
|
-
open.push(scope);
|
|
2010
|
-
append(Buffer.from(scope.open, "utf8"));
|
|
2011
|
-
}
|
|
2012
|
-
const unitUrl = new URL(source.path, url);
|
|
2013
|
-
const bytes2 = boundedRead(unitUrl, `Runtime source unit ${source.path}`, {
|
|
2014
|
-
limit: MAX_SOURCE_BYTES,
|
|
2015
|
-
exact: source.bytes
|
|
2016
|
-
});
|
|
2017
|
-
if (digest(bytes2) !== source.sha256)
|
|
2018
|
-
throw new Error("Runtime source unit failed integrity verification.");
|
|
2019
|
-
append(bytes2);
|
|
2020
|
-
}
|
|
2021
|
-
while (open.length) close();
|
|
2022
|
-
const bytes = Buffer.concat(parts);
|
|
2023
|
-
if (bytes.length !== expected.bytes || digest(bytes) !== expected.sha256)
|
|
2024
|
-
throw new Error("Runtime sources failed integrity verification.");
|
|
2025
|
-
return bytes;
|
|
2026
|
-
}
|
|
2027
|
-
function readRuntimeAsset(file) {
|
|
2028
|
-
const url = typeof file === "string" ? pathToFileURL(resolve(file)) : file;
|
|
2029
|
-
if (existsSync(url)) return readFileSync2(url);
|
|
2030
|
-
const basename = url.pathname.split("/").at(-1);
|
|
2031
|
-
if (!basename) throw new Error("Studio runtime asset identity is invalid.");
|
|
2032
|
-
if (lstatSync(new URL(`${basename}.sources.json`, url), { throwIfNoEntry: false }))
|
|
2033
|
-
return readSourceUnits(url, basename);
|
|
2034
|
-
const manifest = JSON.parse(
|
|
2035
|
-
boundedRead(new URL(`${basename}.parts.json`, url), "Studio runtime fragment manifest", {
|
|
2036
|
-
limit: MAX_MANIFEST_BYTES,
|
|
2037
|
-
links: true
|
|
2038
|
-
}).toString("utf8")
|
|
2039
|
-
);
|
|
2040
|
-
if (!isRecord(manifest) || manifest.schemaVersion !== "1.0.0" || typeof manifest.sha256 !== "string" || !DIGEST.test(manifest.sha256) || !Array.isArray(manifest.parts) || !manifest.parts.length || manifest.parts.length > 64)
|
|
2041
|
-
throw new Error("Studio runtime fragment manifest is invalid.");
|
|
2042
|
-
const parts = manifest.parts.map((part, index) => {
|
|
2043
|
-
if (!isRecord(part) || part.name !== `${basename}.part-${String(index + 1).padStart(3, "0")}` || typeof part.sha256 !== "string" || !DIGEST.test(part.sha256))
|
|
2044
|
-
throw new Error("Studio runtime fragment identity is invalid.");
|
|
2045
|
-
const bytes2 = boundedRead(new URL(part.name, url), "Studio runtime fragment", {
|
|
2046
|
-
limit: MAX_FRAGMENT_BYTES,
|
|
2047
|
-
links: true
|
|
2048
|
-
});
|
|
2049
|
-
if (createHash("sha256").update(bytes2).digest("hex") !== part.sha256)
|
|
2050
|
-
throw new Error("Studio runtime fragment failed integrity verification.");
|
|
2051
|
-
return bytes2;
|
|
2052
|
-
});
|
|
2053
|
-
const bytes = Buffer.concat(parts);
|
|
2054
|
-
if (createHash("sha256").update(bytes).digest("hex") !== manifest.sha256)
|
|
2055
|
-
throw new Error("Studio runtime failed integrity verification.");
|
|
2056
|
-
return bytes;
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2059
|
-
// packages/design/lib/design/studio-render.mjs
|
|
2060
|
-
var DESIGN_STUDIO_VERSION = "1.9.0";
|
|
2061
|
-
var DESIGN_STUDIO_ASSETS = Object.freeze({
|
|
2062
|
-
style: "templates/studio/studio.css",
|
|
2063
|
-
runtime: "templates/studio/studio.js",
|
|
2064
|
-
enhancementsStyle: "templates/studio/enhancements.css",
|
|
2065
|
-
handoffCenterStyle: "templates/studio/handoff-center.css"
|
|
2066
|
-
});
|
|
2067
|
-
function applyDesignStudioTheme(preference) {
|
|
2068
|
-
if (!preference) {
|
|
2069
|
-
try {
|
|
2070
|
-
preference = localStorage.getItem("openplanr.design.theme");
|
|
2071
|
-
} catch {
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
if (!["dark", "light", "system"].includes(preference)) preference = "dark";
|
|
2075
|
-
const resolved = preference === "system" ? globalThis.matchMedia?.("(prefers-color-scheme: light)").matches ? "light" : "dark" : preference;
|
|
2076
|
-
Object.assign(document.documentElement.dataset, {
|
|
2077
|
-
planrTheme: resolved,
|
|
2078
|
-
designTheme: resolved,
|
|
2079
|
-
designThemePreference: preference
|
|
2080
|
-
});
|
|
2081
|
-
const modelNode = document.getElementById("planr-artifact-shell-model");
|
|
2082
|
-
if (modelNode) {
|
|
2083
|
-
try {
|
|
2084
|
-
const model = JSON.parse(modelNode.textContent);
|
|
2085
|
-
model.theme = resolved;
|
|
2086
|
-
modelNode.textContent = JSON.stringify(model);
|
|
2087
|
-
} catch {
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
return { preference, resolved };
|
|
2091
|
-
}
|
|
2092
|
-
function applyDesignStudioResourcePolicy() {
|
|
2093
|
-
const root = document.querySelector(".planr-shell");
|
|
2094
|
-
if (!root || !document.documentElement.hasAttribute("data-design-studio")) return;
|
|
2095
|
-
if (globalThis.matchMedia?.("(pointer: coarse)").matches) {
|
|
2096
|
-
root.dataset.planrFrameBudget = "3";
|
|
2097
|
-
root.dataset.designResourceMode = "bounded";
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
function designStudioThemeBootstrapSource() {
|
|
2101
|
-
return `(${applyDesignStudioTheme.toString()})();(${applyDesignStudioResourcePolicy.toString()})();`;
|
|
2102
|
-
}
|
|
2103
|
-
function button(label, attributes = "", className = "") {
|
|
2104
|
-
return `<button type="button"${className ? ` class="${className}"` : ""} ${attributes}>${escapeHtml(label)}</button>`;
|
|
2105
|
-
}
|
|
2106
|
-
var icons = {
|
|
2107
|
-
left: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M9 4v16"/>',
|
|
2108
|
-
right: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/>',
|
|
2109
|
-
canvas: '<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>',
|
|
2110
|
-
prototype: '<path d="m8 4 12 8-12 8Z"/>',
|
|
2111
|
-
walkthrough: '<rect x="3" y="4" width="18" height="14" rx="2"/><path d="m9 22 3-4 3 4M9 9h6M9 13h3"/>',
|
|
2112
|
-
share: '<path d="M12 16V3m-4 4 4-4 4 4M5 13v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"/>',
|
|
2113
|
-
export: '<path d="M12 3v12m-4-4 4 4 4-4M5 16v4h14v-4"/>',
|
|
2114
|
-
pointer: '<path d="m5 3 15 9-7 1-3 7Z"/>',
|
|
2115
|
-
comment: '<path d="M21 11a8 8 0 0 1-8 8H7l-4 3V11a9 9 0 0 1 18 0Z"/><path d="M8 9h8M8 13h5"/>'
|
|
2116
|
-
};
|
|
2117
|
-
function icon(name) {
|
|
2118
|
-
return `<svg class="design-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${icons[name]}</svg>`;
|
|
2119
|
-
}
|
|
2120
|
-
function iconButton(label, name, attributes = "", className = "") {
|
|
2121
|
-
return `<button type="button" class="${className}" ${attributes} title="${escapeHtml(label)}" aria-label="${escapeHtml(label)}">${icon(name)}<span class="design-button-label">${escapeHtml(label)}</span></button>`;
|
|
2122
|
-
}
|
|
2123
|
-
function toolbar(document2) {
|
|
2124
|
-
return `<header class="planr-toolbar design-toolbar">
|
|
2125
|
-
<div class="design-toolbar-leading">
|
|
2126
|
-
${iconButton("Screens", "left", 'data-design-toggle-nav aria-controls="design-navigator" aria-expanded="true"', "planr-toolbar-action design-panel-toggle")}
|
|
2127
|
-
<div class="planr-brand">${renderPlanrMark()}<span class="design-wordmark" aria-label="OpenPlanr">Open<span>Planr</span></span><span class="planr-title-block"><strong title="${escapeHtml(document2.title)}">${escapeHtml(document2.title)}</strong></span></div>
|
|
2128
|
-
</div>
|
|
2129
|
-
<div class="planr-segment design-view-picker" role="group" aria-label="Design view">${["canvas", "prototype", "walkthrough"].map((view) => iconButton(view[0].toUpperCase() + view.slice(1), view, `data-design-view="${view}" aria-pressed="${document2.defaultView === view}"`)).join("")}</div>
|
|
2130
|
-
<div class="design-toolbar-trailing">
|
|
2131
|
-
<span class="design-preview-state" role="status" data-design-preview-state>Loading preview</span>
|
|
2132
|
-
<span class="design-save-state" role="status" aria-live="polite" data-design-save-state>Loading studio</span>
|
|
2133
|
-
${iconButton("Review", "right", 'data-planr-action="feedback" data-planr-review-label="Review" aria-controls="planr-review-rail" aria-expanded="true"', "planr-toolbar-action design-review-toggle")}
|
|
2134
|
-
${iconButton("Share design", "share", 'data-planr-action="share" aria-haspopup="dialog"', "planr-toolbar-action design-share")}
|
|
2135
|
-
<details class="design-export"><summary aria-label="Export" title="Export">${icon("export")}</summary><div>${button("Portable HTML", 'data-design-export="html"')}${button("Screen PNG", 'data-design-export="png"')}</div></details>
|
|
2136
|
-
</div>
|
|
2137
|
-
</header>`;
|
|
2138
|
-
}
|
|
2139
|
-
function navigator(document2) {
|
|
2140
|
-
const failed = document2.variants.filter(({ status }) => status === "failed");
|
|
2141
|
-
return `<nav class="design-navigator" id="design-navigator" aria-label="Design screens">
|
|
2142
|
-
<div class="design-nav-title"><strong>Screens <span>${document2.screenOrder.length}</span></strong>${iconButton("Close screens", "left", 'data-design-toggle-nav aria-controls="design-navigator" aria-expanded="true"', "design-nav-close")}</div>
|
|
2143
|
-
<label class="design-field" for="design-variant">Direction<select id="design-variant" data-design-variant>${document2.variants.map((variant) => `<option value="${escapeHtml(variant.id)}"${variant.status !== "ready" ? " disabled" : ""}>${escapeHtml(variant.label)}${variant.status === "failed" ? " \u2014 unavailable" : ""}</option>`).join("")}</select></label>
|
|
2144
|
-
${document2.variants.filter(({ status }) => status === "ready").length > 1 ? `<label class="design-compare"><input type="checkbox" data-design-compare> Compare directions</label>` : ""}
|
|
2145
|
-
<label class="design-field design-screen-search">Find a screen<input type="search" data-design-screen-search placeholder="Search screens" aria-label="Search screens"></label>
|
|
2146
|
-
${document2.flows?.length ? `<label class="design-field">Journey<select data-design-screen-group aria-label="Filter screens by journey"><option value="">All screens</option>${document2.flows.map((flow) => `<option value="${escapeHtml(flow.id)}">${escapeHtml(flow.title)}</option>`).join("")}</select></label>` : ""}
|
|
2147
|
-
<p data-design-search-empty hidden>No screens match this search.</p>
|
|
2148
|
-
<div class="design-screen-list">${document2.screenOrder.map((screenId, index) => {
|
|
2149
|
-
const screen = document2.screens.find(({ id }) => id === screenId);
|
|
2150
|
-
return `<button type="button" class="design-screen" data-design-screen="${escapeHtml(screen.id)}" aria-current="${index === 0 ? "page" : "false"}" title="${escapeHtml(screen.title)}"><span class="design-screen-number" aria-hidden="true">${String(index + 1).padStart(2, "0")}</span><span>${escapeHtml(screen.title)}</span></button>`;
|
|
2151
|
-
}).join("")}</div>
|
|
2152
|
-
<div class="design-nav-footer"><span data-design-verification>Browser inspection pending</span><span class="design-local">Local workspace</span></div>
|
|
2153
|
-
${failed.length ? `<details class="design-failed-variants"><summary>${failed.length} unavailable ${failed.length === 1 ? "direction" : "directions"}</summary>${failed.map((variant) => `<p><strong>${escapeHtml(variant.label)}</strong><br>${escapeHtml(variant.issue ?? "Generation did not complete. The previous design remains available.")}</p>`).join("")}</details>` : ""}
|
|
2154
|
-
</nav>`;
|
|
2155
|
-
}
|
|
2156
|
-
function designNotes(document2) {
|
|
2157
|
-
const screens = document2.screens.filter(({ description }) => description);
|
|
2158
|
-
if (!screens.length) return "";
|
|
2159
|
-
return `<details class="design-guidance" data-design-notes><summary aria-controls="design-guidance-panel" aria-expanded="false">Notes <span>${screens.length}</span></summary><div class="design-guidance-panel" id="design-guidance-panel" role="region" aria-labelledby="design-guidance-title"><header><div><strong id="design-guidance-title">Design notes</strong><p>Implementation context stays outside the product UI.</p></div><button type="button" data-design-close-notes aria-label="Close design notes" title="Close design notes">\xD7</button></header>${screens.map(
|
|
2160
|
-
(screen, index) => `<details data-design-note-screen="${escapeHtml(screen.id)}"${index === 0 ? " open" : ""}><summary>${escapeHtml(screen.title)}</summary><p>${escapeHtml(screen.description)}</p></details>`
|
|
2161
|
-
).join("")}</div></details>`;
|
|
2162
|
-
}
|
|
2163
|
-
function stageDetails(document2) {
|
|
2164
|
-
return `<div class="design-stage-context"><div><strong data-design-screen-title>${escapeHtml(document2.screens.find(({ id }) => id === document2.screenOrder[0]).title)}</strong><span data-design-stage-description>Every screen, one connected design</span></div><div class="design-stage-actions">${designNotes(document2)}<label class="design-frame-picker">Frame<select aria-label="Responsive frame" data-design-frame>${document2.frames.map((frame) => `<option value="${escapeHtml(frame.id)}">${escapeHtml(designFrameLabel(frame))}</option>`).join("")}</select></label></div></div>
|
|
2165
|
-
<div class="design-canvas-tools" role="group" aria-label="Canvas controls"><div class="planr-segment design-interaction-picker" role="group" aria-label="Review mode">${iconButton("Interact", "pointer", 'data-planr-mode="interact" aria-pressed="true" aria-keyshortcuts="I"')}${iconButton("Annotate", "comment", 'data-planr-mode="comment" aria-pressed="false" aria-keyshortcuts="C"')}</div><span></span>${button("\u2212", 'data-design-zoom="out" aria-label="Zoom out"')}${button("100%", 'data-design-zoom="reset" aria-label="Reset zoom"')}${button("+", 'data-design-zoom="in" aria-label="Zoom in"')}<span></span>${button("100%", 'data-design-actual-size aria-label="Inspect screen at actual size" aria-pressed="false"')}${button("Focus", 'data-design-focus aria-label="Focus on the preview"')}${button("Fit", 'data-design-fit aria-label="Fit all visible artboards"')}${button("Pan", 'data-design-pan aria-pressed="false" aria-label="Pan canvas" aria-keyshortcuts="H Space" title="Pan canvas (H). Hold Space to pan temporarily; Escape returns to Interact."')}</div>
|
|
2166
|
-
<div class="design-walkthrough-caption" hidden><div><span data-design-step></span><h2 data-design-narrative-title></h2><p data-design-narrative></p></div><div>${button("Previous", 'data-design-step-change="-1"')}${button("Next", 'data-design-step-change="1"')}</div></div>
|
|
2167
|
-
<div class="design-notice" role="status" aria-live="polite" hidden><span data-design-notice-message></span><button type="button" data-design-dismiss-notice aria-label="Dismiss notification" title="Dismiss notification"><span aria-hidden="true">\xD7</span></button></div>`;
|
|
2168
|
-
}
|
|
2169
|
-
function directionDetails() {
|
|
2170
|
-
return `<section class="planr-domain-rail design-direction-review" data-planr-slot="domain-rail" aria-label="Direction review"><div><strong data-design-direction-label>Direction</strong><span data-design-selected-direction></span></div><div class="design-rating" role="group" aria-label="Rate this direction">${[1, 2, 3, 4, 5].map((rating) => button("\u2606", `data-design-rating="${rating}" aria-label="Rate ${rating} out of 5" aria-pressed="false"`)).join("")}</div>${button("Use this direction", "data-design-select-direction")}<details class="design-refinement"><summary>Refine this direction</summary><div><label for="design-remix">Refinement note<textarea id="design-remix" maxlength="8192" data-design-remix placeholder="What should change in the next iteration?"></textarea></label>${button("Save refinement note", "data-design-save-remix")}<p data-design-review-hint>Saved with your review for the next iteration.</p></div></details></section>`;
|
|
2171
|
-
}
|
|
2172
|
-
function earlierFeedback(pins) {
|
|
2173
|
-
if (!pins.length) return "";
|
|
2174
|
-
return `<section class="design-stale-feedback" aria-label="Earlier feedback"><details open><summary>Earlier feedback <span>${pins.length}</span></summary><p>These comments refer to an earlier revision or a missing anchor. Review them before resolving.</p>${pins.map((pin) => `<article data-design-stale-pin="${escapeHtml(pin.id)}"><div><strong>${escapeHtml(pin.screenId ?? pin.anchor?.screen ?? "Earlier screen")}</strong><span>Stale</span></div><p>${escapeHtml(pin.comment)}</p>${pin.anchor?.planrId ? `<small>Anchor: ${escapeHtml(pin.anchor.planrId)}</small>` : ""}</article>`).join("")}</details></section>`;
|
|
2175
|
-
}
|
|
2176
|
-
function renderDesignStudioMarkup({
|
|
2177
|
-
document: document2,
|
|
2178
|
-
envelope,
|
|
2179
|
-
entries,
|
|
2180
|
-
state = null,
|
|
2181
|
-
revision = null,
|
|
2182
|
-
verification = null,
|
|
2183
|
-
stalePins = [],
|
|
2184
|
-
reviewContext = null,
|
|
2185
|
-
contextDigest = null,
|
|
2186
|
-
fingerprints = [],
|
|
2187
|
-
contentIdentity = null
|
|
2188
|
-
} = {}, {
|
|
2189
|
-
stageRuntimeUrl = "./artifact-review-stage.js",
|
|
2190
|
-
renderShell,
|
|
2191
|
-
style = "",
|
|
2192
|
-
runtime = "",
|
|
2193
|
-
lazySources = false
|
|
2194
|
-
} = {}) {
|
|
2195
|
-
if (!document2 || !envelope)
|
|
2196
|
-
throw new TypeError("Design studio requires a design document and artifact envelope.");
|
|
2197
|
-
const artifactIds = new Set(envelope.artifacts.map(({ id }) => id));
|
|
2198
|
-
for (const entry of entries) {
|
|
2199
|
-
if (!artifactIds.has(entry.artifactId) || !document2.screens.some(({ id }) => id === entry.screenId) || !document2.variants.some(({ id, status }) => id === entry.variantId && status === "ready") || !document2.frames.some(({ id }) => id === entry.frameId)) {
|
|
2200
|
-
throw new TypeError(`Invalid design studio entry: ${entry.artifactId}.`);
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
const activeEntry = entries.find(
|
|
2204
|
-
({ variantId, screenId, frameId }) => variantId === (state?.variantId ?? document2.selectedVariant) && screenId === (state?.screenId ?? document2.screenOrder[0]) && frameId === (state?.frameId ?? document2.frames[0].id)
|
|
2205
|
-
);
|
|
2206
|
-
if (!activeEntry)
|
|
2207
|
-
throw new TypeError("Design studio requires the selected direction and first screen/frame.");
|
|
2208
|
-
const payload = {
|
|
2209
|
-
schemaVersion: DESIGN_STUDIO_VERSION,
|
|
2210
|
-
document: document2,
|
|
2211
|
-
entries,
|
|
2212
|
-
state,
|
|
2213
|
-
revision,
|
|
2214
|
-
verification,
|
|
2215
|
-
reviewContext,
|
|
2216
|
-
contextDigest,
|
|
2217
|
-
fingerprints,
|
|
2218
|
-
contentIdentity,
|
|
2219
|
-
artifactKinds: Object.fromEntries(
|
|
2220
|
-
envelope.artifacts.map((artifact) => [artifact.id, artifact.kind])
|
|
2221
|
-
),
|
|
2222
|
-
staticArtifacts: envelope.artifacts.filter(
|
|
2223
|
-
(artifact) => artifact.kind !== "html" || !lazySources && /\bdata-(?:design|planr)-static(?:\s|=|>)/u.test(
|
|
2224
|
-
resolveArtifactHtml(envelope, artifact)
|
|
2225
|
-
)
|
|
2226
|
-
).map((artifact) => artifact.id)
|
|
2227
|
-
};
|
|
2228
|
-
let html = renderShell(
|
|
2229
|
-
{
|
|
2230
|
-
envelope,
|
|
2231
|
-
viewer: {
|
|
2232
|
-
mode: "single",
|
|
2233
|
-
presentation: "canvas",
|
|
2234
|
-
activeArtifactId: activeEntry.artifactId
|
|
2235
|
-
},
|
|
2236
|
-
shell: {
|
|
2237
|
-
title: document2.title,
|
|
2238
|
-
theme: "dark",
|
|
2239
|
-
railOpen: true,
|
|
2240
|
-
zoom: 100
|
|
2241
|
-
}
|
|
2242
|
-
},
|
|
2243
|
-
{ stageRuntimeUrl }
|
|
2244
|
-
);
|
|
2245
|
-
html = html.replace('class="planr-shell"', 'class="planr-shell" data-planr-frame-budget="3"');
|
|
2246
|
-
html = html.replace(
|
|
2247
|
-
/(<iframe\b[^>]*\bsandbox=")allow-scripts(")/g,
|
|
2248
|
-
"$1allow-scripts allow-forms$2"
|
|
2249
|
-
);
|
|
2250
|
-
html = html.replace(/<header class="planr-toolbar">[\s\S]*?<\/header>/, toolbar(document2));
|
|
2251
|
-
html = html.replace(
|
|
2252
|
-
'<div class="planr-workspace">',
|
|
2253
|
-
`<div class="planr-workspace">${navigator(document2)}`
|
|
2254
|
-
);
|
|
2255
|
-
html = html.replace(
|
|
2256
|
-
'<main class="planr-stage" aria-label="Artifact review stage">',
|
|
2257
|
-
`<main class="planr-stage" aria-label="Design canvas">${stageDetails(document2)}`
|
|
2258
|
-
);
|
|
2259
|
-
html = html.replace(/<section class="planr-domain-rail"[^>]*><\/section>/, directionDetails());
|
|
2260
|
-
html = html.replace("<h2>Review comments</h2>", "<h2>Review</h2>");
|
|
2261
|
-
html = html.replace(
|
|
2262
|
-
'aria-label="Close comments">\xD7',
|
|
2263
|
-
`aria-label="Close review" title="Close review">${icon("right")}`
|
|
2264
|
-
);
|
|
2265
|
-
html = html.replace("Overall note for the coding agent\u2026", "Summarize your review\u2026");
|
|
2266
|
-
html = html.replace(
|
|
2267
|
-
'<div class="planr-feedback-slot"',
|
|
2268
|
-
`${earlierFeedback(stalePins)}<div class="design-comment-action">${iconButton("Add comment", "comment", 'data-planr-action="add-comment" aria-pressed="false"')}</div><div class="planr-feedback-slot"`
|
|
2269
|
-
);
|
|
2270
|
-
html = html.replace(
|
|
2271
|
-
'<html lang="en"',
|
|
2272
|
-
`<html lang="en" data-design-studio="${DESIGN_STUDIO_VERSION}" data-design-opening="true"`
|
|
2273
|
-
);
|
|
2274
|
-
html = html.replace(
|
|
2275
|
-
"</head>",
|
|
2276
|
-
`<style>${style}</style><script>${designStudioThemeBootstrapSource()}</script></head>`
|
|
2277
|
-
);
|
|
2278
|
-
html = html.replace(
|
|
2279
|
-
"</body>",
|
|
2280
|
-
`<script type="application/json" id="planr-design-studio-payload">${embedJson(payload)}</script><script>${designStudioThemeBootstrapSource()}</script><script>${runtime}</script></body>`
|
|
2281
|
-
);
|
|
2282
|
-
return html;
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
// packages/design/lib/design/studio.mjs
|
|
2286
|
-
var templateRoot = new URL("../../templates/studio/", new URL("./runtime/packages/design/lib/design/studio.mjs", import.meta.url).href);
|
|
2287
|
-
function readDesignStudioRuntime() {
|
|
2288
|
-
return readRuntimeAsset(new URL("studio.js", templateRoot)).toString("utf8");
|
|
2289
|
-
}
|
|
2290
|
-
function designStudioArtifactId(variantId, screenId, frameId) {
|
|
2291
|
-
const id = [
|
|
2292
|
-
"design",
|
|
2293
|
-
...[variantId, screenId, frameId].map((value) => `${value.length}-${value}`)
|
|
2294
|
-
].join(".");
|
|
2295
|
-
return id.length <= 128 ? id : `design.${createHash2("sha256").update(JSON.stringify([variantId, screenId, frameId])).digest("hex")}`;
|
|
2296
|
-
}
|
|
2297
|
-
function createDesignStudioEntries(document2, envelope) {
|
|
2298
|
-
const ids = new Set(envelope.artifacts.map(({ id }) => id));
|
|
2299
|
-
const entries = [];
|
|
2300
|
-
for (const variant of document2.variants.filter(({ status }) => status === "ready")) {
|
|
2301
|
-
for (const screenId of document2.screenOrder) {
|
|
2302
|
-
for (const frame of document2.frames) {
|
|
2303
|
-
const artifactId = designStudioArtifactId(variant.id, screenId, frame.id);
|
|
2304
|
-
if (!ids.has(artifactId))
|
|
2305
|
-
throw new Error(`Design studio is missing artifact ${artifactId}.`);
|
|
2306
|
-
entries.push({
|
|
2307
|
-
artifactId,
|
|
2308
|
-
variantId: variant.id,
|
|
2309
|
-
screenId,
|
|
2310
|
-
frameId: frame.id,
|
|
2311
|
-
...frame.height === "auto" ? { height: "auto" } : {}
|
|
2312
|
-
});
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
return entries;
|
|
2317
|
-
}
|
|
2318
|
-
function renderDesignStudio(input = {}, options = {}) {
|
|
2319
|
-
return renderDesignStudioMarkup(
|
|
2320
|
-
{
|
|
2321
|
-
...input,
|
|
2322
|
-
entries: input.entries ?? createDesignStudioEntries(input.document, input.envelope)
|
|
2323
|
-
},
|
|
2324
|
-
{
|
|
2325
|
-
...options,
|
|
2326
|
-
renderShell: renderArtifactShellDocument,
|
|
2327
|
-
style: readFileSync3(
|
|
2328
|
-
new URL(new URL("./runtime/packages/artifact/lib/artifact/ui/studio-shell.css", import.meta.url).href),
|
|
2329
|
-
"utf8"
|
|
2330
|
-
) + "\n" + ["studio.css", "enhancements.css", "handoff-center.css"].map((file) => readFileSync3(new URL(file, templateRoot), "utf8")).join("\n"),
|
|
2331
|
-
runtime: readDesignStudioRuntime()
|
|
2332
|
-
}
|
|
2333
|
-
);
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
// packages/design/lib/design/view-context.mjs
|
|
2337
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
2338
|
-
import { join } from "node:path";
|
|
2339
|
-
var anchorPattern = new RegExp(DESIGN_DOCUMENT_SCHEMA.$defs.id.pattern, "u");
|
|
2340
|
-
var exact = (value, keys) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
2341
|
-
var boardFields = ["screenId", "frameId", "variantId"];
|
|
2342
|
-
function validateDesignViewContext(input, current) {
|
|
2343
|
-
if (!exact(input, ["revision", "view", "visibleScreens", "selected", "dirty"]))
|
|
2344
|
-
throw new Error(
|
|
2345
|
-
"View context requires exactly revision, view, visibleScreens, selected and dirty."
|
|
2346
|
-
);
|
|
2347
|
-
if (input.revision !== current.revision)
|
|
2348
|
-
throw Object.assign(
|
|
2349
|
-
new Error(
|
|
2350
|
-
`View context revision ${input.revision} is stale; current revision is ${current.revision}.`
|
|
2351
|
-
),
|
|
2352
|
-
{ statusCode: 409 }
|
|
2353
|
-
);
|
|
2354
|
-
if (!["canvas", "prototype", "walkthrough"].includes(input.view))
|
|
2355
|
-
throw new Error(`Unknown view context view ${input.view}.`);
|
|
2356
|
-
if (typeof input.dirty !== "boolean")
|
|
2357
|
-
throw new Error(`View context dirty must be boolean; received ${input.dirty}.`);
|
|
2358
|
-
const board = (value, selected = false) => {
|
|
2359
|
-
if (!exact(value, selected ? [...boardFields, "planrId"] : boardFields))
|
|
2360
|
-
throw new Error(
|
|
2361
|
-
`View context ${selected ? "selected" : "visibleScreens entry"} has invalid fields.`
|
|
2362
|
-
);
|
|
2363
|
-
if (!current.entries.some((entry) => boardFields.every((key) => entry[key] === value[key])))
|
|
2364
|
-
throw new Error(`Unknown view context board ${JSON.stringify(value)}.`);
|
|
2365
|
-
return boardFields.map((key) => value[key]).join("/");
|
|
2366
|
-
};
|
|
2367
|
-
if (!Array.isArray(input.visibleScreens) || input.visibleScreens.length > current.entries.length)
|
|
2368
|
-
throw new Error(
|
|
2369
|
-
`View context visibleScreens must contain at most ${current.entries.length} boards.`
|
|
2370
|
-
);
|
|
2371
|
-
const keys = input.visibleScreens.map((value) => board(value));
|
|
2372
|
-
if (new Set(keys).size !== keys.length)
|
|
2373
|
-
throw new Error("View context visibleScreens contains duplicate boards.");
|
|
2374
|
-
board(input.selected, true);
|
|
2375
|
-
const { planrId } = input.selected;
|
|
2376
|
-
if (planrId !== null && (typeof planrId !== "string" || planrId.length > 128 || !anchorPattern.test(planrId)))
|
|
2377
|
-
throw new Error(
|
|
2378
|
-
`View context planrId ${JSON.stringify(planrId)} must be null or a document anchor ID of at most 128 characters.`
|
|
2379
|
-
);
|
|
2380
|
-
return structuredClone(input);
|
|
2381
|
-
}
|
|
2382
|
-
function saveDesignViewContext(file, input, clock) {
|
|
2383
|
-
const current = currentDesign(file);
|
|
2384
|
-
const context = validateDesignViewContext(input, current);
|
|
2385
|
-
const value = {
|
|
2386
|
-
kind: "openplanr-design-view-context",
|
|
2387
|
-
schemaVersion: "1.0.0",
|
|
2388
|
-
...context,
|
|
2389
|
-
updatedAt: clock().toISOString()
|
|
2390
|
-
};
|
|
2391
|
-
atomicJson(join(current.root, ".design/view-context.json"), value);
|
|
2392
|
-
return { ok: true };
|
|
2393
|
-
}
|
|
2394
|
-
function readDesignViewContext(file, { requestTime } = {}) {
|
|
2395
|
-
try {
|
|
2396
|
-
const current = currentDesign(file);
|
|
2397
|
-
const path = join(current.root, ".design/view-context.json");
|
|
2398
|
-
const bytes = readFileSync4(path);
|
|
2399
|
-
if (bytes.length > 128 * 1024) throw new Error(`${path} exceeds 128 KB.`);
|
|
2400
|
-
const value = JSON.parse(bytes.toString("utf8"));
|
|
2401
|
-
if (!exact(value, [
|
|
2402
|
-
"kind",
|
|
2403
|
-
"schemaVersion",
|
|
2404
|
-
"revision",
|
|
2405
|
-
"updatedAt",
|
|
2406
|
-
"view",
|
|
2407
|
-
"visibleScreens",
|
|
2408
|
-
"selected",
|
|
2409
|
-
"dirty"
|
|
2410
|
-
]) || value.kind !== "openplanr-design-view-context" || value.schemaVersion !== "1.0.0")
|
|
2411
|
-
throw new Error(`${path} is not a supported design view context.`);
|
|
2412
|
-
if (typeof value.updatedAt !== "string" || !Number.isFinite(Date.parse(value.updatedAt)))
|
|
2413
|
-
throw new Error(`${path} has an invalid updatedAt timestamp.`);
|
|
2414
|
-
const { kind, schemaVersion, updatedAt, ...input } = value;
|
|
2415
|
-
validateDesignViewContext(input, current);
|
|
2416
|
-
if (requestTime !== void 0) {
|
|
2417
|
-
const requestedAt = Date.parse(requestTime);
|
|
2418
|
-
if (!Number.isFinite(requestedAt))
|
|
2419
|
-
throw new Error(`Invalid view context request time ${requestTime}.`);
|
|
2420
|
-
if (Date.parse(updatedAt) < requestedAt)
|
|
2421
|
-
throw new Error(
|
|
2422
|
-
`View context updatedAt ${updatedAt} is older than request ${requestTime}.`
|
|
2423
|
-
);
|
|
2424
|
-
}
|
|
2425
|
-
return value;
|
|
2426
|
-
} catch (error) {
|
|
2427
|
-
return { viewContext: null, reason: error.message };
|
|
2428
|
-
}
|
|
2429
|
-
}
|
|
2430
|
-
|
|
2431
|
-
// packages/design/lib/design/document.mjs
|
|
2432
|
-
var DESIGN_VIEWS = Object.freeze(["canvas", "prototype", "walkthrough"]);
|
|
2433
|
-
var DESIGN_RENDERER_VERSION = "1.3.0";
|
|
2434
|
-
function loadDesignDocument(file, { readSource } = {}) {
|
|
2435
|
-
const checked = readSource?.(file, process.cwd());
|
|
2436
|
-
const path = checked?.file ?? realpathSync(resolve2(file));
|
|
2437
|
-
const document2 = assertDesignDocument(
|
|
2438
|
-
checked ? JSON.parse(checked.value.toString("utf8")) : readJson(path)
|
|
2439
|
-
);
|
|
2440
|
-
return { path, root: dirname(path), document: document2 };
|
|
2441
|
-
}
|
|
2442
|
-
function inspectDesignDocument(file, { readSource } = {}) {
|
|
2443
|
-
const { path, root, document: document2 } = loadDesignDocument(file, { readSource });
|
|
2444
|
-
const ready = document2.variants.filter((variant) => variant.status === "ready").length;
|
|
2445
|
-
const sourceCount = document2.screenOrder.length * ready;
|
|
2446
|
-
const viewCount = sourceCount * document2.frames.length;
|
|
2447
|
-
if (sourceCount > 256 || viewCount > 4096)
|
|
2448
|
-
throw new RangeError(
|
|
2449
|
-
`This board needs ${sourceCount} screen sources and ${viewCount} viewport references. Split it into linked boards: one board supports 256 sources and 4096 views.`
|
|
2450
|
-
);
|
|
2451
|
-
const sources = new Set(document2.assets ?? []);
|
|
2452
|
-
if (existsSync2(join2(root, "review-context.json"))) sources.add("review-context.json");
|
|
2453
|
-
if (document2.designSystem?.tokens) sources.add(document2.designSystem.tokens);
|
|
2454
|
-
for (const screen of document2.screens)
|
|
2455
|
-
for (const source of [
|
|
2456
|
-
screen.source,
|
|
2457
|
-
...document2.variants.filter((variant) => variant.status === "ready").map((variant) => variant.sources?.[screen.id]).filter(Boolean)
|
|
2458
|
-
]) {
|
|
2459
|
-
sources.add(source.html);
|
|
2460
|
-
for (const item of [...source.styles ?? [], ...source.scripts ?? []]) sources.add(item);
|
|
2461
|
-
}
|
|
2462
|
-
const missing = [];
|
|
2463
|
-
for (const source of sources) {
|
|
2464
|
-
try {
|
|
2465
|
-
if (readSource) readSource(source, root);
|
|
2466
|
-
else resolveLocalDocumentFile(root, source);
|
|
2467
|
-
} catch (error) {
|
|
2468
|
-
missing.push({ path: source, message: error.message });
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
return {
|
|
2472
|
-
ok: missing.length === 0,
|
|
2473
|
-
path,
|
|
2474
|
-
root,
|
|
2475
|
-
document: document2,
|
|
2476
|
-
sources: [...sources],
|
|
2477
|
-
missing,
|
|
2478
|
-
specPath: designSpecPath(root),
|
|
2479
|
-
// Guarded source preparation is independent of local render cache state.
|
|
2480
|
-
current: readSource ? null : readJson(join2(root, ".design/current.json"), null)
|
|
2481
|
-
};
|
|
2482
|
-
}
|
|
2483
|
-
function prepareDesignDocument(file, { readSource, passive = false, maxBytes } = {}) {
|
|
2484
|
-
const inspected = inspectDesignDocument(file, { readSource });
|
|
2485
|
-
if (!inspected.ok)
|
|
2486
|
-
throw new Error(inspected.missing.map((item) => `${item.path}: ${item.message}`).join("\n"));
|
|
2487
|
-
const { document: document2, root } = inspected;
|
|
2488
|
-
const reviewContext = loadReviewContext(root, document2, { readSource });
|
|
2489
|
-
const contextDigest = reviewDigest(reviewContext);
|
|
2490
|
-
const fingerprints = [];
|
|
2491
|
-
const artifacts = [], sources = [], entries = [], lint = [], sourceFiles = new Set(inspected.sources);
|
|
2492
|
-
const sourceContents = new Map(
|
|
2493
|
-
inspected.sources.map((source) => [
|
|
2494
|
-
source,
|
|
2495
|
-
readSource ? readSource(source, root).value : readFileSync5(resolveLocalDocumentFile(root, source))
|
|
2496
|
-
])
|
|
2497
|
-
);
|
|
2498
|
-
for (const variant of document2.variants.filter((item) => item.status === "ready")) {
|
|
2499
|
-
const themeWarnings = lintThemeOnlySources(
|
|
2500
|
-
document2.screens.map((screen) => {
|
|
2501
|
-
const source = variant.sources?.[screen.id] ?? screen.source;
|
|
2502
|
-
return {
|
|
2503
|
-
screenId: screen.id,
|
|
2504
|
-
source,
|
|
2505
|
-
html: sourceContents.get(source.html).toString("utf8")
|
|
2506
|
-
};
|
|
2507
|
-
})
|
|
2508
|
-
);
|
|
2509
|
-
for (const screenId of document2.screenOrder) {
|
|
2510
|
-
const screen = document2.screens.find((item) => item.id === screenId);
|
|
2511
|
-
const values = Object.hasOwn(variant.props ?? {}, screenId) ? variant.props[screenId] : {};
|
|
2512
|
-
const bundled = bundleLocalDocument({
|
|
2513
|
-
root,
|
|
2514
|
-
source: variant.sources?.[screenId] ?? screen.source,
|
|
2515
|
-
sharedStyles: document2.designSystem?.tokens ? [document2.designSystem.tokens] : [],
|
|
2516
|
-
screenId,
|
|
2517
|
-
readSource,
|
|
2518
|
-
passive,
|
|
2519
|
-
...screen.props ? {
|
|
2520
|
-
screenProps: Object.fromEntries(
|
|
2521
|
-
Object.entries(screen.props).map(([name, declaration]) => [
|
|
2522
|
-
name,
|
|
2523
|
-
Object.hasOwn(values, name) ? values[name] : declaration.default
|
|
2524
|
-
])
|
|
2525
|
-
)
|
|
2526
|
-
} : {},
|
|
2527
|
-
...maxBytes === void 0 ? {} : { maxBytes }
|
|
2528
|
-
});
|
|
2529
|
-
if (document2.frames.some(({ height }) => height === "auto"))
|
|
2530
|
-
bundled.html = bundled.html.replace(
|
|
2531
|
-
"</body>",
|
|
2532
|
-
`<script>(${observeDesignFrameSize.toString()})()</script></body>`
|
|
2533
|
-
);
|
|
2534
|
-
const navigate = `<script>document.addEventListener('click',function(event){var link=event.target.closest('[data-design-target],[data-planr-navigate],[data-design-navigate]');if(!link)return;event.preventDefault();parent.postMessage({type:'openplanr:design-navigate',screenId:link.getAttribute('data-design-target')||link.getAttribute('data-planr-navigate')||link.getAttribute('data-design-navigate')},'*')});document.addEventListener('submit',function(event){event.preventDefault()});</script>`;
|
|
2535
|
-
if (!passive) bundled.html = bundled.html.replace("</body>", `${navigate}</body>`);
|
|
2536
|
-
for (const name of bundled.files) {
|
|
2537
|
-
sourceFiles.add(name);
|
|
2538
|
-
if (!sourceContents.has(name))
|
|
2539
|
-
sourceContents.set(
|
|
2540
|
-
name,
|
|
2541
|
-
readSource ? readSource(name, root).value : readFileSync5(resolveLocalDocumentFile(root, name))
|
|
2542
|
-
);
|
|
2543
|
-
if (hash(sourceContents.get(name)) !== bundled.sourceDigests[name])
|
|
2544
|
-
throw new Error(
|
|
2545
|
-
`Source ${name} changed during rendering. Retry with the completed source revision.`
|
|
2546
|
-
);
|
|
2547
|
-
}
|
|
2548
|
-
const report = lintDesign(bundled.html, {
|
|
2549
|
-
designSystem: document2.designSystem,
|
|
2550
|
-
screen,
|
|
2551
|
-
variant
|
|
2552
|
-
});
|
|
2553
|
-
report.warnings.push(...themeWarnings.filter((item) => item.screenId === screenId));
|
|
2554
|
-
report.summary = `${report.errors.length} error(s), ${report.warnings.length} warning(s)`;
|
|
2555
|
-
lint.push({ variantId: variant.id, screenId, ...report });
|
|
2556
|
-
for (const anchor of screen.anchors ?? []) {
|
|
2557
|
-
if (!bundled.html.includes(`data-planr-id="${anchor}"`) && !bundled.html.includes(`id="${anchor}"`))
|
|
2558
|
-
throw new Error(`Screen ${screenId} is missing its declared anchor ${anchor}.`);
|
|
2559
|
-
}
|
|
2560
|
-
const sourceId = `source-${hash(`${variant.id}:${screenId}`).slice(0, 32)}`;
|
|
2561
|
-
sources.push({ id: sourceId, kind: "html", html: bundled.html });
|
|
2562
|
-
for (const frame of document2.frames) {
|
|
2563
|
-
fingerprints.push(
|
|
2564
|
-
reviewFingerprints({
|
|
2565
|
-
document: document2,
|
|
2566
|
-
context: reviewContext,
|
|
2567
|
-
screen,
|
|
2568
|
-
variant,
|
|
2569
|
-
frame,
|
|
2570
|
-
sourceDigests: bundled.sourceDigests
|
|
2571
|
-
})
|
|
2572
|
-
);
|
|
2573
|
-
const artifactId = designStudioArtifactId(
|
|
2574
|
-
`${document2.id}-${variant.id}`,
|
|
2575
|
-
screenId,
|
|
2576
|
-
frame.id
|
|
2577
|
-
);
|
|
2578
|
-
artifacts.push({
|
|
2579
|
-
id: artifactId,
|
|
2580
|
-
kind: "html",
|
|
2581
|
-
title: `${screen.title} \xB7 ${variant.label} \xB7 ${frame.label}`,
|
|
2582
|
-
sourceId,
|
|
2583
|
-
viewport: designFrameViewport(document2, frame),
|
|
2584
|
-
colorScheme: "light"
|
|
2585
|
-
});
|
|
2586
|
-
entries.push({
|
|
2587
|
-
artifactId,
|
|
2588
|
-
screenId,
|
|
2589
|
-
variantId: variant.id,
|
|
2590
|
-
frameId: frame.id,
|
|
2591
|
-
...frame.height === "auto" ? { height: "auto" } : {}
|
|
2592
|
-
});
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
const activeArtifactId = [...artifacts].sort((a, b) => a.id.localeCompare(b.id))[0].id;
|
|
2597
|
-
const envelope = createSharedArtifactEnvelope({
|
|
2598
|
-
sources,
|
|
2599
|
-
artifacts,
|
|
2600
|
-
viewer: {
|
|
2601
|
-
mode: artifacts.length > 1 ? "variants" : "single",
|
|
2602
|
-
activeArtifactId,
|
|
2603
|
-
presentation: "canvas"
|
|
2604
|
-
}
|
|
2605
|
-
});
|
|
2606
|
-
const revision = hash(
|
|
2607
|
-
json({ document: document2, contextDigest, digest: digestArtifactEnvelope(envelope) })
|
|
2608
|
-
);
|
|
2609
|
-
return {
|
|
2610
|
-
...inspected,
|
|
2611
|
-
reviewContext,
|
|
2612
|
-
contextDigest,
|
|
2613
|
-
fingerprints,
|
|
2614
|
-
envelope,
|
|
2615
|
-
entries,
|
|
2616
|
-
revision,
|
|
2617
|
-
lint,
|
|
2618
|
-
sourceFiles: [...sourceFiles],
|
|
2619
|
-
sourceContents
|
|
2620
|
-
};
|
|
2621
|
-
}
|
|
2622
|
-
function stageRuntimeBytes() {
|
|
2623
|
-
const stagePath = new URL(
|
|
2624
|
-
"../../../artifact/templates/artifact-review-stage.js",
|
|
2625
|
-
new URL("./runtime/packages/design/lib/design/document.mjs", import.meta.url).href
|
|
2626
|
-
);
|
|
2627
|
-
try {
|
|
2628
|
-
return readRuntimeAsset(stagePath);
|
|
2629
|
-
} catch {
|
|
2630
|
-
return readRuntimeAsset(
|
|
2631
|
-
new URL("../../templates/artifact-review-stage.js", new URL("./runtime/packages/design/lib/design/document.mjs", import.meta.url).href)
|
|
2632
|
-
);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
function designRendererRevision() {
|
|
2636
|
-
return hash(
|
|
2637
|
-
json({
|
|
2638
|
-
version: DESIGN_RENDERER_VERSION,
|
|
2639
|
-
stage: hash(stageRuntimeBytes()),
|
|
2640
|
-
assets: ["studio.css", "studio.js", "enhancements.css", "handoff-center.css"].filter(
|
|
2641
|
-
(name) => name === "studio.js" || existsSync2(new URL(`../../templates/studio/${name}`, new URL("./runtime/packages/design/lib/design/document.mjs", import.meta.url).href))
|
|
2642
|
-
).map(
|
|
2643
|
-
(name) => hash(
|
|
2644
|
-
name === "studio.js" ? readDesignStudioRuntime() : readFileSync5(new URL(`../../templates/studio/${name}`, new URL("./runtime/packages/design/lib/design/document.mjs", import.meta.url).href))
|
|
2645
|
-
)
|
|
2646
|
-
)
|
|
2647
|
-
})
|
|
2648
|
-
);
|
|
2649
|
-
}
|
|
2650
|
-
function standaloneDesignHtml(prepared, view = prepared.document.defaultView, {
|
|
2651
|
-
parentOrigin = "null",
|
|
2652
|
-
sourceTransport = parentOrigin === "null" ? "blob" : "srcdoc",
|
|
2653
|
-
prototypeStateAliases
|
|
2654
|
-
} = {}) {
|
|
2655
|
-
const nonce = createArtifactBridgeNonce();
|
|
2656
|
-
const pool = prepared.envelope.schemaVersion === "1.1.0";
|
|
2657
|
-
const sources = pool ? Object.fromEntries(
|
|
2658
|
-
prepared.envelope.sources.map((source) => [
|
|
2659
|
-
source.id,
|
|
2660
|
-
prepareArtifactSourceTemplate({
|
|
2661
|
-
html: source.html,
|
|
2662
|
-
nonce,
|
|
2663
|
-
parentOrigin,
|
|
2664
|
-
allowLocalForms: true,
|
|
2665
|
-
prototypeState: true,
|
|
2666
|
-
screenId: prepared.entries.find(
|
|
2667
|
-
(entry) => prepared.envelope.artifacts.find((artifact) => artifact.id === entry.artifactId)?.sourceId === source.id
|
|
2668
|
-
)?.screenId ?? source.id
|
|
2669
|
-
})
|
|
2670
|
-
])
|
|
2671
|
-
) : null;
|
|
2672
|
-
const artifacts = pool ? null : Object.fromEntries(
|
|
2673
|
-
prepared.envelope.artifacts.map((artifact) => [
|
|
2674
|
-
artifact.id,
|
|
2675
|
-
prepareArtifactDocument({
|
|
2676
|
-
html: resolveArtifactHtml(prepared.envelope, artifact),
|
|
2677
|
-
artifactId: artifact.id,
|
|
2678
|
-
nonce,
|
|
2679
|
-
parentOrigin,
|
|
2680
|
-
portable: true,
|
|
2681
|
-
allowLocalForms: true,
|
|
2682
|
-
prototypeState: true,
|
|
2683
|
-
screenId: prepared.entries.find((entry) => entry.artifactId === artifact.id)?.screenId ?? artifact.id
|
|
2684
|
-
}).html
|
|
2685
|
-
])
|
|
2686
|
-
);
|
|
2687
|
-
const stage = stageRuntimeBytes();
|
|
2688
|
-
const stageRuntimeUrl = `data:text/javascript;base64,${Buffer.from(stage).toString("base64")}`;
|
|
2689
|
-
const runtime = renderArtifactParentRuntime({
|
|
2690
|
-
nonce,
|
|
2691
|
-
...pool ? {
|
|
2692
|
-
inlineSources: sources,
|
|
2693
|
-
inlineArtifactSources: Object.fromEntries(
|
|
2694
|
-
prepared.envelope.artifacts.map((artifact) => [artifact.id, artifact.sourceId])
|
|
2695
|
-
)
|
|
2696
|
-
} : { inlineArtifacts: artifacts },
|
|
2697
|
-
sourceTransport,
|
|
2698
|
-
frameBudget: 3,
|
|
2699
|
-
stageRuntimeUrl
|
|
2700
|
-
});
|
|
2701
|
-
const configuredRuntime = prototypeStateAliases ? `globalThis.__OPENPLANR_DESIGN_STUDIO_OPTIONS__=${JSON.stringify({ prototypeStateAliases }).replaceAll("<", "\\u003c")};
|
|
2702
|
-
${runtime}` : runtime;
|
|
2703
|
-
const runtimeUrl = `data:text/javascript;base64,${Buffer.from(configuredRuntime).toString("base64")}`;
|
|
2704
|
-
const state = { ...prepared.state, view };
|
|
2705
|
-
if (state.selectedVariant) state.variantId = state.selectedVariant;
|
|
2706
|
-
return renderDesignStudio(
|
|
2707
|
-
{
|
|
2708
|
-
...prepared,
|
|
2709
|
-
state,
|
|
2710
|
-
verification: prepared.verification ?? { status: "unverified" }
|
|
2711
|
-
},
|
|
2712
|
-
{ stageRuntimeUrl: runtimeUrl }
|
|
2713
|
-
);
|
|
2714
|
-
}
|
|
2715
|
-
async function renderDesignDocument(file, options = {}) {
|
|
2716
|
-
return withDesignRenderLock(file, (render) => render(options));
|
|
2717
|
-
}
|
|
2718
|
-
async function withDesignRenderLock(file, action) {
|
|
2719
|
-
const { root } = loadDesignDocument(file);
|
|
2720
|
-
const work = join2(root, ".design");
|
|
2721
|
-
mkdirSync(work, { recursive: true });
|
|
2722
|
-
const release = await acquireStartLock(join2(work, "render.lock"), {
|
|
2723
|
-
timeout: 1e3,
|
|
2724
|
-
stale: 3e4
|
|
2725
|
-
});
|
|
2726
|
-
try {
|
|
2727
|
-
recoverDesignPublication(root, { ownsRenderLock: true });
|
|
2728
|
-
return await action((options) => renderDesignDocumentUnlocked(file, options));
|
|
2729
|
-
} finally {
|
|
2730
|
-
release();
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
async function renderDesignDocumentUnlocked(file, {
|
|
2734
|
-
beforeCommit,
|
|
2735
|
-
writePointer = atomicJson,
|
|
2736
|
-
rendererRevision = designRendererRevision(),
|
|
2737
|
-
now = () => (/* @__PURE__ */ new Date()).toISOString()
|
|
2738
|
-
} = {}) {
|
|
2739
|
-
const { root } = loadDesignDocument(file);
|
|
2740
|
-
const work = join2(root, ".design");
|
|
2741
|
-
let temporary;
|
|
2742
|
-
try {
|
|
2743
|
-
const prepared = prepareDesignDocument(file);
|
|
2744
|
-
const errors = prepared.lint.flatMap(
|
|
2745
|
-
(item) => item.errors.map((error) => `${item.screenId}: ${error.message}`)
|
|
2746
|
-
);
|
|
2747
|
-
if (errors.length) throw new Error(`Design lint failed:
|
|
2748
|
-
${errors.join("\n")}`);
|
|
2749
|
-
const specPath = designSpecPath(root);
|
|
2750
|
-
const spec = readFileSync5(specPath, "utf8");
|
|
2751
|
-
for (let section = 1; section <= 10; section++)
|
|
2752
|
-
if (!new RegExp(`^## ${section}\\. `, "m").test(spec))
|
|
2753
|
-
throw new Error(`design-spec.md is missing section ${section}.`);
|
|
2754
|
-
prepared.revision = hash(json({ source: prepared.revision, spec, rendererRevision }));
|
|
2755
|
-
const directory = join2(work, "revisions", prepared.revision);
|
|
2756
|
-
const previous = readJson(join2(work, "current.json"), null);
|
|
2757
|
-
const generatedAt = now();
|
|
2758
|
-
const manifest = existsSync2(directory) ? readJson(join2(directory, "render.json")).manifest : buildManifest({
|
|
2759
|
-
framework: "vanilla",
|
|
2760
|
-
designFormat: prepared.document.defaultView,
|
|
2761
|
-
source: prepared.document.brief.source,
|
|
2762
|
-
contentProvenance: prepared.document.brief.provenance,
|
|
2763
|
-
generatedAt,
|
|
2764
|
-
screens: prepared.document.screenOrder.map(
|
|
2765
|
-
(id) => prepared.document.screens.find((item) => item.id === id).title
|
|
2766
|
-
),
|
|
2767
|
-
iterations: previous && previous.revision !== prepared.revision ? (previous.iterations ?? 0) + 1 : previous?.iterations ?? 0,
|
|
2768
|
-
htmlFile: `.design/revisions/${prepared.revision}/${prepared.document.defaultView}.html`
|
|
2769
|
-
});
|
|
2770
|
-
const record = {
|
|
2771
|
-
reviewContext: prepared.reviewContext,
|
|
2772
|
-
contextDigest: prepared.contextDigest,
|
|
2773
|
-
fingerprints: prepared.fingerprints,
|
|
2774
|
-
document: prepared.document,
|
|
2775
|
-
envelope: prepared.envelope,
|
|
2776
|
-
entries: prepared.entries,
|
|
2777
|
-
revision: prepared.revision,
|
|
2778
|
-
rendererRevision,
|
|
2779
|
-
lint: prepared.lint,
|
|
2780
|
-
manifest
|
|
2781
|
-
};
|
|
2782
|
-
if (!existsSync2(directory)) {
|
|
2783
|
-
temporary = join2(work, `pending-${randomUUID()}`);
|
|
2784
|
-
mkdirSync(join2(temporary, "sources"), { recursive: true });
|
|
2785
|
-
writeFileSync(join2(temporary, "render.json"), json(record));
|
|
2786
|
-
writeFileSync(join2(temporary, "design-document.json"), json(prepared.document));
|
|
2787
|
-
writeFileSync(join2(temporary, "design-spec.md"), spec);
|
|
2788
|
-
for (const source of prepared.sourceFiles) {
|
|
2789
|
-
const destination = join2(temporary, "sources", source);
|
|
2790
|
-
mkdirSync(dirname(destination), { recursive: true });
|
|
2791
|
-
writeFileSync(destination, prepared.sourceContents.get(source));
|
|
2792
|
-
}
|
|
2793
|
-
for (const view of DESIGN_VIEWS)
|
|
2794
|
-
writeFileSync(join2(temporary, `${view}.html`), standaloneDesignHtml(record, view));
|
|
2795
|
-
beforeCommit?.(record);
|
|
2796
|
-
mkdirSync(dirname(directory), { recursive: true });
|
|
2797
|
-
renameSync(temporary, directory);
|
|
2798
|
-
temporary = null;
|
|
2799
|
-
}
|
|
2800
|
-
const manifestPath = join2(root, "finalized.json");
|
|
2801
|
-
atomicJson(join2(work, "publication.json"), {
|
|
2802
|
-
revision: prepared.revision,
|
|
2803
|
-
previousRevision: previous?.revision ?? null,
|
|
2804
|
-
manifest,
|
|
2805
|
-
previousManifest: existsSync2(manifestPath) ? readFileSync5(manifestPath).toString("base64") : null
|
|
2806
|
-
});
|
|
2807
|
-
try {
|
|
2808
|
-
atomicJson(manifestPath, manifest);
|
|
2809
|
-
writePointer(join2(work, "current.json"), {
|
|
2810
|
-
revision: prepared.revision,
|
|
2811
|
-
previousRevision: previous?.revision === prepared.revision ? previous.previousRevision : previous?.revision ?? null,
|
|
2812
|
-
iterations: manifest.iterations,
|
|
2813
|
-
generatedAt: manifest.generated_at
|
|
2814
|
-
});
|
|
2815
|
-
rmSync(join2(work, "publication.json"), { force: true });
|
|
2816
|
-
} catch (error) {
|
|
2817
|
-
recoverDesignPublication(root, { ownsRenderLock: true });
|
|
2818
|
-
throw error;
|
|
2819
|
-
}
|
|
2820
|
-
return {
|
|
2821
|
-
ok: true,
|
|
2822
|
-
revision: prepared.revision,
|
|
2823
|
-
document: resolve2(file),
|
|
2824
|
-
artifact: join2(directory, `${prepared.document.defaultView}.html`),
|
|
2825
|
-
views: Object.fromEntries(
|
|
2826
|
-
DESIGN_VIEWS.map((view) => [view, join2(directory, `${view}.html`)])
|
|
2827
|
-
),
|
|
2828
|
-
manifest: join2(root, "finalized.json"),
|
|
2829
|
-
spec: specPath,
|
|
2830
|
-
verification: "unverified"
|
|
2831
|
-
};
|
|
2832
|
-
} finally {
|
|
2833
|
-
if (temporary) rmSync(temporary, { recursive: true, force: true });
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
|
|
2837
|
-
// packages/design/lib/design/layout-save.mjs
|
|
2838
|
-
async function saveDesignLayout(file, input) {
|
|
2839
|
-
if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).some((key) => !["revision", "boards", "restoreRevision"].includes(key)) || typeof input.revision !== "string" || Object.hasOwn(input, "boards") === Object.hasOwn(input, "restoreRevision"))
|
|
2840
|
-
throw new Error("Design layout requires a revision and either boards or restoreRevision.");
|
|
2841
|
-
return withDesignRenderLock(file, async (render) => {
|
|
2842
|
-
const current = currentDesign(file);
|
|
2843
|
-
if (input.revision !== current.revision)
|
|
2844
|
-
throw Object.assign(new Error("The design changed. Reload before saving its layout."), {
|
|
2845
|
-
statusCode: 409
|
|
2846
|
-
});
|
|
2847
|
-
const authored = prepareDesignDocument(file);
|
|
2848
|
-
const authoredContent = { ...authored.document };
|
|
2849
|
-
const committedContent = { ...current.document };
|
|
2850
|
-
delete authoredContent.layout;
|
|
2851
|
-
delete committedContent.layout;
|
|
2852
|
-
if (digestArtifactEnvelope(authored.envelope) !== digestArtifactEnvelope(current.envelope) || JSON.stringify(authored.fingerprints) !== JSON.stringify(current.fingerprints) || JSON.stringify(authoredContent) !== JSON.stringify(committedContent) || readFileSync6(designSpecPath(current.root), "utf8") !== readFileSync6(join3(current.directory, "design-spec.md"), "utf8"))
|
|
2853
|
-
throw Object.assign(
|
|
2854
|
-
new Error(
|
|
2855
|
-
"Design sources or content changed. Render the design and reload before saving its layout."
|
|
2856
|
-
),
|
|
2857
|
-
{ statusCode: 409 }
|
|
2858
|
-
);
|
|
2859
|
-
const previous = readFileSync6(file, "utf8");
|
|
2860
|
-
const mode = statSync(file).mode & 4095;
|
|
2861
|
-
const document2 = JSON.parse(previous);
|
|
2862
|
-
if (Object.hasOwn(input, "restoreRevision")) {
|
|
2863
|
-
if (typeof input.restoreRevision !== "string" || !/^[a-f0-9]{64}$/u.test(input.restoreRevision))
|
|
2864
|
-
throw new Error(`Invalid layout restore revision ${input.restoreRevision}.`);
|
|
2865
|
-
const restored = readJson(
|
|
2866
|
-
join3(current.root, ".design/revisions", input.restoreRevision, "design-document.json")
|
|
2867
|
-
);
|
|
2868
|
-
if (restored.layout === void 0) delete document2.layout;
|
|
2869
|
-
else document2.layout = structuredClone(restored.layout);
|
|
2870
|
-
} else {
|
|
2871
|
-
if (!input.boards || typeof input.boards !== "object" || Array.isArray(input.boards))
|
|
2872
|
-
throw new Error("Design layout boards must be an object of exact board keys.");
|
|
2873
|
-
const boards = { ...document2.layout?.boards };
|
|
2874
|
-
for (const [key, point] of Object.entries(input.boards)) {
|
|
2875
|
-
if (!current.entries.some(
|
|
2876
|
-
(entry) => `${entry.screenId}/${entry.frameId}/${entry.variantId}` === key
|
|
2877
|
-
))
|
|
2878
|
-
throw new Error(`Unknown exact design board key ${key}.`);
|
|
2879
|
-
if (point === null) delete boards[key];
|
|
2880
|
-
else {
|
|
2881
|
-
if (!point || typeof point !== "object" || Array.isArray(point) || Object.keys(point).some((field) => !["x", "y"].includes(field)) || !Number.isFinite(point.x) || !Number.isFinite(point.y) || Math.abs(point.x) > 1e7 || Math.abs(point.y) > 1e7)
|
|
2882
|
-
throw new Error(
|
|
2883
|
-
`Design board ${key} requires finite x and y coordinates within \xB110000000.`
|
|
2884
|
-
);
|
|
2885
|
-
boards[key] = { ...boards[key], x: point.x, y: point.y };
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
document2.layout = { ...document2.layout, boards };
|
|
2889
|
-
if (!Object.keys(boards).length) delete document2.layout.boards;
|
|
2890
|
-
if (!Object.keys(document2.layout).length) delete document2.layout;
|
|
2891
|
-
}
|
|
2892
|
-
assertDesignDocument(document2);
|
|
2893
|
-
const indent = previous.match(/^([\t ]+)\S/mu)?.[1] ?? "";
|
|
2894
|
-
const formatted = indent ? JSON.stringify(document2, null, " ").replace(
|
|
2895
|
-
/^\t+/gmu,
|
|
2896
|
-
(tabs) => indent.repeat(tabs.length)
|
|
2897
|
-
) : JSON.stringify(document2);
|
|
2898
|
-
const written = `${formatted}${previous.endsWith("\n") ? "\n" : ""}`;
|
|
2899
|
-
atomicBytes(file, written, mode);
|
|
2900
|
-
try {
|
|
2901
|
-
const result = await render();
|
|
2902
|
-
return { revision: result.revision };
|
|
2903
|
-
} catch (error) {
|
|
2904
|
-
if (hash(readFileSync6(file)) !== hash(written))
|
|
2905
|
-
throw Object.assign(
|
|
2906
|
-
new Error(
|
|
2907
|
-
`The authored document ${file} changed during the save. Its newer contents were preserved; reload before saving its layout.`,
|
|
2908
|
-
{ cause: error }
|
|
2909
|
-
),
|
|
2910
|
-
{ statusCode: 409 }
|
|
2911
|
-
);
|
|
2912
|
-
atomicBytes(file, previous, mode);
|
|
2913
|
-
throw error;
|
|
2914
|
-
}
|
|
2915
|
-
});
|
|
2916
|
-
}
|
|
2917
|
-
|
|
2918
|
-
export {
|
|
2919
|
-
mintCapabilityToken,
|
|
2920
|
-
isCapabilityToken,
|
|
2921
|
-
timingSafeTokenEqual,
|
|
2922
|
-
prepareArtifactDocument,
|
|
2923
|
-
createArtifactBridgeNonce,
|
|
2924
|
-
renderArtifactParentRuntime,
|
|
2925
|
-
matchesDesignFrame,
|
|
2926
|
-
readRuntimeAsset,
|
|
2927
|
-
renderDesignStudio,
|
|
2928
|
-
saveDesignLayout,
|
|
2929
|
-
saveDesignViewContext,
|
|
2930
|
-
readDesignViewContext,
|
|
2931
|
-
inspectDesignDocument,
|
|
2932
|
-
prepareDesignDocument,
|
|
2933
|
-
designRendererRevision,
|
|
2934
|
-
standaloneDesignHtml,
|
|
2935
|
-
renderDesignDocument,
|
|
2936
|
-
withDesignRenderLock
|
|
2937
|
-
};
|