@agent-native/core 0.127.1 → 0.128.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +92 -0
- package/corpus/core/README.md +5 -26
- package/corpus/core/docs/content/actions.mdx +47 -4
- package/corpus/core/docs/content/native-chat-ui.mdx +7 -0
- package/corpus/core/docs/content/organizations-teams-permissions.mdx +109 -3
- package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
- package/corpus/core/docs/content/toolkit-org-team.mdx +72 -12
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +36 -29
- package/corpus/core/src/action.ts +83 -2
- package/corpus/core/src/chat-threads/store.ts +48 -43
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
- package/corpus/core/src/cli/template-sync.ts +96 -0
- package/corpus/core/src/client/AgentPanel.tsx +26 -6
- package/corpus/core/src/client/AssistantChat.tsx +63 -7
- package/corpus/core/src/client/DefaultSpinner.tsx +29 -0
- package/corpus/core/src/client/org/TeamPage.tsx +161 -6
- package/corpus/core/src/client/org/hooks.ts +67 -0
- package/corpus/core/src/client/org/index.ts +9 -0
- package/corpus/core/src/client/transcription/use-live-transcription.ts +62 -10
- package/corpus/core/src/db/client.ts +42 -3
- package/corpus/core/src/db/ensure-additive-columns.ts +60 -33
- package/corpus/core/src/deploy/build.ts +31 -2
- package/corpus/core/src/mcp-client/routes.ts +10 -1
- package/corpus/core/src/org/app-roles-handlers.ts +152 -0
- package/corpus/core/src/org/app-roles.ts +355 -0
- package/corpus/core/src/org/index.ts +27 -1
- package/corpus/core/src/org/migrations.ts +22 -0
- package/corpus/core/src/org/plugin.ts +27 -0
- package/corpus/core/src/org/schema.ts +15 -0
- package/corpus/core/src/scripts/docs/search.ts +17 -0
- package/corpus/core/src/scripts/docs/source-search.ts +10 -0
- package/corpus/core/src/server/agent-chat/script-entries.ts +3 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +53 -52
- package/corpus/core/src/server/builder-design-systems.ts +1 -1
- package/corpus/core/src/server/core-routes-plugin.ts +6 -1
- package/corpus/core/src/server/framework-request-handler.ts +44 -6
- package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/analytics/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/analytics/actions/data-source-status.ts +21 -12
- package/corpus/templates/analytics/app/global.css +7 -7
- package/corpus/templates/analytics/app/i18n-data.ts +30 -0
- package/corpus/templates/analytics/app/lib/data-source-status.ts +2 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +54 -10
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-pages-load-reliably-with-large-dashboard-workspaces.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-28-dashboard-loading-shimmer-now-flows-smoothly-in-one-directi.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +5 -4
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +54 -42
- package/corpus/templates/analytics/server/plugins/db.ts +1 -18
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/chat/_gitignore +2 -0
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/clips/actions/request-transcript.ts +77 -11
- package/corpus/templates/clips/actions/save-browser-transcript.ts +28 -7
- package/corpus/templates/clips/app/routes/record.tsx +11 -3
- package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-save-a-truncated-transcript-as-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-stop-transcribing-partway-throu.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-desktop-full-screen-clips-now-get-an-instant-local-transcrip.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-long-transcriptions-no-longer-show-a-false-transcription-sto.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-restarting-for-an-update-now-installs-the-newest-release-ins.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-the-meeting-recording-pill-now-anchors-to-the-right-edge-of-.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +31 -2
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +114 -13
- package/corpus/templates/clips/desktop/src/lib/updater.ts +12 -4
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +40 -15
- package/corpus/templates/clips/desktop/src/styles.css +10 -7
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -14
- package/corpus/templates/clips/shared/transcript-status.ts +10 -0
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -0
- package/corpus/templates/design/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +46 -3
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +7 -5
- package/corpus/templates/design/actions/create-file.ts +15 -0
- package/corpus/templates/design/actions/generate-design.ts +40 -0
- package/corpus/templates/design/actions/present-design-variants.ts +16 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -4
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +10 -4
- package/corpus/templates/design/app/lib/design-ui-events.ts +5 -0
- package/corpus/templates/design/app/pages/design-editor/save-failure.ts +7 -1
- package/corpus/templates/design/app/root.tsx +18 -1
- package/corpus/templates/design/changelog/2026-07-28-generated-designs-with-malformed-html-are-now-rejected-at-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +6 -0
- package/corpus/templates/design/server/source-workspace.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +608 -18
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -6
- package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/tasks/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +4 -0
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +30 -29
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/action.d.ts +36 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +37 -2
- package/dist/action.js.map +1 -1
- package/dist/chat-threads/store.d.ts +12 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +48 -42
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +46 -3
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/cli/template-sync.d.ts.map +1 -1
- package/dist/cli/template-sync.js +88 -0
- package/dist/cli/template-sync.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +11 -9
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +57 -6
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/DefaultSpinner.d.ts +7 -0
- package/dist/client/DefaultSpinner.d.ts.map +1 -1
- package/dist/client/DefaultSpinner.js +25 -1
- package/dist/client/DefaultSpinner.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts +13 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +41 -11
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/hooks.d.ts +37 -0
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +37 -0
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +3 -2
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +1 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/transcription/use-live-transcription.d.ts +8 -0
- package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
- package/dist/client/transcription/use-live-transcription.js +38 -11
- package/dist/client/transcription/use-live-transcription.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/client.d.ts +12 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +40 -3
- package/dist/db/client.js.map +1 -1
- package/dist/db/ensure-additive-columns.d.ts.map +1 -1
- package/dist/db/ensure-additive-columns.js +54 -32
- package/dist/db/ensure-additive-columns.js.map +1 -1
- package/dist/deploy/build.d.ts +1 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +23 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +3 -1
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +8 -2
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/org/app-roles-handlers.d.ts +27 -0
- package/dist/org/app-roles-handlers.d.ts.map +1 -0
- package/dist/org/app-roles-handlers.js +125 -0
- package/dist/org/app-roles-handlers.js.map +1 -0
- package/dist/org/app-roles.d.ts +136 -0
- package/dist/org/app-roles.d.ts.map +1 -0
- package/dist/org/app-roles.js +216 -0
- package/dist/org/app-roles.js.map +1 -0
- package/dist/org/index.d.ts +4 -1
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +3 -1
- package/dist/org/index.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +22 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/plugin.d.ts +2 -0
- package/dist/org/plugin.d.ts.map +1 -1
- package/dist/org/plugin.js +20 -0
- package/dist/org/plugin.js.map +1 -1
- package/dist/org/schema.d.ts +143 -0
- package/dist/org/schema.d.ts.map +1 -1
- package/dist/org/schema.js +14 -0
- package/dist/org/schema.js.map +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/docs/search.d.ts.map +1 -1
- package/dist/scripts/docs/search.js +15 -0
- package/dist/scripts/docs/search.js.map +1 -1
- package/dist/scripts/docs/source-search.d.ts +7 -0
- package/dist/scripts/docs/source-search.d.ts.map +1 -1
- package/dist/scripts/docs/source-search.js +9 -0
- package/dist/scripts/docs/source-search.js.map +1 -1
- package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
- package/dist/server/agent-chat/script-entries.js +3 -0
- package/dist/server/agent-chat/script-entries.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +46 -48
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.js +1 -1
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +6 -1
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +42 -6
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/dist/templates/chat/_gitignore +2 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/docs/content/actions.mdx +47 -4
- package/docs/content/native-chat-ui.mdx +7 -0
- package/docs/content/organizations-teams-permissions.mdx +109 -3
- package/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
- package/docs/content/toolkit-org-team.mdx +72 -12
- package/package.json +1 -1
- package/src/a2a/artifact-response.ts +36 -29
- package/src/action.ts +83 -2
- package/src/chat-threads/store.ts +48 -43
- package/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
- package/src/cli/template-sync.ts +96 -0
- package/src/client/AgentPanel.tsx +26 -6
- package/src/client/AssistantChat.tsx +63 -7
- package/src/client/DefaultSpinner.tsx +29 -0
- package/src/client/org/TeamPage.tsx +161 -6
- package/src/client/org/hooks.ts +67 -0
- package/src/client/org/index.ts +9 -0
- package/src/client/transcription/use-live-transcription.ts +62 -10
- package/src/db/client.ts +42 -3
- package/src/db/ensure-additive-columns.ts +60 -33
- package/src/deploy/build.ts +31 -2
- package/src/mcp-client/routes.ts +10 -1
- package/src/org/app-roles-handlers.ts +152 -0
- package/src/org/app-roles.ts +355 -0
- package/src/org/index.ts +27 -1
- package/src/org/migrations.ts +22 -0
- package/src/org/plugin.ts +27 -0
- package/src/org/schema.ts +15 -0
- package/src/scripts/docs/search.ts +17 -0
- package/src/scripts/docs/source-search.ts +10 -0
- package/src/server/agent-chat/script-entries.ts +3 -0
- package/src/server/agent-chat-plugin.ts +53 -52
- package/src/server/builder-design-systems.ts +1 -1
- package/src/server/core-routes-plugin.ts +6 -1
- package/src/server/framework-request-handler.ts +44 -6
- package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/src/templates/chat/_gitignore +2 -0
- package/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +0 -530
- package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +0 -191
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +0 -115
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
- package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +0 -221
- package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +0 -220
- package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +0 -263
- package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +0 -174
- package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +0 -232
- package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +0 -282
- package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +0 -119
- package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +0 -123
- package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -122
- package/corpus/core/src/templates/chat/.env.example +0 -5
- package/corpus/core/src/templates/chat/.ignore +0 -0
- package/corpus/core/src/templates/chat/.oxfmtrc.json +0 -8
- package/corpus/core/src/templates/chat/AGENTS.md +0 -77
- package/corpus/core/src/templates/chat/CHANGELOG.md +0 -10
- package/corpus/core/src/templates/chat/DEVELOPING.md +0 -185
- package/corpus/core/src/templates/chat/README.md +0 -32
- package/corpus/core/src/templates/chat/_gitignore +0 -44
- package/corpus/core/src/templates/chat/actions/hello.ts +0 -13
- package/corpus/core/src/templates/chat/actions/navigate.ts +0 -41
- package/corpus/core/src/templates/chat/actions/run.ts +0 -2
- package/corpus/core/src/templates/chat/actions/view-screen.ts +0 -31
- package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +0 -60
- package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +0 -188
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -505
- package/corpus/core/src/templates/chat/app/components/ui/button.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/card.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/input.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/label.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +0 -17
- package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +0 -1
- package/corpus/core/src/templates/chat/app/design-system.ts +0 -3
- package/corpus/core/src/templates/chat/app/entry.client.tsx +0 -22
- package/corpus/core/src/templates/chat/app/entry.server.tsx +0 -10
- package/corpus/core/src/templates/chat/app/global.css +0 -93
- package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +0 -93
- package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +0 -68
- package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/en-US.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +0 -69
- package/corpus/core/src/templates/chat/app/i18n/index.ts +0 -34
- package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +0 -66
- package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +0 -66
- package/corpus/core/src/templates/chat/app/i18n-data.ts +0 -556
- package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +0 -49
- package/corpus/core/src/templates/chat/app/lib/app-config.ts +0 -11
- package/corpus/core/src/templates/chat/app/lib/tab-id.ts +0 -1
- package/corpus/core/src/templates/chat/app/lib/utils.ts +0 -1
- package/corpus/core/src/templates/chat/app/root.tsx +0 -173
- package/corpus/core/src/templates/chat/app/routes/_index.tsx +0 -94
- package/corpus/core/src/templates/chat/app/routes/agent.tsx +0 -24
- package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +0 -1
- package/corpus/core/src/templates/chat/app/routes/database.tsx +0 -17
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +0 -2
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes/extensions.tsx +0 -5
- package/corpus/core/src/templates/chat/app/routes/observability.tsx +0 -19
- package/corpus/core/src/templates/chat/app/routes/settings.tsx +0 -89
- package/corpus/core/src/templates/chat/app/routes/team.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes.ts +0 -4
- package/corpus/core/src/templates/chat/app/vite-env.d.ts +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +0 -5
- package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +0 -6
- package/corpus/core/src/templates/chat/components.json +0 -20
- package/corpus/core/src/templates/chat/learnings.defaults.md +0 -5
- package/corpus/core/src/templates/chat/netlify.toml +0 -11
- package/corpus/core/src/templates/chat/package.json +0 -92
- package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +0 -10
- package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +0 -10
- package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +0 -21
- package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +0 -21
- package/corpus/core/src/templates/chat/public/favicon.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-180.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-192.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-512.svg +0 -1
- package/corpus/core/src/templates/chat/public/manifest.json +0 -21
- package/corpus/core/src/templates/chat/react-router.config.ts +0 -7
- package/corpus/core/src/templates/chat/server/middleware/auth.ts +0 -15
- package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +0 -21
- package/corpus/core/src/templates/chat/server/plugins/auth.ts +0 -17
- package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +0 -5
- package/corpus/core/src/templates/chat/ssr-entry.ts +0 -15
- package/corpus/core/src/templates/chat/tsconfig.json +0 -21
- package/corpus/core/src/templates/chat/vite.config.ts +0 -19
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export const DESIGN_HTML_INTEGRITY_ERROR_CODE = "DESIGN_HTML_INTEGRITY";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Human-facing summary for the editor toast. `message` carries the located,
|
|
9
|
+
* agent-facing detail instead — a person dragging on the canvas did not author
|
|
10
|
+
* the markup, so a line and column are noise to them.
|
|
11
|
+
*/
|
|
12
|
+
export const DESIGN_HTML_INTEGRITY_SUMMARY =
|
|
13
|
+
"The edit was not applied because it would make the design HTML invalid.";
|
|
14
|
+
|
|
7
15
|
export type DesignHtmlIntegrityIssue =
|
|
8
16
|
| "document-boundary"
|
|
9
17
|
| "document-root"
|
|
@@ -11,27 +19,146 @@ export type DesignHtmlIntegrityIssue =
|
|
|
11
19
|
| "document-head"
|
|
12
20
|
| "raw-text-balance"
|
|
13
21
|
| "managed-marker-orphaned"
|
|
14
|
-
| "managed-marker-duplicated"
|
|
22
|
+
| "managed-marker-duplicated"
|
|
23
|
+
| "attribute-unterminated"
|
|
24
|
+
| "element-unclosed"
|
|
25
|
+
| "close-tag-orphaned"
|
|
26
|
+
| "content-truncated"
|
|
27
|
+
| "runtime-missing";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Reporting the symptom instead of the cause sends the fix to the wrong line:
|
|
31
|
+
* an unterminated quote in `<head>` swallows the root tags, which reads as a
|
|
32
|
+
* missing `<html>` unless the quote itself is named.
|
|
33
|
+
*/
|
|
34
|
+
export interface DesignHtmlIntegrityIssueDetail {
|
|
35
|
+
issue: DesignHtmlIntegrityIssue;
|
|
36
|
+
/** 1-based. */
|
|
37
|
+
line: number;
|
|
38
|
+
/** 1-based. */
|
|
39
|
+
column: number;
|
|
40
|
+
/** The offending source line, bounded for readability. */
|
|
41
|
+
excerpt: string;
|
|
42
|
+
tag?: string;
|
|
43
|
+
attribute?: string;
|
|
44
|
+
/** The tag that arrived where this element's close belonged, if any. */
|
|
45
|
+
closedBy?: { tag: string; line: number };
|
|
46
|
+
}
|
|
15
47
|
|
|
16
48
|
export interface DesignHtmlIntegrityResult {
|
|
17
49
|
valid: boolean;
|
|
18
50
|
issue?: DesignHtmlIntegrityIssue;
|
|
51
|
+
/** Present only when invalid; first entry corresponds to `issue`. */
|
|
52
|
+
detail?: DesignHtmlIntegrityIssueDetail[];
|
|
53
|
+
/** Present only when non-empty. Never blocks a write. */
|
|
54
|
+
advisory?: DesignHtmlIntegrityIssueDetail[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Cap cascades: one unclosed tag can leave a dozen ancestors unbalanced. */
|
|
58
|
+
const MAX_REPORTED_ISSUES = 3;
|
|
59
|
+
|
|
60
|
+
const DOCUMENT_SHAPE_MESSAGES: Partial<
|
|
61
|
+
Record<DesignHtmlIntegrityIssue, string>
|
|
62
|
+
> = {
|
|
63
|
+
"document-root":
|
|
64
|
+
"the document must have exactly one <html> element with a matching </html>",
|
|
65
|
+
"document-body":
|
|
66
|
+
"the document must have exactly one <body> element with a matching </body>",
|
|
67
|
+
"document-head":
|
|
68
|
+
"the document must have at most one <head> element, with a matching </head> if present",
|
|
69
|
+
"document-boundary":
|
|
70
|
+
"the document's <html>/<body> tags are out of order, or content sits outside <html>",
|
|
71
|
+
"raw-text-balance":
|
|
72
|
+
"a <style>, <script>, <textarea>, or <title> element is missing its opening or closing tag",
|
|
73
|
+
"managed-marker-orphaned":
|
|
74
|
+
"an editor-managed <style>/<script> marker is no longer attached to its element — it was likely split by a partial edit",
|
|
75
|
+
"managed-marker-duplicated":
|
|
76
|
+
"an editor-managed <style>/<script> block appears more than once; there must be exactly one of each",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export function describeDesignHtmlIntegrityIssue(
|
|
80
|
+
detail: DesignHtmlIntegrityIssueDetail,
|
|
81
|
+
): string {
|
|
82
|
+
const at = `line ${detail.line} col ${detail.column}`;
|
|
83
|
+
switch (detail.issue) {
|
|
84
|
+
case "attribute-unterminated":
|
|
85
|
+
return (
|
|
86
|
+
`the ${detail.attribute ? `\`${detail.attribute}\`` : "attribute"} value on ` +
|
|
87
|
+
`<${detail.tag ?? "element"}> at ${at} is never closed. The HTML parser absorbs ` +
|
|
88
|
+
`everything after it into that attribute — including any markup, <style>, or ` +
|
|
89
|
+
`<script> that follows — so the rest of the document silently stops applying. ` +
|
|
90
|
+
`Close the quote.`
|
|
91
|
+
);
|
|
92
|
+
case "element-unclosed":
|
|
93
|
+
return detail.closedBy
|
|
94
|
+
? `<${detail.tag}> opened at ${at} is never closed; the next closing tag is ` +
|
|
95
|
+
`</${detail.closedBy.tag}> on line ${detail.closedBy.line}, which belongs to an ` +
|
|
96
|
+
`ancestor. Everything between them gets nested inside <${detail.tag}>. ` +
|
|
97
|
+
`Add the missing </${detail.tag}>.`
|
|
98
|
+
: `<${detail.tag}> opened at ${at} is never closed before the document ends. ` +
|
|
99
|
+
`Add the missing </${detail.tag}>.`;
|
|
100
|
+
case "close-tag-orphaned":
|
|
101
|
+
return (
|
|
102
|
+
`</${detail.tag}> at ${at} closes an element that was never opened. ` +
|
|
103
|
+
`Remove the stray closing tag, or add the matching <${detail.tag}>.`
|
|
104
|
+
);
|
|
105
|
+
case "content-truncated":
|
|
106
|
+
return (
|
|
107
|
+
`the content ends mid-markup at ${at} — the final tag or comment is never ` +
|
|
108
|
+
`terminated. This is the signature of a payload that was cut off in transit; ` +
|
|
109
|
+
`re-send this file complete.`
|
|
110
|
+
);
|
|
111
|
+
case "runtime-missing":
|
|
112
|
+
return (
|
|
113
|
+
`no Tailwind runtime is reachable from this document (expected a ` +
|
|
114
|
+
`<script src="…@tailwindcss/browser@4"> or a <style type="text/tailwindcss">). ` +
|
|
115
|
+
`Utility classes will not apply and the design renders unstyled.`
|
|
116
|
+
);
|
|
117
|
+
default:
|
|
118
|
+
return `the document structure is invalid (${detail.issue}) at ${at}.`;
|
|
119
|
+
}
|
|
19
120
|
}
|
|
20
121
|
|
|
21
122
|
export class DesignHtmlIntegrityError extends Error {
|
|
22
123
|
readonly code = DESIGN_HTML_INTEGRITY_ERROR_CODE;
|
|
23
124
|
readonly status = 422;
|
|
24
125
|
readonly issue: DesignHtmlIntegrityIssue;
|
|
126
|
+
readonly detail?: DesignHtmlIntegrityIssueDetail[];
|
|
25
127
|
|
|
26
|
-
constructor(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
128
|
+
constructor(
|
|
129
|
+
issue: DesignHtmlIntegrityIssue,
|
|
130
|
+
options: {
|
|
131
|
+
filename?: string;
|
|
132
|
+
detail?: DesignHtmlIntegrityIssueDetail[];
|
|
133
|
+
} = {},
|
|
134
|
+
) {
|
|
135
|
+
const where = options.filename ? `${options.filename}: ` : "";
|
|
136
|
+
const explained = options.detail?.length
|
|
137
|
+
? options.detail
|
|
138
|
+
.map(
|
|
139
|
+
(entry) =>
|
|
140
|
+
`${describeDesignHtmlIntegrityIssue(entry)}\n\n ${entry.line} | ${entry.excerpt}`,
|
|
141
|
+
)
|
|
142
|
+
.join("\n\n")
|
|
143
|
+
: // Whole-document properties have no single offending character, but must
|
|
144
|
+
// still name which property failed.
|
|
145
|
+
`${DOCUMENT_SHAPE_MESSAGES[issue] ?? "the design HTML is invalid"}. The write was not applied.`;
|
|
146
|
+
super(`${DESIGN_HTML_INTEGRITY_ERROR_CODE}: ${where}${explained}`);
|
|
30
147
|
this.name = "DesignHtmlIntegrityError";
|
|
31
148
|
this.issue = issue;
|
|
149
|
+
this.detail = options.detail;
|
|
32
150
|
}
|
|
33
151
|
}
|
|
34
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Bodies are text, not markup — mis-tokenizing these turns a `'</div>'` string
|
|
155
|
+
* inside Alpine JavaScript into a phantom structural error. Both passes must
|
|
156
|
+
* agree on this set: when only one treated `<title>`/`<textarea>` as raw text,
|
|
157
|
+
* literal `<body>` text inside a title reached the root-tag count as real markup.
|
|
158
|
+
*/
|
|
159
|
+
const RAW_TEXT_TAGS = new Set(["script", "style", "textarea", "title"]);
|
|
160
|
+
type RawTextTag = "script" | "style" | "textarea" | "title";
|
|
161
|
+
|
|
35
162
|
const MANAGED_RAW_TEXT_MARKERS = [
|
|
36
163
|
{ marker: "data-agent-native-breakpoints", tag: "style" },
|
|
37
164
|
{ marker: "data-agent-native-state-breakpoints", tag: "style" },
|
|
@@ -197,7 +324,7 @@ function scanRawTextTags(value: string): RawTextScan {
|
|
|
197
324
|
// seen, ignore every tag-like token except that element's own closer. This
|
|
198
325
|
// mirrors browser tokenization closely enough to avoid rejecting code-heavy
|
|
199
326
|
// Alpine documents while still detecting an orphaned closer/missing opener.
|
|
200
|
-
let active:
|
|
327
|
+
let active: RawTextTag | null = null;
|
|
201
328
|
let bodyStart = -1;
|
|
202
329
|
let severity = 0;
|
|
203
330
|
const bodyRanges: RawTextScan["bodyRanges"] = [];
|
|
@@ -206,7 +333,7 @@ function scanRawTextTags(value: string): RawTextScan {
|
|
|
206
333
|
if (active) {
|
|
207
334
|
// HTML raw-text elements terminate at the first matching end-tag token,
|
|
208
335
|
// even when that text happens to look like a JavaScript/CSS string.
|
|
209
|
-
const closer = new RegExp(`<\\s*\\/\\s*${active}\\
|
|
336
|
+
const closer = new RegExp(`<\\s*\\/\\s*${active}(?=[\\s/>])[^>]*>`, "gi");
|
|
210
337
|
closer.lastIndex = cursor;
|
|
211
338
|
const match = closer.exec(value);
|
|
212
339
|
if (!match) break;
|
|
@@ -243,10 +370,10 @@ function scanRawTextTags(value: string): RawTextScan {
|
|
|
243
370
|
}
|
|
244
371
|
}
|
|
245
372
|
const token = value.slice(nextOpen, tokenEnd);
|
|
246
|
-
const match = token.match(/^<\s*(\/?)\s*(
|
|
247
|
-
if (match) {
|
|
373
|
+
const match = token.match(/^<\s*(\/?)\s*([a-zA-Z][a-zA-Z0-9:-]*)\b/i);
|
|
374
|
+
if (match && RAW_TEXT_TAGS.has(match[2]!.toLowerCase())) {
|
|
248
375
|
const closing = match[1] === "/";
|
|
249
|
-
const tag = match[2]!.toLowerCase() as
|
|
376
|
+
const tag = match[2]!.toLowerCase() as RawTextTag;
|
|
250
377
|
if (closing) severity += 1;
|
|
251
378
|
else {
|
|
252
379
|
active = tag;
|
|
@@ -259,6 +386,391 @@ function scanRawTextTags(value: string): RawTextScan {
|
|
|
259
386
|
return { severity: severity + (active ? 1 : 0), bodyRanges };
|
|
260
387
|
}
|
|
261
388
|
|
|
389
|
+
// ---------------------------------------------------------------------------
|
|
390
|
+
// Structural pass. Counting tokens is blind to order, so an unclosed <div> or
|
|
391
|
+
// a stray </section> leaves every root count above intact — those need a stack
|
|
392
|
+
// walk, and they are the defects browsers recover from most invisibly.
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
/** Closing tag is forbidden, so these never reach the stack. */
|
|
396
|
+
const VOID_TAGS = new Set([
|
|
397
|
+
"area",
|
|
398
|
+
"base",
|
|
399
|
+
"br",
|
|
400
|
+
"col",
|
|
401
|
+
"embed",
|
|
402
|
+
"hr",
|
|
403
|
+
"img",
|
|
404
|
+
"input",
|
|
405
|
+
"link",
|
|
406
|
+
"meta",
|
|
407
|
+
"param",
|
|
408
|
+
"source",
|
|
409
|
+
"track",
|
|
410
|
+
"wbr",
|
|
411
|
+
]);
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Closing tag optional per HTML5, so omitting one is legal authoring.
|
|
415
|
+
* `html`/`head`/`body` belong here because the counting pass already owns them.
|
|
416
|
+
*/
|
|
417
|
+
const OPTIONAL_CLOSE_TAGS = new Set([
|
|
418
|
+
"body",
|
|
419
|
+
"caption",
|
|
420
|
+
"colgroup",
|
|
421
|
+
"dd",
|
|
422
|
+
"dt",
|
|
423
|
+
"head",
|
|
424
|
+
"html",
|
|
425
|
+
"li",
|
|
426
|
+
"optgroup",
|
|
427
|
+
"option",
|
|
428
|
+
"p",
|
|
429
|
+
"rb",
|
|
430
|
+
"rp",
|
|
431
|
+
"rt",
|
|
432
|
+
"rtc",
|
|
433
|
+
"tbody",
|
|
434
|
+
"td",
|
|
435
|
+
"tfoot",
|
|
436
|
+
"th",
|
|
437
|
+
"thead",
|
|
438
|
+
"tr",
|
|
439
|
+
]);
|
|
440
|
+
|
|
441
|
+
/** Opening one of these implicitly closes the listed open sibling. */
|
|
442
|
+
const IMPLICIT_SIBLING_CLOSE = new Map<string, Set<string>>([
|
|
443
|
+
["li", new Set(["li"])],
|
|
444
|
+
["p", new Set(["p"])],
|
|
445
|
+
["td", new Set(["td", "th"])],
|
|
446
|
+
["th", new Set(["td", "th"])],
|
|
447
|
+
["tr", new Set(["tr", "td", "th"])],
|
|
448
|
+
["dt", new Set(["dt", "dd"])],
|
|
449
|
+
["dd", new Set(["dt", "dd"])],
|
|
450
|
+
["option", new Set(["option"])],
|
|
451
|
+
["optgroup", new Set(["optgroup", "option"])],
|
|
452
|
+
["tbody", new Set(["thead", "tbody", "tr", "td", "th"])],
|
|
453
|
+
["tfoot", new Set(["thead", "tbody", "tr", "td", "th"])],
|
|
454
|
+
]);
|
|
455
|
+
|
|
456
|
+
const MAX_EXCERPT_CHARS = 120;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Deliberately conservative: a miss costs one unreported advisory, a false hit
|
|
460
|
+
* costs trust in every advisory after it.
|
|
461
|
+
*/
|
|
462
|
+
const USES_TAILWIND_UTILITIES =
|
|
463
|
+
/\bclass\s*=\s*["'][^"']*(?:\b(?:flex|grid|hidden|absolute|relative|sticky)\b|\b(?:p|m|px|py|mx|my|pt|pb|pl|pr|gap|w|h|text|bg|border|rounded|shadow|items|justify|font|leading|tracking|space-x|space-y|min-h|max-w|opacity|ring|z)-[a-z0-9[\]./-]+)/i;
|
|
464
|
+
|
|
465
|
+
type Locator = (index: number) => {
|
|
466
|
+
line: number;
|
|
467
|
+
column: number;
|
|
468
|
+
excerpt: string;
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Scanning to the offset per call made validation quadratic on VALID documents,
|
|
473
|
+
* not just malformed ones — a 117KB screen cost ~700ms on every save. Index the
|
|
474
|
+
* line starts once, lazily, and binary search.
|
|
475
|
+
*/
|
|
476
|
+
function createLocator(value: string): Locator {
|
|
477
|
+
let starts: number[] | null = null;
|
|
478
|
+
return (index) => {
|
|
479
|
+
if (!starts) {
|
|
480
|
+
starts = [0];
|
|
481
|
+
for (let cursor = 0; cursor < value.length; cursor += 1) {
|
|
482
|
+
if (value[cursor] === "\n") starts.push(cursor + 1);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
let low = 0;
|
|
486
|
+
let high = starts.length - 1;
|
|
487
|
+
while (low < high) {
|
|
488
|
+
const mid = (low + high + 1) >> 1;
|
|
489
|
+
if (starts[mid]! <= index) low = mid;
|
|
490
|
+
else high = mid - 1;
|
|
491
|
+
}
|
|
492
|
+
const lineStart = starts[low]!;
|
|
493
|
+
let lineEnd = value.indexOf("\n", lineStart);
|
|
494
|
+
if (lineEnd === -1) lineEnd = value.length;
|
|
495
|
+
const raw = value.slice(lineStart, lineEnd).trim();
|
|
496
|
+
return {
|
|
497
|
+
line: low + 1,
|
|
498
|
+
column: index - lineStart + 1,
|
|
499
|
+
excerpt:
|
|
500
|
+
raw.length > MAX_EXCERPT_CHARS
|
|
501
|
+
? `${raw.slice(0, MAX_EXCERPT_CHARS)}…`
|
|
502
|
+
: raw,
|
|
503
|
+
};
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
interface TagScan {
|
|
508
|
+
end: number;
|
|
509
|
+
/** False when EOF arrived before the tag's `>`. */
|
|
510
|
+
terminated: boolean;
|
|
511
|
+
/** Whether EOF arrived inside a quoted value — the truncation cause. */
|
|
512
|
+
quoteOpen: boolean;
|
|
513
|
+
/** The attribute that quote belonged to, when one was named. */
|
|
514
|
+
unterminatedAttribute?: string;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* A raw-text end tag only closes the element when the name is followed by
|
|
519
|
+
* whitespace, `/`, or `>`. Matching on a word boundary instead treats script
|
|
520
|
+
* text like `"</script=template>"` as the closer, which orphans the real one.
|
|
521
|
+
*/
|
|
522
|
+
function rawTextCloser(tag: string): RegExp {
|
|
523
|
+
return new RegExp(`<\\s*/\\s*${tag}(?=[\\s/>])`, "gi");
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/** Consume one markup token starting at `start` (the `<`), honoring quotes. */
|
|
527
|
+
function scanTag(value: string, start: number): TagScan {
|
|
528
|
+
let quote: '"' | "'" | null = null;
|
|
529
|
+
let pendingAttribute: string | undefined;
|
|
530
|
+
let quotedAttribute: string | undefined;
|
|
531
|
+
let word = "";
|
|
532
|
+
for (let cursor = start; cursor < value.length; cursor += 1) {
|
|
533
|
+
const character = value[cursor]!;
|
|
534
|
+
if (quote) {
|
|
535
|
+
if (character === quote) {
|
|
536
|
+
quote = null;
|
|
537
|
+
quotedAttribute = undefined;
|
|
538
|
+
}
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
if (character === '"' || character === "'") {
|
|
542
|
+
quote = character;
|
|
543
|
+
quotedAttribute = pendingAttribute;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (character === ">") {
|
|
547
|
+
return { end: cursor + 1, terminated: true, quoteOpen: false };
|
|
548
|
+
}
|
|
549
|
+
if (character === "=") {
|
|
550
|
+
if (word) pendingAttribute = word;
|
|
551
|
+
word = "";
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
if (character === "<" || character === "/" || /\s/.test(character)) {
|
|
555
|
+
word = "";
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
word += character;
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
end: value.length,
|
|
562
|
+
terminated: false,
|
|
563
|
+
quoteOpen: quote !== null,
|
|
564
|
+
unterminatedAttribute: quotedAttribute,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* One parse for both facts. Deriving `isClose` separately let `< /div>` be read
|
|
570
|
+
* as a close tag by one check and an open tag by the other.
|
|
571
|
+
*/
|
|
572
|
+
function tagAt(
|
|
573
|
+
value: string,
|
|
574
|
+
index: number,
|
|
575
|
+
): { tag: string; isClose: boolean } | null {
|
|
576
|
+
const match = /^<(\s*\/)?\s*([a-zA-Z][a-zA-Z0-9:-]*)/.exec(
|
|
577
|
+
value.slice(index, index + 64),
|
|
578
|
+
);
|
|
579
|
+
return match
|
|
580
|
+
? { tag: match[2]!.toLowerCase(), isClose: match[1] !== undefined }
|
|
581
|
+
: null;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Runs on fragments as well as documents — an unterminated quote is as
|
|
586
|
+
* destructive in a `<template>` snippet as in a full page.
|
|
587
|
+
*/
|
|
588
|
+
function collectStructuralIssues(
|
|
589
|
+
value: string,
|
|
590
|
+
): DesignHtmlIntegrityIssueDetail[] {
|
|
591
|
+
const issues: DesignHtmlIntegrityIssueDetail[] = [];
|
|
592
|
+
const stack: Array<{ tag: string; start: number }> = [];
|
|
593
|
+
const locate = createLocator(value);
|
|
594
|
+
let cursor = 0;
|
|
595
|
+
|
|
596
|
+
while (cursor < value.length) {
|
|
597
|
+
const open = value.indexOf("<", cursor);
|
|
598
|
+
if (open === -1) break;
|
|
599
|
+
|
|
600
|
+
if (value.startsWith("<!--", open)) {
|
|
601
|
+
const commentEnd = value.indexOf("-->", open + 4);
|
|
602
|
+
if (commentEnd === -1) {
|
|
603
|
+
issues.push({ issue: "content-truncated", ...locate(open) });
|
|
604
|
+
return issues;
|
|
605
|
+
}
|
|
606
|
+
cursor = commentEnd + 3;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (value.startsWith("<!", open)) {
|
|
611
|
+
cursor = Math.max(scanTag(value, open).end, open + 1);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const parsed = tagAt(value, open);
|
|
616
|
+
if (!parsed) {
|
|
617
|
+
// A bare `<` in text content. Not markup, not a defect.
|
|
618
|
+
cursor = open + 1;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
const { tag, isClose } = parsed;
|
|
622
|
+
|
|
623
|
+
const scan = scanTag(value, open);
|
|
624
|
+
if (!scan.terminated) {
|
|
625
|
+
// Anything after an unclosed tag would be invented structure. Stop here.
|
|
626
|
+
const located = locate(open);
|
|
627
|
+
issues.push(
|
|
628
|
+
scan.quoteOpen
|
|
629
|
+
? {
|
|
630
|
+
issue: "attribute-unterminated",
|
|
631
|
+
...located,
|
|
632
|
+
tag,
|
|
633
|
+
attribute: scan.unterminatedAttribute,
|
|
634
|
+
}
|
|
635
|
+
: { issue: "content-truncated", ...located, tag },
|
|
636
|
+
);
|
|
637
|
+
return issues;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (isClose) {
|
|
641
|
+
let matched = -1;
|
|
642
|
+
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
643
|
+
if (stack[index]!.tag === tag) {
|
|
644
|
+
matched = index;
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (matched === -1) {
|
|
649
|
+
if (!OPTIONAL_CLOSE_TAGS.has(tag) && !VOID_TAGS.has(tag)) {
|
|
650
|
+
issues.push({ issue: "close-tag-orphaned", ...locate(open), tag });
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
// Located lazily: computing this for every balanced close tag is what
|
|
654
|
+
// made a valid document quadratic.
|
|
655
|
+
let closeLine: number | null = null;
|
|
656
|
+
for (let index = stack.length - 1; index > matched; index -= 1) {
|
|
657
|
+
const abandoned = stack[index]!;
|
|
658
|
+
if (OPTIONAL_CLOSE_TAGS.has(abandoned.tag)) continue;
|
|
659
|
+
closeLine ??= locate(open).line;
|
|
660
|
+
issues.push({
|
|
661
|
+
issue: "element-unclosed",
|
|
662
|
+
...locate(abandoned.start),
|
|
663
|
+
tag: abandoned.tag,
|
|
664
|
+
closedBy: { tag, line: closeLine },
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
stack.length = matched;
|
|
668
|
+
}
|
|
669
|
+
cursor = scan.end;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const selfClosing = /\/\s*>$/.test(value.slice(open, scan.end));
|
|
674
|
+
|
|
675
|
+
if (RAW_TEXT_TAGS.has(tag) && !selfClosing) {
|
|
676
|
+
// Resume AT the closing tag so the close branch pops this element,
|
|
677
|
+
// rather than duplicating that logic here. An unterminated body is left
|
|
678
|
+
// to the raw-text-balance check, which names that cause correctly.
|
|
679
|
+
const closer = rawTextCloser(tag);
|
|
680
|
+
closer.lastIndex = scan.end;
|
|
681
|
+
const found = closer.exec(value);
|
|
682
|
+
if (!found) {
|
|
683
|
+
// Report here rather than deferring to the document-only raw-text
|
|
684
|
+
// balance check: fragments never reach that check, so an unclosed
|
|
685
|
+
// <script> would pass the well-formedness gate entirely.
|
|
686
|
+
issues.push({
|
|
687
|
+
issue: "raw-text-balance",
|
|
688
|
+
...locate(open),
|
|
689
|
+
tag,
|
|
690
|
+
});
|
|
691
|
+
return issues;
|
|
692
|
+
}
|
|
693
|
+
stack.push({ tag, start: open });
|
|
694
|
+
cursor = found.index;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
if (VOID_TAGS.has(tag) || selfClosing) {
|
|
699
|
+
cursor = scan.end;
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// An implied close discards the target AND everything opened inside it —
|
|
704
|
+
// the browser closes those too, so popping only the stack top reports a
|
|
705
|
+
// still-open inline descendant (`<li><span>one<li>`) as unclosed.
|
|
706
|
+
const impliedClose = IMPLICIT_SIBLING_CLOSE.get(tag);
|
|
707
|
+
if (impliedClose) {
|
|
708
|
+
for (let index = stack.length - 1; index >= 0; index -= 1) {
|
|
709
|
+
if (!impliedClose.has(stack[index]!.tag)) continue;
|
|
710
|
+
stack.length = index;
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
stack.push({ tag, start: open });
|
|
715
|
+
cursor = scan.end;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Stack order is document order, so stopping at the cap keeps the earliest
|
|
719
|
+
// (outermost) unclosed elements without locating every one of them.
|
|
720
|
+
for (const abandoned of stack) {
|
|
721
|
+
if (issues.length >= MAX_REPORTED_ISSUES) break;
|
|
722
|
+
if (OPTIONAL_CLOSE_TAGS.has(abandoned.tag)) continue;
|
|
723
|
+
issues.push({
|
|
724
|
+
issue: "element-unclosed",
|
|
725
|
+
...locate(abandoned.start),
|
|
726
|
+
tag: abandoned.tag,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return issues
|
|
731
|
+
.sort((left, right) =>
|
|
732
|
+
left.line === right.line
|
|
733
|
+
? left.column - right.column
|
|
734
|
+
: left.line - right.line,
|
|
735
|
+
)
|
|
736
|
+
.slice(0, MAX_REPORTED_ISSUES);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Reported, never enforced: legitimate fragments and token-only screens carry no
|
|
741
|
+
* runtime of their own, so blocking here would reject valid work.
|
|
742
|
+
*/
|
|
743
|
+
function collectAdvisoryIssues(
|
|
744
|
+
value: string,
|
|
745
|
+
rawTextBodyRanges: RawTextScan["bodyRanges"],
|
|
746
|
+
): DesignHtmlIntegrityIssueDetail[] {
|
|
747
|
+
if (!isDocumentHtml(value, rawTextBodyRanges)) return [];
|
|
748
|
+
// Only documents that actually depend on utility classes can be broken by a
|
|
749
|
+
// missing runtime. A screen styled entirely through its own CSS needs no
|
|
750
|
+
// Tailwind, and flagging it would train authors to ignore this warning.
|
|
751
|
+
if (!USES_TAILWIND_UTILITIES.test(value)) return [];
|
|
752
|
+
// Comments are not markup: a commented-out runtime tag is not a runtime.
|
|
753
|
+
const value_ = value.replace(/<!--[\s\S]*?-->/g, "");
|
|
754
|
+
const hasTailwindRuntime =
|
|
755
|
+
/<script\b[^>]*\bsrc\s*=\s*(?:"[^"]*tailwind[^"]*"|'[^']*tailwind[^']*')/i.test(
|
|
756
|
+
value_,
|
|
757
|
+
) ||
|
|
758
|
+
/<style\b[^>]*\btype\s*=\s*(?:"text\/tailwindcss"|'text\/tailwindcss')/i.test(
|
|
759
|
+
value_,
|
|
760
|
+
) ||
|
|
761
|
+
/<link\b[^>]*\bhref\s*=\s*(?:"[^"]*tailwind[^"]*"|'[^']*tailwind[^']*')/i.test(
|
|
762
|
+
value_,
|
|
763
|
+
);
|
|
764
|
+
if (hasTailwindRuntime) return [];
|
|
765
|
+
const headIndex = value.search(/<head\b/i);
|
|
766
|
+
return [
|
|
767
|
+
{
|
|
768
|
+
issue: "runtime-missing",
|
|
769
|
+
...createLocator(value)(headIndex === -1 ? 0 : headIndex),
|
|
770
|
+
},
|
|
771
|
+
];
|
|
772
|
+
}
|
|
773
|
+
|
|
262
774
|
function markerCounts(
|
|
263
775
|
value: string,
|
|
264
776
|
marker: string,
|
|
@@ -292,8 +804,28 @@ export function inspectDesignHtmlDocumentIntegrity(
|
|
|
292
804
|
value: string,
|
|
293
805
|
): DesignHtmlIntegrityResult {
|
|
294
806
|
const rawText = scanRawTextTags(value);
|
|
807
|
+
|
|
808
|
+
// Structure first, counting second. An unterminated quote swallows the root
|
|
809
|
+
// tags, so the counting pass would report `document-root` — sending the fix to
|
|
810
|
+
// a `<html>` tag that is present and correct instead of to the quote.
|
|
811
|
+
const structural = collectStructuralIssues(value);
|
|
812
|
+
if (structural.length > 0) {
|
|
813
|
+
return {
|
|
814
|
+
valid: false,
|
|
815
|
+
issue: structural[0]!.issue,
|
|
816
|
+
detail: structural,
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
|
|
295
820
|
if (!isDocumentHtml(value, rawText.bodyRanges)) return { valid: true };
|
|
296
821
|
|
|
822
|
+
// Before the root counts: an unbalanced raw-text element swallows the tags
|
|
823
|
+
// those counts look for, so checking order decides whether the report names
|
|
824
|
+
// the cause or its effect.
|
|
825
|
+
if (rawText.severity > 0) {
|
|
826
|
+
return { valid: false, issue: "raw-text-balance" };
|
|
827
|
+
}
|
|
828
|
+
|
|
297
829
|
const html = tagCount(value, "html", rawText.bodyRanges);
|
|
298
830
|
if (html.open !== 1 || html.close !== 1) {
|
|
299
831
|
return { valid: false, issue: "document-root" };
|
|
@@ -349,10 +881,6 @@ export function inspectDesignHtmlDocumentIntegrity(
|
|
|
349
881
|
return { valid: false, issue: "document-boundary" };
|
|
350
882
|
}
|
|
351
883
|
|
|
352
|
-
if (rawText.severity > 0) {
|
|
353
|
-
return { valid: false, issue: "raw-text-balance" };
|
|
354
|
-
}
|
|
355
|
-
|
|
356
884
|
for (const { marker, tag } of MANAGED_RAW_TEXT_MARKERS) {
|
|
357
885
|
const counts = markerCounts(value, marker, tag, rawText.bodyRanges);
|
|
358
886
|
if (counts.raw !== counts.attached) {
|
|
@@ -363,7 +891,8 @@ export function inspectDesignHtmlDocumentIntegrity(
|
|
|
363
891
|
}
|
|
364
892
|
}
|
|
365
893
|
|
|
366
|
-
|
|
894
|
+
const advisory = collectAdvisoryIssues(value, rawText.bodyRanges);
|
|
895
|
+
return advisory.length > 0 ? { valid: true, advisory } : { valid: true };
|
|
367
896
|
}
|
|
368
897
|
|
|
369
898
|
/**
|
|
@@ -376,18 +905,79 @@ export function assertDesignHtmlEditIntegrity(args: {
|
|
|
376
905
|
previousContent: string;
|
|
377
906
|
nextContent: string;
|
|
378
907
|
fileType: string;
|
|
908
|
+
filename?: string;
|
|
379
909
|
}): void {
|
|
380
910
|
if (args.fileType.toLowerCase() !== "html") return;
|
|
381
911
|
const previousIsDocument = isDocumentHtml(args.previousContent);
|
|
382
912
|
const nextIsDocument = isDocumentHtml(args.nextContent);
|
|
383
|
-
|
|
913
|
+
// Fragments still get the structural pass; only the document-shape checks
|
|
914
|
+
// below need a document to apply to.
|
|
915
|
+
if (!previousIsDocument && !nextIsDocument) {
|
|
916
|
+
const structural = collectStructuralIssues(args.nextContent);
|
|
917
|
+
if (structural.length > 0) {
|
|
918
|
+
throw new DesignHtmlIntegrityError(structural[0]!.issue, {
|
|
919
|
+
filename: args.filename,
|
|
920
|
+
detail: structural,
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
384
925
|
if (previousIsDocument && !nextIsDocument) {
|
|
385
|
-
throw new DesignHtmlIntegrityError("document-root"
|
|
926
|
+
throw new DesignHtmlIntegrityError("document-root", {
|
|
927
|
+
filename: args.filename,
|
|
928
|
+
});
|
|
386
929
|
}
|
|
387
930
|
const result = inspectDesignHtmlDocumentIntegrity(args.nextContent);
|
|
388
931
|
if (!result.valid) {
|
|
389
|
-
throw new DesignHtmlIntegrityError(result.issue ?? "document-root"
|
|
932
|
+
throw new DesignHtmlIntegrityError(result.issue ?? "document-root", {
|
|
933
|
+
filename: args.filename,
|
|
934
|
+
detail: result.detail,
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Creation counterpart to the edit transition above. Every creation path must
|
|
941
|
+
* run this, or a design's first save is the one write with no gate at all.
|
|
942
|
+
*
|
|
943
|
+
* Returns advisory issues for the caller to surface; throws on anything
|
|
944
|
+
* blocking.
|
|
945
|
+
*/
|
|
946
|
+
/**
|
|
947
|
+
* Well-formedness only — no document-shape rules. For markup that is not
|
|
948
|
+
* required to be a complete screen, such as a variant sketch, where `<html>` and
|
|
949
|
+
* `<body>` are legitimately implied. Unbalanced tags are defects at any level of
|
|
950
|
+
* completeness; a missing skeleton is not.
|
|
951
|
+
*/
|
|
952
|
+
export function assertDesignHtmlWellFormed(args: {
|
|
953
|
+
content: string;
|
|
954
|
+
filename?: string;
|
|
955
|
+
}): void {
|
|
956
|
+
if (!args.content.trim()) return;
|
|
957
|
+
const structural = collectStructuralIssues(args.content);
|
|
958
|
+
if (structural.length > 0) {
|
|
959
|
+
throw new DesignHtmlIntegrityError(structural[0]!.issue, {
|
|
960
|
+
filename: args.filename,
|
|
961
|
+
detail: structural,
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
export function assertDesignHtmlCreateIntegrity(args: {
|
|
967
|
+
content: string;
|
|
968
|
+
fileType: string;
|
|
969
|
+
filename?: string;
|
|
970
|
+
}): DesignHtmlIntegrityIssueDetail[] {
|
|
971
|
+
if ((args.fileType || "html").toLowerCase() !== "html") return [];
|
|
972
|
+
if (!args.content.trim()) return [];
|
|
973
|
+
const result = inspectDesignHtmlDocumentIntegrity(args.content);
|
|
974
|
+
if (!result.valid) {
|
|
975
|
+
throw new DesignHtmlIntegrityError(result.issue ?? "document-root", {
|
|
976
|
+
filename: args.filename,
|
|
977
|
+
detail: result.detail,
|
|
978
|
+
});
|
|
390
979
|
}
|
|
980
|
+
return result.advisory ?? [];
|
|
391
981
|
}
|
|
392
982
|
|
|
393
983
|
export function isDesignHtmlIntegrityError(error: unknown): boolean {
|