create-yss-spec 1.1.2 → 1.1.4
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/package.json +1 -1
- package/template/.agents/skills/excalidraw-diagram-generator/SKILL.md +3 -3
- package/template/.agents/skills/grill-me/SKILL.md +7 -0
- package/template/.agents/skills/grilling/SKILL.md +12 -0
- package/template/.agents/skills/handoff/SKILL.md +16 -0
- package/template/.agents/skills/to-issues/SKILL.md +2 -2
- package/template/.claude/skills/grill-me/SKILL.md +7 -0
- package/template/.claude/skills/grilling/SKILL.md +12 -0
- package/template/.claude/skills/handoff/SKILL.md +16 -0
- package/template/.claude/skills/to-issues/SKILL.md +2 -2
- package/template/.codex/skills/cross-repo-implementation-routing/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/.app.json +84 -0
- package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +66 -0
- package/template/.codex/skills/data-analytics/.mcp.json +30 -0
- package/template/.codex/skills/data-analytics/AGENTS.md +20 -0
- package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +27 -0
- package/template/.codex/skills/data-analytics/README.md +60 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +16 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +16 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-small.svg +5 -0
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +16 -0
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +1 -0
- package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
- package/template/.codex/skills/data-analytics/assets/datascience.svg +10 -0
- package/template/.codex/skills/data-analytics/mcp/server.cjs +2963 -0
- package/template/.codex/skills/data-analytics/package-lock.json +3048 -0
- package/template/.codex/skills/data-analytics/package.json +43 -0
- package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +75 -0
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +160 -0
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +148 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +18 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +24 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +71 -0
- package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +85 -0
- package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +207 -0
- package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +70 -0
- package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +66 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +118 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +1 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +57 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +61 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +362 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +829 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +30 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +129 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +374 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +613 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +54 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +16 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +9 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +78 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +2379 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +88 -0
- package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +97 -0
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +59 -0
- package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +75 -0
- package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +103 -0
- package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +68 -0
- package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/index/SKILL.md +251 -0
- package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +131 -0
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +141 -0
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +32 -0
- package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +106 -0
- package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +130 -0
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +141 -0
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +178 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +9 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +31 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +466 -0
- package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +99 -0
- package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +195 -0
- package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +26 -0
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +51 -0
- package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +66 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +69 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +204 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +87 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +497 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +74 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +65 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +160 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +75 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +96 -0
- package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +263 -0
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +1460 -0
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +128 -0
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +101 -0
- package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +499 -0
- package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +978 -0
- package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +190 -0
- package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +157 -0
- package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +6 -0
- package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +774 -0
- package/template/.codex/skills/data-analytics/src/DESIGN.md +222 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +3666 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +136 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +54 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +96 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +1647 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +245 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +462 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +107 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +164 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +192 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +203 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +619 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +402 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +370 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +536 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +681 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +164 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +21 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +472 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +75 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +142 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +142 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +377 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +3468 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +44 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +639 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +327 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +225 -0
- package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +204 -0
- package/template/.codex/skills/data-analytics/src/analytics-app-core.md +85 -0
- package/template/.codex/skills/data-analytics/src/codex-style-contract.md +30 -0
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +12 -0
- package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +1008 -0
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +2117 -0
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +122 -0
- package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +3588 -0
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +483 -0
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +32 -0
- package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +333 -0
- package/template/.codex/skills/data-analytics/src/mcp-host.js +153 -0
- package/template/.codex/skills/data-analytics/src/recharts-config.js +315 -0
- package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +294 -0
- package/template/.codex/skills/data-analytics/src/sql-source-view.css +33 -0
- package/template/.codex/skills/data-analytics/src/sql-source-view.js +157 -0
- package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +121 -0
- package/template/.codex/skills/data-analytics/src/table-renderer.jsx +28 -0
- package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +87 -0
- package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +105 -0
- package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +233 -0
- package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +1500 -0
- package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +219 -0
- package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +144 -0
- package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +11 -0
- package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +54 -0
- package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +321 -0
- package/template/.codex/skills/data-analytics/tsconfig.json +19 -0
- package/template/.codex/skills/data-analytics/vite.config.ts +46 -0
- package/template/.codex/skills/product-design/.app.json +7 -0
- package/template/.codex/skills/product-design/.codex-plugin/plugin.json +50 -0
- package/template/.codex/skills/product-design/README.md +50 -0
- package/template/.codex/skills/product-design/agents/openai.yaml +4 -0
- package/template/.codex/skills/product-design/assets/composerIcon.svg +9 -0
- package/template/.codex/skills/product-design/assets/logo.png +0 -0
- package/template/.codex/skills/product-design/package.json +11 -0
- package/template/.codex/skills/product-design/references/browser-order.md +9 -0
- package/template/.codex/skills/product-design/references/communication-protocol.md +45 -0
- package/template/.codex/skills/product-design/references/critical-overrides.md +58 -0
- package/template/.codex/skills/product-design/references/local-prototype-preflight.md +16 -0
- package/template/.codex/skills/product-design/scripts/bootstrap-prototype.mjs +147 -0
- package/template/.codex/skills/product-design/skills/audit/SKILL.md +159 -0
- package/template/.codex/skills/product-design/skills/audit/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/audit/references/design-audit-framework.md +73 -0
- package/template/.codex/skills/product-design/skills/design-qa/SKILL.md +133 -0
- package/template/.codex/skills/product-design/skills/design-qa/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/design-qa/references/qa-rubric.md +61 -0
- package/template/.codex/skills/product-design/skills/get-context/SKILL.md +81 -0
- package/template/.codex/skills/product-design/skills/get-context/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/ideate/SKILL.md +179 -0
- package/template/.codex/skills/product-design/skills/ideate/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/image-to-code/SKILL.md +105 -0
- package/template/.codex/skills/product-design/skills/image-to-code/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/index/SKILL.md +126 -0
- package/template/.codex/skills/product-design/skills/index/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/prototype/SKILL.md +128 -0
- package/template/.codex/skills/product-design/skills/prototype/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/prototype/references/existing-codebase-edits.md +18 -0
- package/template/.codex/skills/product-design/skills/research/SKILL.md +92 -0
- package/template/.codex/skills/product-design/skills/research/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/share/SKILL.md +41 -0
- package/template/.codex/skills/product-design/skills/share/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/url-to-code/SKILL.md +124 -0
- package/template/.codex/skills/product-design/skills/url-to-code/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/user-context/SKILL.md +147 -0
- package/template/.codex/skills/product-design/skills/user-context/agents/openai.yaml +6 -0
- package/template/.codex/skills/product-design/skills/user-context/plugin-author-config/user-context-template.md +63 -0
- package/template/.codex/skills/product-design/skills/user-context/references/onboarding.md +66 -0
- package/template/.codex/skills/product-design/skills/user-context/scripts/init_user_context.py +86 -0
- package/template/.codex/skills/product-design/skills/user-context/scripts/user_context_preflight.py +204 -0
- package/template/.codex/skills/product-design/templates/prototype/AGENTS.md +7 -0
- package/template/.codex/skills/product-design/templates/prototype/index.html +12 -0
- package/template/.codex/skills/product-design/templates/prototype/package.json +18 -0
- package/template/.codex/skills/product-design/templates/prototype/src/App.jsx +5 -0
- package/template/.codex/skills/product-design/templates/prototype/src/main.jsx +10 -0
- package/template/.codex/skills/product-design/templates/prototype/src/styles.css +13 -0
- package/template/.codex/skills/product-design/templates/prototype/vite.config.mjs +14 -0
- package/template/.codex/skills/product-design-prototype/SKILL.md +1 -1
- package/template/.codex/skills/yss-api-integration/SKILL.md +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +1 -1
- package/template/.codex/skills/yss-filerunner/SKILL.md +1 -1
- package/template/.codex/skills/yss-openapi/SKILL.md +1 -1
- package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +1 -1
- package/template/.codex/skills/yss-openapi-governance/SKILL.md +3 -3
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +23 -7
- package/template/.codex/skills/yss-router/SKILL.md +1 -2
- package/template/.codex/skills/yss-router/references/boundaries.md +0 -1
- package/template/.codex/skills/yss-source-index/references/source-map-config.md +0 -1
- package/template/.codex/skills/yss-source-index/scripts/refresh-yss-skill-index.py +0 -3
- package/template/.hermes/skills/grill-me/SKILL.md +7 -0
- package/template/.hermes/skills/grilling/SKILL.md +12 -0
- package/template/.hermes/skills/handoff/SKILL.md +16 -0
- package/template/.hermes/skills/to-issues/SKILL.md +2 -2
- package/template/.pi/skills/grill-me/SKILL.md +7 -0
- package/template/.pi/skills/grilling/SKILL.md +12 -0
- package/template/.pi/skills/handoff/SKILL.md +16 -0
- package/template/.pi/skills/to-issues/SKILL.md +2 -2
- package/template/.trae/skills/grill-me/SKILL.md +7 -0
- package/template/.trae/skills/grilling/SKILL.md +12 -0
- package/template/.trae/skills/handoff/SKILL.md +16 -0
- package/template/.trae/skills/to-issues/SKILL.md +2 -2
- package/template/AGENTS.md +11 -3
- package/template/docs/adr/adr-product-design-prototype-entrypoint.md +54 -0
- package/template/docs/api/templates/openapi-draft-review-checklist.md +4 -2
- package/template/docs/design/README.md +8 -4
- package/template/docs/design/templates/interaction-spec-template.md +4 -1
- package/template/docs/design/templates/prototype-confirmation-template.md +57 -0
- package/template/docs/design/templates/prototype-review-checklist.md +6 -2
- package/template/docs/design/templates/state-matrix-template.md +1 -1
- package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +22 -3
- package/template/docs/process/harness-executive-blueprint.md +2 -1
- package/template/docs/process/harness-process-tailoring.md +8 -1
- package/template/docs/process/harness-work-unit-map.md +1 -1
- package/template/docs/process/lifecycle-artifact-map.md +17 -8
- package/template/docs/process/product-prototype-workflow.md +82 -0
- package/template/docs/user-guide/product-lifecycle-workflow.md +2 -2
- package/template/docs/user-guide/product-rd-lifecycle-best-practices.md +15 -10
- package/template/scripts/verify-template +0 -14
- package/template/skills-lock.json +70 -27
- package/template/.codex/skills/yss-anti-scheduler/SKILL.md +0 -53
- package/template/.codex/skills/yss-anti-scheduler/agents/openai.yaml +0 -4
- package/template/.codex/skills/yss-anti-scheduler/references/source-index.md +0 -157
|
@@ -0,0 +1,1008 @@
|
|
|
1
|
+
import React, { StrictMode, useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
|
|
4
|
+
import { connectMcpWidgetHost } from "./mcp-host.js";
|
|
5
|
+
import AnalyticsApp from "./analytics-app/App";
|
|
6
|
+
import "./styles/codex-theme.css";
|
|
7
|
+
import "./analytics-app/tokens.css";
|
|
8
|
+
import "./analytics-app/charting/chart-tokens.css";
|
|
9
|
+
import "./analytics-app/styles.css";
|
|
10
|
+
|
|
11
|
+
const CHART_WIDGET_RESOURCE_URI = "ui://widget/datascience-chart.html";
|
|
12
|
+
const ARTIFACT_BLOCK_TYPES = new Set(["markdown", "metric-strip", "chart", "table", "html"]);
|
|
13
|
+
let chartWidgetHtmlPromise = null;
|
|
14
|
+
|
|
15
|
+
const artifactApp = connectMcpWidgetHost({
|
|
16
|
+
name: "Data Analytics Artifact App",
|
|
17
|
+
version: "0.1.8",
|
|
18
|
+
availableDisplayModes: ["inline", "fullscreen"],
|
|
19
|
+
initialDisplayMode: "fullscreen",
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const fallbackPayload = {
|
|
23
|
+
ok: true,
|
|
24
|
+
widget_type: "artifact",
|
|
25
|
+
surface: "report",
|
|
26
|
+
manifest: {
|
|
27
|
+
version: 1,
|
|
28
|
+
surface: "report",
|
|
29
|
+
title: "Core Business Metrics Trend Readout",
|
|
30
|
+
description: "Fallback report payload for local widget development.",
|
|
31
|
+
generatedAt: "2026-05-08T00:00:00Z",
|
|
32
|
+
blocks: [
|
|
33
|
+
{
|
|
34
|
+
id: "summary_text",
|
|
35
|
+
type: "markdown",
|
|
36
|
+
layout: "full",
|
|
37
|
+
body:
|
|
38
|
+
"## Executive Summary\n\nThis fallback verifies that MCP artifact payloads render with the full report app shell.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "revenue_metrics",
|
|
42
|
+
type: "metric-strip",
|
|
43
|
+
layout: "full",
|
|
44
|
+
cardIds: ["revenue_card"],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "revenue_chart_block",
|
|
48
|
+
type: "chart",
|
|
49
|
+
layout: "full",
|
|
50
|
+
chartId: "revenue_chart",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "revenue_table_block",
|
|
54
|
+
type: "table",
|
|
55
|
+
layout: "full",
|
|
56
|
+
tableId: "revenue_table",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
cards: [
|
|
60
|
+
{
|
|
61
|
+
id: "revenue_card",
|
|
62
|
+
dataset: "weekly_revenue",
|
|
63
|
+
metrics: [
|
|
64
|
+
{ label: "Revenue", field: "revenue_m", format: "currency" },
|
|
65
|
+
{ label: "WoW", field: "wow", format: "percent", signed: true },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
charts: [
|
|
70
|
+
{
|
|
71
|
+
id: "revenue_chart",
|
|
72
|
+
title: "Revenue Trend",
|
|
73
|
+
type: "line",
|
|
74
|
+
dataset: "weekly_revenue_trend",
|
|
75
|
+
sourceId: "snapshot",
|
|
76
|
+
encodings: {
|
|
77
|
+
x: { field: "week", type: "ordinal" },
|
|
78
|
+
y: { field: "revenue_m", type: "quantitative", label: "Revenue", format: "currency" },
|
|
79
|
+
color: { field: "series", type: "nominal" },
|
|
80
|
+
},
|
|
81
|
+
valueFormat: "currency",
|
|
82
|
+
layout: "full",
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
tables: [
|
|
86
|
+
{
|
|
87
|
+
id: "revenue_table",
|
|
88
|
+
title: "Revenue Segment Detail",
|
|
89
|
+
dataset: "revenue_segment_detail",
|
|
90
|
+
sourceId: "snapshot",
|
|
91
|
+
columns: [
|
|
92
|
+
{ field: "reporting_week", label: "Reporting week" },
|
|
93
|
+
{ field: "segment", label: "Customer segment" },
|
|
94
|
+
{ field: "gross_revenue_m", label: "Gross revenue", format: "currency" },
|
|
95
|
+
{ field: "year_over_year_growth_rate", label: "Year-over-year growth rate", format: "percent" },
|
|
96
|
+
{ field: "estimated_pipeline_coverage_ratio", label: "Estimated pipeline coverage ratio" },
|
|
97
|
+
{ field: "customer_activation_rate", label: "Customer activation rate", format: "percent" },
|
|
98
|
+
{ field: "operating_margin_rate", label: "Operating margin rate", format: "percent" },
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
sources: [{ id: "snapshot", label: "Fallback snapshot", path: "data/snapshot.json" }],
|
|
103
|
+
},
|
|
104
|
+
snapshot: {
|
|
105
|
+
version: 1,
|
|
106
|
+
generatedAt: "2026-05-08T00:00:00Z",
|
|
107
|
+
status: "fixture",
|
|
108
|
+
datasets: {
|
|
109
|
+
weekly_revenue: [
|
|
110
|
+
{ week: "Apr 27", revenue_m: 12, wow: "+8% w/w" },
|
|
111
|
+
{ week: "May 04", revenue_m: 18, wow: "+16% w/w" },
|
|
112
|
+
],
|
|
113
|
+
weekly_revenue_trend: [
|
|
114
|
+
{ week: "Apr 27", series: "Actual", revenue_m: 12 },
|
|
115
|
+
{ week: "May 04", series: "Actual", revenue_m: 18 },
|
|
116
|
+
{ week: "Apr 27", series: "Target", revenue_m: 14 },
|
|
117
|
+
{ week: "May 04", series: "Target", revenue_m: 16 },
|
|
118
|
+
],
|
|
119
|
+
revenue_segment_detail: [
|
|
120
|
+
{ reporting_week: "2026-04-27", segment: "Enterprise", gross_revenue_m: 8.2, year_over_year_growth_rate: 0.31, estimated_pipeline_coverage_ratio: "4.3x", customer_activation_rate: 0.67, operating_margin_rate: 0.42 },
|
|
121
|
+
{ reporting_week: "2026-05-04", segment: "Self-serve", gross_revenue_m: 9.8, year_over_year_growth_rate: 0.28, estimated_pipeline_coverage_ratio: "3.8x", customer_activation_rate: 0.61, operating_margin_rate: 0.39 },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
package_info: {
|
|
126
|
+
manifestPath: "tool payload",
|
|
127
|
+
root: "mcp://datascience-artifact",
|
|
128
|
+
snapshotPath: "tool payload",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const hostedEmptyPayload = {
|
|
133
|
+
ok: false,
|
|
134
|
+
widget_type: "artifact",
|
|
135
|
+
surface: "report",
|
|
136
|
+
manifest: {
|
|
137
|
+
version: 1,
|
|
138
|
+
surface: "report",
|
|
139
|
+
title: "Data Analytics artifact",
|
|
140
|
+
description: "Waiting for an artifact payload.",
|
|
141
|
+
blocks: [
|
|
142
|
+
{
|
|
143
|
+
id: "empty_state",
|
|
144
|
+
type: "markdown",
|
|
145
|
+
layout: "full",
|
|
146
|
+
body:
|
|
147
|
+
"## Waiting for Data\n\nThis hosted artifact has not received a manifest and bounded snapshot yet.",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
sources: [],
|
|
151
|
+
},
|
|
152
|
+
snapshot: {
|
|
153
|
+
version: 1,
|
|
154
|
+
status: "blocked",
|
|
155
|
+
datasets: {},
|
|
156
|
+
accessIssues: [
|
|
157
|
+
{
|
|
158
|
+
id: "missing_artifact_payload",
|
|
159
|
+
message: "The hosted widget has not received a Data Analytics artifact payload yet.",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
sources: [],
|
|
164
|
+
package_info: {
|
|
165
|
+
manifestPath: "tool payload",
|
|
166
|
+
root: "mcp://datascience-artifact",
|
|
167
|
+
snapshotPath: "tool payload",
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
function createMemoryStorage() {
|
|
172
|
+
const values = new Map();
|
|
173
|
+
return {
|
|
174
|
+
get length() {
|
|
175
|
+
return values.size;
|
|
176
|
+
},
|
|
177
|
+
clear() {
|
|
178
|
+
values.clear();
|
|
179
|
+
},
|
|
180
|
+
getItem(key) {
|
|
181
|
+
const normalizedKey = String(key);
|
|
182
|
+
return values.has(normalizedKey) ? values.get(normalizedKey) : null;
|
|
183
|
+
},
|
|
184
|
+
key(index) {
|
|
185
|
+
return Array.from(values.keys())[index] ?? null;
|
|
186
|
+
},
|
|
187
|
+
removeItem(key) {
|
|
188
|
+
values.delete(String(key));
|
|
189
|
+
},
|
|
190
|
+
setItem(key, value) {
|
|
191
|
+
values.set(String(key), String(value));
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function storageWorks(storage) {
|
|
197
|
+
if (!storage) return false;
|
|
198
|
+
const key = "__datascience_artifact_storage_probe__";
|
|
199
|
+
try {
|
|
200
|
+
storage.setItem(key, "1");
|
|
201
|
+
storage.removeItem(key);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function installStorageFallback() {
|
|
209
|
+
if (typeof window === "undefined") return;
|
|
210
|
+
try {
|
|
211
|
+
if (storageWorks(window.localStorage)) return;
|
|
212
|
+
} catch {
|
|
213
|
+
// Accessing localStorage can throw in sandboxed MCP iframes.
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const storage = window.__datascienceArtifactMemoryStorage || createMemoryStorage();
|
|
217
|
+
window.__datascienceArtifactMemoryStorage = storage;
|
|
218
|
+
try {
|
|
219
|
+
Object.defineProperty(window, "localStorage", {
|
|
220
|
+
configurable: true,
|
|
221
|
+
value: storage,
|
|
222
|
+
});
|
|
223
|
+
} catch {
|
|
224
|
+
// If the host prevents overriding localStorage, the fallback is still
|
|
225
|
+
// available for future wrapper code through __datascienceArtifactMemoryStorage.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isArtifactPayload(value) {
|
|
230
|
+
return Boolean(
|
|
231
|
+
value &&
|
|
232
|
+
typeof value === "object" &&
|
|
233
|
+
value.widget_type === "artifact" &&
|
|
234
|
+
value.manifest &&
|
|
235
|
+
value.snapshot,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function decodePayload(raw) {
|
|
240
|
+
if (typeof raw !== "string") return raw;
|
|
241
|
+
try {
|
|
242
|
+
return JSON.parse(raw);
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function pickPayload(raw) {
|
|
249
|
+
raw = decodePayload(raw);
|
|
250
|
+
if (!raw || typeof raw !== "object") return null;
|
|
251
|
+
if (Array.isArray(raw.content)) {
|
|
252
|
+
for (const item of raw.content) {
|
|
253
|
+
const found = pickPayload(item && (item.text || item));
|
|
254
|
+
if (found) return found;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (isArtifactPayload(raw)) return raw;
|
|
258
|
+
if (raw.structuredContent && typeof raw.structuredContent === "object") {
|
|
259
|
+
return pickPayload(raw.structuredContent);
|
|
260
|
+
}
|
|
261
|
+
if (raw.payload && typeof raw.payload === "object") {
|
|
262
|
+
return pickPayload(raw.payload);
|
|
263
|
+
}
|
|
264
|
+
if (raw.toolOutput && typeof raw.toolOutput === "object") {
|
|
265
|
+
return pickPayload(raw.toolOutput);
|
|
266
|
+
}
|
|
267
|
+
if (raw.toolResponseMetadata && typeof raw.toolResponseMetadata === "object") {
|
|
268
|
+
return pickPayload(raw.toolResponseMetadata);
|
|
269
|
+
}
|
|
270
|
+
if (raw.detail && typeof raw.detail === "object") return pickPayload(raw.detail);
|
|
271
|
+
if (raw.globals && typeof raw.globals === "object") return pickPayload(raw.globals);
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function isPlainObject(value) {
|
|
276
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function hasOwn(value, key) {
|
|
280
|
+
return isPlainObject(value) && Object.prototype.hasOwnProperty.call(value, key);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function validateDatasetRows(rows, path, issues) {
|
|
284
|
+
if (!Array.isArray(rows)) {
|
|
285
|
+
issues.push(`${path} must be an array of row objects.`);
|
|
286
|
+
return [];
|
|
287
|
+
}
|
|
288
|
+
rows.forEach((row, index) => {
|
|
289
|
+
if (!isPlainObject(row)) {
|
|
290
|
+
issues.push(`${path}[${index}] must be an object.`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
return rows.filter(isPlainObject);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function validateFieldReference(rows, field, path, issues) {
|
|
297
|
+
if (typeof field !== "string" || !field.trim()) {
|
|
298
|
+
issues.push(`${path} must be a non-empty field name.`);
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!rows.length) return;
|
|
302
|
+
if (!rows.some((row) => hasOwn(row, field))) {
|
|
303
|
+
issues.push(`${path} references "${field}", but no sampled row contains that field.`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function chartEncoding(chart, role) {
|
|
308
|
+
return isPlainObject(chart.encodings) && isPlainObject(chart.encodings[role]) ? chart.encodings[role] : {};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function chartEncodingField(chart, role) {
|
|
312
|
+
const field = chartEncoding(chart, role).field;
|
|
313
|
+
return typeof field === "string" && field.trim() ? field : null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function chartEncodingFields(chart, role) {
|
|
317
|
+
const fields = chartEncoding(chart, role).fields;
|
|
318
|
+
return Array.isArray(fields) ? fields.filter((field) => typeof field === "string" && field.trim()) : [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function hasChartEncodingSpec(chart) {
|
|
322
|
+
return Boolean(
|
|
323
|
+
isPlainObject(chart.encodings) &&
|
|
324
|
+
chartEncodingField(chart, "x") &&
|
|
325
|
+
(chartEncodingField(chart, "y") || chartEncodingFields(chart, "y").length),
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function validateEncodedChartFields(rows, chart, path, issues) {
|
|
330
|
+
if (chart.xField != null) {
|
|
331
|
+
issues.push(`${path}.xField is not supported for artifact charts; use encodings.`);
|
|
332
|
+
}
|
|
333
|
+
if (chart.series != null) {
|
|
334
|
+
issues.push(`${path}.series is not supported for artifact charts; use encodings.`);
|
|
335
|
+
}
|
|
336
|
+
if (!hasChartEncodingSpec(chart)) {
|
|
337
|
+
issues.push(`${path}.encodings must declare x and y fields before rendering.`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
validateFieldReference(rows, chartEncodingField(chart, "x"), `${path}.encodings.x.field`, issues);
|
|
341
|
+
const yField = chartEncodingField(chart, "y");
|
|
342
|
+
if (yField) validateFieldReference(rows, yField, `${path}.encodings.y.field`, issues);
|
|
343
|
+
chartEncodingFields(chart, "y").forEach((field, fieldIndex) => {
|
|
344
|
+
validateFieldReference(rows, field, `${path}.encodings.y.fields[${fieldIndex}]`, issues);
|
|
345
|
+
});
|
|
346
|
+
["color", "size", "facet", "label"].forEach((role) => {
|
|
347
|
+
const field = chartEncodingField(chart, role);
|
|
348
|
+
if (field) validateFieldReference(rows, field, `${path}.encodings.${role}.field`, issues);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function validateArtifactPayload(payload) {
|
|
353
|
+
const issues = [];
|
|
354
|
+
if (!isPlainObject(payload)) {
|
|
355
|
+
return ["Artifact payload must be an object."];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const manifest = payload.manifest;
|
|
359
|
+
const snapshot = payload.snapshot;
|
|
360
|
+
if (!isPlainObject(manifest)) issues.push("manifest must be an object.");
|
|
361
|
+
if (!isPlainObject(snapshot)) issues.push("snapshot must be an object.");
|
|
362
|
+
if (!isPlainObject(manifest) || !isPlainObject(snapshot)) return issues;
|
|
363
|
+
if (typeof manifest.title !== "string" || !manifest.title.trim()) {
|
|
364
|
+
issues.push("manifest.title is required for artifact rendering.");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const datasets = snapshot.datasets;
|
|
368
|
+
if (!isPlainObject(datasets)) {
|
|
369
|
+
issues.push("snapshot.datasets must be an object keyed by dataset id.");
|
|
370
|
+
}
|
|
371
|
+
const datasetRows = new Map();
|
|
372
|
+
if (isPlainObject(datasets)) {
|
|
373
|
+
Object.entries(datasets).forEach(([datasetId, rows]) => {
|
|
374
|
+
datasetRows.set(datasetId, validateDatasetRows(rows, `snapshot.datasets.${datasetId}`, issues));
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const blocks = manifest.blocks;
|
|
379
|
+
if (!Array.isArray(blocks)) issues.push("manifest.blocks must be an array.");
|
|
380
|
+
|
|
381
|
+
if (Array.isArray(blocks)) {
|
|
382
|
+
blocks.forEach((block, index) => {
|
|
383
|
+
if (!isPlainObject(block)) {
|
|
384
|
+
issues.push(`manifest.blocks[${index}] must be an object.`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
if (typeof block.id !== "string" || !block.id.trim()) {
|
|
388
|
+
issues.push(`manifest.blocks[${index}].id must be a non-empty string.`);
|
|
389
|
+
}
|
|
390
|
+
if (typeof block.type !== "string" || !block.type.trim()) {
|
|
391
|
+
issues.push(`manifest.blocks[${index}].type must be a non-empty string.`);
|
|
392
|
+
} else if (!ARTIFACT_BLOCK_TYPES.has(block.type)) {
|
|
393
|
+
issues.push(`manifest.blocks[${index}].type is not supported.`);
|
|
394
|
+
}
|
|
395
|
+
if (block.type === "markdown" && (typeof block.body !== "string" || !block.body.trim())) {
|
|
396
|
+
issues.push(`manifest.blocks[${index}].body must be a non-empty Markdown string.`);
|
|
397
|
+
}
|
|
398
|
+
if (block.type === "html" && (typeof block.body !== "string" || !block.body.trim())) {
|
|
399
|
+
issues.push(`manifest.blocks[${index}].body must be a non-empty HTML string.`);
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const charts = Array.isArray(manifest.charts) ? manifest.charts : [];
|
|
405
|
+
const chartIds = new Set();
|
|
406
|
+
if (manifest.charts != null && !Array.isArray(manifest.charts)) {
|
|
407
|
+
issues.push("manifest.charts must be an array when provided.");
|
|
408
|
+
}
|
|
409
|
+
charts.forEach((chart, index) => {
|
|
410
|
+
const path = `manifest.charts[${index}]`;
|
|
411
|
+
if (!isPlainObject(chart)) {
|
|
412
|
+
issues.push(`${path} must be an object.`);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (typeof chart.id !== "string" || !chart.id.trim()) {
|
|
416
|
+
issues.push(`${path}.id must be a non-empty string.`);
|
|
417
|
+
} else {
|
|
418
|
+
chartIds.add(chart.id);
|
|
419
|
+
}
|
|
420
|
+
if (typeof chart.dataset !== "string" || !chart.dataset.trim()) {
|
|
421
|
+
issues.push(`${path}.dataset must be a non-empty string.`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const rows = datasetRows.get(chart.dataset);
|
|
425
|
+
if (!rows) {
|
|
426
|
+
issues.push(`${path}.dataset references missing dataset "${chart.dataset}".`);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
validateEncodedChartFields(rows, chart, path, issues);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const cards = Array.isArray(manifest.cards) ? manifest.cards : [];
|
|
433
|
+
const cardIds = new Set();
|
|
434
|
+
if (manifest.cards != null && !Array.isArray(manifest.cards)) {
|
|
435
|
+
issues.push("manifest.cards must be an array when provided.");
|
|
436
|
+
}
|
|
437
|
+
cards.forEach((card, index) => {
|
|
438
|
+
const path = `manifest.cards[${index}]`;
|
|
439
|
+
if (!isPlainObject(card)) {
|
|
440
|
+
issues.push(`${path} must be an object.`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
if (typeof card.id !== "string" || !card.id.trim()) {
|
|
444
|
+
issues.push(`${path}.id must be a non-empty string.`);
|
|
445
|
+
} else {
|
|
446
|
+
cardIds.add(card.id);
|
|
447
|
+
}
|
|
448
|
+
if (typeof card.dataset !== "string" || !card.dataset.trim()) {
|
|
449
|
+
issues.push(`${path}.dataset must be a non-empty string.`);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const rows = datasetRows.get(card.dataset);
|
|
453
|
+
if (!rows) {
|
|
454
|
+
issues.push(`${path}.dataset references missing dataset "${card.dataset}".`);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
["valueField", "format", "label", "title", "indicators", "deltaField", "deltaLabel"].forEach((removedField) => {
|
|
458
|
+
if (hasOwn(card, removedField)) {
|
|
459
|
+
issues.push(`${path}.${removedField} is not supported; use metrics[].`);
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
if (!Array.isArray(card.metrics) || !card.metrics.length) {
|
|
463
|
+
issues.push(`${path}.metrics must contain at least one metric.`);
|
|
464
|
+
}
|
|
465
|
+
const metrics = Array.isArray(card.metrics) ? card.metrics : [];
|
|
466
|
+
metrics.forEach((metric, metricIndex) => {
|
|
467
|
+
const metricPath = `${path}.metrics[${metricIndex}]`;
|
|
468
|
+
if (!isPlainObject(metric)) {
|
|
469
|
+
issues.push(`${metricPath} must be an object.`);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (typeof metric.label !== "string" || !metric.label.trim()) {
|
|
473
|
+
issues.push(`${metricPath}.label must be a non-empty string.`);
|
|
474
|
+
}
|
|
475
|
+
validateFieldReference(rows, metric.field, `${metricPath}.field`, issues);
|
|
476
|
+
if (metric.format != null && typeof metric.format !== "string") {
|
|
477
|
+
issues.push(`${metricPath}.format must be a string when provided.`);
|
|
478
|
+
}
|
|
479
|
+
if (metric.signed != null && typeof metric.signed !== "boolean") {
|
|
480
|
+
issues.push(`${metricPath}.signed must be a boolean when provided.`);
|
|
481
|
+
}
|
|
482
|
+
if (hasOwn(metric, "movement")) {
|
|
483
|
+
issues.push(`${metricPath}.movement is not supported; use signed.`);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const tables = Array.isArray(manifest.tables) ? manifest.tables : [];
|
|
489
|
+
const tableIds = new Set();
|
|
490
|
+
if (manifest.tables != null && !Array.isArray(manifest.tables)) {
|
|
491
|
+
issues.push("manifest.tables must be an array when provided.");
|
|
492
|
+
}
|
|
493
|
+
tables.forEach((table, index) => {
|
|
494
|
+
const path = `manifest.tables[${index}]`;
|
|
495
|
+
if (!isPlainObject(table)) {
|
|
496
|
+
issues.push(`${path} must be an object.`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (typeof table.id !== "string" || !table.id.trim()) {
|
|
500
|
+
issues.push(`${path}.id must be a non-empty string.`);
|
|
501
|
+
} else {
|
|
502
|
+
tableIds.add(table.id);
|
|
503
|
+
}
|
|
504
|
+
if (typeof table.dataset !== "string" || !table.dataset.trim()) {
|
|
505
|
+
issues.push(`${path}.dataset must be a non-empty string.`);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const rows = datasetRows.get(table.dataset);
|
|
509
|
+
if (!rows) {
|
|
510
|
+
issues.push(`${path}.dataset references missing dataset "${table.dataset}".`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (!Array.isArray(table.columns) || !table.columns.length) {
|
|
514
|
+
issues.push(`${path}.columns must be a non-empty array.`);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
table.columns.forEach((column, columnIndex) => {
|
|
518
|
+
if (!isPlainObject(column)) {
|
|
519
|
+
issues.push(`${path}.columns[${columnIndex}] must be an object.`);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
validateFieldReference(rows, column.field, `${path}.columns[${columnIndex}].field`, issues);
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (Array.isArray(blocks)) {
|
|
527
|
+
blocks.forEach((block, index) => {
|
|
528
|
+
if (!isPlainObject(block)) return;
|
|
529
|
+
if (block.type === "chart" && !chartIds.has(block.chartId)) {
|
|
530
|
+
issues.push(`manifest.blocks[${index}].chartId references a missing chart.`);
|
|
531
|
+
}
|
|
532
|
+
if (block.type === "metric-strip") {
|
|
533
|
+
if (!Array.isArray(block.cardIds) || !block.cardIds.length) {
|
|
534
|
+
issues.push(`manifest.blocks[${index}].cardIds must reference at least one metric card.`);
|
|
535
|
+
} else {
|
|
536
|
+
block.cardIds.forEach((cardId, cardIndex) => {
|
|
537
|
+
if (!cardIds.has(cardId)) {
|
|
538
|
+
issues.push(`manifest.blocks[${index}].cardIds[${cardIndex}] references a missing metric card.`);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (block.type === "table" && !tableIds.has(block.tableId)) {
|
|
544
|
+
issues.push(`manifest.blocks[${index}].tableId references a missing table.`);
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return issues;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function InvalidArtifactPayload({ issues, surface }) {
|
|
553
|
+
const visibleIssues = Array.isArray(issues) ? issues.slice(0, 8) : [];
|
|
554
|
+
const issueCount = Array.isArray(issues) ? issues.length : visibleIssues.length;
|
|
555
|
+
const artifactLabel =
|
|
556
|
+
surface === "dashboard" || surface === "report" ? surface : "artifact";
|
|
557
|
+
return (
|
|
558
|
+
<main
|
|
559
|
+
style={{
|
|
560
|
+
background: "#fff",
|
|
561
|
+
color: "#111827",
|
|
562
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
563
|
+
minHeight: "100vh",
|
|
564
|
+
padding: 24,
|
|
565
|
+
}}
|
|
566
|
+
>
|
|
567
|
+
<section
|
|
568
|
+
style={{
|
|
569
|
+
border: "1px solid #e5e7eb",
|
|
570
|
+
borderRadius: 12,
|
|
571
|
+
maxWidth: 760,
|
|
572
|
+
padding: 20,
|
|
573
|
+
}}
|
|
574
|
+
>
|
|
575
|
+
<h1 style={{ fontSize: 18, lineHeight: "24px", margin: "0 0 8px" }}>
|
|
576
|
+
Artifact validation failed
|
|
577
|
+
</h1>
|
|
578
|
+
<p style={{ color: "#4b5563", fontSize: 14, lineHeight: "20px", margin: "0 0 16px" }}>
|
|
579
|
+
The {artifactLabel} was blocked before rendering because its manifest or snapshot does not match
|
|
580
|
+
the Data Analytics artifact contract.
|
|
581
|
+
</p>
|
|
582
|
+
<ul style={{ color: "#374151", fontSize: 13, lineHeight: "20px", margin: 0, paddingLeft: 20 }}>
|
|
583
|
+
{visibleIssues.map((issue, index) => (
|
|
584
|
+
<li key={`${issue}-${index}`}>{issue}</li>
|
|
585
|
+
))}
|
|
586
|
+
</ul>
|
|
587
|
+
{issueCount > visibleIssues.length ? (
|
|
588
|
+
<p style={{ color: "#6b7280", fontSize: 12, margin: "12px 0 0" }}>
|
|
589
|
+
{issueCount - visibleIssues.length} more validation issue
|
|
590
|
+
{issueCount - visibleIssues.length === 1 ? "" : "s"} hidden.
|
|
591
|
+
</p>
|
|
592
|
+
) : null}
|
|
593
|
+
</section>
|
|
594
|
+
</main>
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function currentHostPayload() {
|
|
599
|
+
if (typeof window === "undefined") return null;
|
|
600
|
+
return (
|
|
601
|
+
pickPayload(window.openai?.toolOutput) ||
|
|
602
|
+
pickPayload(window.openai?.toolResponseMetadata) ||
|
|
603
|
+
pickPayload(window.openai)
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function fallbackForEnvironment() {
|
|
608
|
+
if (typeof window !== "undefined" && window.openai) return hostedEmptyPayload;
|
|
609
|
+
return fallbackPayload;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function artifactIdentity(payload) {
|
|
613
|
+
const manifest = payload?.manifest || {};
|
|
614
|
+
const snapshot = payload?.snapshot || {};
|
|
615
|
+
return [
|
|
616
|
+
manifest.title || "artifact",
|
|
617
|
+
manifest.generatedAt || "",
|
|
618
|
+
snapshot.generatedAt || "",
|
|
619
|
+
payload?.surface || manifest.surface || "artifact",
|
|
620
|
+
].join(":");
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function jsonResponse(body, init = {}) {
|
|
624
|
+
return new Response(JSON.stringify(body), {
|
|
625
|
+
status: init.status || 200,
|
|
626
|
+
headers: {
|
|
627
|
+
"content-type": "application/json; charset=utf-8",
|
|
628
|
+
...(init.headers || {}),
|
|
629
|
+
},
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function textResponse(body, init = {}) {
|
|
634
|
+
return new Response(String(body ?? ""), {
|
|
635
|
+
status: init.status || 200,
|
|
636
|
+
headers: {
|
|
637
|
+
"content-type": "text/plain; charset=utf-8",
|
|
638
|
+
...(init.headers || {}),
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function htmlResponse(body, init = {}) {
|
|
644
|
+
return new Response(String(body ?? ""), {
|
|
645
|
+
status: init.status || 200,
|
|
646
|
+
headers: {
|
|
647
|
+
"content-type": "text/html; charset=utf-8",
|
|
648
|
+
...(init.headers || {}),
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function readChartWidgetHtml() {
|
|
654
|
+
if (!chartWidgetHtmlPromise) {
|
|
655
|
+
chartWidgetHtmlPromise = (async () => {
|
|
656
|
+
await artifactApp.ready;
|
|
657
|
+
const resource = await artifactApp.readServerResource({
|
|
658
|
+
uri: CHART_WIDGET_RESOURCE_URI,
|
|
659
|
+
});
|
|
660
|
+
const content = Array.isArray(resource?.contents)
|
|
661
|
+
? resource.contents.find((entry) => typeof entry?.text === "string")
|
|
662
|
+
: null;
|
|
663
|
+
if (!content?.text) {
|
|
664
|
+
throw new Error("The shared chart detail resource did not include HTML.");
|
|
665
|
+
}
|
|
666
|
+
return content.text;
|
|
667
|
+
})();
|
|
668
|
+
}
|
|
669
|
+
return chartWidgetHtmlPromise;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function sourceTextForPath(payload, sourcePath) {
|
|
673
|
+
const manifestSources = Array.isArray(payload?.manifest?.sources)
|
|
674
|
+
? payload.manifest.sources
|
|
675
|
+
: [];
|
|
676
|
+
const inlineSources = Array.isArray(payload?.sources) ? payload.sources : [];
|
|
677
|
+
const declared = [...manifestSources, ...inlineSources].find((source) => {
|
|
678
|
+
if (!source || typeof source !== "object") return false;
|
|
679
|
+
return source.path === sourcePath || source.id === sourcePath || source.href === sourcePath;
|
|
680
|
+
});
|
|
681
|
+
if (typeof declared?.query?.sql === "string") return declared.query.sql;
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function installMcpFetchShim(payload) {
|
|
686
|
+
window.__datascienceArtifactPayload = payload;
|
|
687
|
+
if (window.__datascienceArtifactFetchShimInstalled) return;
|
|
688
|
+
|
|
689
|
+
const nativeFetch = window.fetch.bind(window);
|
|
690
|
+
window.__datascienceArtifactFetchShimInstalled = true;
|
|
691
|
+
window.fetch = async (input, init) => {
|
|
692
|
+
const requestUrl = typeof input === "string" ? input : input?.url;
|
|
693
|
+
const url = new URL(requestUrl || "", window.location.origin);
|
|
694
|
+
const currentPayload = window.__datascienceArtifactPayload || fallbackForEnvironment();
|
|
695
|
+
|
|
696
|
+
if (url.origin === window.location.origin && url.pathname === "/api/manifest") {
|
|
697
|
+
return jsonResponse(currentPayload.manifest || {});
|
|
698
|
+
}
|
|
699
|
+
if (url.origin === window.location.origin && url.pathname === "/api/snapshot") {
|
|
700
|
+
return jsonResponse(currentPayload.snapshot || {});
|
|
701
|
+
}
|
|
702
|
+
if (url.origin === window.location.origin && url.pathname === "/api/package") {
|
|
703
|
+
return jsonResponse(
|
|
704
|
+
currentPayload.package_info ||
|
|
705
|
+
currentPayload.packageInfo || {
|
|
706
|
+
manifestPath: "tool payload",
|
|
707
|
+
root: "mcp://datascience-artifact",
|
|
708
|
+
snapshotPath: "tool payload",
|
|
709
|
+
},
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
if (url.origin === window.location.origin && url.pathname === "/api/inline-chart-widget") {
|
|
713
|
+
try {
|
|
714
|
+
return htmlResponse(await readChartWidgetHtml());
|
|
715
|
+
} catch (error) {
|
|
716
|
+
return textResponse(error instanceof Error ? error.message : "Inline chart widget asset is unavailable.", {
|
|
717
|
+
status: 503,
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (
|
|
722
|
+
url.origin === window.location.origin &&
|
|
723
|
+
(url.pathname === "/api/source-file" || url.pathname === "/api/source")
|
|
724
|
+
) {
|
|
725
|
+
const sourcePath = url.searchParams.get("path") || "";
|
|
726
|
+
const text = sourceTextForPath(currentPayload, sourcePath);
|
|
727
|
+
if (text != null) return textResponse(text);
|
|
728
|
+
return textResponse("Source text was not included in this hosted artifact.", {
|
|
729
|
+
status: 404,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return nativeFetch(input, init);
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function displayModeFromPayload(payload) {
|
|
738
|
+
return (
|
|
739
|
+
normalizeDisplayMode(
|
|
740
|
+
payload?.displayMode ||
|
|
741
|
+
payload?.display_mode ||
|
|
742
|
+
payload?.manifest?.displayMode ||
|
|
743
|
+
payload?.manifest?.display_mode,
|
|
744
|
+
) || "inline"
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function normalizeDisplayMode(mode) {
|
|
749
|
+
return mode === "inline" || mode === "fullscreen" || mode === "pip" ? mode : "";
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function readUrlDisplayMode() {
|
|
753
|
+
if (typeof window === "undefined") return "";
|
|
754
|
+
try {
|
|
755
|
+
const params = new URLSearchParams(window.location.search);
|
|
756
|
+
const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ""));
|
|
757
|
+
const raw = (
|
|
758
|
+
params.get("displayMode") ||
|
|
759
|
+
params.get("display_mode") ||
|
|
760
|
+
params.get("preview") ||
|
|
761
|
+
hashParams.get("displayMode") ||
|
|
762
|
+
hashParams.get("display_mode") ||
|
|
763
|
+
hashParams.get("preview") ||
|
|
764
|
+
""
|
|
765
|
+
).toLowerCase();
|
|
766
|
+
if (raw === "fullscreen" || raw === "full" || raw === "expanded") return "fullscreen";
|
|
767
|
+
if (raw === "inline" || raw === "compact") return "inline";
|
|
768
|
+
} catch {
|
|
769
|
+
// Ignore malformed URLs from host previews.
|
|
770
|
+
}
|
|
771
|
+
return "";
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function pickDisplayMode(raw) {
|
|
775
|
+
const value = decodePayload(raw);
|
|
776
|
+
if (!value || typeof value !== "object") return "";
|
|
777
|
+
if (value.type === "datascience-chart-widget-display-mode") return "";
|
|
778
|
+
return normalizeDisplayMode(
|
|
779
|
+
value.displayMode ||
|
|
780
|
+
value.display_mode ||
|
|
781
|
+
value.mode ||
|
|
782
|
+
value.view?.displayMode ||
|
|
783
|
+
value.hostContext?.displayMode ||
|
|
784
|
+
value.host?.displayMode ||
|
|
785
|
+
value.globals?.displayMode ||
|
|
786
|
+
value.globals?.view?.displayMode ||
|
|
787
|
+
value.globals?.hostContext?.displayMode,
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function currentHostDisplayMode() {
|
|
792
|
+
if (typeof window === "undefined") return "inline";
|
|
793
|
+
const hostApi = window.openai || {};
|
|
794
|
+
return (
|
|
795
|
+
readUrlDisplayMode() ||
|
|
796
|
+
normalizeDisplayMode(
|
|
797
|
+
hostApi.displayMode ||
|
|
798
|
+
hostApi.view?.displayMode ||
|
|
799
|
+
hostApi.hostContext?.displayMode ||
|
|
800
|
+
hostApi.host?.displayMode,
|
|
801
|
+
) ||
|
|
802
|
+
"inline"
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function hostSupportsDisplayMode() {
|
|
807
|
+
if (typeof window === "undefined") return false;
|
|
808
|
+
const hostApi = window.openai || {};
|
|
809
|
+
const availableDisplayModes =
|
|
810
|
+
hostApi.availableDisplayModes ||
|
|
811
|
+
hostApi.hostContext?.availableDisplayModes ||
|
|
812
|
+
hostApi.view?.availableDisplayModes;
|
|
813
|
+
if (Array.isArray(availableDisplayModes) && availableDisplayModes.includes("fullscreen")) {
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
return Boolean(window.openai) || typeof hostApi.requestDisplayMode === "function";
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function applyDocumentDisplayMode(mode) {
|
|
820
|
+
if (typeof document === "undefined") return;
|
|
821
|
+
const normalized = normalizeDisplayMode(mode) || "inline";
|
|
822
|
+
document.documentElement.dataset.displayMode = normalized;
|
|
823
|
+
document.body.dataset.displayMode = normalized;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function useHostPayload() {
|
|
827
|
+
const [payload, setPayload] = useState(() => currentHostPayload() || fallbackForEnvironment());
|
|
828
|
+
|
|
829
|
+
useEffect(() => {
|
|
830
|
+
function apply(raw) {
|
|
831
|
+
const next = pickPayload(raw);
|
|
832
|
+
if (next) setPayload(next);
|
|
833
|
+
}
|
|
834
|
+
function handleMessage(event) {
|
|
835
|
+
apply(event.data);
|
|
836
|
+
}
|
|
837
|
+
function handleToolResult(event) {
|
|
838
|
+
apply(event.detail);
|
|
839
|
+
}
|
|
840
|
+
function handleGlobals(event) {
|
|
841
|
+
apply(event.detail?.globals);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
apply(currentHostPayload());
|
|
845
|
+
window.addEventListener("message", handleMessage);
|
|
846
|
+
window.addEventListener("datascience-widget-tool-result", handleToolResult);
|
|
847
|
+
window.addEventListener("openai:set_globals", handleGlobals);
|
|
848
|
+
return () => {
|
|
849
|
+
window.removeEventListener("message", handleMessage);
|
|
850
|
+
window.removeEventListener("datascience-widget-tool-result", handleToolResult);
|
|
851
|
+
window.removeEventListener("openai:set_globals", handleGlobals);
|
|
852
|
+
};
|
|
853
|
+
}, []);
|
|
854
|
+
|
|
855
|
+
return payload;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function useHostDisplayMode() {
|
|
859
|
+
const [displayMode, setDisplayMode] = useState(() => currentHostDisplayMode());
|
|
860
|
+
|
|
861
|
+
useEffect(() => {
|
|
862
|
+
function apply(raw) {
|
|
863
|
+
const next = pickDisplayMode(raw);
|
|
864
|
+
if (next) setDisplayMode(next);
|
|
865
|
+
}
|
|
866
|
+
function handleMessage(event) {
|
|
867
|
+
apply(event.data);
|
|
868
|
+
}
|
|
869
|
+
function handleGlobals(event) {
|
|
870
|
+
apply(event.detail?.globals || event.detail);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
setDisplayMode(currentHostDisplayMode());
|
|
874
|
+
applyDocumentDisplayMode(currentHostDisplayMode());
|
|
875
|
+
window.addEventListener("message", handleMessage);
|
|
876
|
+
window.addEventListener("openai:set_globals", handleGlobals);
|
|
877
|
+
return () => {
|
|
878
|
+
window.removeEventListener("message", handleMessage);
|
|
879
|
+
window.removeEventListener("openai:set_globals", handleGlobals);
|
|
880
|
+
};
|
|
881
|
+
}, []);
|
|
882
|
+
|
|
883
|
+
useEffect(() => {
|
|
884
|
+
applyDocumentDisplayMode(displayMode);
|
|
885
|
+
}, [displayMode]);
|
|
886
|
+
|
|
887
|
+
return { displayMode, setDisplayMode };
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async function requestArtifactDisplayMode(mode, setDisplayMode) {
|
|
891
|
+
const requestDisplayMode = window.openai?.requestDisplayMode;
|
|
892
|
+
if (typeof requestDisplayMode !== "function") return { mode: currentHostDisplayMode() };
|
|
893
|
+
|
|
894
|
+
try {
|
|
895
|
+
const result = await Promise.resolve(requestDisplayMode({ mode }));
|
|
896
|
+
const resultMode = normalizeDisplayMode(result?.mode || result?.displayMode);
|
|
897
|
+
if (resultMode) {
|
|
898
|
+
setDisplayMode(resultMode);
|
|
899
|
+
applyDocumentDisplayMode(resultMode);
|
|
900
|
+
return result;
|
|
901
|
+
}
|
|
902
|
+
} catch {
|
|
903
|
+
const currentMode = currentHostDisplayMode();
|
|
904
|
+
setDisplayMode(currentMode);
|
|
905
|
+
applyDocumentDisplayMode(currentMode);
|
|
906
|
+
}
|
|
907
|
+
return { mode: currentHostDisplayMode() };
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function ArtifactApp() {
|
|
911
|
+
const payload = useHostPayload();
|
|
912
|
+
const surface = payload?.surface || payload?.manifest?.surface;
|
|
913
|
+
const identity = useMemo(() => artifactIdentity(payload), [payload]);
|
|
914
|
+
const validationIssues = useMemo(() => validateArtifactPayload(payload), [identity, payload]);
|
|
915
|
+
const { displayMode, setDisplayMode } = useHostDisplayMode();
|
|
916
|
+
const requestedDisplayMode = useMemo(() => displayModeFromPayload(payload), [identity, payload]);
|
|
917
|
+
const hasFullscreenArtifactSurface = surface === "report" || surface === "dashboard";
|
|
918
|
+
const canRequestFullscreen =
|
|
919
|
+
hasFullscreenArtifactSurface && hostSupportsDisplayMode() && displayMode !== "fullscreen";
|
|
920
|
+
const requestFullscreen = canRequestFullscreen
|
|
921
|
+
? () => {
|
|
922
|
+
void requestArtifactDisplayMode("fullscreen", setDisplayMode);
|
|
923
|
+
}
|
|
924
|
+
: undefined;
|
|
925
|
+
|
|
926
|
+
useLayoutEffect(() => {
|
|
927
|
+
installMcpFetchShim(payload);
|
|
928
|
+
}, [payload]);
|
|
929
|
+
|
|
930
|
+
useEffect(() => {
|
|
931
|
+
const mode = requestedDisplayMode;
|
|
932
|
+
if (mode === "inline") return;
|
|
933
|
+
const requestDisplayMode = window.openai?.requestDisplayMode;
|
|
934
|
+
if (typeof requestDisplayMode !== "function") return;
|
|
935
|
+
try {
|
|
936
|
+
void Promise.resolve(requestDisplayMode({ mode }))
|
|
937
|
+
.then((result) => {
|
|
938
|
+
const resultMode = normalizeDisplayMode(result?.mode || result?.displayMode);
|
|
939
|
+
if (resultMode) {
|
|
940
|
+
setDisplayMode(resultMode);
|
|
941
|
+
applyDocumentDisplayMode(resultMode);
|
|
942
|
+
}
|
|
943
|
+
})
|
|
944
|
+
.catch(() => {
|
|
945
|
+
// Hosts may decline fullscreen/pip; the compact artifact launcher remains inline.
|
|
946
|
+
});
|
|
947
|
+
} catch {
|
|
948
|
+
// Some hosts expose requestDisplayMode as a fire-and-forget function.
|
|
949
|
+
}
|
|
950
|
+
}, [identity, requestedDisplayMode, setDisplayMode]);
|
|
951
|
+
|
|
952
|
+
if (validationIssues.length) {
|
|
953
|
+
return <InvalidArtifactPayload issues={validationIssues} surface={surface} />;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return (
|
|
957
|
+
<ArtifactErrorBoundary surface={surface}>
|
|
958
|
+
<div className="datascience-artifact-shell" data-display-mode={displayMode}>
|
|
959
|
+
<AnalyticsApp
|
|
960
|
+
key={identity}
|
|
961
|
+
displayMode={displayMode === "fullscreen" ? "fullscreen" : "inline"}
|
|
962
|
+
onRequestFullscreen={requestFullscreen}
|
|
963
|
+
/>
|
|
964
|
+
</div>
|
|
965
|
+
</ArtifactErrorBoundary>
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
class ArtifactErrorBoundary extends React.Component {
|
|
970
|
+
constructor(props) {
|
|
971
|
+
super(props);
|
|
972
|
+
this.state = { error: null };
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
static getDerivedStateFromError(error) {
|
|
976
|
+
return { error };
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
render() {
|
|
980
|
+
if (this.state.error) {
|
|
981
|
+
const message =
|
|
982
|
+
this.state.error instanceof Error ? this.state.error.message : String(this.state.error);
|
|
983
|
+
return (
|
|
984
|
+
<InvalidArtifactPayload
|
|
985
|
+
issues={[`Unexpected render failure: ${message}`]}
|
|
986
|
+
surface={this.props.surface}
|
|
987
|
+
/>
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
return this.props.children;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const root = document.getElementById("root");
|
|
995
|
+
|
|
996
|
+
if (!root) {
|
|
997
|
+
throw new Error("Missing root element");
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
installStorageFallback();
|
|
1001
|
+
|
|
1002
|
+
createRoot(root).render(
|
|
1003
|
+
<StrictMode>
|
|
1004
|
+
<ArtifactErrorBoundary>
|
|
1005
|
+
<ArtifactApp />
|
|
1006
|
+
</ArtifactErrorBoundary>
|
|
1007
|
+
</StrictMode>,
|
|
1008
|
+
);
|