@seqyuan/annodex 0.1.10 → 0.1.12
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/app/api/agent/[id]/events/route.ts +94 -0
- package/app/api/agent/[id]/route.ts +83 -0
- package/app/api/agent/new/route.ts +53 -0
- package/app/api/auth/all-providers/route.ts +21 -0
- package/app/api/auth/api-key/[provider]/route.ts +7 -0
- package/app/api/auth/login/[provider]/route.ts +7 -0
- package/app/api/auth/login/route.ts +22 -0
- package/app/api/auth/logout/[provider]/route.ts +7 -0
- package/app/api/auth/providers/route.ts +15 -0
- package/app/api/auth/status/route.ts +6 -0
- package/app/api/default-cwd/route.ts +22 -0
- package/app/api/files/[...path]/route.ts +621 -0
- package/app/api/harness/route.ts +47 -0
- package/app/api/home/route.ts +6 -0
- package/app/api/internal/runtime/route.ts +26 -0
- package/app/api/models/route.ts +67 -0
- package/app/api/models-config/discover/route.ts +42 -0
- package/app/api/models-config/route.ts +152 -0
- package/app/api/models-config/test/route.ts +154 -0
- package/app/api/projects/browse/route.ts +51 -0
- package/app/api/projects/route.ts +83 -0
- package/app/api/reports/[id]/route.ts +108 -0
- package/app/api/search/route.ts +122 -0
- package/app/api/sessions/[id]/context/route.ts +23 -0
- package/app/api/sessions/[id]/route.ts +124 -0
- package/app/api/sessions/new/route.ts +5 -0
- package/app/api/sessions/route.ts +16 -0
- package/app/api/settings/route.ts +51 -0
- package/app/api/skills/install/route.ts +249 -0
- package/app/api/skills/route.ts +161 -0
- package/app/api/skills/search/route.ts +121 -0
- package/app/api/soul/route.ts +47 -0
- package/app/api/version/route.ts +55 -0
- package/app/globals.css +736 -0
- package/app/layout.tsx +40 -0
- package/app/login/page.tsx +133 -0
- package/app/page.tsx +10 -0
- package/components/AppShell.tsx +1058 -0
- package/components/ChatInput.tsx +1103 -0
- package/components/ChatMinimap.tsx +381 -0
- package/components/ChatWindow.tsx +576 -0
- package/components/CodeMirrorEditor.tsx +137 -0
- package/components/ConversationSearch.tsx +369 -0
- package/components/DataTableViewer.tsx +248 -0
- package/components/FileExplorer.tsx +758 -0
- package/components/FileIcons.tsx +241 -0
- package/components/FileViewer.tsx +1273 -0
- package/components/GlobalFileEditor.tsx +98 -0
- package/components/MarkdownRenderer.tsx +331 -0
- package/components/MermaidDiagram.tsx +80 -0
- package/components/MessageView.tsx +1141 -0
- package/components/ModelsConfig.tsx +1991 -0
- package/components/ProjectContext.tsx +252 -0
- package/components/ProjectFolderPicker.tsx +202 -0
- package/components/ProjectsConfig.tsx +288 -0
- package/components/ProviderIcons.tsx +91 -0
- package/components/ReportPanel.tsx +237 -0
- package/components/ResizeHandle.tsx +105 -0
- package/components/SessionSidebar.tsx +1464 -0
- package/components/SettingsDialog.tsx +287 -0
- package/components/SkillsConfig.tsx +1093 -0
- package/components/SubagentPanel.tsx +191 -0
- package/components/TabBar.tsx +115 -0
- package/components/ToolPanel.tsx +131 -0
- package/components/WidgetRenderer.tsx +505 -0
- package/components/viewers/DocumentToolbar.tsx +78 -0
- package/components/viewers/DocxViewer.tsx +97 -0
- package/components/viewers/PdfViewer.tsx +206 -0
- package/components/viewers/PptxViewer.tsx +240 -0
- package/components/viewers/XlsxViewer.tsx +143 -0
- package/hooks/useAgentSession.ts +710 -0
- package/hooks/useAudio.ts +50 -0
- package/hooks/useDragDrop.ts +52 -0
- package/hooks/useResizable.ts +60 -0
- package/hooks/useTheme.ts +85 -0
- package/lib/agent-client.ts +39 -0
- package/lib/annodex-config.ts +556 -0
- package/lib/auth-token.ts +74 -0
- package/lib/auth.ts +90 -0
- package/lib/brand.ts +5 -0
- package/lib/code-theme.ts +32 -0
- package/lib/codex-compat-proxy.ts +1603 -0
- package/lib/codex-home.ts +6 -0
- package/lib/codex-server.ts +796 -0
- package/lib/codex-session.ts +590 -0
- package/lib/codex-usage.ts +213 -0
- package/lib/file-paths.ts +34 -0
- package/lib/model-discovery.ts +379 -0
- package/lib/normalize.ts +30 -0
- package/lib/npx.ts +87 -0
- package/lib/pi-types.ts +49 -0
- package/lib/projects.ts +269 -0
- package/lib/provider-api.ts +88 -0
- package/lib/report-prompt.ts +61 -0
- package/lib/report-store.ts +597 -0
- package/lib/report-update-parser.ts +66 -0
- package/lib/rpc-manager.ts +668 -0
- package/lib/runtime-state.ts +117 -0
- package/lib/session-reader.ts +903 -0
- package/lib/session-runtime.ts +105 -0
- package/lib/subagent-progress.ts +279 -0
- package/lib/types.ts +241 -0
- package/lib/widget-export.ts +318 -0
- package/lib/widget-guidelines.ts +288 -0
- package/lib/widget-prompt.ts +76 -0
- package/lib/widget-utils.ts +523 -0
- package/package.json +23 -18
- package/postcss.config.mjs +8 -0
- package/proxy.ts +64 -0
- package/scripts/postinstall.cjs +25 -0
- package/tsconfig.json +41 -0
- package/.next/BUILD_ID +0 -1
- package/.next/app-path-routes-manifest.json +0 -39
- package/.next/build-manifest.json +0 -20
- package/.next/diagnostics/build-diagnostics.json +0 -6
- package/.next/diagnostics/framework.json +0 -1
- package/.next/export-marker.json +0 -6
- package/.next/images-manifest.json +0 -68
- package/.next/next-minimal-server.js.nft.json +0 -1
- package/.next/next-server.js.nft.json +0 -1
- package/.next/package.json +0 -1
- package/.next/prerender-manifest.json +0 -109
- package/.next/react-loadable-manifest.json +0 -2320
- package/.next/required-server-files.js +0 -343
- package/.next/required-server-files.json +0 -343
- package/.next/routes-manifest.json +0 -286
- package/.next/server/app/_global-error/page.js +0 -32
- package/.next/server/app/_global-error/page.js.nft.json +0 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +0 -1
- package/.next/server/app/_global-error.html +0 -1
- package/.next/server/app/_global-error.meta +0 -16
- package/.next/server/app/_global-error.rsc +0 -14
- package/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +0 -5
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +0 -5
- package/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
- package/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
- package/.next/server/app/_not-found/page.js +0 -2
- package/.next/server/app/_not-found/page.js.nft.json +0 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
- package/.next/server/app/_not-found.html +0 -1
- package/.next/server/app/_not-found.meta +0 -16
- package/.next/server/app/_not-found.rsc +0 -18
- package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -18
- package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
- package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -4
- package/.next/server/app/api/agent/[id]/events/route.js +0 -3
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +0 -1
- package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/agent/[id]/route.js +0 -1
- package/.next/server/app/api/agent/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/agent/new/route.js +0 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +0 -1
- package/.next/server/app/api/agent/new/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/all-providers/route.js +0 -1
- package/.next/server/app/api/auth/all-providers/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +0 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +0 -1
- package/.next/server/app/api/auth/login/[provider]/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/login/route.js +0 -1
- package/.next/server/app/api/auth/login/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +0 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/providers/route.js +0 -1
- package/.next/server/app/api/auth/providers/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/auth/status/route.js +0 -1
- package/.next/server/app/api/auth/status/route.js.nft.json +0 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/default-cwd/route.js +0 -1
- package/.next/server/app/api/default-cwd/route.js.nft.json +0 -1
- package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/files/[...path]/route.js +0 -4
- package/.next/server/app/api/files/[...path]/route.js.nft.json +0 -1
- package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/harness/route.js +0 -1
- package/.next/server/app/api/harness/route.js.nft.json +0 -1
- package/.next/server/app/api/harness/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/home/route.js +0 -1
- package/.next/server/app/api/home/route.js.nft.json +0 -1
- package/.next/server/app/api/home/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/internal/runtime/route.js +0 -1
- package/.next/server/app/api/internal/runtime/route.js.nft.json +0 -1
- package/.next/server/app/api/internal/runtime/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/models/route.js +0 -1
- package/.next/server/app/api/models/route.js.nft.json +0 -1
- package/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/models-config/discover/route.js +0 -1
- package/.next/server/app/api/models-config/discover/route.js.nft.json +0 -1
- package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/models-config/route.js +0 -1
- package/.next/server/app/api/models-config/route.js.nft.json +0 -1
- package/.next/server/app/api/models-config/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/models-config/test/route.js +0 -1
- package/.next/server/app/api/models-config/test/route.js.nft.json +0 -1
- package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/projects/browse/route.js +0 -1
- package/.next/server/app/api/projects/browse/route.js.nft.json +0 -1
- package/.next/server/app/api/projects/browse/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/projects/route.js +0 -1
- package/.next/server/app/api/projects/route.js.nft.json +0 -1
- package/.next/server/app/api/projects/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/reports/[id]/route.js +0 -10
- package/.next/server/app/api/reports/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/reports/[id]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/search/route.js +0 -1
- package/.next/server/app/api/search/route.js.nft.json +0 -1
- package/.next/server/app/api/search/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +0 -1
- package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +0 -1
- package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/sessions/[id]/route.js +0 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +0 -1
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/sessions/new/route.js +0 -1
- package/.next/server/app/api/sessions/new/route.js.nft.json +0 -1
- package/.next/server/app/api/sessions/new/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/sessions/route.js +0 -1
- package/.next/server/app/api/sessions/route.js.nft.json +0 -1
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/settings/route.js +0 -1
- package/.next/server/app/api/settings/route.js.nft.json +0 -1
- package/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/skills/install/route.js +0 -5
- package/.next/server/app/api/skills/install/route.js.nft.json +0 -1
- package/.next/server/app/api/skills/install/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/skills/route.js +0 -6
- package/.next/server/app/api/skills/route.js.nft.json +0 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/skills/search/route.js +0 -1
- package/.next/server/app/api/skills/search/route.js.nft.json +0 -1
- package/.next/server/app/api/skills/search/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/soul/route.js +0 -1
- package/.next/server/app/api/soul/route.js.nft.json +0 -1
- package/.next/server/app/api/soul/route_client-reference-manifest.js +0 -1
- package/.next/server/app/api/version/route.js +0 -1
- package/.next/server/app/api/version/route.js.nft.json +0 -1
- package/.next/server/app/api/version/route_client-reference-manifest.js +0 -1
- package/.next/server/app/index.html +0 -1
- package/.next/server/app/index.meta +0 -14
- package/.next/server/app/index.rsc +0 -17
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -6
- package/.next/server/app/index.segments/_full.segment.rsc +0 -17
- package/.next/server/app/index.segments/_head.segment.rsc +0 -6
- package/.next/server/app/index.segments/_index.segment.rsc +0 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +0 -4
- package/.next/server/app/login/page.js +0 -2
- package/.next/server/app/login/page.js.nft.json +0 -1
- package/.next/server/app/login/page_client-reference-manifest.js +0 -1
- package/.next/server/app/login.html +0 -1
- package/.next/server/app/login.meta +0 -15
- package/.next/server/app/login.rsc +0 -22
- package/.next/server/app/login.segments/_full.segment.rsc +0 -22
- package/.next/server/app/login.segments/_head.segment.rsc +0 -6
- package/.next/server/app/login.segments/_index.segment.rsc +0 -5
- package/.next/server/app/login.segments/_tree.segment.rsc +0 -4
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +0 -9
- package/.next/server/app/login.segments/login.segment.rsc +0 -5
- package/.next/server/app/page.js +0 -261
- package/.next/server/app/page.js.nft.json +0 -1
- package/.next/server/app/page_client-reference-manifest.js +0 -1
- package/.next/server/app-paths-manifest.json +0 -39
- package/.next/server/chunks/1048.js +0 -1
- package/.next/server/chunks/1367.js +0 -77
- package/.next/server/chunks/1381.js +0 -1
- package/.next/server/chunks/165.js +0 -1
- package/.next/server/chunks/1681.js +0 -215
- package/.next/server/chunks/1688.js +0 -45
- package/.next/server/chunks/1703.js +0 -79
- package/.next/server/chunks/1712.js +0 -43
- package/.next/server/chunks/1813.js +0 -1
- package/.next/server/chunks/2325.js +0 -80
- package/.next/server/chunks/258.js +0 -1
- package/.next/server/chunks/2671.js +0 -287
- package/.next/server/chunks/2778.js +0 -1
- package/.next/server/chunks/2943.js +0 -1
- package/.next/server/chunks/3031.js +0 -226
- package/.next/server/chunks/3181.js +0 -1
- package/.next/server/chunks/3493.js +0 -1
- package/.next/server/chunks/3672.js +0 -1
- package/.next/server/chunks/3701.js +0 -104
- package/.next/server/chunks/4013.js +0 -1
- package/.next/server/chunks/402.js +0 -2
- package/.next/server/chunks/4035.js +0 -80
- package/.next/server/chunks/4248.js +0 -153
- package/.next/server/chunks/4367.js +0 -1
- package/.next/server/chunks/4406.js +0 -141
- package/.next/server/chunks/4741.js +0 -18
- package/.next/server/chunks/4768.js +0 -1
- package/.next/server/chunks/4858.js +0 -148
- package/.next/server/chunks/4980.js +0 -1
- package/.next/server/chunks/5155.js +0 -5
- package/.next/server/chunks/5293.js +0 -166
- package/.next/server/chunks/5399.js +0 -8
- package/.next/server/chunks/5409.js +0 -1
- package/.next/server/chunks/5797.js +0 -93
- package/.next/server/chunks/5851.js +0 -36
- package/.next/server/chunks/6206.js +0 -1
- package/.next/server/chunks/6296.js +0 -1
- package/.next/server/chunks/63.js +0 -45
- package/.next/server/chunks/6346.js +0 -1
- package/.next/server/chunks/6406.js +0 -23
- package/.next/server/chunks/642.js +0 -1
- package/.next/server/chunks/6429.js +0 -50
- package/.next/server/chunks/6729.js +0 -64
- package/.next/server/chunks/6907.js +0 -115
- package/.next/server/chunks/6980.js +0 -1
- package/.next/server/chunks/7073.js +0 -24
- package/.next/server/chunks/7233.js +0 -24
- package/.next/server/chunks/7307.js +0 -1
- package/.next/server/chunks/7362.js +0 -9
- package/.next/server/chunks/7567.js +0 -29
- package/.next/server/chunks/7765.js +0 -1
- package/.next/server/chunks/7890.js +0 -1
- package/.next/server/chunks/8065.js +0 -1
- package/.next/server/chunks/8238.js +0 -34
- package/.next/server/chunks/8276.js +0 -1
- package/.next/server/chunks/8336.js +0 -1
- package/.next/server/chunks/8477.js +0 -3
- package/.next/server/chunks/8490.js +0 -1
- package/.next/server/chunks/8916.js +0 -1
- package/.next/server/chunks/9280.js +0 -252
- package/.next/server/chunks/9315.js +0 -1
- package/.next/server/chunks/9537.js +0 -90
- package/.next/server/chunks/966.js +0 -1
- package/.next/server/chunks/9818.js +0 -21
- package/.next/server/chunks/static/media/pdf.worker.min.c476e1a0.mjs +0 -6
- package/.next/server/functions-config-manifest.json +0 -16
- package/.next/server/interception-route-rewrite-manifest.js +0 -1
- package/.next/server/middleware-build-manifest.js +0 -1
- package/.next/server/middleware-manifest.json +0 -6
- package/.next/server/middleware-react-loadable-manifest.js +0 -1
- package/.next/server/middleware.js +0 -18
- package/.next/server/middleware.js.nft.json +0 -1
- package/.next/server/next-font-manifest.js +0 -1
- package/.next/server/next-font-manifest.json +0 -1
- package/.next/server/pages/404.html +0 -1
- package/.next/server/pages/500.html +0 -1
- package/.next/server/pages-manifest.json +0 -4
- package/.next/server/prefetch-hints.json +0 -1
- package/.next/server/server-reference-manifest.js +0 -1
- package/.next/server/server-reference-manifest.json +0 -1
- package/.next/server/webpack-runtime.js +0 -1
- package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_buildManifest.js +0 -1
- package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_ssgManifest.js +0 -1
- package/.next/static/chunks/0b9a0da7.9075af772487e743.js +0 -62
- package/.next/static/chunks/1413.922d232de90c0c41.js +0 -115
- package/.next/static/chunks/1643.467a526a1f24f54d.js +0 -24
- package/.next/static/chunks/1852.5543122f11aa7fed.js +0 -1
- package/.next/static/chunks/1960.b1e26436d7a5f586.js +0 -1
- package/.next/static/chunks/2170a4aa.4213bb2183c9cdf9.js +0 -1
- package/.next/static/chunks/2274.6cd173f80a1405a2.js +0 -21
- package/.next/static/chunks/2419.347fdfe3c170854d.js +0 -166
- package/.next/static/chunks/2619.9aac8983f30c7c8a.js +0 -1
- package/.next/static/chunks/2623.d20fabd8e18197c6.js +0 -287
- package/.next/static/chunks/2729.f5365061a849d659.js +0 -34
- package/.next/static/chunks/2821.934bcf60fbdc28c6.js +0 -1
- package/.next/static/chunks/2918becc.abff2ece1de37bc1.js +0 -153
- package/.next/static/chunks/2947.114e51cb06d1c01a.js +0 -23
- package/.next/static/chunks/3079.4c511fa1144e3adf.js +0 -79
- package/.next/static/chunks/3274.208ca44844cd7d95.js +0 -148
- package/.next/static/chunks/3308.465a94263d04bfea.js +0 -73
- package/.next/static/chunks/3325.e4bfe1ca657f3b5b.js +0 -80
- package/.next/static/chunks/3506.2a7eaa08b9f55337.js +0 -90
- package/.next/static/chunks/363642f4-043c1475ab9af70e.js +0 -1
- package/.next/static/chunks/3794-123fdf632563f469.js +0 -32
- package/.next/static/chunks/3837.a755ccfe6f9c1c1c.js +0 -5
- package/.next/static/chunks/394.91597771688df6d0.js +0 -1
- package/.next/static/chunks/3997.1009c06025691712.js +0 -1
- package/.next/static/chunks/4453.91a357dc43c21745.js +0 -1
- package/.next/static/chunks/4491.44fdf20580ac72bd.js +0 -24
- package/.next/static/chunks/4829.cf1d50e43e6d9db5.js +0 -1
- package/.next/static/chunks/498.fe1d9da9ecad6c36.js +0 -1
- package/.next/static/chunks/4bd1b696-e356ca5ba0218e27.js +0 -1
- package/.next/static/chunks/5019.b5a1a2b8daf17525.js +0 -1
- package/.next/static/chunks/5034.8f16c3fa3ce75411.js +0 -1
- package/.next/static/chunks/5074.d16651da01ec4e02.js +0 -1
- package/.next/static/chunks/51fb665c.0950e1b79671348d.js +0 -45
- package/.next/static/chunks/532.5956ed631aff722b.js +0 -9
- package/.next/static/chunks/5326.69460442bdcd6cd3.js +0 -1
- package/.next/static/chunks/5403.ff110bf5bf600758.js +0 -64
- package/.next/static/chunks/547.902a733488cfe3f7.js +0 -77
- package/.next/static/chunks/5567.540d7fc108ad6ee5.js +0 -215
- package/.next/static/chunks/5590.ef62922166d308b4.js +0 -1
- package/.next/static/chunks/5690.9d6eb1edb1399995.js +0 -1
- package/.next/static/chunks/5749.25faee4a1e55b854.js +0 -226
- package/.next/static/chunks/58bb9007.1ccb6bba34b4c635.js +0 -80
- package/.next/static/chunks/6121.f3f43f1896ea0cd9.js +0 -1
- package/.next/static/chunks/6600.583c88eef37aa524.js +0 -1
- package/.next/static/chunks/6696.a41aec266e657d54.js +0 -141
- package/.next/static/chunks/6922.42148793782d2fe7.js +0 -1
- package/.next/static/chunks/7006.e191611ffc2b9528.js +0 -43
- package/.next/static/chunks/7343.9fbb58204d8ac681.js +0 -1
- package/.next/static/chunks/73972abe.25a4cffa03b2bcef.js +0 -119
- package/.next/static/chunks/7547.58bda8a2aabba0d4.js +0 -93
- package/.next/static/chunks/7648.4ae2f183b4db0353.js +0 -1
- package/.next/static/chunks/7874.8db6929b94cdf697.js +0 -1
- package/.next/static/chunks/7959.1f20a35df316216a.js +0 -104
- package/.next/static/chunks/83.85d62d7fc9850b75.js +0 -29
- package/.next/static/chunks/8436.cab94b59cca0a8ff.js +0 -1
- package/.next/static/chunks/8451.ff6ff72b57dc52e1.js +0 -1
- package/.next/static/chunks/8489.45f22859734f514f.js +0 -36
- package/.next/static/chunks/8568.f85d8b36fc9a9037.js +0 -1
- package/.next/static/chunks/8771-3e14b6810486df1f.js +0 -1
- package/.next/static/chunks/8863.be51033a67436277.js +0 -1
- package/.next/static/chunks/90542734.dc1a2723e4f6affb.js +0 -1
- package/.next/static/chunks/9500.1488aec06ee78127.js +0 -1
- package/.next/static/chunks/9633.155548b5fca6e580.js +0 -1
- package/.next/static/chunks/9779.673004a62d70e36a.js +0 -1
- package/.next/static/chunks/app/_global-error/page-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/_not-found/page-c72daab99269beff.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/login/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/auth/status/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/harness/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/home/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/internal/runtime/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/models/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/projects/browse/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/projects/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/reports/[id]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/search/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/sessions/new/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/settings/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/skills/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/soul/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/api/version/route-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/app/layout-be148b7ae915b22a.js +0 -1
- package/.next/static/chunks/app/login/page-ebf0e6de99062783.js +0 -1
- package/.next/static/chunks/app/page-c45d98ea81c548ca.js +0 -260
- package/.next/static/chunks/d3ac728e.7964f816a1ca64e5.js +0 -1
- package/.next/static/chunks/framework-711ef29bc66f648c.js +0 -1
- package/.next/static/chunks/main-app-45a0f19af99d61b6.js +0 -1
- package/.next/static/chunks/main-f74964b7ae52493e.js +0 -5
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-9bfa08b9491621f2.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-cc518af6b1ffb191.js +0 -1
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
- package/.next/static/chunks/webpack-fcf4a889ecbd753c.js +0 -1
- package/.next/static/css/45029451a1d7255d.css +0 -3
- package/.next/static/media/15605e25b523335c-s.woff2 +0 -0
- package/.next/static/media/1a3dce5cfb5f7760-s.woff2 +0 -0
- package/.next/static/media/1cdd02902f937a18-s.woff2 +0 -0
- package/.next/static/media/4c4b3b30b6bcb2be-s.woff2 +0 -0
- package/.next/static/media/641a7b8a5800ee0e-s.woff2 +0 -0
- package/.next/static/media/7deddc85b7ffd1dc-s.p.woff2 +0 -0
- package/.next/static/media/ec14413c594b3356-s.p.woff2 +0 -0
- package/.next/static/media/pdf.worker.min.29aaf158.mjs +0 -6
- package/.next/trace +0 -74
- package/.next/trace-build +0 -1
- package/.next/types/app/api/agent/[id]/events/route.ts +0 -351
- package/.next/types/app/api/agent/[id]/route.ts +0 -351
- package/.next/types/app/api/agent/new/route.ts +0 -351
- package/.next/types/app/api/auth/all-providers/route.ts +0 -351
- package/.next/types/app/api/auth/api-key/[provider]/route.ts +0 -351
- package/.next/types/app/api/auth/login/[provider]/route.ts +0 -351
- package/.next/types/app/api/auth/login/route.ts +0 -351
- package/.next/types/app/api/auth/logout/[provider]/route.ts +0 -351
- package/.next/types/app/api/auth/providers/route.ts +0 -351
- package/.next/types/app/api/auth/status/route.ts +0 -351
- package/.next/types/app/api/default-cwd/route.ts +0 -351
- package/.next/types/app/api/files/[...path]/route.ts +0 -351
- package/.next/types/app/api/harness/route.ts +0 -351
- package/.next/types/app/api/home/route.ts +0 -351
- package/.next/types/app/api/internal/runtime/route.ts +0 -351
- package/.next/types/app/api/models/route.ts +0 -351
- package/.next/types/app/api/models-config/discover/route.ts +0 -351
- package/.next/types/app/api/models-config/route.ts +0 -351
- package/.next/types/app/api/models-config/test/route.ts +0 -351
- package/.next/types/app/api/projects/browse/route.ts +0 -351
- package/.next/types/app/api/projects/route.ts +0 -351
- package/.next/types/app/api/reports/[id]/route.ts +0 -351
- package/.next/types/app/api/search/route.ts +0 -351
- package/.next/types/app/api/sessions/[id]/context/route.ts +0 -351
- package/.next/types/app/api/sessions/[id]/route.ts +0 -351
- package/.next/types/app/api/sessions/new/route.ts +0 -351
- package/.next/types/app/api/sessions/route.ts +0 -351
- package/.next/types/app/api/settings/route.ts +0 -351
- package/.next/types/app/api/skills/install/route.ts +0 -351
- package/.next/types/app/api/skills/route.ts +0 -351
- package/.next/types/app/api/skills/search/route.ts +0 -351
- package/.next/types/app/api/soul/route.ts +0 -351
- package/.next/types/app/api/version/route.ts +0 -351
- package/.next/types/app/layout.ts +0 -87
- package/.next/types/app/login/page.ts +0 -87
- package/.next/types/app/page.ts +0 -87
- package/.next/types/package.json +0 -1
- package/.next/types/routes.d.ts +0 -106
- package/.next/types/validator.ts +0 -376
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { sanitizeForIframe } from "@/lib/widget-utils";
|
|
4
|
+
|
|
5
|
+
export interface WidgetCanvasSnapshot {
|
|
6
|
+
dataUrl: string;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface WidgetCapturePayload {
|
|
12
|
+
html: string | null;
|
|
13
|
+
styles?: string;
|
|
14
|
+
canvases?: Array<WidgetCanvasSnapshot | null>;
|
|
15
|
+
bodyWidth?: number;
|
|
16
|
+
bodyHeight?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_EXPORT_WIDTH = 640;
|
|
20
|
+
const DEFAULT_EXPORT_HEIGHT = 360;
|
|
21
|
+
const MIN_EXPORT_WIDTH = 240;
|
|
22
|
+
const MIN_EXPORT_HEIGHT = 80;
|
|
23
|
+
const MAX_EXPORT_WIDTH = 2000;
|
|
24
|
+
const MAX_EXPORT_HEIGHT = 4000;
|
|
25
|
+
const MAX_EXPORT_PIXELS = 8_000_000;
|
|
26
|
+
|
|
27
|
+
const THEME_VAR_NAMES = [
|
|
28
|
+
"--bg",
|
|
29
|
+
"--bg-panel",
|
|
30
|
+
"--bg-hover",
|
|
31
|
+
"--bg-selected",
|
|
32
|
+
"--border",
|
|
33
|
+
"--text",
|
|
34
|
+
"--text-muted",
|
|
35
|
+
"--text-dim",
|
|
36
|
+
"--accent",
|
|
37
|
+
"--accent-hover",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function buildShowWidgetSource(code: string, title?: string): string {
|
|
41
|
+
const data: { title?: string; widget_code: string } = { widget_code: code };
|
|
42
|
+
if (title?.trim()) data.title = title.trim();
|
|
43
|
+
return "```show-widget\n" + JSON.stringify(data, null, 2) + "\n```";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function copyTextToClipboard(text: string): Promise<void> {
|
|
47
|
+
if (navigator.clipboard?.writeText) {
|
|
48
|
+
await navigator.clipboard.writeText(text);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const textarea = document.createElement("textarea");
|
|
53
|
+
textarea.value = text;
|
|
54
|
+
textarea.style.position = "fixed";
|
|
55
|
+
textarea.style.inset = "0";
|
|
56
|
+
textarea.style.opacity = "0";
|
|
57
|
+
document.body.appendChild(textarea);
|
|
58
|
+
textarea.focus();
|
|
59
|
+
textarea.select();
|
|
60
|
+
const ok = document.execCommand("copy");
|
|
61
|
+
document.body.removeChild(textarea);
|
|
62
|
+
if (!ok) throw new Error("Clipboard copy failed.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function downloadBlob(blob: Blob, filename: string): void {
|
|
66
|
+
const url = URL.createObjectURL(blob);
|
|
67
|
+
const anchor = document.createElement("a");
|
|
68
|
+
anchor.href = url;
|
|
69
|
+
anchor.download = filename;
|
|
70
|
+
document.body.appendChild(anchor);
|
|
71
|
+
anchor.click();
|
|
72
|
+
document.body.removeChild(anchor);
|
|
73
|
+
URL.revokeObjectURL(url);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function safeWidgetFilename(title?: string): string {
|
|
77
|
+
const name = (title || "widget").trim() || "widget";
|
|
78
|
+
return name.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "") || "widget";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function exportWidgetPng(options: {
|
|
82
|
+
code: string;
|
|
83
|
+
capture?: WidgetCapturePayload;
|
|
84
|
+
}): Promise<Blob> {
|
|
85
|
+
const svgBlob = await tryExportSvgCodeAsPng(options.code);
|
|
86
|
+
if (svgBlob) return svgBlob;
|
|
87
|
+
if (options.capture) return exportCapturedWidgetAsPng(options.capture);
|
|
88
|
+
throw new Error("PNG export needs a rendered widget capture.");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function tryExportSvgCodeAsPng(code: string): Promise<Blob | null> {
|
|
92
|
+
const svg = extractSvgElement(code);
|
|
93
|
+
if (!svg) return null;
|
|
94
|
+
|
|
95
|
+
sanitizeElementForExport(svg);
|
|
96
|
+
svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
97
|
+
|
|
98
|
+
const size = normalizeExportSize(getSvgSize(svg));
|
|
99
|
+
svg.setAttribute("width", String(size.width));
|
|
100
|
+
svg.setAttribute("height", String(size.height));
|
|
101
|
+
if (!svg.getAttribute("viewBox")) {
|
|
102
|
+
svg.setAttribute("viewBox", "0 0 " + size.width + " " + size.height);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const markup = resolveCssVarsInString(new XMLSerializer().serializeToString(svg));
|
|
106
|
+
return svgMarkupToPngBlob(markup, size.width, size.height);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function exportCapturedWidgetAsPng(capture: WidgetCapturePayload): Promise<Blob> {
|
|
110
|
+
if (!capture.html) {
|
|
111
|
+
throw new Error("The widget did not provide capturable HTML.");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const size = normalizeExportSize({
|
|
115
|
+
width: capture.bodyWidth || DEFAULT_EXPORT_WIDTH,
|
|
116
|
+
height: capture.bodyHeight || DEFAULT_EXPORT_HEIGHT,
|
|
117
|
+
});
|
|
118
|
+
const html = serializeCapturedHtml(capture);
|
|
119
|
+
if (!html.trim()) {
|
|
120
|
+
throw new Error("The widget capture was empty.");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const styles = buildCapturedStyles(capture.styles);
|
|
124
|
+
const svg = [
|
|
125
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="' + size.width + '" height="' + size.height + '" viewBox="0 0 ' + size.width + " " + size.height + '">',
|
|
126
|
+
"<defs><style><![CDATA[" + styles.replace(/\]\]>/g, "]]]]><![CDATA[>") + "]]></style></defs>",
|
|
127
|
+
'<foreignObject x="0" y="0" width="100%" height="100%">',
|
|
128
|
+
'<div xmlns="http://www.w3.org/1999/xhtml" id="__widget_export_root" style="width:' + size.width + "px;min-height:" + size.height + 'px;overflow:visible;">',
|
|
129
|
+
html,
|
|
130
|
+
"</div>",
|
|
131
|
+
"</foreignObject>",
|
|
132
|
+
"</svg>",
|
|
133
|
+
].join("");
|
|
134
|
+
|
|
135
|
+
return svgMarkupToPngBlob(resolveCssVarsInString(svg), size.width, size.height);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractSvgElement(code: string): SVGSVGElement | null {
|
|
139
|
+
const sanitized = sanitizeForIframe(code).trim();
|
|
140
|
+
if (!/^<\s*(style|svg)\b/i.test(sanitized)) return null;
|
|
141
|
+
|
|
142
|
+
const doc = new DOMParser().parseFromString(sanitized, "text/html");
|
|
143
|
+
const elements = Array.from(doc.body.children);
|
|
144
|
+
const svgElements = elements.filter((element) => element.tagName.toLowerCase() === "svg");
|
|
145
|
+
const nonStyleElements = elements.filter((element) => element.tagName.toLowerCase() !== "style");
|
|
146
|
+
if (svgElements.length !== 1 || nonStyleElements.length !== 1) return null;
|
|
147
|
+
|
|
148
|
+
const svg = svgElements[0].cloneNode(true) as SVGSVGElement;
|
|
149
|
+
const styles = Array.from(doc.body.querySelectorAll("style"))
|
|
150
|
+
.map((style) => style.textContent || "")
|
|
151
|
+
.join("\n")
|
|
152
|
+
.trim();
|
|
153
|
+
if (styles) {
|
|
154
|
+
const style = document.createElementNS("http://www.w3.org/2000/svg", "style");
|
|
155
|
+
style.textContent = styles;
|
|
156
|
+
svg.insertBefore(style, svg.firstChild);
|
|
157
|
+
}
|
|
158
|
+
return svg;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function serializeCapturedHtml(capture: WidgetCapturePayload): string {
|
|
162
|
+
const template = document.createElement("template");
|
|
163
|
+
template.innerHTML = sanitizeForIframe(capture.html || "");
|
|
164
|
+
|
|
165
|
+
const root = document.createElement("div");
|
|
166
|
+
root.appendChild(template.content.cloneNode(true));
|
|
167
|
+
|
|
168
|
+
for (const img of Array.from(root.querySelectorAll("img[data-canvas-export]"))) {
|
|
169
|
+
const index = Number(img.getAttribute("data-canvas-export"));
|
|
170
|
+
const snapshot = Number.isFinite(index) ? capture.canvases?.[index] : null;
|
|
171
|
+
if (!snapshot?.dataUrl) {
|
|
172
|
+
img.remove();
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
img.setAttribute("src", snapshot.dataUrl);
|
|
176
|
+
img.setAttribute("width", String(snapshot.width));
|
|
177
|
+
img.setAttribute("height", String(snapshot.height));
|
|
178
|
+
img.setAttribute("style", mergeInlineStyle(img.getAttribute("style"), {
|
|
179
|
+
width: snapshot.width + "px",
|
|
180
|
+
height: snapshot.height + "px",
|
|
181
|
+
display: "block",
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
sanitizeElementForExport(root);
|
|
186
|
+
return Array.from(root.childNodes)
|
|
187
|
+
.map((node) => new XMLSerializer().serializeToString(node))
|
|
188
|
+
.join("");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sanitizeElementForExport(root: Element): void {
|
|
192
|
+
for (const el of Array.from(root.querySelectorAll("script, iframe, object, embed, meta, link, base, form"))) {
|
|
193
|
+
el.remove();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const el of [root, ...Array.from(root.querySelectorAll("*"))]) {
|
|
197
|
+
for (const attr of Array.from(el.attributes)) {
|
|
198
|
+
const name = attr.name.toLowerCase();
|
|
199
|
+
const value = attr.value.trim();
|
|
200
|
+
if (name.startsWith("on")) {
|
|
201
|
+
el.removeAttribute(attr.name);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
if ((name === "href" || name === "src" || name.endsWith(":href")) && /^\s*(javascript|data:text\/html)/i.test(value)) {
|
|
205
|
+
el.removeAttribute(attr.name);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getSvgSize(svg: SVGSVGElement): { width: number; height: number } {
|
|
212
|
+
const viewBox = parseViewBox(svg.getAttribute("viewBox"));
|
|
213
|
+
const width = parseSvgLength(svg.getAttribute("width")) ?? viewBox?.width ?? DEFAULT_EXPORT_WIDTH;
|
|
214
|
+
const ratio = viewBox ? viewBox.height / viewBox.width : DEFAULT_EXPORT_HEIGHT / DEFAULT_EXPORT_WIDTH;
|
|
215
|
+
const height = parseSvgLength(svg.getAttribute("height")) ?? viewBox?.height ?? width * ratio;
|
|
216
|
+
return { width, height };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function parseViewBox(value: string | null): { width: number; height: number } | null {
|
|
220
|
+
if (!value) return null;
|
|
221
|
+
const parts = value.trim().split(/[\s,]+/).map(Number);
|
|
222
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isFinite(part))) return null;
|
|
223
|
+
if (parts[2] <= 0 || parts[3] <= 0) return null;
|
|
224
|
+
return { width: parts[2], height: parts[3] };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function parseSvgLength(value: string | null): number | null {
|
|
228
|
+
if (!value || value.includes("%")) return null;
|
|
229
|
+
const parsed = Number.parseFloat(value);
|
|
230
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function normalizeExportSize(size: { width: number; height: number }): { width: number; height: number } {
|
|
234
|
+
let width = clamp(Math.ceil(size.width || DEFAULT_EXPORT_WIDTH), MIN_EXPORT_WIDTH, MAX_EXPORT_WIDTH);
|
|
235
|
+
let height = clamp(Math.ceil(size.height || DEFAULT_EXPORT_HEIGHT), MIN_EXPORT_HEIGHT, MAX_EXPORT_HEIGHT);
|
|
236
|
+
const pixels = width * height;
|
|
237
|
+
if (pixels > MAX_EXPORT_PIXELS) {
|
|
238
|
+
const scale = Math.sqrt(MAX_EXPORT_PIXELS / pixels);
|
|
239
|
+
width = Math.max(MIN_EXPORT_WIDTH, Math.floor(width * scale));
|
|
240
|
+
height = Math.max(MIN_EXPORT_HEIGHT, Math.floor(height * scale));
|
|
241
|
+
}
|
|
242
|
+
return { width, height };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function clamp(value: number, min: number, max: number): number {
|
|
246
|
+
return Math.min(Math.max(value, min), max);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function svgMarkupToPngBlob(svg: string, width: number, height: number): Promise<Blob> {
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
const img = new Image();
|
|
252
|
+
const url = URL.createObjectURL(new Blob([svg], { type: "image/svg+xml;charset=utf-8" }));
|
|
253
|
+
img.onload = () => {
|
|
254
|
+
URL.revokeObjectURL(url);
|
|
255
|
+
try {
|
|
256
|
+
const canvas = document.createElement("canvas");
|
|
257
|
+
const scale = Math.min(window.devicePixelRatio || 1, 2);
|
|
258
|
+
canvas.width = Math.ceil(width * scale);
|
|
259
|
+
canvas.height = Math.ceil(height * scale);
|
|
260
|
+
canvas.style.width = width + "px";
|
|
261
|
+
canvas.style.height = height + "px";
|
|
262
|
+
const ctx = canvas.getContext("2d");
|
|
263
|
+
if (!ctx) throw new Error("Canvas is not available.");
|
|
264
|
+
ctx.scale(scale, scale);
|
|
265
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
266
|
+
canvas.toBlob((blob) => {
|
|
267
|
+
if (blob) resolve(blob);
|
|
268
|
+
else reject(new Error("Canvas export returned no image."));
|
|
269
|
+
}, "image/png");
|
|
270
|
+
} catch (error) {
|
|
271
|
+
reject(error instanceof Error ? error : new Error("PNG export failed."));
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
img.onerror = () => {
|
|
275
|
+
URL.revokeObjectURL(url);
|
|
276
|
+
reject(new Error("The browser could not render this widget as PNG."));
|
|
277
|
+
};
|
|
278
|
+
img.src = url;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function buildCapturedStyles(capturedStyles?: string): string {
|
|
283
|
+
return [
|
|
284
|
+
capturedStyles || "",
|
|
285
|
+
"#__widget_export_root{" + buildCssVarDeclarations() + "font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",system-ui,sans-serif;font-size:14px;line-height:1.6;color:var(--text,#1a1a1a);background:transparent;}",
|
|
286
|
+
"#__widget_export_root *{box-sizing:border-box;}",
|
|
287
|
+
"#__widget_export_root svg{max-width:100%;height:auto;display:block;}",
|
|
288
|
+
].join("\n");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function buildCssVarDeclarations(): string {
|
|
292
|
+
if (typeof document === "undefined") return "";
|
|
293
|
+
const computed = getComputedStyle(document.documentElement);
|
|
294
|
+
return THEME_VAR_NAMES
|
|
295
|
+
.map((name) => {
|
|
296
|
+
const value = computed.getPropertyValue(name).trim();
|
|
297
|
+
return value ? name + ":" + value + ";" : "";
|
|
298
|
+
})
|
|
299
|
+
.join("");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function resolveCssVarsInString(input: string): string {
|
|
303
|
+
if (typeof document === "undefined") return input;
|
|
304
|
+
const computed = getComputedStyle(document.documentElement);
|
|
305
|
+
return input.replace(/var\(\s*(--[a-zA-Z0-9_-]+)(?:\s*,\s*([^)]+))?\)/g, (_match, name: string, fallback?: string) => {
|
|
306
|
+
return computed.getPropertyValue(name).trim() || fallback?.trim() || "#000";
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function mergeInlineStyle(current: string | null, next: Record<string, string>): string {
|
|
311
|
+
const parts = new Map<string, string>();
|
|
312
|
+
for (const item of (current || "").split(";")) {
|
|
313
|
+
const [key, ...value] = item.split(":");
|
|
314
|
+
if (key?.trim() && value.length > 0) parts.set(key.trim(), value.join(":").trim());
|
|
315
|
+
}
|
|
316
|
+
for (const [key, value] of Object.entries(next)) parts.set(key, value);
|
|
317
|
+
return Array.from(parts.entries()).map(([key, value]) => key + ":" + value).join(";");
|
|
318
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
export type WidgetGuidelineModule = "interactive" | "chart" | "mockup" | "art" | "diagram";
|
|
2
|
+
|
|
3
|
+
export interface WidgetGuidelineSelection {
|
|
4
|
+
modules: WidgetGuidelineModule[];
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface WidgetGuidelineOptions {
|
|
9
|
+
force?: boolean;
|
|
10
|
+
modules?: WidgetGuidelineModule[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const WIDGET_GUIDELINE_MODULES: WidgetGuidelineModule[] = [
|
|
14
|
+
"interactive",
|
|
15
|
+
"chart",
|
|
16
|
+
"mockup",
|
|
17
|
+
"art",
|
|
18
|
+
"diagram",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const WIDGET_GUIDELINES_BLOCK_PATTERN = /\n?\s*<widget-guidelines\b[^>]*>[\s\S]*?<\/widget-guidelines>\s*/g;
|
|
22
|
+
|
|
23
|
+
const CORE_DESIGN_SYSTEM = `## Core design system
|
|
24
|
+
|
|
25
|
+
### When to use widgets
|
|
26
|
+
- Use \`show-widget\` by default when visual output is the primary answer.
|
|
27
|
+
- This includes charts, diagrams, flowcharts, timelines, dashboards, UI prototypes, mockups, SVG illustrations, icons, logos, infographics, hierarchy views, comparisons, and visual explanations.
|
|
28
|
+
- Do not output raw \`\`\`svg, \`\`\`html, or plain Mermaid for visual answers unless the user explicitly asks for source code, raw Markdown, raw Mermaid, or a saved file.
|
|
29
|
+
- For diagrams, flowcharts, architecture maps, sequence-style explanations, and Markdown-to-diagram requests, prefer \`show-widget\` with SVG/HTML over Mermaid because widgets stream more reliably in this UI.
|
|
30
|
+
- Put explanatory prose outside the code fence. The fence contains exactly one JSON object.
|
|
31
|
+
|
|
32
|
+
### Format rules
|
|
33
|
+
- \`widget_code\` is a JSON string: escape quotes and newlines, close the JSON object, then close the fence.
|
|
34
|
+
- Do not include DOCTYPE, html, head, body, iframe, object, embed, form, link, base, or meta tags.
|
|
35
|
+
- Keep outer backgrounds transparent. Use host variables: \`--bg\`, \`--bg-panel\`, \`--bg-hover\`, \`--bg-selected\`, \`--border\`, \`--text\`, \`--text-muted\`, \`--text-dim\`, \`--accent\`, \`--accent-hover\`.
|
|
36
|
+
- CodePilot-compatible variables are also available: \`--color-background-primary\`, \`--color-background-secondary\`, \`--color-background-tertiary\`, \`--color-text-primary\`, \`--color-text-secondary\`, \`--color-text-tertiary\`, \`--color-border-tertiary\`, \`--color-border-secondary\`, \`--color-border-primary\`, \`--font-sans\`, \`--font-mono\`.
|
|
37
|
+
- HTML widgets should stream in this order: \`<style>\`, visible markup, \`<script>\`. SVG widgets should put \`<defs>\` before visible shapes.
|
|
38
|
+
- Use \`min-height\` instead of fixed outer \`height\`; avoid bottom clipping at chat widths.
|
|
39
|
+
- Use readable text: no font smaller than 11px, no negative letter spacing, no text overlap.
|
|
40
|
+
|
|
41
|
+
### Visual style
|
|
42
|
+
- The widget should feel native to chat, not like a foreign embed.
|
|
43
|
+
- Use flat, solid fills with clear borders. Avoid decorative gradients, shadows, blur, glow, neon, and busy backgrounds.
|
|
44
|
+
- Use 2-3 color ramps per widget and gray/slate for structure.
|
|
45
|
+
- Prefer the visualization type that best fits the content. Do not default to one type for every request.
|
|
46
|
+
- Title should be human-readable in the user's language.`;
|
|
47
|
+
|
|
48
|
+
const COLOR_AND_LAYOUT = `## Color and layout
|
|
49
|
+
|
|
50
|
+
- Palette ramps:
|
|
51
|
+
- Indigo: \`#EEF2FF/#C7D2FE/#818CF8/#4F46E5/#3730A3\`
|
|
52
|
+
- Emerald: \`#ECFDF5/#A7F3D0/#34D399/#059669/#065F46\`
|
|
53
|
+
- Amber: \`#FFFBEB/#FDE68A/#FBBF24/#D97706/#92400E\`
|
|
54
|
+
- Slate: \`#F8FAFC/#E2E8F0/#94A3B8/#64748B/#334155\`
|
|
55
|
+
- Rose: \`#FFF1F2/#FECDD3/#FB7185/#E11D48/#9F1239\`
|
|
56
|
+
- Sky: \`#F0F9FF/#BAE6FD/#38BDF8/#0284C7/#075985\`
|
|
57
|
+
- Use 50 for fills, 200 for strokes, 400 for accents, 600 for subtitles, 800 for titles.
|
|
58
|
+
- Text on colored fills should use the darkest tone in the same ramp, never pure black on saturated color.
|
|
59
|
+
- Cards and nodes: radius 6-12px, border 0.5-1px, enough padding for labels.
|
|
60
|
+
- If the widget contains several panels, align them to a clear grid rather than nesting cards inside cards.
|
|
61
|
+
- Round displayed numbers. Keep typography at weights 400/500 unless a short heading needs emphasis.`;
|
|
62
|
+
|
|
63
|
+
const UI_COMPONENTS = `## UI components
|
|
64
|
+
|
|
65
|
+
- Use HTML when the user asks for controls, calculators, filters, dashboards, drill-down, comparison modes, or parameter exploration.
|
|
66
|
+
- Form elements can use plain button, input, select, textarea, and range controls; the host iframe styles them.
|
|
67
|
+
- Good patterns: chart plus controls, metric dashboard, calculator, bar comparison, toggle/select switching, sortable or filterable list.
|
|
68
|
+
- Controls must visibly change the output. Wire sliders, buttons, toggles, and selects to update values, labels, charts, or highlighted elements.
|
|
69
|
+
- For clickable follow-up actions, call \`window.__widgetSendMessage("short request")\`.
|
|
70
|
+
- For cross-widget filtering, publish with \`window.__widgetPublish("topic", { key: "value" })\` and listen with \`window.addEventListener("widget-filter", function(e) { ... })\`.
|
|
71
|
+
- Keep state in plain JavaScript. Use unique IDs. Initialize scripts after the visible markup exists.`;
|
|
72
|
+
|
|
73
|
+
const INTERACTIVE_GUIDELINES = `## Interactive widgets
|
|
74
|
+
|
|
75
|
+
- Controls must visibly change the output. Wire sliders, buttons, toggles, and selects to update values, labels, and charts.
|
|
76
|
+
- Keep state in plain JavaScript. Use unique IDs. Initialize after the visible markup exists.
|
|
77
|
+
- If a CDN library is used, include a fallback initializer: \`if (window.Chart) init();\` after the script tag.
|
|
78
|
+
- Keep inline scripts short and resilient. Do not depend on network calls; \`connect-src\` is disabled.`;
|
|
79
|
+
|
|
80
|
+
const CHART_GUIDELINES = `## Charts and data views
|
|
81
|
+
|
|
82
|
+
- Choose the chart by data shape: bar for categories, line for trends, scatter for correlation, heatmap for matrices, histogram or box for distributions, Sankey or flow only for flows.
|
|
83
|
+
- Always include labels, units when known, legend or direct labels, and a short note outside the fence when interpretation matters.
|
|
84
|
+
- SVG is best for simple bar, line, scatter, heatmap, timeline, and matrix charts with modest data. Use \`<svg width="100%" viewBox="0 0 680 H">\`.
|
|
85
|
+
- Chart.js is acceptable for interactive line, bar, scatter, doughnut, and mixed charts.
|
|
86
|
+
- Chart.js wrapper: \`<div style="position:relative;width:100%;height:320px;min-height:320px"><canvas id="c"></canvas></div>\`; set \`responsive:true\` and \`maintainAspectRatio:false\`.
|
|
87
|
+
- Interactive chart controls must update \`chart.data\` or options and then call \`chart.update()\`.
|
|
88
|
+
- Use unique canvas IDs if there is more than one chart.
|
|
89
|
+
- For script-generated plots, prefer embedding a base64 PNG in an \`<img>\` with \`max-width:100%\`; do not rely on local \`file://\` paths.`;
|
|
90
|
+
|
|
91
|
+
const CHART_JS_GUIDELINES = `## Chart.js pattern
|
|
92
|
+
|
|
93
|
+
\`\`\`html
|
|
94
|
+
<div style="position:relative;width:100%;height:300px;min-height:300px"><canvas id="c"></canvas></div>
|
|
95
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js" onload="init()"></script>
|
|
96
|
+
<script>
|
|
97
|
+
var chart;
|
|
98
|
+
function init(){
|
|
99
|
+
chart = new Chart(document.getElementById("c"), {
|
|
100
|
+
type: "line",
|
|
101
|
+
data: { labels: ["Jan","Feb","Mar"], datasets: [{ data: [30,45,28], borderColor: "#818CF8", backgroundColor: "rgba(129,140,248,0.1)", fill: true, tension: 0.3 }] },
|
|
102
|
+
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } } }
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (window.Chart) init();
|
|
106
|
+
</script>
|
|
107
|
+
\`\`\`
|
|
108
|
+
|
|
109
|
+
- Canvas cannot use CSS variables directly; use hex values from the palette.
|
|
110
|
+
- Height belongs on the wrapper div. Use \`responsive:true\` and \`maintainAspectRatio:false\`.
|
|
111
|
+
- Usually disable legends and prefer direct labels unless the chart has several datasets.
|
|
112
|
+
- For interactive charts, update \`chart.data\` or \`chart.options\`, then call \`chart.update()\`.
|
|
113
|
+
- Multiple charts need unique canvas IDs.`;
|
|
114
|
+
|
|
115
|
+
const MOCKUP_GUIDELINES = `## UI mockups and prototypes
|
|
116
|
+
|
|
117
|
+
- Use HTML/CSS for app screens, forms, settings panels, tables, dashboards, and product mockups.
|
|
118
|
+
- Make the first viewport look like the requested product or workflow, not a landing page unless the user asks for one.
|
|
119
|
+
- Use realistic controls: buttons for commands, inputs for text, selects for option sets, checkboxes or toggles for binary settings, tabs for alternate views.
|
|
120
|
+
- Keep operational tools dense and scannable. Prefer restrained panels, clear labels, and predictable navigation.
|
|
121
|
+
- Include meaningful empty, selected, hover, disabled, and error states when they are relevant to the requested prototype.
|
|
122
|
+
- Do not include explanatory help text inside the mockup unless the user asked for onboarding copy.`;
|
|
123
|
+
|
|
124
|
+
const ART_GUIDELINES = `## SVG illustrations and spatial visuals
|
|
125
|
+
|
|
126
|
+
- Use SVG for diagrams that are mostly shapes, anatomy, geometry, spatial reasoning, visual metaphors, icons, and explanatory illustrations.
|
|
127
|
+
- Use SVG for icons and logos unless the user explicitly asks for source code only. Wrap the SVG in \`show-widget\` so it renders in chat.
|
|
128
|
+
- Start with \`<svg width="100%" viewBox="0 0 680 H" role="img" aria-label="...">\`.
|
|
129
|
+
- Keep all content inside x=0..680 and y=0..H. Set H to the lowest element bottom plus at least 32px.
|
|
130
|
+
- Use simple paths, circles, rectangles, labels, and callouts. Avoid high-detail art that will not survive compact rendering.
|
|
131
|
+
- Label important parts directly. Use leader lines when labels might collide with shapes.
|
|
132
|
+
- Prefer inline SVG styles over external CSS for small illustrations.`;
|
|
133
|
+
|
|
134
|
+
const SVG_SETUP = `## SVG setup
|
|
135
|
+
|
|
136
|
+
- Use \`<svg width="100%" viewBox="0 0 680 H">\` with a 680px fixed width. Adjust H to fit content plus a 32-40px buffer.
|
|
137
|
+
- ViewBox checklist: max lowest y plus height plus buffer equals H; all content stays within x=0..680; avoid negative coordinates; account for text anchors that extend left or right.
|
|
138
|
+
- Arrow marker:
|
|
139
|
+
\`<defs><marker id="a" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker></defs>\`
|
|
140
|
+
- Use inline font styles with system-ui fallback. 13-14px labels, 11-12px subtitles, 0.5-1px borders, 1.5px arrows, rx=8-12 for nodes.
|
|
141
|
+
- One SVG per widget unless the user asks for a sprite sheet or icon set.`;
|
|
142
|
+
|
|
143
|
+
const DIAGRAM_GUIDELINES = `## Diagrams
|
|
144
|
+
|
|
145
|
+
- Use SVG for flowcharts, architecture diagrams, timelines, dependency maps, sequence-style explanations, hierarchies, cycles, and process views.
|
|
146
|
+
- Prefer SVG/HTML in \`show-widget\` over Mermaid for generated diagrams. Use Mermaid only when the user explicitly asks for Mermaid source, raw Mermaid, raw Markdown, or a saved Mermaid/Markdown file.
|
|
147
|
+
- Recommended setup: \`<svg width="100%" viewBox="0 0 680 H"><defs><marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker></defs>...\`.
|
|
148
|
+
- Flowchart: left-to-right or top-to-bottom, straight arrows, no more than 4 nodes per row, decision nodes visually distinct.
|
|
149
|
+
- Timeline: axis line with event markers, staggered labels, dates aligned and readable.
|
|
150
|
+
- Architecture: horizontal layers from user-facing at top to infrastructure at bottom; group related services in bounded regions.
|
|
151
|
+
- Hierarchy/tree: root at top, children below, aligned levels, sibling groups balanced.
|
|
152
|
+
- Cycle: 3-5 nodes around a loop with curved arrows and a center label.
|
|
153
|
+
- Comparison: two parallel groups with matching rows and direct labels.
|
|
154
|
+
- Node titles should be short. Minimum node width should fit the longest label plus padding.
|
|
155
|
+
- Verify arrows do not cross unrelated boxes and text does not overlap connectors.`;
|
|
156
|
+
|
|
157
|
+
const DIAGRAM_TYPES = `## Diagram type catalog
|
|
158
|
+
|
|
159
|
+
- Flowchart/process: nodes left-to-right or top-to-bottom. Straight arrows. Decision points visually distinct. No more than 4 nodes per row.
|
|
160
|
+
- Timeline: horizontal or vertical axis with event markers. Stagger labels to avoid overlap.
|
|
161
|
+
- Cycle/feedback loop: 3-5 nodes connected by curved arrows with a center label.
|
|
162
|
+
- Hierarchy/tree: root at top, children below, aligned levels, sibling groups balanced.
|
|
163
|
+
- Layered architecture: full-width horizontal bands from user-facing at top to infrastructure at bottom.
|
|
164
|
+
- Quadrant/matrix: two axes with four lightly colored regions and plotted items.
|
|
165
|
+
- Hub-spoke/radial: central node plus surrounding nodes connected by lines.
|
|
166
|
+
- Side-by-side comparison: two parallel groups with matching rows and optional correspondence lines.
|
|
167
|
+
- Multi-widget narratives: for complex topics, output multiple widgets of different types, with short prose between them.`;
|
|
168
|
+
|
|
169
|
+
const MODULE_SECTIONS: Record<WidgetGuidelineModule, string[]> = {
|
|
170
|
+
interactive: [CORE_DESIGN_SYSTEM, UI_COMPONENTS, COLOR_AND_LAYOUT, INTERACTIVE_GUIDELINES],
|
|
171
|
+
chart: [CORE_DESIGN_SYSTEM, UI_COMPONENTS, COLOR_AND_LAYOUT, CHART_GUIDELINES, CHART_JS_GUIDELINES],
|
|
172
|
+
mockup: [CORE_DESIGN_SYSTEM, UI_COMPONENTS, COLOR_AND_LAYOUT, MOCKUP_GUIDELINES, INTERACTIVE_GUIDELINES],
|
|
173
|
+
art: [CORE_DESIGN_SYSTEM, COLOR_AND_LAYOUT, SVG_SETUP, ART_GUIDELINES],
|
|
174
|
+
diagram: [CORE_DESIGN_SYSTEM, COLOR_AND_LAYOUT, SVG_SETUP, ART_GUIDELINES, DIAGRAM_GUIDELINES, DIAGRAM_TYPES],
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const NON_WIDGET_FORMAT_PATTERN = /\b(raw markdown|plain markdown|raw mermaid|plain mermaid|mermaid only|no widgets?|without widgets?|no generative ui|save (it )?(to|as) (a )?file)\b/i;
|
|
178
|
+
const NON_WIDGET_FORMAT_ZH_PATTERN = /不要.*widget|不要.*组件|不用.*组件|不要.*生成式\s*UI|只要.*mermaid|原始\s*mermaid|保存到文件/;
|
|
179
|
+
|
|
180
|
+
const CHART_PATTERN = /\b(chart|plot|graph|visuali[sz]e|data viz|dashboard|trend|time series|timeseries|metric|statistics?|histogram|scatter|heat ?map|box ?plot|volcano|pca|umap|tsne|t-sne|bar chart|line chart|pie chart|donut|treemap|sankey)\b/i;
|
|
181
|
+
const CHART_ZH_PATTERN = /图表|画图|绘图|可视化|仪表盘|看板|趋势图|统计图|热图|散点图|折线图|柱状图|条形图|饼图|箱线图|小提琴图|火山图|雷达图|矩阵图|相关性图|降维图/;
|
|
182
|
+
|
|
183
|
+
const DIAGRAM_PATTERN = /\b(diagram|flowchart|flow chart|architecture|sequence|timeline|workflow|pipeline|process map|dependency|system design|hierarchy|tree|cycle|feedback loop|state machine|mind ?map|concept map|comparison|compare|infographic)\b/i;
|
|
184
|
+
const DIAGRAM_ZH_PATTERN = /流程图|架构图|结构图|时序图|序列图|时间线|工作流|管线|过程图|依赖图|拓扑图|层级图|树状图|循环图|状态机|脑图|概念图|关系图|桑基图|对比|比较|信息图/;
|
|
185
|
+
|
|
186
|
+
const MOCKUP_PATTERN = /\b(mockup|prototype|wireframe|ui prototype|ui mockup|ux mockup|screen design|page layout|app layout|interface prototype|interface mockup|form mockup|settings panel|admin panel mockup|product page|landing page)\b/i;
|
|
187
|
+
const MOCKUP_ZH_PATTERN = /原型图|原型|线框图|界面原型|页面原型|布局草图|界面设计|页面设计|设置面板|产品页|落地页|管理台原型|控制台原型/;
|
|
188
|
+
|
|
189
|
+
const INTERACTIVE_PATTERN = /\b(interactive|control|slider|toggle|filter|drill ?down|calculator|simulator|playground|clickable|button|select|dropdown|tabs?|stepper|what-if|scenario|sortable|editable)\b/i;
|
|
190
|
+
const INTERACTIVE_ZH_PATTERN = /交互式|交互|控件|滑块|切换|筛选|过滤|下钻|计算器|模拟器|可点击|按钮|下拉|选项卡|参数|联动|排序|可编辑/;
|
|
191
|
+
|
|
192
|
+
const ART_PATTERN = /\b(draw|sketch|illustrat(e|ion)|svg|canvas|icon|logo|visual explanation|spatial|geometry|geometric|anatomy|callout|infographic)\b/i;
|
|
193
|
+
const ART_ZH_PATTERN = /绘制|画一个|插图|示意图|空间|几何|图形|解剖|标注|图解|图示|图标|标志|logo|信息图/i;
|
|
194
|
+
|
|
195
|
+
const WIDGET_PATTERN = /\b(show-widget|generative ui|visual output)\b|\b(create|build|make|generate|render|show)\b.{0,40}\bwidget\b|\bwidget\b.{0,40}\b(showing|for|with)\b/i;
|
|
196
|
+
const WIDGET_ZH_PATTERN = /生成式\s*UI|可视化输出/;
|
|
197
|
+
|
|
198
|
+
function hasMatch(pattern: RegExp, message: string): boolean {
|
|
199
|
+
pattern.lastIndex = 0;
|
|
200
|
+
return pattern.test(message);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function hasAnyVisualIntent(message: string): boolean {
|
|
204
|
+
return hasMatch(CHART_PATTERN, message)
|
|
205
|
+
|| hasMatch(CHART_ZH_PATTERN, message)
|
|
206
|
+
|| hasMatch(DIAGRAM_PATTERN, message)
|
|
207
|
+
|| hasMatch(DIAGRAM_ZH_PATTERN, message)
|
|
208
|
+
|| hasMatch(MOCKUP_PATTERN, message)
|
|
209
|
+
|| hasMatch(MOCKUP_ZH_PATTERN, message)
|
|
210
|
+
|| hasMatch(INTERACTIVE_PATTERN, message)
|
|
211
|
+
|| hasMatch(INTERACTIVE_ZH_PATTERN, message)
|
|
212
|
+
|| hasMatch(ART_PATTERN, message)
|
|
213
|
+
|| hasMatch(ART_ZH_PATTERN, message)
|
|
214
|
+
|| hasMatch(WIDGET_PATTERN, message)
|
|
215
|
+
|| hasMatch(WIDGET_ZH_PATTERN, message);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function selectWidgetGuidelineModules(message: string): WidgetGuidelineModule[] {
|
|
219
|
+
const text = message.trim();
|
|
220
|
+
if (!text || hasMatch(NON_WIDGET_FORMAT_PATTERN, text) || hasMatch(NON_WIDGET_FORMAT_ZH_PATTERN, text)) return [];
|
|
221
|
+
|
|
222
|
+
const selected = new Set<WidgetGuidelineModule>();
|
|
223
|
+
const chart = hasMatch(CHART_PATTERN, text) || hasMatch(CHART_ZH_PATTERN, text);
|
|
224
|
+
const diagram = hasMatch(DIAGRAM_PATTERN, text) || hasMatch(DIAGRAM_ZH_PATTERN, text);
|
|
225
|
+
const mockup = hasMatch(MOCKUP_PATTERN, text) || hasMatch(MOCKUP_ZH_PATTERN, text);
|
|
226
|
+
const interactive = hasMatch(INTERACTIVE_PATTERN, text) || hasMatch(INTERACTIVE_ZH_PATTERN, text);
|
|
227
|
+
const art = hasMatch(ART_PATTERN, text) || hasMatch(ART_ZH_PATTERN, text);
|
|
228
|
+
const widget = hasMatch(WIDGET_PATTERN, text) || hasMatch(WIDGET_ZH_PATTERN, text);
|
|
229
|
+
|
|
230
|
+
if (interactive) selected.add("interactive");
|
|
231
|
+
if (chart) selected.add("chart");
|
|
232
|
+
if (diagram) selected.add("diagram");
|
|
233
|
+
if (mockup) selected.add("mockup");
|
|
234
|
+
if (art && !chart && !diagram) selected.add("art");
|
|
235
|
+
|
|
236
|
+
if (mockup && interactive) selected.add("interactive");
|
|
237
|
+
if (chart && /\b(dashboard|filter|drill ?down|interactive|control|slider|toggle|what-if)\b/i.test(text)) {
|
|
238
|
+
selected.add("interactive");
|
|
239
|
+
}
|
|
240
|
+
if (chart && /仪表盘|看板|筛选|过滤|下钻|交互|滑块|参数|联动/.test(text)) {
|
|
241
|
+
selected.add("interactive");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (selected.size === 0 && (widget || hasAnyVisualIntent(text))) {
|
|
245
|
+
selected.add("diagram");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return WIDGET_GUIDELINE_MODULES.filter((module) => selected.has(module));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function getWidgetGuidelines(modules: WidgetGuidelineModule[]): string {
|
|
252
|
+
const seen = new Set<string>();
|
|
253
|
+
const parts: string[] = [];
|
|
254
|
+
for (const moduleName of modules) {
|
|
255
|
+
for (const section of MODULE_SECTIONS[moduleName]) {
|
|
256
|
+
if (seen.has(section)) continue;
|
|
257
|
+
seen.add(section);
|
|
258
|
+
parts.push(section);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return parts.join("\n\n");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function getWidgetGuidelinesForMessage(message: string, options?: WidgetGuidelineOptions): WidgetGuidelineSelection | null {
|
|
265
|
+
let modules = selectWidgetGuidelineModules(message);
|
|
266
|
+
if (modules.length === 0 && options?.force) {
|
|
267
|
+
modules = options.modules?.length ? options.modules : WIDGET_GUIDELINE_MODULES;
|
|
268
|
+
}
|
|
269
|
+
if (modules.length === 0) return null;
|
|
270
|
+
return { modules, content: getWidgetGuidelines(modules) };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function stripWidgetGuidelines(systemPrompt: string): string {
|
|
274
|
+
return systemPrompt
|
|
275
|
+
.replace(WIDGET_GUIDELINES_BLOCK_PATTERN, "\n")
|
|
276
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
277
|
+
.trim();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function withWidgetGuidelinesForMessage(systemPrompt: string, message: string, options?: WidgetGuidelineOptions): string {
|
|
281
|
+
const base = stripWidgetGuidelines(systemPrompt);
|
|
282
|
+
const selection = getWidgetGuidelinesForMessage(message, options);
|
|
283
|
+
if (!selection) return base;
|
|
284
|
+
|
|
285
|
+
const modules = selection.modules.join(",");
|
|
286
|
+
const block = `<widget-guidelines modules="${modules}">\n${selection.content}\n</widget-guidelines>`;
|
|
287
|
+
return base ? `${base}\n\n${block}` : block;
|
|
288
|
+
}
|