create-yss-spec 3.5.0 → 3.5.3

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.
Files changed (882) hide show
  1. package/README.md +6 -2
  2. package/package.json +1 -1
  3. package/src/api/_sync-service.js +31 -6
  4. package/src/cli/args.js +2 -0
  5. package/src/cli/help.js +4 -1
  6. package/src/commands/attach.js +9 -3
  7. package/src/commands/diff.js +1 -0
  8. package/src/commands/doctor.js +1 -0
  9. package/src/commands/init.js +2 -1
  10. package/src/commands/sync.js +1 -0
  11. package/src/family-identity.js +1 -1
  12. package/src/template/asset-runtime.js +59 -49
  13. package/src/template/attach-planner.js +1 -1
  14. package/src/template/distribution-runtime.js +9 -9
  15. package/src/template/instance-runtime.js +9 -9
  16. package/src/template/layout-migration.js +160 -0
  17. package/src/template/migration-runtime.js +2 -2
  18. package/src/template/sync-planner.js +1 -1
  19. package/template/.agents/rules/yss-ai-skills.md +1 -1
  20. package/template/.agents/skills/.strategic-design-skills-manifest.json +7 -8
  21. package/template/.agents/skills/.yss-skills-manifest.json +2 -2
  22. package/template/.agents/skills/alibaba-java-code-style/SKILL.md +1 -1
  23. package/template/.agents/skills/code-review/SKILL.md +6 -6
  24. package/template/.agents/skills/code-review/references/yss-review-standards.md +1 -1
  25. package/template/.agents/skills/codebase-design/SKILL.md +4 -0
  26. package/template/{.codex/skills/improve-codebase-architecture/SKILL.md → .agents/skills/codebase-design/references/architecture-audit.md} +3 -9
  27. package/template/.agents/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  28. package/template/.agents/skills/frontend-commit/SKILL.md +4 -93
  29. package/template/.agents/skills/git-commit-core/SKILL.md +18 -0
  30. package/template/.agents/skills/i-have-adhd/SKILL.md +1 -1
  31. package/template/.agents/skills/i-have-adhd/references/yss-adaptation.md +1 -1
  32. package/template/.agents/skills/i-have-adhd/tests/verify-integration.mjs +2 -2
  33. package/template/.agents/skills/implementation-repo-onboarding/SKILL.md +2 -2
  34. package/template/.agents/skills/java-backend-commit/SKILL.md +4 -96
  35. package/template/.agents/skills/llm-wiki/SKILL.md +12 -13
  36. package/template/.agents/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  37. package/template/.agents/skills/llm-wiki/references/compile.md +21 -114
  38. package/template/.agents/skills/llm-wiki/references/discover.md +3 -3
  39. package/template/.agents/skills/llm-wiki/references/ingest.md +8 -33
  40. package/template/.agents/skills/llm-wiki/references/lint.md +9 -46
  41. package/template/.agents/skills/llm-wiki/references/query.md +9 -18
  42. package/template/.agents/skills/llm-wiki/references/schema.md +56 -29
  43. package/template/.agents/skills/llm-wiki/references/transactions.md +48 -0
  44. package/template/.agents/skills/llm-wiki/references/writing.md +4 -4
  45. package/template/.agents/skills/llm-wiki/scripts/advise.mjs +46 -58
  46. package/template/.agents/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  47. package/template/.agents/skills/llm-wiki/scripts/core.mjs +262 -0
  48. package/template/.agents/skills/llm-wiki/scripts/extract.mjs +35 -18
  49. package/template/.agents/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  50. package/template/.agents/skills/llm-wiki/scripts/feedback.mjs +51 -0
  51. package/template/.agents/skills/llm-wiki/scripts/inventory.mjs +43 -171
  52. package/template/.agents/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  53. package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  54. package/template/.agents/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  55. package/template/.agents/skills/llm-wiki/scripts/migrate.mjs +51 -0
  56. package/template/.agents/skills/llm-wiki/scripts/query.mjs +149 -0
  57. package/template/.agents/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  58. package/template/.agents/skills/llm-wiki/scripts/sources.mjs +166 -0
  59. package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +384 -0
  60. package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  61. package/template/.agents/skills/prototype/LOGIC.md +1 -1
  62. package/template/.agents/skills/prototype/SKILL.md +5 -19
  63. package/template/.agents/skills/prototype-review/SKILL.md +4 -4
  64. package/template/.agents/skills/setup-matt-pocock-skills/SKILL.md +11 -11
  65. package/template/.agents/skills/triage/SKILL.md +1 -1
  66. package/template/.agents/skills/yss-application/references/profiles/layered-mvc-service.md +1 -1
  67. package/template/.agents/skills/yss-application/references/profiles/mvc-data-analysis-v1.md +1 -1
  68. package/template/.agents/skills/yss-backend-spec-review/SKILL.md +1 -1
  69. package/template/.agents/skills/yss-backend-spec-review/references/check-inputs.md +1 -1
  70. package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +2 -2
  71. package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  72. package/template/.agents/skills/yss-design-system/SKILL.md +10 -10
  73. package/template/.agents/skills/yss-design-system/references/design-qa-theme.md +3 -3
  74. package/template/.agents/skills/yss-design-system/references/design-system.md +3 -3
  75. package/template/.agents/skills/yss-exception/SKILL.md +1 -1
  76. package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +2 -2
  77. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  78. package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/refresh_baseline.mjs +2 -2
  79. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +11 -9
  80. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +3 -4
  81. package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +9 -9
  82. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +3 -3
  83. package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +1 -1
  84. package/template/.agents/skills/yss-implementation-contract-compiler/references/strategic-handoff-routing.md +1 -1
  85. package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  86. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  87. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/data-analysis-profile.mjs +5 -5
  88. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +2 -2
  89. package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +42 -0
  90. package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +2 -2
  91. package/template/.agents/skills/yss-openapi-governance/SKILL.md +7 -7
  92. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +17 -17
  93. package/template/.agents/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  94. package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +10 -10
  95. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +85 -47
  96. package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +24 -16
  97. package/template/.agents/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  98. package/template/.agents/skills/yss-product-lifecycle/references/request-triage.md +2 -0
  99. package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +4 -4
  100. package/template/.agents/skills/yss-product-lifecycle/references/user-decisions.md +2 -2
  101. package/template/.agents/skills/yss-prototype-stage/SKILL.md +7 -7
  102. package/template/.agents/skills/yss-prototype-stage/references/existing-ui-entry.md +1 -1
  103. package/template/.agents/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +1 -1
  104. package/template/.agents/skills/yss-prototype-stage/scripts/offline-html.mjs +3 -3
  105. package/template/.agents/skills/yss-prototype-stage/tests/browser-smoke.mjs +2 -2
  106. package/template/.agents/skills/yss-prototype-stage/tests/run-scenarios.mjs +4 -4
  107. package/template/.agents/skills/yss-prototype-stage/tests/workbench-smoke.mjs +1 -1
  108. package/template/.agents/skills/yss-repository/SKILL.md +2 -2
  109. package/template/.agents/skills/yss-repository/references/profiles/layered-mvc-service.md +1 -1
  110. package/template/.agents/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +1 -1
  111. package/template/.agents/skills/yss-repository/tests/profile-routing.test.mjs +2 -2
  112. package/template/.agents/skills/yss-research/SKILL.md +6 -2
  113. package/template/.agents/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
  114. package/template/.agents/skills/yss-stage-decision/SKILL.md +5 -5
  115. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +1 -1
  116. package/template/.agents/skills/yss-stage-decision/references/strategic-handoff-routing.md +1 -1
  117. package/template/.agents/skills/yss-tactical-design/SKILL.md +2 -2
  118. package/template/.agents/skills/yss-tactical-design/references/strategic-handoff-routing.md +1 -1
  119. package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
  120. package/template/.agents/skills/yss-technical-design/SKILL.md +6 -6
  121. package/template/.agents/skills/yss-technical-design/references/technical-design-contract.md +1 -1
  122. package/template/.agents/skills/yss-ui/SKILL.md +1 -1
  123. package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
  124. package/template/.agents/skills/yss-web-controller/SKILL.md +1 -1
  125. package/template/.agents/skills/yss-web-controller/references/profiles/layered-mvc-service.md +1 -1
  126. package/template/.codex/skills/alibaba-java-code-style/SKILL.md +1 -1
  127. package/template/.codex/skills/code-review/SKILL.md +6 -6
  128. package/template/.codex/skills/code-review/references/yss-review-standards.md +1 -1
  129. package/template/.codex/skills/codebase-design/SKILL.md +4 -0
  130. package/template/{.pi/skills/improve-codebase-architecture/SKILL.md → .codex/skills/codebase-design/references/architecture-audit.md} +3 -9
  131. package/template/.codex/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  132. package/template/.codex/skills/frontend-commit/SKILL.md +4 -93
  133. package/template/.codex/skills/git-commit-core/SKILL.md +18 -0
  134. package/template/.codex/skills/i-have-adhd/SKILL.md +1 -1
  135. package/template/.codex/skills/i-have-adhd/references/yss-adaptation.md +1 -1
  136. package/template/.codex/skills/i-have-adhd/tests/verify-integration.mjs +2 -2
  137. package/template/.codex/skills/implementation-repo-onboarding/SKILL.md +2 -2
  138. package/template/.codex/skills/java-backend-commit/SKILL.md +4 -96
  139. package/template/.codex/skills/llm-wiki/SKILL.md +12 -13
  140. package/template/.codex/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  141. package/template/.codex/skills/llm-wiki/references/compile.md +21 -114
  142. package/template/.codex/skills/llm-wiki/references/discover.md +3 -3
  143. package/template/.codex/skills/llm-wiki/references/ingest.md +8 -33
  144. package/template/.codex/skills/llm-wiki/references/lint.md +9 -46
  145. package/template/.codex/skills/llm-wiki/references/query.md +9 -18
  146. package/template/.codex/skills/llm-wiki/references/schema.md +56 -29
  147. package/template/.codex/skills/llm-wiki/references/transactions.md +48 -0
  148. package/template/.codex/skills/llm-wiki/references/writing.md +4 -4
  149. package/template/.codex/skills/llm-wiki/scripts/advise.mjs +46 -58
  150. package/template/.codex/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  151. package/template/.codex/skills/llm-wiki/scripts/core.mjs +262 -0
  152. package/template/.codex/skills/llm-wiki/scripts/extract.mjs +35 -18
  153. package/template/.codex/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  154. package/template/.codex/skills/llm-wiki/scripts/feedback.mjs +51 -0
  155. package/template/.codex/skills/llm-wiki/scripts/inventory.mjs +43 -171
  156. package/template/.codex/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  157. package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  158. package/template/.codex/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  159. package/template/.codex/skills/llm-wiki/scripts/migrate.mjs +51 -0
  160. package/template/.codex/skills/llm-wiki/scripts/query.mjs +149 -0
  161. package/template/.codex/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  162. package/template/.codex/skills/llm-wiki/scripts/sources.mjs +166 -0
  163. package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +384 -0
  164. package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  165. package/template/.codex/skills/prototype/LOGIC.md +1 -1
  166. package/template/.codex/skills/prototype/SKILL.md +5 -19
  167. package/template/.codex/skills/prototype-review/SKILL.md +4 -4
  168. package/template/.codex/skills/setup-matt-pocock-skills/SKILL.md +11 -11
  169. package/template/.codex/skills/triage/SKILL.md +1 -1
  170. package/template/.codex/skills/yss-application/references/profiles/layered-mvc-service.md +1 -1
  171. package/template/.codex/skills/yss-application/references/profiles/mvc-data-analysis-v1.md +1 -1
  172. package/template/.codex/skills/yss-backend-spec-review/SKILL.md +1 -1
  173. package/template/.codex/skills/yss-backend-spec-review/references/check-inputs.md +1 -1
  174. package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +2 -2
  175. package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  176. package/template/.codex/skills/yss-design-system/SKILL.md +10 -10
  177. package/template/.codex/skills/yss-design-system/references/design-qa-theme.md +3 -3
  178. package/template/.codex/skills/yss-design-system/references/design-system.md +3 -3
  179. package/template/.codex/skills/yss-exception/SKILL.md +1 -1
  180. package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +2 -2
  181. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  182. package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/refresh_baseline.mjs +2 -2
  183. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +11 -9
  184. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +3 -4
  185. package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +9 -9
  186. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +3 -3
  187. package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +1 -1
  188. package/template/.codex/skills/yss-implementation-contract-compiler/references/strategic-handoff-routing.md +1 -1
  189. package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  190. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  191. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/data-analysis-profile.mjs +5 -5
  192. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +2 -2
  193. package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +42 -0
  194. package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +2 -2
  195. package/template/.codex/skills/yss-openapi-governance/SKILL.md +7 -7
  196. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +17 -17
  197. package/template/.codex/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  198. package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +10 -10
  199. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +85 -47
  200. package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +24 -16
  201. package/template/.codex/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  202. package/template/.codex/skills/yss-product-lifecycle/references/request-triage.md +2 -0
  203. package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +4 -4
  204. package/template/.codex/skills/yss-product-lifecycle/references/user-decisions.md +2 -2
  205. package/template/.codex/skills/yss-prototype-stage/SKILL.md +7 -7
  206. package/template/.codex/skills/yss-prototype-stage/references/existing-ui-entry.md +1 -1
  207. package/template/.codex/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +1 -1
  208. package/template/.codex/skills/yss-prototype-stage/scripts/offline-html.mjs +3 -3
  209. package/template/.codex/skills/yss-prototype-stage/tests/browser-smoke.mjs +2 -2
  210. package/template/.codex/skills/yss-prototype-stage/tests/run-scenarios.mjs +4 -4
  211. package/template/.codex/skills/yss-prototype-stage/tests/workbench-smoke.mjs +1 -1
  212. package/template/.codex/skills/yss-repository/SKILL.md +2 -2
  213. package/template/.codex/skills/yss-repository/references/profiles/layered-mvc-service.md +1 -1
  214. package/template/.codex/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +1 -1
  215. package/template/.codex/skills/yss-repository/tests/profile-routing.test.mjs +2 -2
  216. package/template/.codex/skills/yss-research/SKILL.md +6 -2
  217. package/template/.codex/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
  218. package/template/.codex/skills/yss-stage-decision/SKILL.md +5 -5
  219. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +1 -1
  220. package/template/.codex/skills/yss-stage-decision/references/strategic-handoff-routing.md +1 -1
  221. package/template/.codex/skills/yss-tactical-design/SKILL.md +2 -2
  222. package/template/.codex/skills/yss-tactical-design/references/strategic-handoff-routing.md +1 -1
  223. package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
  224. package/template/.codex/skills/yss-technical-design/SKILL.md +6 -6
  225. package/template/.codex/skills/yss-technical-design/references/technical-design-contract.md +1 -1
  226. package/template/.codex/skills/yss-ui/SKILL.md +1 -1
  227. package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
  228. package/template/.codex/skills/yss-web-controller/SKILL.md +1 -1
  229. package/template/.codex/skills/yss-web-controller/references/profiles/layered-mvc-service.md +1 -1
  230. package/template/.cursor/skills/alibaba-java-code-style/SKILL.md +1 -1
  231. package/template/.cursor/skills/code-review/SKILL.md +6 -6
  232. package/template/.cursor/skills/code-review/references/yss-review-standards.md +1 -1
  233. package/template/.cursor/skills/codebase-design/SKILL.md +4 -0
  234. package/template/.cursor/skills/{improve-codebase-architecture/SKILL.md → codebase-design/references/architecture-audit.md} +3 -9
  235. package/template/.cursor/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  236. package/template/.cursor/skills/frontend-commit/SKILL.md +4 -93
  237. package/template/.cursor/skills/git-commit-core/SKILL.md +18 -0
  238. package/template/.cursor/skills/i-have-adhd/SKILL.md +1 -1
  239. package/template/.cursor/skills/i-have-adhd/references/yss-adaptation.md +1 -1
  240. package/template/.cursor/skills/i-have-adhd/tests/verify-integration.mjs +2 -2
  241. package/template/.cursor/skills/implementation-repo-onboarding/SKILL.md +2 -2
  242. package/template/.cursor/skills/java-backend-commit/SKILL.md +4 -96
  243. package/template/.cursor/skills/llm-wiki/SKILL.md +12 -13
  244. package/template/.cursor/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  245. package/template/.cursor/skills/llm-wiki/references/compile.md +21 -114
  246. package/template/.cursor/skills/llm-wiki/references/discover.md +3 -3
  247. package/template/.cursor/skills/llm-wiki/references/ingest.md +8 -33
  248. package/template/.cursor/skills/llm-wiki/references/lint.md +9 -46
  249. package/template/.cursor/skills/llm-wiki/references/query.md +9 -18
  250. package/template/.cursor/skills/llm-wiki/references/schema.md +56 -29
  251. package/template/.cursor/skills/llm-wiki/references/transactions.md +48 -0
  252. package/template/.cursor/skills/llm-wiki/references/writing.md +4 -4
  253. package/template/.cursor/skills/llm-wiki/scripts/advise.mjs +46 -58
  254. package/template/.cursor/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  255. package/template/.cursor/skills/llm-wiki/scripts/core.mjs +262 -0
  256. package/template/.cursor/skills/llm-wiki/scripts/extract.mjs +35 -18
  257. package/template/.cursor/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  258. package/template/.cursor/skills/llm-wiki/scripts/feedback.mjs +51 -0
  259. package/template/.cursor/skills/llm-wiki/scripts/inventory.mjs +43 -171
  260. package/template/.cursor/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  261. package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  262. package/template/.cursor/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  263. package/template/.cursor/skills/llm-wiki/scripts/migrate.mjs +51 -0
  264. package/template/.cursor/skills/llm-wiki/scripts/query.mjs +149 -0
  265. package/template/.cursor/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  266. package/template/.cursor/skills/llm-wiki/scripts/sources.mjs +166 -0
  267. package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +384 -0
  268. package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  269. package/template/.cursor/skills/prototype/LOGIC.md +1 -1
  270. package/template/.cursor/skills/prototype/SKILL.md +5 -19
  271. package/template/.cursor/skills/prototype-review/SKILL.md +4 -4
  272. package/template/.cursor/skills/setup-matt-pocock-skills/SKILL.md +11 -11
  273. package/template/.cursor/skills/triage/SKILL.md +1 -1
  274. package/template/.cursor/skills/yss-application/references/profiles/layered-mvc-service.md +1 -1
  275. package/template/.cursor/skills/yss-application/references/profiles/mvc-data-analysis-v1.md +1 -1
  276. package/template/.cursor/skills/yss-backend-spec-review/SKILL.md +1 -1
  277. package/template/.cursor/skills/yss-backend-spec-review/references/check-inputs.md +1 -1
  278. package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +2 -2
  279. package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  280. package/template/.cursor/skills/yss-design-system/SKILL.md +10 -10
  281. package/template/.cursor/skills/yss-design-system/references/design-qa-theme.md +3 -3
  282. package/template/.cursor/skills/yss-design-system/references/design-system.md +3 -3
  283. package/template/.cursor/skills/yss-exception/SKILL.md +1 -1
  284. package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +2 -2
  285. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  286. package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/refresh_baseline.mjs +2 -2
  287. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +11 -9
  288. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +3 -4
  289. package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +9 -9
  290. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +3 -3
  291. package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +1 -1
  292. package/template/.cursor/skills/yss-implementation-contract-compiler/references/strategic-handoff-routing.md +1 -1
  293. package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  294. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  295. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/data-analysis-profile.mjs +5 -5
  296. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +2 -2
  297. package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +42 -0
  298. package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +2 -2
  299. package/template/.cursor/skills/yss-openapi-governance/SKILL.md +7 -7
  300. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +17 -17
  301. package/template/.cursor/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  302. package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +10 -10
  303. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +85 -47
  304. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +24 -16
  305. package/template/.cursor/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  306. package/template/.cursor/skills/yss-product-lifecycle/references/request-triage.md +2 -0
  307. package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +4 -4
  308. package/template/.cursor/skills/yss-product-lifecycle/references/user-decisions.md +2 -2
  309. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +7 -7
  310. package/template/.cursor/skills/yss-prototype-stage/references/existing-ui-entry.md +1 -1
  311. package/template/.cursor/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +1 -1
  312. package/template/.cursor/skills/yss-prototype-stage/scripts/offline-html.mjs +3 -3
  313. package/template/.cursor/skills/yss-prototype-stage/tests/browser-smoke.mjs +2 -2
  314. package/template/.cursor/skills/yss-prototype-stage/tests/run-scenarios.mjs +4 -4
  315. package/template/.cursor/skills/yss-prototype-stage/tests/workbench-smoke.mjs +1 -1
  316. package/template/.cursor/skills/yss-repository/SKILL.md +2 -2
  317. package/template/.cursor/skills/yss-repository/references/profiles/layered-mvc-service.md +1 -1
  318. package/template/.cursor/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +1 -1
  319. package/template/.cursor/skills/yss-repository/tests/profile-routing.test.mjs +2 -2
  320. package/template/.cursor/skills/yss-research/SKILL.md +6 -2
  321. package/template/.cursor/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
  322. package/template/.cursor/skills/yss-stage-decision/SKILL.md +5 -5
  323. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +1 -1
  324. package/template/.cursor/skills/yss-stage-decision/references/strategic-handoff-routing.md +1 -1
  325. package/template/.cursor/skills/yss-tactical-design/SKILL.md +2 -2
  326. package/template/.cursor/skills/yss-tactical-design/references/strategic-handoff-routing.md +1 -1
  327. package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
  328. package/template/.cursor/skills/yss-technical-design/SKILL.md +6 -6
  329. package/template/.cursor/skills/yss-technical-design/references/technical-design-contract.md +1 -1
  330. package/template/.cursor/skills/yss-ui/SKILL.md +1 -1
  331. package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
  332. package/template/.cursor/skills/yss-web-controller/SKILL.md +1 -1
  333. package/template/.cursor/skills/yss-web-controller/references/profiles/layered-mvc-service.md +1 -1
  334. package/template/.cursorrules +1 -1
  335. package/template/.pi/skills/alibaba-java-code-style/SKILL.md +1 -1
  336. package/template/.pi/skills/code-review/SKILL.md +6 -6
  337. package/template/.pi/skills/code-review/references/yss-review-standards.md +1 -1
  338. package/template/.pi/skills/codebase-design/SKILL.md +4 -0
  339. package/template/{.agents/skills/improve-codebase-architecture/SKILL.md → .pi/skills/codebase-design/references/architecture-audit.md} +3 -9
  340. package/template/.pi/skills/cross-repo-implementation-routing/SKILL.md +2 -2
  341. package/template/.pi/skills/frontend-commit/SKILL.md +4 -93
  342. package/template/.pi/skills/git-commit-core/SKILL.md +18 -0
  343. package/template/.pi/skills/i-have-adhd/SKILL.md +1 -1
  344. package/template/.pi/skills/i-have-adhd/references/yss-adaptation.md +1 -1
  345. package/template/.pi/skills/i-have-adhd/tests/verify-integration.mjs +2 -2
  346. package/template/.pi/skills/implementation-repo-onboarding/SKILL.md +2 -2
  347. package/template/.pi/skills/java-backend-commit/SKILL.md +4 -96
  348. package/template/.pi/skills/llm-wiki/SKILL.md +12 -13
  349. package/template/.pi/skills/llm-wiki/assets/CLAUDE.md.template +7 -1
  350. package/template/.pi/skills/llm-wiki/references/compile.md +21 -114
  351. package/template/.pi/skills/llm-wiki/references/discover.md +3 -3
  352. package/template/.pi/skills/llm-wiki/references/ingest.md +8 -33
  353. package/template/.pi/skills/llm-wiki/references/lint.md +9 -46
  354. package/template/.pi/skills/llm-wiki/references/query.md +9 -18
  355. package/template/.pi/skills/llm-wiki/references/schema.md +56 -29
  356. package/template/.pi/skills/llm-wiki/references/transactions.md +48 -0
  357. package/template/.pi/skills/llm-wiki/references/writing.md +4 -4
  358. package/template/.pi/skills/llm-wiki/scripts/advise.mjs +46 -58
  359. package/template/.pi/skills/llm-wiki/scripts/advise.test.mjs +11 -16
  360. package/template/.pi/skills/llm-wiki/scripts/core.mjs +262 -0
  361. package/template/.pi/skills/llm-wiki/scripts/extract.mjs +35 -18
  362. package/template/.pi/skills/llm-wiki/scripts/extract.test.mjs +5 -8
  363. package/template/.pi/skills/llm-wiki/scripts/feedback.mjs +51 -0
  364. package/template/.pi/skills/llm-wiki/scripts/inventory.mjs +43 -171
  365. package/template/.pi/skills/llm-wiki/scripts/inventory.test.mjs +25 -18
  366. package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.mjs +69 -92
  367. package/template/.pi/skills/llm-wiki/scripts/lint-wikilinks.test.mjs +25 -37
  368. package/template/.pi/skills/llm-wiki/scripts/migrate.mjs +51 -0
  369. package/template/.pi/skills/llm-wiki/scripts/query.mjs +149 -0
  370. package/template/.pi/skills/llm-wiki/scripts/regressions.test.mjs +101 -0
  371. package/template/.pi/skills/llm-wiki/scripts/sources.mjs +166 -0
  372. package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +384 -0
  373. package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +354 -0
  374. package/template/.pi/skills/prototype/LOGIC.md +1 -1
  375. package/template/.pi/skills/prototype/SKILL.md +5 -19
  376. package/template/.pi/skills/prototype-review/SKILL.md +4 -4
  377. package/template/.pi/skills/setup-matt-pocock-skills/SKILL.md +11 -11
  378. package/template/.pi/skills/triage/SKILL.md +1 -1
  379. package/template/.pi/skills/yss-application/references/profiles/layered-mvc-service.md +1 -1
  380. package/template/.pi/skills/yss-application/references/profiles/mvc-data-analysis-v1.md +1 -1
  381. package/template/.pi/skills/yss-backend-spec-review/SKILL.md +1 -1
  382. package/template/.pi/skills/yss-backend-spec-review/references/check-inputs.md +1 -1
  383. package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +2 -2
  384. package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  385. package/template/.pi/skills/yss-design-system/SKILL.md +10 -10
  386. package/template/.pi/skills/yss-design-system/references/design-qa-theme.md +3 -3
  387. package/template/.pi/skills/yss-design-system/references/design-system.md +3 -3
  388. package/template/.pi/skills/yss-exception/SKILL.md +1 -1
  389. package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +2 -2
  390. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/generate_scaffold.mjs +1 -1
  391. package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/refresh_baseline.mjs +2 -2
  392. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +11 -9
  393. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +3 -4
  394. package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +9 -9
  395. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +3 -3
  396. package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +1 -1
  397. package/template/.pi/skills/yss-implementation-contract-compiler/references/strategic-handoff-routing.md +1 -1
  398. package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +14 -0
  399. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +2 -2
  400. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/data-analysis-profile.mjs +5 -5
  401. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +2 -2
  402. package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +42 -0
  403. package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +2 -2
  404. package/template/.pi/skills/yss-openapi-governance/SKILL.md +7 -7
  405. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +17 -17
  406. package/template/.pi/skills/yss-product-lifecycle/references/external-input-questionnaire.md +19 -0
  407. package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +10 -10
  408. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +85 -47
  409. package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +24 -16
  410. package/template/.pi/skills/yss-product-lifecycle/references/plan-requirements.md +10 -0
  411. package/template/.pi/skills/yss-product-lifecycle/references/request-triage.md +2 -0
  412. package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +4 -4
  413. package/template/.pi/skills/yss-product-lifecycle/references/user-decisions.md +2 -2
  414. package/template/.pi/skills/yss-prototype-stage/SKILL.md +7 -7
  415. package/template/.pi/skills/yss-prototype-stage/references/existing-ui-entry.md +1 -1
  416. package/template/.pi/skills/yss-prototype-stage/scripts/build-antd-prototype.mjs +1 -1
  417. package/template/.pi/skills/yss-prototype-stage/scripts/offline-html.mjs +3 -3
  418. package/template/.pi/skills/yss-prototype-stage/tests/browser-smoke.mjs +2 -2
  419. package/template/.pi/skills/yss-prototype-stage/tests/run-scenarios.mjs +4 -4
  420. package/template/.pi/skills/yss-prototype-stage/tests/workbench-smoke.mjs +1 -1
  421. package/template/.pi/skills/yss-repository/SKILL.md +2 -2
  422. package/template/.pi/skills/yss-repository/references/profiles/layered-mvc-service.md +1 -1
  423. package/template/.pi/skills/yss-repository/references/profiles/mvc-data-analysis-v1.md +1 -1
  424. package/template/.pi/skills/yss-repository/tests/profile-routing.test.mjs +2 -2
  425. package/template/.pi/skills/yss-research/SKILL.md +6 -2
  426. package/template/.pi/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
  427. package/template/.pi/skills/yss-stage-decision/SKILL.md +5 -5
  428. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +1 -1
  429. package/template/.pi/skills/yss-stage-decision/references/strategic-handoff-routing.md +1 -1
  430. package/template/.pi/skills/yss-tactical-design/SKILL.md +2 -2
  431. package/template/.pi/skills/yss-tactical-design/references/strategic-handoff-routing.md +1 -1
  432. package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
  433. package/template/.pi/skills/yss-technical-design/SKILL.md +6 -6
  434. package/template/.pi/skills/yss-technical-design/references/technical-design-contract.md +1 -1
  435. package/template/.pi/skills/yss-ui/SKILL.md +1 -1
  436. package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
  437. package/template/.pi/skills/yss-web-controller/SKILL.md +1 -1
  438. package/template/.pi/skills/yss-web-controller/references/profiles/layered-mvc-service.md +1 -1
  439. package/template/{docs → .template-spec}/adr/README.md +1 -1
  440. package/template/{docs → .template-spec}/agents/README.md +2 -2
  441. package/template/{docs → .template-spec}/agents/backend-architecture-profiles.md +2 -2
  442. package/template/{docs → .template-spec}/agents/digital-human-roles.md +6 -6
  443. package/template/{docs → .template-spec}/agents/digital-human-roles.yaml +6 -6
  444. package/template/{docs → .template-spec}/agents/gitlab-workflow-skills.md +2 -2
  445. package/template/{docs → .template-spec}/agents/issue-tracker.md +1 -1
  446. package/template/{docs → .template-spec}/agents/skill-migrations.md +17 -2
  447. package/template/{docs → .template-spec}/agents/triage-labels.md +1 -1
  448. package/template/{docs → .template-spec}/agents/yss-skill-registry.yaml +12 -31
  449. package/template/{docs → .template-spec}/architecture/README.md +5 -36
  450. package/template/{docs → .template-spec}/architecture/templates/tech-design-template.md +3 -1
  451. package/template/{docs → .template-spec}/design/README.md +12 -11
  452. package/template/{docs → .template-spec}/design/design.md +19 -19
  453. package/template/{docs/design/templates/interaction-spec-template.md → .template-spec/design/templates/examples/model-publishing-interaction.example.md} +5 -3
  454. package/template/{docs/design/templates/state-matrix-template.md → .template-spec/design/templates/examples/model-publishing-state-matrix.example.md} +3 -1
  455. package/template/.template-spec/design/templates/interaction-spec-template.md +61 -0
  456. package/template/{docs → .template-spec}/design/templates/product-overview-design-template.md +11 -11
  457. package/template/{docs → .template-spec}/design/templates/prototype-evidence-template.yaml +4 -4
  458. package/template/{docs → .template-spec}/design/templates/prototype-review-checklist.md +1 -1
  459. package/template/.template-spec/design/templates/state-matrix-template.md +29 -0
  460. package/template/.template-spec/design/tokens/.design-md-projection.json +13 -0
  461. package/template/{docs → .template-spec}/engineering/backend-platforms.md +1 -1
  462. package/template/{docs → .template-spec}/plan/entry-review.md +8 -4
  463. package/template/{docs → .template-spec}/plan/templates/competitive-analysis-template.md +2 -2
  464. package/template/{docs → .template-spec}/plan/templates/plan-template.md +1 -1
  465. package/template/{docs → .template-spec}/process/contract-reading.md +25 -1
  466. package/template/{docs → .template-spec}/process/lifecycle-artifact-map.md +5 -5
  467. package/template/.template-spec/process/lifecycle-registry-baseline.json +255 -0
  468. package/template/{docs → .template-spec}/process/lifecycle-registry.yaml +25 -12
  469. package/template/{docs → .template-spec}/process/plan-migration.md +1 -1
  470. package/template/.template-spec/process/research-completion.md +23 -0
  471. package/template/{docs → .template-spec}/process/schemas/digital-human-task-package.schema.json +209 -6
  472. package/template/{docs → .template-spec}/process/schemas/frontend-implementation-evidence.schema.json +7 -15
  473. package/template/{docs → .template-spec}/process/schemas/lifecycle-checkpoint.schema.json +15 -0
  474. package/template/{docs → .template-spec}/process/schemas/lifecycle-registry.schema.json +1 -1
  475. package/template/{docs → .template-spec}/process/schemas/strategic-design-handoff-v3.schema.json +1 -1
  476. package/template/{docs → .template-spec}/process/schemas/strategic-design-handoff-v4.schema.json +1 -1
  477. package/template/{docs → .template-spec}/process/schemas/strategic-design-handoff-v5.schema.json +1 -1
  478. package/template/{docs → .template-spec}/process/schemas/strategic-design-handoff.schema.json +1 -1
  479. package/template/{docs → .template-spec}/process/stage-tracking.md +1 -1
  480. package/template/.template-spec/process/subagent-collaboration.md +21 -0
  481. package/template/{docs → .template-spec}/process/templates/frontend-implementation-verification-template.yaml +15 -1
  482. package/template/{docs → .template-spec}/templates/build-architecture-checklist-template.md +2 -2
  483. package/template/{docs → .template-spec}/templates/digital-human-runtime-profile-template.md +2 -2
  484. package/template/{docs → .template-spec}/templates/grok-bot-profile-template.md +1 -1
  485. package/template/{docs → .template-spec}/templates/local-parent-ticket-template.md +1 -1
  486. package/template/.template-spec/templates/release-note-template.md +29 -0
  487. package/template/.template-spec/templates/retro-report-template.md +24 -0
  488. package/template/{docs → .template-spec}/templates/subagent-task-package-template.md +3 -3
  489. package/template/.template-spec/templates/verification-record-template.md +64 -0
  490. package/template/{docs → .template-spec}/templates/vertical-slice-ticket-template.md +5 -10
  491. package/template/{docs → .template-spec}/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
  492. package/template/{docs → .template-spec}/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +4 -4
  493. package/template/AGENTS.md +13 -13
  494. package/template/CONTEXT.md +8 -4
  495. package/template/README.md +33 -45
  496. package/template/scripts/backend-delivery +1 -1
  497. package/template/scripts/contract +5 -5
  498. package/template/scripts/lib/api-contract-decision.mjs +1 -1
  499. package/template/scripts/lib/approved-execution-context.mjs +5 -5
  500. package/template/scripts/lib/backend-delivery.mjs +4 -4
  501. package/template/scripts/lib/backend-platform.mjs +1 -1
  502. package/template/scripts/lib/backend-review.mjs +1 -1
  503. package/template/scripts/lib/backend-scaffold-prerequisites.mjs +1 -1
  504. package/template/scripts/lib/backend-standards-coverage.mjs +2 -2
  505. package/template/scripts/lib/context-reconciliation.mjs +1 -1
  506. package/template/scripts/lib/contract-views.mjs +66 -11
  507. package/template/scripts/lib/delivery-preflight.mjs +6 -6
  508. package/template/scripts/lib/design-md.mjs +2 -2
  509. package/template/scripts/lib/digital-human-roles.mjs +2 -2
  510. package/template/scripts/lib/execution-evidence.mjs +51 -0
  511. package/template/scripts/lib/existing-backend-architecture.mjs +1 -1
  512. package/template/scripts/lib/existing-ui-baseline.mjs +1 -1
  513. package/template/scripts/lib/frontend-delivery.mjs +2 -2
  514. package/template/scripts/lib/governance-layout.mjs +74 -0
  515. package/template/scripts/lib/harness-execution-scope.mjs +3 -3
  516. package/template/scripts/lib/implementation-contract-compiler.mjs +2 -0
  517. package/template/scripts/lib/lifecycle-context-query.mjs +26 -6
  518. package/template/scripts/lib/lifecycle-registry.mjs +46 -7
  519. package/template/scripts/lib/lifecycle-status.mjs +137 -0
  520. package/template/scripts/lib/lifecycle-transition.mjs +90 -11
  521. package/template/scripts/lib/maintenance-research.mjs +63 -0
  522. package/template/scripts/lib/openapi-draft-validation.mjs +1 -1
  523. package/template/scripts/lib/plan-spec-entry-view.mjs +48 -0
  524. package/template/scripts/lib/plan-spec-entry.mjs +3 -3
  525. package/template/scripts/lib/read-only-intake.mjs +132 -0
  526. package/template/scripts/lib/skill-registry.mjs +10 -9
  527. package/template/scripts/lib/skill-supply-chain.mjs +5 -1
  528. package/template/scripts/lib/slice-contract-preparation.mjs +3 -3
  529. package/template/scripts/lib/slice-contract.mjs +1 -1
  530. package/template/scripts/lib/slice-repositories.mjs +1 -1
  531. package/template/scripts/lib/slice-task-package.mjs +4 -4
  532. package/template/scripts/lib/stage-tracking-migration.mjs +2 -2
  533. package/template/scripts/lib/stage-tracking.mjs +5 -5
  534. package/template/scripts/lib/strategic-feedback.mjs +2 -2
  535. package/template/scripts/lib/strategic-handoff-routing.mjs +2 -2
  536. package/template/scripts/lib/strategic-handoff.mjs +15 -15
  537. package/template/scripts/lib/task-package-schema.mjs +2 -2
  538. package/template/scripts/lib/task-package.mjs +10 -3
  539. package/template/scripts/lib/technical-design-boundary.mjs +1 -1
  540. package/template/scripts/lib/user-decision.mjs +2 -1
  541. package/template/scripts/lifecycle-status +14 -0
  542. package/template/scripts/plan-spec-entry +21 -0
  543. package/template/scripts/prepare-read-only-intake +16 -0
  544. package/template/scripts/run-read-only-intake +14 -0
  545. package/template/scripts/strategic-consumer-entry +1 -1
  546. package/template/scripts/strategic-handoff +1 -1
  547. package/template/scripts/verify-digital-human-task-package +2 -2
  548. package/template/scripts/verify-frontend-implementation-evidence +25 -2
  549. package/template/scripts/verify-governance-layout +15 -0
  550. package/template/scripts/verify-lifecycle-checkpoint +1 -1
  551. package/template/scripts/verify-maintenance-research +13 -0
  552. package/template/scripts/verify-project-instance +2 -0
  553. package/template/scripts/verify-scaffold-architecture-decisions +1 -1
  554. package/template/scripts/verify-strategic-context-import +3 -3
  555. package/template/skills-lock.json +82 -139
  556. package/template.manifest.json +135 -46
  557. package/template.snapshot.json +5 -6
  558. package/template/.agents/skills/grill-with-docs/SKILL.md +0 -20
  559. package/template/.agents/skills/grill-with-docs/agents/openai.yaml +0 -5
  560. package/template/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  561. package/template/.agents/skills/prototype/UI.md +0 -112
  562. package/template/.agents/skills/setup-matt-pocock-skills/domain.md +0 -37
  563. package/template/.agents/skills/to-questionnaire/SKILL.md +0 -55
  564. package/template/.agents/skills/to-questionnaire/agents/openai.yaml +0 -5
  565. package/template/.agents/skills/wait-what/SKILL.md +0 -7
  566. package/template/.agents/skills/wait-what/agents/openai.yaml +0 -5
  567. package/template/.codex/skills/data-analytics/.app.json +0 -84
  568. package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +0 -66
  569. package/template/.codex/skills/data-analytics/.mcp.json +0 -30
  570. package/template/.codex/skills/data-analytics/AGENTS.md +0 -20
  571. package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +0 -27
  572. package/template/.codex/skills/data-analytics/README.md +0 -60
  573. package/template/.codex/skills/data-analytics/__yss_dotfile__.gitignore +0 -2
  574. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +0 -16
  575. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +0 -1
  576. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +0 -1
  577. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +0 -1
  578. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +0 -1
  579. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +0 -1
  580. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +0 -1
  581. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +0 -16
  582. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +0 -1
  583. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +0 -1
  584. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +0 -1
  585. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +0 -1
  586. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +0 -1
  587. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +0 -1
  588. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +0 -1
  589. package/template/.codex/skills/data-analytics/assets/datascience-small.svg +0 -5
  590. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +0 -16
  591. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +0 -1
  592. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +0 -1
  593. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +0 -1
  594. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +0 -1
  595. package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
  596. package/template/.codex/skills/data-analytics/assets/datascience.svg +0 -10
  597. package/template/.codex/skills/data-analytics/mcp/server.cjs +0 -2964
  598. package/template/.codex/skills/data-analytics/package-lock.json +0 -3048
  599. package/template/.codex/skills/data-analytics/package.json +0 -43
  600. package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +0 -75
  601. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +0 -137
  602. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +0 -6
  603. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/references/quality-checks.md +0 -29
  604. package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +0 -148
  605. package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +0 -6
  606. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +0 -18
  607. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +0 -24
  608. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +0 -71
  609. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +0 -85
  610. package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +0 -207
  611. package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +0 -6
  612. package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +0 -70
  613. package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +0 -66
  614. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +0 -108
  615. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +0 -6
  616. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +0 -1
  617. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +0 -52
  618. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +0 -61
  619. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +0 -362
  620. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +0 -829
  621. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +0 -30
  622. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +0 -127
  623. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +0 -374
  624. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +0 -613
  625. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +0 -54
  626. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +0 -16
  627. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +0 -9
  628. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/tests/test_delivery_plan.py +0 -45
  629. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +0 -77
  630. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +0 -6
  631. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +0 -2379
  632. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +0 -88
  633. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +0 -6
  634. package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +0 -97
  635. package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +0 -59
  636. package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +0 -75
  637. package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +0 -103
  638. package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +0 -6
  639. package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +0 -68
  640. package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +0 -6
  641. package/template/.codex/skills/data-analytics/skills/index/SKILL.md +0 -251
  642. package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +0 -6
  643. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +0 -131
  644. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +0 -6
  645. package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +0 -141
  646. package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +0 -6
  647. package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +0 -32
  648. package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +0 -106
  649. package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +0 -6
  650. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +0 -130
  651. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +0 -6
  652. package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +0 -141
  653. package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +0 -6
  654. package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +0 -178
  655. package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +0 -9
  656. package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
  657. package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +0 -31
  658. package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +0 -466
  659. package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +0 -99
  660. package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +0 -197
  661. package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +0 -6
  662. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +0 -26
  663. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +0 -51
  664. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +0 -66
  665. package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +0 -69
  666. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +0 -204
  667. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +0 -87
  668. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +0 -497
  669. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +0 -74
  670. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +0 -65
  671. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +0 -160
  672. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +0 -75
  673. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +0 -96
  674. package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +0 -263
  675. package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +0 -1462
  676. package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +0 -128
  677. package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +0 -101
  678. package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +0 -499
  679. package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +0 -978
  680. package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +0 -126
  681. package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +0 -6
  682. package/template/.codex/skills/data-analytics/skills/validate-data/references/validation-methods.md +0 -70
  683. package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +0 -157
  684. package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +0 -6
  685. package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +0 -774
  686. package/template/.codex/skills/data-analytics/src/DESIGN.md +0 -222
  687. package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +0 -3666
  688. package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +0 -136
  689. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +0 -54
  690. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +0 -96
  691. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +0 -1647
  692. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +0 -245
  693. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +0 -462
  694. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +0 -107
  695. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +0 -164
  696. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +0 -192
  697. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +0 -203
  698. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +0 -619
  699. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +0 -402
  700. package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
  701. package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +0 -370
  702. package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +0 -536
  703. package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +0 -681
  704. package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +0 -164
  705. package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +0 -21
  706. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +0 -472
  707. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +0 -75
  708. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +0 -142
  709. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +0 -142
  710. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +0 -377
  711. package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +0 -3468
  712. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +0 -44
  713. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +0 -639
  714. package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +0 -327
  715. package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +0 -225
  716. package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +0 -204
  717. package/template/.codex/skills/data-analytics/src/analytics-app-core.md +0 -85
  718. package/template/.codex/skills/data-analytics/src/codex-style-contract.md +0 -30
  719. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +0 -12
  720. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +0 -1008
  721. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +0 -2117
  722. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +0 -122
  723. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +0 -3588
  724. package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +0 -483
  725. package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +0 -32
  726. package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +0 -333
  727. package/template/.codex/skills/data-analytics/src/mcp-host.js +0 -153
  728. package/template/.codex/skills/data-analytics/src/recharts-config.js +0 -315
  729. package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +0 -294
  730. package/template/.codex/skills/data-analytics/src/sql-source-view.css +0 -33
  731. package/template/.codex/skills/data-analytics/src/sql-source-view.js +0 -157
  732. package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +0 -121
  733. package/template/.codex/skills/data-analytics/src/table-renderer.jsx +0 -28
  734. package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +0 -87
  735. package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +0 -105
  736. package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +0 -233
  737. package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +0 -1500
  738. package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +0 -219
  739. package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +0 -144
  740. package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +0 -11
  741. package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +0 -54
  742. package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +0 -321
  743. package/template/.codex/skills/data-analytics/tsconfig.json +0 -19
  744. package/template/.codex/skills/data-analytics/vite.config.ts +0 -46
  745. package/template/.codex/skills/grill-with-docs/SKILL.md +0 -20
  746. package/template/.codex/skills/grill-with-docs/agents/openai.yaml +0 -5
  747. package/template/.codex/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  748. package/template/.codex/skills/prototype/UI.md +0 -112
  749. package/template/.codex/skills/setup-matt-pocock-skills/domain.md +0 -37
  750. package/template/.codex/skills/to-questionnaire/SKILL.md +0 -55
  751. package/template/.codex/skills/to-questionnaire/agents/openai.yaml +0 -5
  752. package/template/.codex/skills/wait-what/SKILL.md +0 -7
  753. package/template/.codex/skills/wait-what/agents/openai.yaml +0 -5
  754. package/template/.cursor/skills/grill-with-docs/SKILL.md +0 -20
  755. package/template/.cursor/skills/grill-with-docs/agents/openai.yaml +0 -5
  756. package/template/.cursor/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  757. package/template/.cursor/skills/prototype/UI.md +0 -112
  758. package/template/.cursor/skills/setup-matt-pocock-skills/domain.md +0 -37
  759. package/template/.cursor/skills/to-questionnaire/SKILL.md +0 -55
  760. package/template/.cursor/skills/to-questionnaire/agents/openai.yaml +0 -5
  761. package/template/.cursor/skills/wait-what/SKILL.md +0 -7
  762. package/template/.cursor/skills/wait-what/agents/openai.yaml +0 -5
  763. package/template/.pi/skills/grill-with-docs/SKILL.md +0 -20
  764. package/template/.pi/skills/grill-with-docs/agents/openai.yaml +0 -5
  765. package/template/.pi/skills/improve-codebase-architecture/agents/openai.yaml +0 -5
  766. package/template/.pi/skills/prototype/UI.md +0 -112
  767. package/template/.pi/skills/setup-matt-pocock-skills/domain.md +0 -37
  768. package/template/.pi/skills/to-questionnaire/SKILL.md +0 -55
  769. package/template/.pi/skills/to-questionnaire/agents/openai.yaml +0 -5
  770. package/template/.pi/skills/wait-what/SKILL.md +0 -7
  771. package/template/.pi/skills/wait-what/agents/openai.yaml +0 -5
  772. package/template/docs/agents/archify-integration.md +0 -19
  773. package/template/docs/agents/domain.md +0 -31
  774. package/template/docs/design/design-system-sync.yaml +0 -13
  775. package/template/docs/design/preview-dark.html +0 -178
  776. package/template/docs/design/preview.css +0 -730
  777. package/template/docs/design/preview.html +0 -178
  778. package/template/docs/design/preview.js +0 -71
  779. package/template/docs/design/tokens/.design-md-projection.json +0 -13
  780. package/template/docs/engineering/evidence/aliyun-artifact-resolution.json +0 -446
  781. package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +0 -13
  782. package/template/docs/process/github-workflows.md +0 -52
  783. package/template/docs/process/subagent-collaboration.md +0 -13
  784. package/template/docs/templates/release-note-template.md +0 -14
  785. package/template/docs/templates/retro-report-template.md +0 -16
  786. package/template/docs/templates/verification-record-template.md +0 -53
  787. /package/template/.agents/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  788. /package/template/.codex/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  789. /package/template/.cursor/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  790. /package/template/.pi/skills/{improve-codebase-architecture → codebase-design/references}/HTML-REPORT.md +0 -0
  791. /package/template/{docs → .template-spec}/api/templates/openapi-draft-review-checklist.md +0 -0
  792. /package/template/{docs → .template-spec}/api/templates/openapi-draft-validation-record-template.yaml +0 -0
  793. /package/template/{docs → .template-spec}/api/templates/openapi-freeze-record-template.md +0 -0
  794. /package/template/{docs → .template-spec}/api/templates/openapi-json-export-record-template.md +0 -0
  795. /package/template/{docs → .template-spec}/architecture/templates/architecture-deepening-template.md +0 -0
  796. /package/template/{docs → .template-spec}/architecture/templates/architecture-review-checklist.md +0 -0
  797. /package/template/{docs → .template-spec}/architecture/templates/business-architecture-template.md +0 -0
  798. /package/template/{docs → .template-spec}/architecture/templates/data-architecture-template.md +0 -0
  799. /package/template/{docs → .template-spec}/architecture/templates/engineering-baseline-review-template.md +0 -0
  800. /package/template/{docs → .template-spec}/architecture/templates/functional-architecture-template.md +0 -0
  801. /package/template/{docs → .template-spec}/architecture/templates/system-overview-design-template.md +0 -0
  802. /package/template/{docs → .template-spec}/design/diagrams/README.md +0 -0
  803. /package/template/{docs → .template-spec}/design/schemas/visual-baseline.schema.json +0 -0
  804. /package/template/{docs → .template-spec}/design/templates/prototype-confirmation-template.md +0 -0
  805. /package/template/{docs → .template-spec}/design/templates/visual-baseline-template.yaml +0 -0
  806. /package/template/{docs → .template-spec}/design/tokens/theme.json +0 -0
  807. /package/template/{docs → .template-spec}/design/tokens/tokens.compact.json +0 -0
  808. /package/template/{docs → .template-spec}/design/tokens/tokens.dark.json +0 -0
  809. /package/template/{docs → .template-spec}/design/tokens/tokens.default.json +0 -0
  810. /package/template/{docs → .template-spec}/design/tokens/variables.css +0 -0
  811. /package/template/{docs → .template-spec}/design/tokens/variables.dark.css +0 -0
  812. /package/template/{docs → .template-spec}/engineering/backend-platforms.json +0 -0
  813. /package/template/{docs → .template-spec}/plan/README.md +0 -0
  814. /package/template/{docs → .template-spec}/plan/templates/competitive-matrix-template.md +0 -0
  815. /package/template/{docs → .template-spec}/plan/templates/market-analysis-template.md +0 -0
  816. /package/template/{docs → .template-spec}/plan/templates/user-pain-points-template.md +0 -0
  817. /package/template/{docs → .template-spec}/process/delivery-preflight.md +0 -0
  818. /package/template/{docs → .template-spec}/process/document-writing.md +0 -0
  819. /package/template/{docs → .template-spec}/process/existing-backend-architecture.md +0 -0
  820. /package/template/{docs → .template-spec}/process/existing-ui-baseline.md +0 -0
  821. /package/template/{docs → .template-spec}/process/frontend-backend-delivery.md +0 -0
  822. /package/template/{docs → .template-spec}/process/harness-process-tailoring.md +0 -0
  823. /package/template/{docs → .template-spec}/process/implementation-repo-integration.md +0 -0
  824. /package/template/{docs/process/lifecycle-registry-baseline.json → .template-spec/process/lifecycle-registry-baseline-v1.json} +0 -0
  825. /package/template/{docs → .template-spec}/process/schemas/api-contract-decision-v2.schema.json +0 -0
  826. /package/template/{docs → .template-spec}/process/schemas/api-contract-decision.schema.json +0 -0
  827. /package/template/{docs → .template-spec}/process/schemas/backend-architecture-identity.schema.json +0 -0
  828. /package/template/{docs → .template-spec}/process/schemas/backend-delivery-verification.schema.json +0 -0
  829. /package/template/{docs → .template-spec}/process/schemas/backend-delivery.schema.json +0 -0
  830. /package/template/{docs → .template-spec}/process/schemas/context-reconciliation.schema.json +0 -0
  831. /package/template/{docs → .template-spec}/process/schemas/data-architecture-decision.schema.json +0 -0
  832. /package/template/{docs → .template-spec}/process/schemas/delivery-preflight-input.schema.json +0 -0
  833. /package/template/{docs → .template-spec}/process/schemas/delivery-preflight-result.schema.json +0 -0
  834. /package/template/{docs → .template-spec}/process/schemas/existing-ui-baseline.schema.json +0 -0
  835. /package/template/{docs → .template-spec}/process/schemas/frontend-delivery-acceptance-v1.schema.json +0 -0
  836. /package/template/{docs → .template-spec}/process/schemas/frontend-delivery-acceptance-v3.schema.json +0 -0
  837. /package/template/{docs → .template-spec}/process/schemas/frontend-delivery-acceptance.schema.json +0 -0
  838. /package/template/{docs → .template-spec}/process/schemas/frontend-strategic-preflight-v2.schema.json +0 -0
  839. /package/template/{docs → .template-spec}/process/schemas/frontend-strategic-preflight.schema.json +0 -0
  840. /package/template/{docs → .template-spec}/process/schemas/implementation-repository-preparation-result.schema.json +0 -0
  841. /package/template/{docs → .template-spec}/process/schemas/openapi-draft-validation-record.schema.json +0 -0
  842. /package/template/{docs → .template-spec}/process/schemas/project-scaffold-contract.schema.json +0 -0
  843. /package/template/{docs → .template-spec}/process/schemas/review-bundle.schema.json +0 -0
  844. /package/template/{docs → .template-spec}/process/schemas/scaffold-architecture-decisions.schema.json +0 -0
  845. /package/template/{docs → .template-spec}/process/schemas/slice-implementation-contract-v3.schema.json +0 -0
  846. /package/template/{docs → .template-spec}/process/schemas/stage-tracking.schema.json +0 -0
  847. /package/template/{docs → .template-spec}/process/schemas/strategic-consumer-feedback.schema.json +0 -0
  848. /package/template/{docs → .template-spec}/process/schemas/strategic-feedback-adjudication.schema.json +0 -0
  849. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-delivery-verification.schema.json +0 -0
  850. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-delivery.schema.json +0 -0
  851. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-domain-strategy-v3.schema.json +0 -0
  852. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-domain-strategy.schema.json +0 -0
  853. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-export-v2.schema.json +0 -0
  854. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-export.schema.json +0 -0
  855. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-import-receipt-v3.schema.json +0 -0
  856. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-import-receipt.schema.json +0 -0
  857. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-package.schema.json +0 -0
  858. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-stage-decision-package-v3.schema.json +0 -0
  859. /package/template/{docs → .template-spec}/process/schemas/strategic-handoff-stage-decision-package.schema.json +0 -0
  860. /package/template/{docs → .template-spec}/process/schemas/subagent-task-package.schema.json +0 -0
  861. /package/template/{docs → .template-spec}/process/schemas/user-decision.schema.json +0 -0
  862. /package/template/{docs → .template-spec}/process/strategic-handoff-package.md +0 -0
  863. /package/template/{docs → .template-spec}/process/templates/context-reconciliation-template.yaml +0 -0
  864. /package/template/{docs → .template-spec}/process/templates/frontend-implementation-plan-template.yaml +0 -0
  865. /package/template/{docs → .template-spec}/process/templates/lifecycle-checkpoint-template.yaml +0 -0
  866. /package/template/{docs → .template-spec}/process/templates/scaffold-architecture-decisions-template.yaml +0 -0
  867. /package/template/{docs → .template-spec}/process/templates/stage-checkpoint-template.md +0 -0
  868. /package/template/{docs → .template-spec}/process/templates/user-decision-template.yaml +0 -0
  869. /package/template/{docs → .template-spec}/templates/adr-template.md +0 -0
  870. /package/template/{docs → .template-spec}/templates/approval-record-template.yaml +0 -0
  871. /package/template/{docs → .template-spec}/templates/cross-repo-slice-template.md +0 -0
  872. /package/template/{docs → .template-spec}/templates/examples/lifecycle-writing-examples.md +0 -0
  873. /package/template/{docs → .template-spec}/templates/implementation-repo-registry-template.md +0 -0
  874. /package/template/{docs → .template-spec}/templates/openapi-spec-template.yaml +0 -0
  875. /package/template/{docs → .template-spec}/templates/review-bundle-template.yaml +0 -0
  876. /package/template/{docs → .template-spec}/templates/review-report-template.md +0 -0
  877. /package/template/{docs → .template-spec}/templates/spec-delta-template.md +0 -0
  878. /package/template/{docs → .template-spec}/templates/spec-template.md +0 -0
  879. /package/template/{docs → .template-spec}/user-guide/yss-ui-mcp.md +0 -0
  880. /package/template/{docs → .template-spec}/user-guide//346/212/200/346/234/257/350/256/276/350/256/241/347/224/250/346/210/267/346/214/207/345/215/227.md" +0 -0
  881. /package/template/{docs → .template-spec}/user-guide//350/256/276/345/244/207/345/200/237/347/224/250/350/264/257/347/251/277/346/241/210/344/276/213.md" +0 -0
  882. /package/template/{docs → .template-spec}/user-guide//351/230/266/346/256/265/346/216/245/345/205/245/344/270/216/344/272/247/347/211/251/345/244/215/347/224/250/344/275/277/347/224/250/346/211/213/345/206/214.md" +0 -0
@@ -1,3588 +0,0 @@
1
- import { connectMcpWidgetHost } from "./mcp-host.js";
2
- import {
3
- CANONICAL_CHART_TYPES,
4
- canonicalVisualizationType,
5
- chartPickerSections,
6
- resolveCssColors,
7
- } from "./recharts-config.js";
8
- import { destroyRechartsChart, renderRechartsChart } from "./recharts-renderer.jsx";
9
- import { destroyDataTable, renderDataTable } from "./table-renderer.jsx";
10
- import { renderSourceCode, sourceCodeLanguage, sourceCodeText, sourceQueryFromSource } from "./sql-source-view.js";
11
- import "./styles/codex-theme.css";
12
- import "./analytics-app/tokens.css";
13
- import "./sql-source-view.css";
14
- import "./datascience-chart-widget.css";
15
-
16
- connectMcpWidgetHost({
17
- name: "Data Analytics Chart Widget",
18
- version: "0.1.1",
19
- availableDisplayModes: ["inline", "fullscreen"],
20
- });
21
-
22
-
23
- function round(value, digits = 1) {
24
- const factor = 10 ** digits;
25
- return Math.round(value * factor) / factor;
26
- }
27
-
28
- function isoDateFromDay(startMs, dayIndex) {
29
- return new Date(startMs + dayIndex * 86400000).toISOString().slice(0, 10);
30
- }
31
-
32
- function weekdayName(startMs, dayIndex) {
33
- return new Date(startMs + dayIndex * 86400000).toLocaleDateString(undefined, { weekday: "short", timeZone: "UTC" });
34
- }
35
-
36
- function buildFallbackRows() {
37
- const startMs = Date.UTC(2026, 0, 1);
38
- const weekdayLift = [0.62, 1.05, 1.1, 1.08, 1.12, 1.0, 0.58];
39
- const regions = [
40
- { region: "North America", multiplier: 1.22, accountMultiplier: 1.18, productOffset: 0 },
41
- { region: "EMEA", multiplier: 0.82, accountMultiplier: 0.9, productOffset: 1 },
42
- ];
43
- const plans = [
44
- { plan: "Self-serve", channel: "Organic", baseRevenue: 0.78, weeklyGrowth: 0.055, baseAccounts: 3.1, accountGrowth: 0.09, activation: 43, conversion: 7.8, margin: 61 },
45
- { plan: "Team", channel: "Partner", baseRevenue: 1.18, weeklyGrowth: 0.085, baseAccounts: 1.85, accountGrowth: 0.055, activation: 56, conversion: 12.4, margin: 66 },
46
- { plan: "Enterprise", channel: "Sales-led", baseRevenue: 1.72, weeklyGrowth: 0.13, baseAccounts: 0.72, accountGrowth: 0.025, activation: 69, conversion: 18.2, margin: 72 },
47
- ];
48
- const products = ["Chat", "API", "Agents", "Voice"];
49
- const rows = [];
50
- for (let dayIndex = 0; dayIndex < 91; dayIndex += 1) {
51
- const weekIndex = Math.floor(dayIndex / 7);
52
- const weekday = new Date(startMs + dayIndex * 86400000).getUTCDay();
53
- for (const [regionIndex, region] of regions.entries()) {
54
- for (const [planIndex, plan] of plans.entries()) {
55
- const wave = Math.sin((dayIndex + 1) * (planIndex + 1.7) + regionIndex * 2.1) * 0.07;
56
- const lift = weekdayLift[weekday] * (1 + wave);
57
- const revenue = (plan.baseRevenue + weekIndex * plan.weeklyGrowth) * region.multiplier * lift;
58
- const activeAccounts = (plan.baseAccounts + weekIndex * plan.accountGrowth) * region.accountMultiplier * lift;
59
- rows.push({
60
- date: isoDateFromDay(startMs, dayIndex),
61
- weekday: weekdayName(startMs, dayIndex),
62
- region: region.region,
63
- plan: plan.plan,
64
- acquisition_channel: plan.channel,
65
- product_area: products[(planIndex + region.productOffset + weekIndex) % products.length],
66
- lifecycle_stage: dayIndex < 28 ? "New" : planIndex === 0 ? "Retention" : "Expansion",
67
- revenue_m: round(revenue, 2),
68
- active_accounts_k: round(activeAccounts, 1),
69
- activation_rate_pct: round(plan.activation + weekIndex * 0.35 + regionIndex * 0.8 + wave * 18, 1),
70
- conversion_rate_pct: round(plan.conversion + weekIndex * 0.18 + regionIndex * 0.4 + wave * 6, 1),
71
- pipeline_m: round(revenue * (planIndex === 2 ? 1.75 : planIndex === 1 ? 1.32 : 0.92), 2),
72
- spend_m: round(revenue * (planIndex === 0 ? 0.19 : planIndex === 1 ? 0.16 : 0.13), 2),
73
- margin_pct: round(plan.margin + weekIndex * 0.18 - (weekday === 0 || weekday === 6 ? 1.4 : 0) + wave * 7, 1),
74
- });
75
- }
76
- }
77
- }
78
- return rows;
79
- }
80
-
81
- const fallbackRows = buildFallbackRows();
82
- const fallbackPayload = {
83
- ok: true,
84
- widget_type: "chart",
85
- title: "Daily product growth demo by plan and region",
86
- source: {
87
- query: {
88
- engine: "static-demo",
89
- id: "widget-demo-growth-configs",
90
- executed_at: "2026-05-01T00:00:00Z",
91
- sql:
92
- "SELECT date, weekday, region, plan, acquisition_channel, product_area, lifecycle_stage, revenue_m, active_accounts_k, activation_rate_pct, conversion_rate_pct, pipeline_m, spend_m, margin_pct FROM demo.widget_growth_configurations_daily",
93
- },
94
- },
95
- table: {
96
- columns: [
97
- { key: "date", label: "Date", type: "date" },
98
- { key: "weekday", label: "Weekday", type: "text" },
99
- { key: "region", label: "Region", type: "text" },
100
- { key: "plan", label: "Plan", type: "text" },
101
- { key: "acquisition_channel", label: "Acquisition channel", type: "text" },
102
- { key: "product_area", label: "Product area", type: "text" },
103
- { key: "lifecycle_stage", label: "Lifecycle stage", type: "text" },
104
- { key: "revenue_m", label: "Revenue", type: "number", unit: "$M" },
105
- { key: "active_accounts_k", label: "Active accounts", type: "number", unit: "K" },
106
- { key: "activation_rate_pct", label: "Activation rate", type: "number", unit: "%" },
107
- { key: "conversion_rate_pct", label: "Conversion rate", type: "number", unit: "%" },
108
- { key: "pipeline_m", label: "Pipeline", type: "number", unit: "$M" },
109
- { key: "spend_m", label: "Spend", type: "number", unit: "$M" },
110
- { key: "margin_pct", label: "Margin", type: "number", unit: "%" },
111
- ],
112
- rows: fallbackRows,
113
- row_count: fallbackRows.length,
114
- truncated: false,
115
- },
116
- chart: {
117
- type: "bar",
118
- fields: {
119
- x: { field: "date", type: "temporal", time_unit: "week" },
120
- y: { field: "revenue_m", type: "quantitative", aggregate: "sum", unit: "$M" },
121
- color: { field: "plan", type: "nominal" },
122
- },
123
- },
124
- display: {
125
- unit: "$M",
126
- controls: true,
127
- },
128
- };
129
-
130
- const colors = [
131
- "var(--ds-chart-series-blue)",
132
- "var(--ds-chart-series-orange)",
133
- "var(--ds-chart-series-green)",
134
- "var(--ds-chart-series-purple)",
135
- "var(--ds-chart-series-red)",
136
- "var(--ds-chart-series-pink)",
137
- "var(--ds-chart-series-yellow)",
138
- "var(--ds-chart-series-neutral)",
139
- ];
140
- const colorEdges = colors;
141
- const visualizationTypes = CANONICAL_CHART_TYPES;
142
- const aggregationOptions = ["Sum", "Average", "Minimum", "Maximum", "Count"];
143
- const fieldMenuSearchThreshold = 12;
144
- const measureNamesField = "__measure_names__";
145
- let payload = fallbackPayload;
146
- let activeVisualizationType = "bar";
147
- let chartSettings = {
148
- barOrientation: "vertical",
149
- barGroupMode: "grouped",
150
- };
151
- let viewMode = "both";
152
- let dataMode = "table";
153
- let chartConfig = {
154
- xField: "x",
155
- yField: "y",
156
- colorField: "series",
157
- lineStyleField: "",
158
- labelField: "",
159
- sizeField: "",
160
- timeUnit: "none",
161
- yAggregation: "sum",
162
- };
163
- let visibleSeries = new Set();
164
- let visibleSeriesSignature = "";
165
- let revealAllSeriesAfterGroupingChange = false;
166
- let chartInstance = null;
167
- let lastPayloadSignature = "";
168
- let hasExternalPayload = false;
169
- let displayMode = "inline";
170
- let selectedDataFilters = {};
171
- let shareMenuOpen = false;
172
- const splitStorageKeyPrefix = "datascience-chart-split:";
173
- const defaultSplitFraction = 0.618;
174
- const minSplitFraction = 0.35;
175
- const maxSplitFraction = 0.78;
176
- let splitFraction = defaultSplitFraction;
177
- let resizeFrame = 0;
178
-
179
- function isDetailDisplayMode(mode = displayMode) {
180
- return mode === "fullscreen" || mode === "modal";
181
- }
182
-
183
- function decodePayload(raw) {
184
- if (typeof raw !== "string") return raw;
185
- try {
186
- return JSON.parse(raw);
187
- } catch {
188
- return null;
189
- }
190
- }
191
-
192
- function readUrlWidgetInstanceId() {
193
- try {
194
- const params = new URLSearchParams(window.location.search);
195
- const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ""));
196
- return (
197
- params.get("widgetInstanceId") ||
198
- params.get("widgetId") ||
199
- hashParams.get("widgetInstanceId") ||
200
- hashParams.get("widgetId") ||
201
- ""
202
- );
203
- } catch {
204
- return "";
205
- }
206
- }
207
-
208
- const urlWidgetInstanceId = readUrlWidgetInstanceId();
209
-
210
- function readUrlDisplayMode() {
211
- try {
212
- const params = new URLSearchParams(window.location.search);
213
- const hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ""));
214
- const raw = (
215
- params.get("displayMode") ||
216
- params.get("display_mode") ||
217
- params.get("preview") ||
218
- hashParams.get("displayMode") ||
219
- hashParams.get("display_mode") ||
220
- hashParams.get("preview") ||
221
- ""
222
- ).toLowerCase();
223
- if (raw === "fullscreen" || raw === "full" || raw === "expanded") return "fullscreen";
224
- if (raw === "modal" || raw === "dialog") return "modal";
225
- if (raw === "inline" || raw === "compact") return "inline";
226
- return "";
227
- } catch {
228
- return "";
229
- }
230
- }
231
-
232
- const urlDisplayMode = readUrlDisplayMode();
233
-
234
- function firstString(...values) {
235
- for (const value of values) {
236
- if (value == null || value === "") continue;
237
- return String(value);
238
- }
239
- return "";
240
- }
241
-
242
- function currentWidgetInstanceId() {
243
- const hostApi = window.openai || {};
244
- const hostContext = hostApi.hostContext || {};
245
- const view = hostApi.view || {};
246
- return firstString(
247
- hostApi.widgetInstanceId,
248
- hostApi.widgetId,
249
- hostApi.widget_instance_id,
250
- hostApi.widget_id,
251
- hostContext.widgetInstanceId,
252
- hostContext.widgetId,
253
- view.widgetInstanceId,
254
- view.widgetId,
255
- urlWidgetInstanceId,
256
- );
257
- }
258
-
259
- function targetWidgetInstanceId(raw) {
260
- raw = decodePayload(raw);
261
- if (!raw || typeof raw !== "object") return "";
262
- const meta = raw._meta || raw.meta || {};
263
- const globals = raw.globals || {};
264
- const hostContext = raw.hostContext || {};
265
- const view = raw.view || {};
266
- return firstString(
267
- raw.targetWidgetInstanceId,
268
- raw.targetWidgetId,
269
- raw.target_widget_instance_id,
270
- raw.target_widget_id,
271
- meta.targetWidgetInstanceId,
272
- meta.targetWidgetId,
273
- globals.targetWidgetInstanceId,
274
- globals.targetWidgetId,
275
- hostContext.targetWidgetInstanceId,
276
- hostContext.targetWidgetId,
277
- view.targetWidgetInstanceId,
278
- view.targetWidgetId,
279
- );
280
- }
281
-
282
- function messageTargetsThisWidget(raw) {
283
- const target = targetWidgetInstanceId(raw);
284
- if (!target) return true;
285
- return target === currentWidgetInstanceId();
286
- }
287
-
288
- function pickPayload(raw) {
289
- raw = decodePayload(raw);
290
- if (!raw || typeof raw !== "object") return null;
291
- if (Array.isArray(raw.content)) {
292
- for (const item of raw.content) {
293
- const found = pickPayload(item && (item.text || item));
294
- if (found) return found;
295
- }
296
- }
297
- if (raw.structuredContent && typeof raw.structuredContent === "object") return pickPayload(raw.structuredContent);
298
- if (raw.payload && typeof raw.payload === "object") return pickPayload(raw.payload);
299
- if (
300
- Array.isArray(raw.data) ||
301
- raw.table ||
302
- raw.chart ||
303
- raw.result_table ||
304
- raw.visualization_spec ||
305
- raw.widget_type === "table" ||
306
- Array.isArray(raw.rows)
307
- ) {
308
- return raw;
309
- }
310
- return null;
311
- }
312
-
313
- function currentHostPayload() {
314
- return (
315
- pickPayload(window.openai && window.openai.toolOutput) ||
316
- pickPayload(window.openai && window.openai.toolResponseMetadata)
317
- );
318
- }
319
-
320
- function currentHostDisplayMode() {
321
- const hostApi = window.openai || {};
322
- return (
323
- urlDisplayMode ||
324
- hostApi.displayMode ||
325
- (hostApi.view && hostApi.view.displayMode) ||
326
- (hostApi.hostContext && hostApi.hostContext.displayMode) ||
327
- (hostApi.host && hostApi.host.displayMode) ||
328
- displayMode
329
- );
330
- }
331
-
332
- function hostSupportsDisplayMode() {
333
- const hostApi = window.openai || {};
334
- const availableDisplayModes =
335
- hostApi.availableDisplayModes ||
336
- (hostApi.hostContext && hostApi.hostContext.availableDisplayModes) ||
337
- (hostApi.view && hostApi.view.availableDisplayModes);
338
- if (Array.isArray(availableDisplayModes) && availableDisplayModes.includes("fullscreen")) {
339
- return true;
340
- }
341
- return Boolean(window.openai) || typeof hostApi.requestDisplayMode === "function";
342
- }
343
-
344
- function hostedEmptyPayload() {
345
- return { title: "Data Analytics chart", data: [] };
346
- }
347
-
348
- function payloadSignature(value) {
349
- try {
350
- return JSON.stringify(value);
351
- } catch {
352
- return String(Date.now());
353
- }
354
- }
355
-
356
- function canApplyPayload(raw, routingContext = raw) {
357
- if (!hasExternalPayload) return true;
358
- const target = targetWidgetInstanceId(routingContext);
359
- return Boolean(target && target === currentWidgetInstanceId());
360
- }
361
-
362
- function applyPayload(raw, routingContext = raw) {
363
- if (!canApplyPayload(raw, routingContext)) return false;
364
- const next = pickPayload(raw);
365
- if (!next) return false;
366
- const signature = payloadSignature(next);
367
- hasExternalPayload = true;
368
- if (signature === lastPayloadSignature) return true;
369
- lastPayloadSignature = signature;
370
- normalizePayload(next);
371
- render();
372
- return true;
373
- }
374
-
375
- function applyHostState(raw) {
376
- const next = decodePayload(raw);
377
- if (!next || typeof next !== "object") return;
378
- const mode =
379
- next.displayMode ||
380
- next.display_mode ||
381
- (next.view && next.view.displayMode) ||
382
- (next.hostContext && next.hostContext.displayMode) ||
383
- (next.globals && (next.globals.displayMode || (next.globals.view && next.globals.view.displayMode)));
384
- if (mode === "inline" || mode === "fullscreen" || mode === "modal") {
385
- setDisplayMode(mode);
386
- }
387
- }
388
-
389
- async function requestDisplayMode(mode) {
390
- const hostApi = window.openai || {};
391
- setDisplayMode(mode);
392
- if (typeof hostApi.requestDisplayMode !== "function") {
393
- return { mode };
394
- }
395
-
396
- try {
397
- const result = await hostApi.requestDisplayMode({ mode });
398
- if (result && (result.mode === "inline" || result.mode === "fullscreen" || result.mode === "modal")) {
399
- setDisplayMode(result.mode);
400
- return result;
401
- }
402
- } catch {
403
- setDisplayMode(currentHostDisplayMode());
404
- }
405
- return { mode: displayMode };
406
- }
407
-
408
- function setDisplayMode(mode) {
409
- if (mode !== "inline" && mode !== "fullscreen" && mode !== "modal") return;
410
- const changed = displayMode !== mode;
411
- displayMode = mode;
412
- const widget = document.querySelector(".widget");
413
- if (widget) {
414
- widget.dataset.displayMode = mode;
415
- const instanceId = currentWidgetInstanceId();
416
- if (instanceId) widget.dataset.widgetInstanceId = instanceId;
417
- }
418
- document.documentElement.dataset.displayMode = mode;
419
- document.body.dataset.displayMode = mode;
420
- applySplitLayout();
421
- const button = document.getElementById("display-mode-button");
422
- if (!button) return;
423
- button.hidden = !hostSupportsDisplayMode();
424
- const label = isDetailDisplayMode(mode) ? "Done" : "Edit";
425
- button.title = isDetailDisplayMode(mode) ? "Return chart inline" : "Edit chart";
426
- button.setAttribute("aria-label", isDetailDisplayMode(mode) ? "Return chart inline" : "Edit chart");
427
- const labelElement = document.createElement("span");
428
- labelElement.className = "display-mode-button-label";
429
- labelElement.textContent = label;
430
- if (isDetailDisplayMode(mode)) {
431
- button.replaceChildren(labelElement);
432
- } else {
433
- button.replaceChildren(labelElement, exploreIcon());
434
- }
435
- const modalCloseButton = document.getElementById("modal-title-close-button");
436
- if (modalCloseButton) modalCloseButton.hidden = mode !== "modal";
437
- if (changed) render();
438
- }
439
-
440
- function exploreIcon() {
441
- const icon = document.createElement("span");
442
- icon.className = "display-mode-button-icon";
443
- const svg = svgEl("svg", {
444
- viewBox: "0 0 16 16",
445
- "aria-hidden": "true",
446
- focusable: "false",
447
- });
448
- const path = svgEl("path", {
449
- d: "M11.949 3.47949C12.0997 3.46465 12.2553 3.51279 12.3709 3.62793C12.4863 3.74328 12.5338 3.89898 12.5193 4.0498C12.5206 4.06633 12.5251 4.08275 12.5252 4.09961V10.667C12.525 10.9565 12.2902 11.191 12.0007 11.1914C11.7109 11.1914 11.4755 10.9568 11.4754 10.667V5.2666L4.37184 12.376C4.16684 12.5807 3.83365 12.5808 3.62867 12.376C3.42385 12.1711 3.42396 11.8388 3.62867 11.6338L10.7332 4.52539H5.33375C5.0438 4.52539 4.80836 4.28995 4.80836 4C4.80836 3.71005 5.0438 3.47461 5.33375 3.47461H11.9002C11.9167 3.47462 11.9328 3.47822 11.949 3.47949Z",
450
- fill: "currentColor",
451
- });
452
- svg.appendChild(path);
453
- icon.appendChild(svg);
454
- return icon;
455
- }
456
-
457
- function setViewMode(mode) {
458
- viewMode = canonicalViewMode(mode);
459
- const widget = document.querySelector(".widget");
460
- if (widget) widget.dataset.viewMode = viewMode;
461
- document.querySelectorAll("[data-view-mode-choice]").forEach((button) => {
462
- button.setAttribute("aria-pressed", String(button.dataset.viewModeChoice === viewMode));
463
- });
464
- applySplitLayout();
465
- }
466
-
467
- function clampSplitFraction(value) {
468
- const parsed = Number(value);
469
- if (!Number.isFinite(parsed)) return defaultSplitFraction;
470
- return Math.min(maxSplitFraction, Math.max(minSplitFraction, parsed));
471
- }
472
-
473
- function splitStorageKey() {
474
- const title = text((activeDataset() && activeDataset().title) || payload.title || "default").slice(0, 80);
475
- return `${splitStorageKeyPrefix}${currentWidgetInstanceId() || "local"}:${title}`;
476
- }
477
-
478
- function readStoredSplitFraction() {
479
- try {
480
- return clampSplitFraction(window.localStorage.getItem(splitStorageKey()) || defaultSplitFraction);
481
- } catch {
482
- return defaultSplitFraction;
483
- }
484
- }
485
-
486
- function writeStoredSplitFraction() {
487
- try {
488
- window.localStorage.setItem(splitStorageKey(), String(splitFraction));
489
- } catch {
490
- // Ignore storage failures; the current drag still applies for this session.
491
- }
492
- }
493
-
494
- function shouldUseSplitLayout() {
495
- return false;
496
- }
497
-
498
- function splitMetrics() {
499
- const appMain = document.querySelector(".app-main");
500
- const controls = document.getElementById("controls");
501
- const resizer = document.getElementById("split-resizer");
502
- if (!appMain || !resizer) return null;
503
- appMain.style.gridTemplateRows = "";
504
- const controlsVisible = controls && !controls.hidden && window.getComputedStyle(controls).display !== "none";
505
- const controlsHeight = controlsVisible ? controls.getBoundingClientRect().height : 0;
506
- const resizerHeight = resizer.getBoundingClientRect().height || 12;
507
- const availableHeight = appMain.clientHeight - controlsHeight - resizerHeight;
508
- if (availableHeight <= 0) return null;
509
- return { appMain, resizer, controlsHeight, resizerHeight, availableHeight };
510
- }
511
-
512
- function resetSplitLayout() {
513
- const appMain = document.querySelector(".app-main");
514
- if (appMain) appMain.style.gridTemplateRows = "";
515
- resizeChartInstance();
516
- }
517
-
518
- function applySplitLayout() {
519
- if (resizeFrame) window.cancelAnimationFrame(resizeFrame);
520
- resizeFrame = window.requestAnimationFrame(() => {
521
- resizeFrame = 0;
522
- if (!shouldUseSplitLayout()) {
523
- resetSplitLayout();
524
- return;
525
- }
526
- const metrics = splitMetrics();
527
- if (!metrics) return;
528
- const minChartHeight = Math.min(260, metrics.availableHeight * 0.5);
529
- const minBottomHeight = Math.min(190, metrics.availableHeight * 0.4);
530
- const lowerBound = Math.max(minSplitFraction, minChartHeight / metrics.availableHeight);
531
- const upperBound = Math.min(maxSplitFraction, 1 - minBottomHeight / metrics.availableHeight);
532
- const effectiveFraction = Math.min(Math.max(splitFraction, lowerBound), Math.max(lowerBound, upperBound));
533
- const chartHeight = Math.round(metrics.availableHeight * effectiveFraction);
534
- const bottomHeight = Math.max(0, metrics.availableHeight - chartHeight);
535
- metrics.appMain.style.gridTemplateRows = `${Math.round(metrics.controlsHeight)}px ${chartHeight}px ${Math.round(metrics.resizerHeight)}px ${bottomHeight}px`;
536
- metrics.resizer.setAttribute("aria-valuenow", String(Math.round(effectiveFraction * 100)));
537
- resizeChartInstance();
538
- });
539
- }
540
-
541
- function setSplitFraction(nextFraction, options = {}) {
542
- splitFraction = clampSplitFraction(nextFraction);
543
- if (options.persist) writeStoredSplitFraction();
544
- applySplitLayout();
545
- }
546
-
547
- function setSplitFractionFromPointer(clientY, options = {}) {
548
- const metrics = splitMetrics();
549
- if (!metrics) return;
550
- const rect = metrics.appMain.getBoundingClientRect();
551
- const chartHeight = clientY - rect.top - metrics.controlsHeight - metrics.resizerHeight / 2;
552
- setSplitFraction(chartHeight / metrics.availableHeight, options);
553
- }
554
-
555
- function setupSplitResizer() {
556
- const resizer = document.getElementById("split-resizer");
557
- const widget = document.querySelector(".widget");
558
- if (!resizer) return;
559
- let activePointerId = null;
560
-
561
- resizer.addEventListener("pointerdown", (event) => {
562
- if (!shouldUseSplitLayout()) return;
563
- activePointerId = event.pointerId;
564
- resizer.setPointerCapture?.(event.pointerId);
565
- widget?.classList.add("is-resizing");
566
- setSplitFractionFromPointer(event.clientY);
567
- event.preventDefault();
568
- });
569
-
570
- resizer.addEventListener("pointermove", (event) => {
571
- if (activePointerId !== event.pointerId) return;
572
- setSplitFractionFromPointer(event.clientY);
573
- });
574
-
575
- const finishPointerDrag = (event) => {
576
- if (activePointerId !== event.pointerId) return;
577
- activePointerId = null;
578
- widget?.classList.remove("is-resizing");
579
- writeStoredSplitFraction();
580
- };
581
- resizer.addEventListener("pointerup", finishPointerDrag);
582
- resizer.addEventListener("pointercancel", finishPointerDrag);
583
- resizer.addEventListener("lostpointercapture", () => {
584
- activePointerId = null;
585
- widget?.classList.remove("is-resizing");
586
- });
587
-
588
- resizer.addEventListener("dblclick", () => {
589
- setSplitFraction(defaultSplitFraction, { persist: true });
590
- });
591
-
592
- resizer.addEventListener("keydown", (event) => {
593
- if (!shouldUseSplitLayout()) return;
594
- if (event.key === "ArrowUp") {
595
- setSplitFraction(splitFraction - 0.03, { persist: true });
596
- event.preventDefault();
597
- } else if (event.key === "ArrowDown") {
598
- setSplitFraction(splitFraction + 0.03, { persist: true });
599
- event.preventDefault();
600
- } else if (event.key === "Home") {
601
- setSplitFraction(defaultSplitFraction, { persist: true });
602
- event.preventDefault();
603
- } else if (event.key === "End") {
604
- setSplitFraction(maxSplitFraction, { persist: true });
605
- event.preventDefault();
606
- }
607
- });
608
- }
609
-
610
- function setupViewModeControls() {
611
- document.querySelectorAll("[data-view-mode-choice]").forEach((button) => {
612
- button.addEventListener("click", () => {
613
- setViewMode(button.dataset.viewModeChoice);
614
- });
615
- });
616
- setViewMode(viewMode);
617
- }
618
-
619
- function canonicalDataMode(value) {
620
- const raw = text(value || "").toLowerCase();
621
- return raw === "query" || raw === "sql" || raw === "python" || raw === "code" ? "query" : "table";
622
- }
623
-
624
- function setDataMode(mode) {
625
- dataMode = canonicalDataMode(mode);
626
- const widget = document.querySelector(".widget");
627
- const bottomSplit = document.getElementById("bottom-split");
628
- if (widget) widget.dataset.dataMode = dataMode;
629
- if (bottomSplit) bottomSplit.dataset.dataMode = dataMode;
630
- document.querySelectorAll(".data-mode-segmented").forEach((control) => {
631
- control.dataset.activeMode = dataMode;
632
- });
633
- document.querySelectorAll("[data-data-mode-choice]").forEach((button) => {
634
- button.setAttribute("aria-pressed", String(button.dataset.dataModeChoice === dataMode));
635
- });
636
- }
637
-
638
- function setupDataModeControls() {
639
- document.querySelectorAll("[data-data-mode-choice]").forEach((button) => {
640
- button.addEventListener("click", () => {
641
- setDataMode(button.dataset.dataModeChoice);
642
- });
643
- });
644
- setDataMode(dataMode);
645
- }
646
-
647
- function setupDisplayModeControls() {
648
- const button = document.getElementById("display-mode-button");
649
- const backButton = document.getElementById("detail-back-button");
650
- const modalCloseButton = document.getElementById("modal-title-close-button");
651
- const toggleDisplayMode = () => {
652
- if (!button) return;
653
- button.disabled = true;
654
- const nextMode = isDetailDisplayMode() ? "inline" : "fullscreen";
655
- requestDisplayMode(nextMode).finally(() => {
656
- button.disabled = false;
657
- });
658
- };
659
- if (button) button.addEventListener("click", toggleDisplayMode);
660
- if (backButton) {
661
- backButton.addEventListener("click", () => {
662
- requestDisplayMode("inline");
663
- });
664
- }
665
- if (modalCloseButton) {
666
- modalCloseButton.addEventListener("click", () => {
667
- requestDisplayMode("inline");
668
- });
669
- }
670
- setDisplayMode(currentHostDisplayMode());
671
- }
672
-
673
- function asArray(value) {
674
- return Array.isArray(value) ? value : [];
675
- }
676
-
677
- function text(value) {
678
- return value == null ? "" : String(value);
679
- }
680
-
681
- function number(value) {
682
- const parsed = Number(value);
683
- return Number.isFinite(parsed) ? parsed : null;
684
- }
685
-
686
- function valueFor(dataset, key, fallback = null) {
687
- return dataset && dataset[key] != null ? dataset[key] : payload[key] != null ? payload[key] : fallback;
688
- }
689
-
690
- function objectFor(dataset, key) {
691
- const value = valueFor(dataset, key);
692
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
693
- }
694
-
695
- function resultTableFor(dataset) {
696
- return objectFor(dataset, "table") || objectFor(dataset, "result_table") || null;
697
- }
698
-
699
- function sourceQueryFor(dataset) {
700
- return sourceQueryFromSource(objectFor(dataset, "source"));
701
- }
702
-
703
- function sourceLabelFor(dataset) {
704
- const source = valueFor(dataset, "source", payload.source);
705
- if (source && typeof source === "object" && !Array.isArray(source)) return text(source.label);
706
- return text(source);
707
- }
708
-
709
- function sqlTextFor(dataset) {
710
- return sourceCodeText(sourceQueryFor(dataset));
711
- }
712
-
713
- function queryLanguageFor(dataset) {
714
- return sourceCodeLanguage(sourceQueryFor(dataset));
715
- }
716
-
717
- function compactList(values) {
718
- return values.map((value) => text(value).trim()).filter(Boolean);
719
- }
720
-
721
- function listFromValue(value) {
722
- if (Array.isArray(value)) {
723
- return compactList(value.map((item) => {
724
- if (item && typeof item === "object") {
725
- const label = item.label || item.name || item.table || item.field || item.metric || item.id;
726
- const detail = item.description || item.definition || item.value || item.expression;
727
- return label && detail ? `${label}: ${detail}` : label || detail;
728
- }
729
- return item;
730
- }));
731
- }
732
- if (value && typeof value === "object") {
733
- return compactList(Object.entries(value).map(([key, item]) => `${key}: ${item}`));
734
- }
735
- return compactList([value]);
736
- }
737
-
738
- function firstListFromObjects(objects, keys) {
739
- for (const object of objects) {
740
- if (!object) continue;
741
- for (const key of keys) {
742
- const values = listFromValue(object[key]);
743
- if (values.length) return values;
744
- }
745
- }
746
- return [];
747
- }
748
-
749
- function isLikelySourceTableName(value) {
750
- const tableName = text(value).trim().replace(/^[`"\[]|[`"\]]$/g, "");
751
- return /^[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+){1,3}$/.test(tableName);
752
- }
753
-
754
- function sourceTableNamesFromMetadata(objects) {
755
- return firstListFromObjects(objects, [
756
- "tables_used",
757
- "tablesUsed",
758
- "source_tables",
759
- "sourceTables",
760
- "tables",
761
- ]).filter(isLikelySourceTableName);
762
- }
763
-
764
- function extractTablesFromSql(sql) {
765
- const tables = [];
766
- const seen = new Set();
767
- const pattern = /\b(?:from|join)\s+([`"\[]?[\w.-]+(?:\.[\w.-]+){0,3}[`"\]]?)/gi;
768
- let match;
769
- while ((match = pattern.exec(sql))) {
770
- const table = match[1].replace(/^[`"\[]|[`"\]]$/g, "");
771
- const normalized = table.toLowerCase();
772
- if (!table || seen.has(normalized)) continue;
773
- seen.add(normalized);
774
- tables.push(table);
775
- }
776
- return tables;
777
- }
778
-
779
- function sourceMetadataObjects(dataset) {
780
- return [
781
- objectFor(dataset, "source"),
782
- sourceQueryFor(dataset),
783
- objectFor(dataset, "display"),
784
- objectFor(dataset, "chart"),
785
- dataset && typeof dataset === "object" ? dataset : null,
786
- payload && typeof payload === "object" ? payload : null,
787
- ];
788
- }
789
-
790
- function metricDefinitionList(dataset) {
791
- const explicit = firstListFromObjects(sourceMetadataObjects(dataset), [
792
- "metric_definitions",
793
- "metricDefinitions",
794
- "metrics_definition",
795
- "metricDefinition",
796
- ]);
797
- if (explicit.length) return explicit;
798
- const table = resultTableFor(dataset) || {};
799
- const columns = asArray(table.columns);
800
- const yFields = asArray(visualizationSpecFor(dataset).encodings?.y?.fields);
801
- const yField = visualizationSpecFor(dataset).encodings?.y?.field || chartConfig.yField;
802
- const metricFields = new Set(compactList([yField, ...yFields]));
803
- return compactList(columns
804
- .filter((column) => column && (!metricFields.size || metricFields.has(column.key)))
805
- .map((column) => `${column.label || column.key}: displayed from ${column.key}${column.unit ? ` (${column.unit})` : ""}`))
806
- .slice(0, 6);
807
- }
808
-
809
- function sourceDetailText(values, fallback = "Not declared") {
810
- const items = compactList(Array.isArray(values) ? values : [values]);
811
- return items.length ? items.join("; ") : fallback;
812
- }
813
-
814
- function sourceDetailList(values, fallback = "Not declared") {
815
- const items = compactList(Array.isArray(values) ? values : [values]);
816
- return items.length ? items : [fallback];
817
- }
818
-
819
- function sourceFieldsForDataset(dataset) {
820
- const spec = visualizationSpecFor(dataset);
821
- const fields = new Set();
822
- function addField(field) {
823
- if (field) fields.add(field);
824
- }
825
- addField(spec.encodings?.x?.field);
826
- addField(spec.encodings?.y?.field);
827
- for (const field of spec.encodings?.y?.fields ?? []) addField(field);
828
- addField(spec.encodings?.color?.field);
829
- return Array.from(fields);
830
- }
831
-
832
- function visualizationSpecFor(dataset) {
833
- const chart = objectFor(dataset, "chart");
834
- if (chart) {
835
- const fields = chart.fields && typeof chart.fields === "object" && !Array.isArray(chart.fields)
836
- ? chart.fields
837
- : {};
838
- const options = chart.options && typeof chart.options === "object" && !Array.isArray(chart.options)
839
- ? chart.options
840
- : {};
841
- const display = objectFor(dataset, "display") || {};
842
- return {
843
- visualization_type: chart.type,
844
- encodings: fields,
845
- settings: {
846
- ...(options.orientation != null ? { orientation: options.orientation } : {}),
847
- ...(options.grouping != null ? { group_mode: options.grouping } : {}),
848
- ...(options.points != null ? { show_points: options.points } : {}),
849
- ...(options.multi_measure_series != null
850
- ? { multi_measure_series: options.multi_measure_series }
851
- : {}),
852
- },
853
- presentation: {
854
- ...(display.unit != null ? { unit: display.unit } : {}),
855
- ...(display.baseline != null ? { baseline: display.baseline } : {}),
856
- ...(display.x_axis_title != null ? { x_axis_title: display.x_axis_title } : {}),
857
- ...(display.y_axis_title != null ? { y_axis_title: display.y_axis_title } : {}),
858
- ...(display.controls != null ? { show_controls: display.controls } : {}),
859
- },
860
- };
861
- }
862
- return objectFor(dataset, "visualization_spec") || {};
863
- }
864
-
865
- function chartSpecFor(dataset) {
866
- return objectFor(dataset, "chart_spec") || {};
867
- }
868
-
869
- function specEncodings(dataset) {
870
- const encodings = visualizationSpecFor(dataset).encodings;
871
- return encodings && typeof encodings === "object" ? encodings : {};
872
- }
873
-
874
- function specEncoding(dataset, role) {
875
- const encoding = specEncodings(dataset)[role];
876
- return encoding && typeof encoding === "object" ? encoding : {};
877
- }
878
-
879
- function specPresentation(dataset) {
880
- const presentation = visualizationSpecFor(dataset).presentation;
881
- return presentation && typeof presentation === "object" ? presentation : {};
882
- }
883
-
884
- function specSettings(dataset) {
885
- const settings = visualizationSpecFor(dataset).settings;
886
- if (settings && typeof settings === "object" && !Array.isArray(settings)) return settings;
887
- return objectFor(dataset, "settings") || {};
888
- }
889
-
890
- function specValue(dataset, key, fallback = null) {
891
- const presentation = specPresentation(dataset);
892
- return presentation[key] != null ? presentation[key] : valueFor(dataset, key, fallback);
893
- }
894
-
895
- function currentWidgetSpec() {
896
- const dataset = activeDataset();
897
- return {
898
- version: "1",
899
- visualization_type: activeVisualizationType,
900
- encodings: {
901
- x: {
902
- field: chartConfig.xField,
903
- type: fieldLooksDateLike(dataset, chartConfig.xField) ? "temporal" : "nominal",
904
- ...(chartConfig.timeUnit && chartConfig.timeUnit !== "none" ? { time_unit: chartConfig.timeUnit } : {}),
905
- },
906
- y: {
907
- aggregate: chartConfig.yAggregation,
908
- field: chartConfig.yField,
909
- type: "quantitative",
910
- },
911
- ...(chartConfig.colorField ? { color: { field: chartConfig.colorField, type: "nominal" } } : {}),
912
- ...(chartConfig.lineStyleField ? { lineStyle: { field: chartConfig.lineStyleField, type: "nominal" } } : {}),
913
- ...(chartConfig.labelField ? { label: { field: chartConfig.labelField, type: "text" } } : {}),
914
- ...(chartConfig.sizeField ? { size: { field: chartConfig.sizeField, type: "quantitative" } } : {}),
915
- },
916
- presentation: {
917
- data_mode: dataMode,
918
- unit: unitFor(dataset),
919
- view_mode: viewMode,
920
- },
921
- settings: {
922
- orientation: chartSettings.barOrientation,
923
- group_mode: chartSettings.barGroupMode,
924
- },
925
- };
926
- }
927
-
928
- function syntheticSeriesField(value, index) {
929
- const slug = text(value || "value").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40);
930
- return `__series_${index}_${slug || "value"}`;
931
- }
932
-
933
- function uniqueProjectedSeriesValues(rows) {
934
- const values = [];
935
- const seen = new Set();
936
- for (const row of rows) {
937
- const value = text((row && row.series) || "Value");
938
- if (seen.has(value)) continue;
939
- seen.add(value);
940
- values.push(value);
941
- }
942
- return values;
943
- }
944
-
945
- function projectedRowsUseColorEncoding(rows) {
946
- return Boolean(chartConfig.colorField) || usesMeasureNames() || uniqueProjectedSeriesValues(rows).length > 1;
947
- }
948
-
949
- function projectedRenderRows(rows) {
950
- return rows.map((row) => {
951
- const output = {};
952
- for (const [key, value] of Object.entries(row || {})) {
953
- if (key === "raw") continue;
954
- output[key] = value;
955
- }
956
- return output;
957
- });
958
- }
959
-
960
- function projectedRenderColumns(rows, dataset) {
961
- const columns = [
962
- {
963
- key: "x",
964
- label: fieldLabel(dataset, chartConfig.xField, "X"),
965
- type: fieldLooksDateLike(dataset, chartConfig.xField) ? "date" : "text",
966
- },
967
- {
968
- key: "y",
969
- label: fieldLabel(dataset, chartConfig.yField, "Value"),
970
- type: "number",
971
- unit: unitFor(dataset),
972
- },
973
- ];
974
- if (projectedRowsUseColorEncoding(rows)) {
975
- columns.splice(1, 0, { key: "series", label: "Series", type: "text" });
976
- }
977
- if (rows.some((row) => row && row.size != null)) {
978
- columns.push({ key: "size", label: fieldLabel(dataset, chartConfig.sizeField, "Size"), type: "number" });
979
- }
980
- if (rows.some((row) => row && row.lineStyle != null)) {
981
- columns.push({ key: "lineStyle", label: "Line style", type: "text" });
982
- }
983
- if (rows.some((row) => row && row.label != null)) {
984
- columns.push({ key: "label", label: fieldLabel(dataset, chartConfig.labelField, "Label"), type: "text" });
985
- }
986
- return columns;
987
- }
988
-
989
- function projectedChartSpec(dataset, type, rows) {
990
- const existing = chartSpecFor(dataset);
991
- const sourceSettings = specSettings(dataset);
992
- const useColor = projectedRowsUseColorEncoding(rows);
993
- const xAxisTitle = specValue(dataset, "x_axis_title", existing.xAxisTitle);
994
- const yAxisTitle = specValue(dataset, "y_axis_title", existing.yAxisTitle);
995
- return {
996
- id: text(existing.id || valueFor(dataset, "id", "default")) || "default",
997
- title: text(valueFor(dataset, "title", payload.title || "Data Analytics chart")),
998
- subtitle: specValue(dataset, "subtitle", payload.subtitle),
999
- type,
1000
- dataset: text(valueFor(dataset, "dataset", valueFor(dataset, "id", "default"))) || "default",
1001
- encodings: {
1002
- x: {
1003
- field: "x",
1004
- label: fieldLabel(dataset, chartConfig.xField, "X"),
1005
- type: fieldLooksDateLike(dataset, chartConfig.xField) ? "temporal" : "nominal",
1006
- ...(chartConfig.timeUnit && chartConfig.timeUnit !== "none" ? { time_unit: chartConfig.timeUnit } : {}),
1007
- },
1008
- y: {
1009
- aggregate: "none",
1010
- field: "y",
1011
- label: fieldLabel(dataset, chartConfig.yField, "Value"),
1012
- type: "quantitative",
1013
- unit: unitFor(dataset),
1014
- },
1015
- ...(useColor ? { color: { field: "series", label: "Series", type: "nominal" } } : {}),
1016
- ...(rows.some((row) => row && row.lineStyle != null)
1017
- ? { lineStyle: { field: "lineStyle", label: "Line style", type: "nominal" } }
1018
- : {}),
1019
- ...(rows.some((row) => row && row.label != null)
1020
- ? { label: { field: "label", label: fieldLabel(dataset, chartConfig.labelField, "Label"), type: "text" } }
1021
- : {}),
1022
- ...(rows.some((row) => row && row.size != null)
1023
- ? { size: { field: "size", label: fieldLabel(dataset, chartConfig.sizeField, "Size"), type: "quantitative" } }
1024
- : {}),
1025
- },
1026
- ...(xAxisTitle != null ? { xAxisTitle } : {}),
1027
- ...(yAxisTitle != null ? { yAxisTitle } : {}),
1028
- unit: unitFor(dataset),
1029
- valueFormat: existing.valueFormat,
1030
- settings: {
1031
- orientation: chartSettings.barOrientation,
1032
- groupMode: chartSettings.barGroupMode,
1033
- group_mode: chartSettings.barGroupMode,
1034
- ...(sourceSettings.showPoints != null || sourceSettings.show_points != null
1035
- ? {
1036
- showPoints: sourceSettings.showPoints ?? sourceSettings.show_points,
1037
- show_points: sourceSettings.show_points ?? sourceSettings.showPoints,
1038
- }
1039
- : {}),
1040
- },
1041
- surface: {
1042
- ...(existing.surface || {}),
1043
- surface: isDetailDisplayMode() ? "explorer" : "compact",
1044
- showControls: Boolean(specValue(dataset, "show_controls", payload.show_controls)),
1045
- },
1046
- };
1047
- }
1048
-
1049
- function notifyChartSpecChange() {
1050
- if (typeof window.parent?.postMessage !== "function" || window.parent === window) return;
1051
- window.parent.postMessage(
1052
- {
1053
- type: "datascience-chart-widget-spec-change",
1054
- widgetInstanceId: currentWidgetInstanceId(),
1055
- visualization_spec: currentWidgetSpec(),
1056
- },
1057
- "*",
1058
- );
1059
- }
1060
-
1061
- function notifyChartSpecReset() {
1062
- if (typeof window.parent?.postMessage !== "function" || window.parent === window) return;
1063
- window.parent.postMessage(
1064
- {
1065
- type: "datascience-chart-widget-spec-reset",
1066
- widgetInstanceId: currentWidgetInstanceId(),
1067
- },
1068
- "*",
1069
- );
1070
- }
1071
-
1072
- function widgetVisualizationType(type) {
1073
- return canonicalVisualizationType(type);
1074
- }
1075
-
1076
- function canonicalBarOrientation(value, fallback = "vertical") {
1077
- const raw = text(value || "").trim().toLowerCase().replaceAll("_", "").replaceAll("-", "");
1078
- if (raw === "vertical" || raw === "column") return "vertical";
1079
- if (raw === "horizontal") return "horizontal";
1080
- if (raw === "auto" || raw === "automatic") return fallback;
1081
- return fallback;
1082
- }
1083
-
1084
- function canonicalBarGroupMode(value, fallback = "grouped") {
1085
- const raw = text(value || "").trim().toLowerCase().replaceAll("_", "").replaceAll("-", "").replaceAll("%", "100");
1086
- if (raw === "single") return "single";
1087
- if (raw === "grouped" || raw === "group") return "grouped";
1088
- if (raw === "stacked" || raw === "stack") return "stacked";
1089
- if (raw === "stacked100" || raw === "stack100" || raw === "100" || raw === "100stacked") return "stacked100";
1090
- return fallback;
1091
- }
1092
-
1093
- function resetChartSettings(dataset = activeDataset(), requestedType = activeVisualizationType) {
1094
- const settings = specSettings(dataset);
1095
- chartSettings = {
1096
- barOrientation: canonicalBarOrientation(
1097
- settings.orientation ?? settings.bar_orientation ?? valueFor(dataset, "bar_orientation"),
1098
- "vertical",
1099
- ),
1100
- barGroupMode: canonicalBarGroupMode(
1101
- settings.group_mode ?? settings.groupMode ?? settings.bar_group_mode ?? valueFor(dataset, "bar_group_mode"),
1102
- "grouped",
1103
- ),
1104
- };
1105
- }
1106
-
1107
- function rendererBarOrientation() {
1108
- return chartSettings.barOrientation === "horizontal" ? "horizontal" : "vertical";
1109
- }
1110
-
1111
- function rendererVisualizationType(type = activeVisualizationType) {
1112
- const canonical = widgetVisualizationType(type);
1113
- return canonical;
1114
- }
1115
-
1116
- function applyChartSettingsAttributes() {
1117
- const widget = document.querySelector(".widget");
1118
- if (!widget) return;
1119
- widget.dataset.barOrientation = chartSettings.barOrientation;
1120
- widget.dataset.barGroupMode = chartSettings.barGroupMode;
1121
- }
1122
-
1123
- function unitFor(dataset) {
1124
- const selectedMeasure = asArray(resultTableFor(dataset)?.columns).find(
1125
- (column) => column && column.key === chartConfig.yField,
1126
- );
1127
- if (selectedMeasure?.unit != null && text(selectedMeasure.unit).trim()) {
1128
- return text(selectedMeasure.unit).trim();
1129
- }
1130
- return specValue(dataset, "unit", payload.unit);
1131
- }
1132
-
1133
- function activeDataset() {
1134
- return payload;
1135
- }
1136
-
1137
- function isTablePayload(dataset) {
1138
- return (
1139
- valueFor(dataset, "widget_type", payload.widget_type) === "table" ||
1140
- visualizationSpecFor(dataset).visualization_type === "table"
1141
- );
1142
- }
1143
-
1144
- function setQueryStatus(message) {
1145
- const status = document.getElementById("query-status");
1146
- if (status) status.textContent = message || "";
1147
- }
1148
-
1149
- function formatRunTime(value) {
1150
- const raw = text(value);
1151
- if (!raw) return "";
1152
- const date = new Date(raw);
1153
- if (!Number.isFinite(date.getTime())) return raw;
1154
- return new Intl.DateTimeFormat(undefined, {
1155
- day: "numeric",
1156
- hour: "numeric",
1157
- minute: "2-digit",
1158
- month: "short",
1159
- year: "numeric",
1160
- }).format(date);
1161
- }
1162
-
1163
- function renderDetailHeader(dataset, title, subtitle) {
1164
- const detailTitle = document.getElementById("detail-title");
1165
- const detailSubtitle = document.getElementById("detail-subtitle");
1166
- const runTime = document.getElementById("detail-run-time");
1167
- const sourceQuery = sourceQueryFor(dataset) || {};
1168
- if (detailTitle) detailTitle.textContent = text(title);
1169
- if (detailSubtitle) {
1170
- detailSubtitle.textContent = text(subtitle);
1171
- detailSubtitle.hidden = !subtitle;
1172
- }
1173
- if (runTime) {
1174
- const formatted = formatRunTime(sourceQuery.executed_at || valueFor(dataset, "executed_at", payload.executed_at));
1175
- runTime.textContent = formatted;
1176
- const refreshButton = document.getElementById("detail-refresh-button");
1177
- if (refreshButton) refreshButton.hidden = !formatted;
1178
- }
1179
- }
1180
-
1181
- function renderQueryControls(dataset) {
1182
- if (displayMode !== "fullscreen") {
1183
- setQueryStatus("");
1184
- return;
1185
- }
1186
- const sourceQuery = sourceQueryFor(dataset);
1187
- const resultTable = resultTableFor(dataset);
1188
- const label =
1189
- (sourceQuery && (sourceQuery.label || sourceQuery.id || sourceQuery.engine));
1190
- const rowCount = resultTable && Number.isFinite(Number(resultTable.row_count)) ? Number(resultTable.row_count) : null;
1191
- const suffix = rowCount != null ? ` · ${formatNumber(rowCount, 0)} rows${resultTable.truncated ? " sampled" : ""}` : "";
1192
- setQueryStatus(label ? `${label}${suffix}` : "");
1193
- }
1194
-
1195
- function setupMenuDismissal() {
1196
- document.addEventListener("click", () => {
1197
- closeFieldMenus();
1198
- closeChartPicker();
1199
- closeDataFilterMenus();
1200
- closeShareMenu();
1201
- });
1202
- document.addEventListener("keydown", (event) => {
1203
- if (event.key === "Escape") {
1204
- closeFieldMenus();
1205
- closeChartPicker();
1206
- closeDataFilterMenus();
1207
- closeShareMenu();
1208
- }
1209
- });
1210
- }
1211
-
1212
- function canonicalViewMode(value) {
1213
- const raw = text(value || "").toLowerCase().replaceAll("-", "_").replaceAll(" ", "_");
1214
- if (raw === "vis" || raw === "viz" || raw === "chart") return "visualization";
1215
- if (raw === "table" || raw === "data") return "table";
1216
- if (raw === "both" || raw === "split") return "both";
1217
- return "both";
1218
- }
1219
-
1220
- function normalizePayload(nextPayload) {
1221
- payload = nextPayload || fallbackPayload;
1222
- const requestedVisualizationType =
1223
- visualizationSpecFor(activeDataset()).visualization_type;
1224
- activeVisualizationType = widgetVisualizationType(requestedVisualizationType);
1225
- resetChartSettings(activeDataset(), requestedVisualizationType);
1226
- viewMode = canonicalViewMode(viewMode);
1227
- dataMode = canonicalDataMode(specValue(activeDataset(), "data_mode", payload.data_mode || dataMode));
1228
- selectedDataFilters = {};
1229
- splitFraction = readStoredSplitFraction();
1230
- resetChartConfig(activeDataset());
1231
- resetVisibleSeries();
1232
- }
1233
-
1234
- function resetVisibleSeries() {
1235
- const names = rechartsLegendNamesForDataset(projectedDataset(activeDataset()), activeVisualizationType);
1236
- visibleSeries = new Set(names);
1237
- visibleSeriesSignature = names.join("\u0000");
1238
- }
1239
-
1240
- function orderedVisibleSeries(seriesNames, selectedSeries) {
1241
- const selected = selectedSeries instanceof Set ? selectedSeries : new Set(asArray(selectedSeries).map(text));
1242
- return new Set(asArray(seriesNames).map(text).filter((series) => selected.has(series)));
1243
- }
1244
-
1245
- function reconcileVisibleSeries(seriesNames) {
1246
- const names = asArray(seriesNames).map(text);
1247
- const signature = names.join("\u0000");
1248
- if (revealAllSeriesAfterGroupingChange) {
1249
- visibleSeries = new Set(names);
1250
- visibleSeriesSignature = signature;
1251
- revealAllSeriesAfterGroupingChange = false;
1252
- return;
1253
- }
1254
- if (signature === visibleSeriesSignature) return;
1255
- const available = new Set(names);
1256
- const stillVisible = [...visibleSeries].filter((series) => available.has(series));
1257
- visibleSeries = stillVisible.length ? orderedVisibleSeries(names, new Set(stillVisible)) : new Set(names);
1258
- visibleSeriesSignature = signature;
1259
- }
1260
-
1261
- const COMPACT_VALUE_THRESHOLD = 100000;
1262
-
1263
- function compactDigits(value) {
1264
- const absolute = Math.abs(value);
1265
- if (absolute >= COMPACT_VALUE_THRESHOLD) return 1;
1266
- return 2;
1267
- }
1268
-
1269
- function formatNumber(value, compact = false) {
1270
- const numeric = number(value);
1271
- if (numeric == null) return "";
1272
- const absolute = Math.abs(numeric);
1273
- return new Intl.NumberFormat(undefined, {
1274
- maximumFractionDigits: compactDigits(numeric),
1275
- notation: compact && absolute >= COMPACT_VALUE_THRESHOLD ? "compact" : "standard",
1276
- }).format(numeric);
1277
- }
1278
-
1279
- function isUsdUnit(unit) {
1280
- return unit === "$" || text(unit).toUpperCase() === "USD";
1281
- }
1282
-
1283
- function unitScaleSuffix(unit) {
1284
- const normalized = text(unit).toLowerCase();
1285
- if (normalized === "usd millions" || normalized === "usd million") return "M";
1286
- if (normalized === "usd billions" || normalized === "usd billion") return "B";
1287
- if (normalized === "usd thousands" || normalized === "usd thousand") return "K";
1288
- if (/^\$[kmbt]$/i.test(text(unit))) return text(unit).slice(1).toUpperCase();
1289
- return null;
1290
- }
1291
-
1292
- function formatCurrency(value) {
1293
- const numeric = number(value);
1294
- if (numeric == null) return "";
1295
- const absolute = Math.abs(numeric);
1296
- return new Intl.NumberFormat(undefined, {
1297
- currency: "USD",
1298
- maximumFractionDigits: compactDigits(numeric),
1299
- notation: absolute >= COMPACT_VALUE_THRESHOLD ? "compact" : "standard",
1300
- style: "currency",
1301
- }).format(numeric);
1302
- }
1303
-
1304
- function formatValue(value, unit) {
1305
- const cleanUnit = text(unit);
1306
- if (!cleanUnit) return formatNumber(value, true);
1307
- if (cleanUnit === "%" || cleanUnit.startsWith("%")) return `${formatNumber(value)}%`;
1308
- if (isUsdUnit(cleanUnit)) return formatCurrency(value);
1309
- const scaleSuffix = unitScaleSuffix(cleanUnit);
1310
- if (scaleSuffix) return `$${formatNumber(value)}${scaleSuffix}`;
1311
- if (cleanUnit.startsWith("$")) return `$${formatNumber(value)}${cleanUnit.slice(1)}`;
1312
- return `${formatNumber(value, true)} ${cleanUnit}`;
1313
- }
1314
-
1315
- function formatTableCell(value, type) {
1316
- if (value == null) return "";
1317
- const numeric = number(value);
1318
- if (type === "number" && numeric != null) return formatNumber(numeric, true);
1319
- if (type === "percent" && numeric != null) return `${formatNumber(numeric * 100)}%`;
1320
- if (type === "currency" && numeric != null) return formatCurrency(numeric);
1321
- return text(value);
1322
- }
1323
-
1324
- function tableColumns(dataset, rows) {
1325
- const resultTable = resultTableFor(dataset);
1326
- const columns = asArray(resultTable && resultTable.columns);
1327
- if (columns.length) return columns;
1328
- const keys = Object.keys(rows[0] || {});
1329
- return keys.map((key) => ({ key, label: key }));
1330
- }
1331
-
1332
- function rawChartRows(dataset) {
1333
- const resultTable = resultTableFor(dataset);
1334
- const resultRows = resultTable && Array.isArray(resultTable.rows) ? resultTable.rows : null;
1335
- if (resultRows) {
1336
- return resultRows.map((row) => (row && typeof row === "object" ? { ...row } : {}));
1337
- }
1338
- return asArray(dataset.data).map((point) => {
1339
- const row = point && typeof point === "object" ? { ...point } : {};
1340
- row.x = text(point && point.x);
1341
- row.series = text((point && point.series) || "Value");
1342
- row.y = number(point && point.y);
1343
- if (point && point.label != null) row.label = point.label;
1344
- return row;
1345
- });
1346
- }
1347
-
1348
- function dataFilterKey(dataset, fieldKey) {
1349
- return `${text(dataset && dataset.id) || "default"}\u0000${fieldKey}`;
1350
- }
1351
-
1352
- function selectedDataFilterValue(dataset, fieldKey) {
1353
- return selectedDataFilters[dataFilterKey(dataset, fieldKey)] || "all";
1354
- }
1355
-
1356
- function uniqueFilterValues(rows, fieldKey) {
1357
- const seen = new Set();
1358
- const values = [];
1359
- for (const row of rows) {
1360
- const value = text(row && row[fieldKey]);
1361
- if (!value || seen.has(value)) continue;
1362
- seen.add(value);
1363
- values.push(value);
1364
- }
1365
- return values.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
1366
- }
1367
-
1368
- function filterRowsExcluding(dataset, rows, excludedFieldKey) {
1369
- const specs = dataFilterSpecs(dataset);
1370
- if (!specs.length) return rows;
1371
- return rows.filter((row) =>
1372
- specs.every((spec) => {
1373
- if (spec.key === excludedFieldKey) return true;
1374
- const selected = selectedDataFilterValue(dataset, spec.key);
1375
- return selected === "all" || text(row && row[spec.key]) === selected;
1376
- }),
1377
- );
1378
- }
1379
-
1380
- function filterOptionsForSpec(dataset, spec) {
1381
- return uniqueFilterValues(filterRowsExcluding(dataset, rawChartRows(dataset), spec.key), spec.key);
1382
- }
1383
-
1384
- function pruneDataFilters(dataset) {
1385
- let changed = true;
1386
- while (changed) {
1387
- changed = false;
1388
- for (const spec of dataFilterSpecs(dataset)) {
1389
- const key = dataFilterKey(dataset, spec.key);
1390
- const selected = selectedDataFilters[key];
1391
- if (!selected) continue;
1392
- if (!filterOptionsForSpec(dataset, spec).includes(selected)) {
1393
- delete selectedDataFilters[key];
1394
- changed = true;
1395
- }
1396
- }
1397
- }
1398
- }
1399
-
1400
- function dataFilterSpecs(dataset) {
1401
- const rows = rawChartRows(dataset);
1402
- return availableFields(dataset)
1403
- .filter((field) => {
1404
- if (field.synthetic || field.numeric || field.type === "date") return false;
1405
- if (field.key === chartConfig.yField || field.key === measureNamesField) return false;
1406
- if (fieldLooksDateLike(dataset, field.key)) return false;
1407
- const values = uniqueFilterValues(rows, field.key);
1408
- return values.length > 1 && values.length <= 24;
1409
- })
1410
- .map((field) => ({
1411
- ...field,
1412
- options: uniqueFilterValues(rows, field.key),
1413
- }));
1414
- }
1415
-
1416
- function applyDataFilters(dataset, rows) {
1417
- const specs = dataFilterSpecs(dataset);
1418
- if (!specs.length) return rows;
1419
- return rows.filter((row) =>
1420
- specs.every((spec) => {
1421
- const selected = selectedDataFilterValue(dataset, spec.key);
1422
- return selected === "all" || text(row && row[spec.key]) === selected;
1423
- }),
1424
- );
1425
- }
1426
-
1427
- function chartRows(dataset) {
1428
- return applyDataFilters(dataset, rawChartRows(dataset));
1429
- }
1430
-
1431
- function chartColumns(rows, dataset = activeDataset()) {
1432
- const resultTable = resultTableFor(dataset);
1433
- const declared = asArray(resultTable && resultTable.columns)
1434
- .map((column) => column && column.key)
1435
- .filter((key) => typeof key === "string" && key);
1436
- if (declared.length) return declared;
1437
- const preferred = resultTable ? [] : ["series", "x", "y", "label"];
1438
- const seen = new Set();
1439
- const keys = [];
1440
- for (const key of preferred) {
1441
- if (rows.some((row) => row[key] != null && row[key] !== "")) {
1442
- seen.add(key);
1443
- keys.push(key);
1444
- }
1445
- }
1446
- for (const row of rows) {
1447
- for (const key of Object.keys(row)) {
1448
- if (seen.has(key)) continue;
1449
- seen.add(key);
1450
- keys.push(key);
1451
- }
1452
- }
1453
- return keys;
1454
- }
1455
-
1456
- function columnLabel(key, dataset = activeDataset()) {
1457
- if (key === measureNamesField) return "Measure names";
1458
- const resultTable = resultTableFor(dataset);
1459
- const declared = asArray(resultTable && resultTable.columns).find((column) => column && column.key === key);
1460
- if (declared && declared.label) return text(declared.label);
1461
- const labels = {
1462
- x: "X",
1463
- y: "Y",
1464
- series: "Series",
1465
- label: "Label",
1466
- };
1467
- return labels[key] || key.replaceAll("_", " ").replace(/\b\w/g, (char) => char.toUpperCase());
1468
- }
1469
-
1470
- function columnType(dataset, key) {
1471
- const resultTable = resultTableFor(dataset);
1472
- const declared = asArray(resultTable && resultTable.columns).find((column) => column && column.key === key);
1473
- return declared && declared.type ? text(declared.type) : "";
1474
- }
1475
-
1476
- function columnUnit(dataset, key) {
1477
- const resultTable = resultTableFor(dataset);
1478
- const declared = asArray(resultTable && resultTable.columns).find((column) => column && column.key === key);
1479
- return declared && declared.unit != null ? text(declared.unit).trim() : "";
1480
- }
1481
-
1482
- function isNumericColumn(rows, key, dataset = activeDataset()) {
1483
- const type = columnType(dataset, key);
1484
- if (["number", "percent", "currency"].includes(type)) return true;
1485
- if (["text", "date"].includes(type)) return false;
1486
- return rows.some((row) => number(row[key]) != null);
1487
- }
1488
-
1489
- function availableFields(dataset) {
1490
- const rows = rawChartRows(dataset);
1491
- return chartColumns(rows, dataset).map((key) => ({
1492
- key,
1493
- label: columnLabel(key, dataset),
1494
- numeric: isNumericColumn(rows, key, dataset),
1495
- type: columnType(dataset, key),
1496
- }));
1497
- }
1498
-
1499
- function hasField(dataset, key) {
1500
- if (key === measureNamesField) return compatibleMeasureNameFields(dataset).length > 1;
1501
- return availableFields(dataset).some((field) => field.key === key);
1502
- }
1503
-
1504
- function fieldLabel(dataset, key, fallback = "None") {
1505
- const field = availableFields(dataset).find((item) => item.key === key);
1506
- return field ? field.label : fallback;
1507
- }
1508
-
1509
- function colorGroupingRoleLabel(type = activeVisualizationType) {
1510
- type = widgetVisualizationType(type);
1511
- if (type === "pie") return "Slice by";
1512
- if (type === "heatmap") return "Y-axis";
1513
- if ((type === "bar" && (chartSettings.barGroupMode === "stacked" || chartSettings.barGroupMode === "stacked100")) || type === "stackedArea") return "Stack by";
1514
- if (type === "line" || type === "area") return "Series by";
1515
- if (type === "bar") return "Group by";
1516
- return "Color by";
1517
- }
1518
-
1519
- function chartUsesSingleSeriesOnly(type = activeVisualizationType) {
1520
- return ["funnel", "leaderboard", "waterfall"].includes(widgetVisualizationType(type));
1521
- }
1522
-
1523
- function chartRoleSpecs(type = activeVisualizationType) {
1524
- type = widgetVisualizationType(type);
1525
- if (type === "funnel") {
1526
- return [
1527
- { role: "x", label: "Stage", fallback: "Stage", required: true, aggregate: false, time: false },
1528
- { role: "y", label: "Value", fallback: "Value", required: true, aggregate: true, time: false },
1529
- ];
1530
- }
1531
- if (type === "waterfall") {
1532
- return [
1533
- { role: "x", label: "Step", fallback: "Step", required: true, aggregate: false, time: false },
1534
- { role: "y", label: "Change", fallback: "Change", required: true, aggregate: true, time: false },
1535
- ];
1536
- }
1537
- if (type === "leaderboard") {
1538
- return [
1539
- { role: "x", label: "Category", fallback: "Category", required: true, aggregate: false, time: false },
1540
- { role: "y", label: "Value", fallback: "Value", required: true, aggregate: true, time: false },
1541
- ];
1542
- }
1543
- if (type === "pie") {
1544
- return [
1545
- { role: "color", label: colorGroupingRoleLabel(type), fallback: "Slice", required: true, aggregate: false, time: false },
1546
- { role: "y", label: "Value", fallback: "Value", required: true, aggregate: true, time: false },
1547
- ];
1548
- }
1549
- if (type === "histogram") {
1550
- return [
1551
- { role: "y", label: "Value", fallback: "Value", required: true, aggregate: false, time: false },
1552
- ];
1553
- }
1554
- if (type === "scatter") {
1555
- return [
1556
- { role: "x", label: "X-axis", fallback: "X-axis", required: true, aggregate: false, time: false, numeric: true },
1557
- { role: "y", label: "Y-axis", fallback: "Y-axis", required: true, aggregate: false, time: false, numeric: true },
1558
- { role: "size", label: "Size by", fallback: "None", required: false, aggregate: false, time: false, numeric: true },
1559
- { role: "label", label: "Label by", fallback: "None", required: false, aggregate: false, time: false },
1560
- { role: "color", label: colorGroupingRoleLabel(type), fallback: "None", required: false, aggregate: false, time: false },
1561
- ];
1562
- }
1563
- if (type === "heatmap") {
1564
- return [
1565
- { role: "x", label: "X-axis", fallback: "X-axis", required: true, aggregate: false, time: true },
1566
- { role: "color", label: "Y-axis", fallback: "Y-axis", required: true, aggregate: false, time: false },
1567
- { role: "y", label: "Value", fallback: "Value", required: true, aggregate: true, time: false },
1568
- ];
1569
- }
1570
- return [
1571
- { role: "x", label: "X-axis", fallback: "X-axis", required: true, aggregate: false, time: true },
1572
- { role: "y", label: "Y-axis", fallback: "Y-axis", required: true, aggregate: true, time: false },
1573
- { role: "color", label: colorGroupingRoleLabel(type), fallback: "None", required: false, aggregate: false, time: false },
1574
- ];
1575
- }
1576
-
1577
- function chartRoleSpec(role, type = activeVisualizationType) {
1578
- return chartRoleSpecs(type).find((spec) => spec.role === role) || { role, label: role, fallback: "None", required: false };
1579
- }
1580
-
1581
- function detailControlLabel(spec) {
1582
- if (spec && spec.detailLabel) return spec.detailLabel;
1583
- if (spec && spec.label && !/^x-axis$|^y-axis$/i.test(spec.label)) {
1584
- return text(spec.label).replace("-axis", " axis");
1585
- }
1586
- if (spec && spec.role === "color" && /^y-axis$/i.test(spec.label || "")) return "Y axis";
1587
- if (spec && spec.role === "x") return "X axis";
1588
- if (spec && spec.role === "y") return "Y axis";
1589
- if (spec && spec.role === "size") return "Size by";
1590
- if (spec && spec.role === "label") return "Label by";
1591
- if (spec && spec.role === "color") return "Stack by";
1592
- if (spec && spec.role === "time") return "Time period";
1593
- return text((spec && spec.label) || "").replace("-axis", " axis");
1594
- }
1595
-
1596
- function fieldKeyForRole(role) {
1597
- if (role === "x") return chartConfig.xField;
1598
- if (role === "y") return chartConfig.yField;
1599
- if (role === "size") return chartConfig.sizeField;
1600
- if (role === "label") return chartConfig.labelField;
1601
- if (role === "color") return chartConfig.colorField;
1602
- return "";
1603
- }
1604
-
1605
- function compatibleFieldsForRole(dataset, role, type = activeVisualizationType) {
1606
- const fields = availableFields(dataset);
1607
- const spec = chartRoleSpec(role, type);
1608
- let compatible = fields;
1609
- if (["funnel", "leaderboard", "waterfall"].includes(widgetVisualizationType(type)) && role === "x") {
1610
- compatible = fields.filter((field) => !field.numeric);
1611
- }
1612
- if (role === "y" || role === "size" || spec.numeric) compatible = fields.filter((field) => field.numeric);
1613
- if (role === "color") {
1614
- const dimensions = fields.filter((field) => !field.numeric && field.key !== chartConfig.xField);
1615
- const measures = compatibleMeasureNameFields(dataset);
1616
- compatible = [
1617
- ...dimensions,
1618
- ...(measures.length > 1 ? [{ key: measureNamesField, label: "Measure names", numeric: false, type: "text", synthetic: true }] : []),
1619
- ];
1620
- }
1621
- return compatible.length ? compatible : fields;
1622
- }
1623
-
1624
- function canonicalTimeUnit(value) {
1625
- const raw = text(value || "").toLowerCase();
1626
- return ["year", "quarter", "month", "week", "day", "hour", "minute", "second"].includes(raw) ? raw : "none";
1627
- }
1628
-
1629
- function canonicalAggregation(value) {
1630
- const raw = text(value || "").toLowerCase();
1631
- const aliases = { average: "avg", mean: "avg", minimum: "min", maximum: "max" };
1632
- const next = aliases[raw] || raw;
1633
- return ["sum", "avg", "min", "max", "count"].includes(next) ? next : "sum";
1634
- }
1635
-
1636
- function aggregationLabel(value) {
1637
- const labels = { sum: "Sum", avg: "Avg", min: "Min", max: "Max", count: "Count" };
1638
- return labels[canonicalAggregation(value)] || "Sum";
1639
- }
1640
-
1641
- function timeUnitLabel(value) {
1642
- const unit = canonicalTimeUnit(value);
1643
- return unit === "none" ? "None" : unit.replace(/^\w/, (char) => char.toUpperCase());
1644
- }
1645
-
1646
- function timeUnitOptionsForDataset(dataset, fieldKey = chartConfig.xField) {
1647
- if (!fieldLooksDateLike(dataset, fieldKey)) return [];
1648
- const values = rawChartRows(dataset).map((row) => text(row && row[fieldKey])).filter(Boolean);
1649
- const hasClockTime = values.some((value) => /[T\s]\d{1,2}:\d{2}/.test(value));
1650
- const hasSeconds = values.some((value) => /[T\s]\d{1,2}:\d{2}:\d{2}/.test(value));
1651
- const options = ["None", "Year", "Quarter", "Month", "Week", "Day"];
1652
- if (hasClockTime) options.push("Hour", "Minute");
1653
- if (hasSeconds) options.push("Second");
1654
- return options;
1655
- }
1656
-
1657
- function parseDateValue(value) {
1658
- if (value instanceof Date && Number.isFinite(value.getTime())) return value;
1659
- if (typeof value !== "string") return null;
1660
- const raw = text(value);
1661
- if (!raw) return null;
1662
- if (!/^\d{4}[-/]\d{1,2}(?:[-/]\d{1,2})?(?:[T\s]\d{1,2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?$/.test(raw)) return null;
1663
- const parsed = new Date(raw);
1664
- return Number.isFinite(parsed.getTime()) ? parsed : null;
1665
- }
1666
-
1667
- function formatTimeValue(value, unit) {
1668
- unit = canonicalTimeUnit(unit);
1669
- if (unit === "none") return text(value);
1670
- const date = parseDateValue(value);
1671
- if (!date) return text(value);
1672
- const year = date.getUTCFullYear();
1673
- const month = date.getUTCMonth() + 1;
1674
- const pad = (next) => String(next).padStart(2, "0");
1675
- if (unit === "year") return String(year);
1676
- if (unit === "quarter") return `${year} Q${Math.floor((month - 1) / 3) + 1}`;
1677
- if (unit === "month") return `${year}-${pad(month)}`;
1678
- if (unit === "week") {
1679
- const first = Date.UTC(year, 0, 1);
1680
- const current = Date.UTC(year, date.getUTCMonth(), date.getUTCDate());
1681
- const week = Math.floor((current - first) / (7 * 24 * 60 * 60 * 1000)) + 1;
1682
- return `${year} W${pad(week)}`;
1683
- }
1684
- if (unit === "day") return `${year}-${pad(month)}-${pad(date.getUTCDate())}`;
1685
- if (unit === "hour") return `${year}-${pad(month)}-${pad(date.getUTCDate())} ${pad(date.getUTCHours())}:00`;
1686
- if (unit === "minute") return `${year}-${pad(month)}-${pad(date.getUTCDate())} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}`;
1687
- return `${year}-${pad(month)}-${pad(date.getUTCDate())} ${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`;
1688
- }
1689
-
1690
- function resetChartConfig(dataset = activeDataset()) {
1691
- const fields = availableFields(dataset);
1692
- const rows = rawChartRows(dataset);
1693
- const xEncoding = specEncoding(dataset, "x");
1694
- const yEncoding = specEncoding(dataset, "y");
1695
- const sizeEncoding = specEncoding(dataset, "size");
1696
- const colorEncoding = specEncoding(dataset, "color");
1697
- const lineStyleEncoding = specEncoding(dataset, "lineStyle");
1698
- const labelEncoding = specEncoding(dataset, "label");
1699
- const firstDimension = fields.find((field) => !field.numeric)?.key || "x";
1700
- const firstMeasure = fields.find((field) => field.numeric)?.key || "y";
1701
- const hasResultTable = Boolean(resultTableFor(dataset));
1702
- const fallbackColor = hasResultTable ? "" : fields.find((field) => field.key === "series")?.key || "";
1703
- const nextXField = text(xEncoding.field || chartConfig.xField || firstDimension) || firstDimension;
1704
- const nextYField = text(yEncoding.field || chartConfig.yField || firstMeasure) || firstMeasure;
1705
- const requestedTimeUnit = xEncoding.time_unit;
1706
- const explicitColor = colorEncoding.field || (multiMeasureSeriesEnabled(dataset) ? measureNamesField : "");
1707
- const explicitLineStyle = lineStyleEncoding.field;
1708
- const explicitLabel = labelEncoding.field;
1709
- const explicitSize = sizeEncoding.field;
1710
- chartConfig = {
1711
- xField: nextXField,
1712
- yField: nextYField,
1713
- colorField: text(explicitColor || fallbackColor) || fallbackColor,
1714
- lineStyleField: text(explicitLineStyle || chartConfig.lineStyleField || "") || "",
1715
- labelField: text(explicitLabel || chartConfig.labelField || "") || "",
1716
- sizeField: text(explicitSize || chartConfig.sizeField || "") || "",
1717
- timeUnit: requestedTimeUnit != null ? canonicalTimeUnit(requestedTimeUnit) : canonicalTimeUnit(chartConfig.timeUnit || "none"),
1718
- yAggregation: canonicalAggregation(yEncoding.aggregate || chartConfig.yAggregation),
1719
- };
1720
- if (!hasField(dataset, chartConfig.xField)) chartConfig.xField = firstDimension;
1721
- if (!hasField(dataset, chartConfig.yField)) chartConfig.yField = firstMeasure;
1722
- if (!hasField(dataset, chartConfig.colorField)) chartConfig.colorField = fallbackColor;
1723
- if (!hasField(dataset, chartConfig.lineStyleField)) chartConfig.lineStyleField = "";
1724
- if (!hasField(dataset, chartConfig.labelField)) chartConfig.labelField = "";
1725
- if (!hasField(dataset, chartConfig.sizeField)) chartConfig.sizeField = "";
1726
- if (!fieldLooksDateLike(dataset, chartConfig.xField)) chartConfig.timeUnit = "none";
1727
- }
1728
-
1729
- function normalizeChartConfigForType(dataset = activeDataset(), type = activeVisualizationType) {
1730
- if (!chartUsesSingleSeriesOnly(type)) return;
1731
- const compatibleStageFields = compatibleFieldsForRole(dataset, "x", type);
1732
- const compatibleValueFields = compatibleFieldsForRole(dataset, "y", type);
1733
- if (!compatibleStageFields.some((field) => field.key === chartConfig.xField)) {
1734
- chartConfig.xField = compatibleStageFields[0]?.key || chartConfig.xField;
1735
- }
1736
- if (!compatibleValueFields.some((field) => field.key === chartConfig.yField)) {
1737
- chartConfig.yField = compatibleValueFields[0]?.key || chartConfig.yField;
1738
- }
1739
- chartConfig.colorField = "";
1740
- chartConfig.timeUnit = "none";
1741
- }
1742
-
1743
- function fieldLooksDateLike(dataset, fieldKey) {
1744
- if (!fieldKey) return false;
1745
- const field = availableFields(dataset).find((candidate) => candidate.key === fieldKey);
1746
- if (field && field.type === "date") return true;
1747
- if (field && field.numeric) return false;
1748
- return rawChartRows(dataset).some((row) => row[fieldKey] != null && parseDateValue(row[fieldKey]));
1749
- }
1750
-
1751
- function measureNameFields(dataset) {
1752
- return availableFields(dataset).filter((field) => field.numeric && field.key !== chartConfig.xField);
1753
- }
1754
-
1755
- function multiMeasureSeriesEnabled(dataset) {
1756
- return specSettings(dataset).multi_measure_series === true;
1757
- }
1758
-
1759
- function measureCompatibilityKey(dataset, field) {
1760
- if (!field) return "";
1761
- const unit = columnUnit(dataset, field.key);
1762
- if (unit) return `unit:${unit.toLowerCase()}`;
1763
- return field.type === "percent" ? "type:percent" : "";
1764
- }
1765
-
1766
- function compatibleMeasureNameFields(dataset) {
1767
- const measures = measureNameFields(dataset);
1768
- if (multiMeasureSeriesEnabled(dataset)) return measures;
1769
- const selectedMeasure = measures.find((field) => field.key === chartConfig.yField);
1770
- const compatibilityKey = measureCompatibilityKey(dataset, selectedMeasure);
1771
- if (!compatibilityKey) return [];
1772
- return measures.filter((field) => measureCompatibilityKey(dataset, field) === compatibilityKey);
1773
- }
1774
-
1775
- function usesMeasureNames() {
1776
- return chartConfig.colorField === measureNamesField;
1777
- }
1778
-
1779
- function appendAggregatedValue(groups, x, series, y, row, lineStyle = null) {
1780
- const key = `${x}\u0000${series}`;
1781
- if (y == null) return;
1782
- if (!groups.has(key)) {
1783
- groups.set(key, { x, series, lineStyle, values: [], raw: row });
1784
- }
1785
- if (lineStyle && !groups.get(key).lineStyle) groups.get(key).lineStyle = lineStyle;
1786
- groups.get(key).values.push(y);
1787
- }
1788
-
1789
- function aggregateRows(rows, dataset = activeDataset()) {
1790
- const groups = new Map();
1791
- for (const row of rows) {
1792
- const x = formatTimeValue(row[chartConfig.xField], chartConfig.timeUnit);
1793
- if (usesMeasureNames()) {
1794
- for (const field of compatibleMeasureNameFields(dataset)) {
1795
- appendAggregatedValue(groups, x, field.label, number(row[field.key]), row);
1796
- }
1797
- continue;
1798
- }
1799
- const series = chartConfig.colorField ? text(row[chartConfig.colorField] || "Value") : "Value";
1800
- const y = chartConfig.yAggregation === "count" ? 1 : number(row[chartConfig.yField]);
1801
- const lineStyle = chartConfig.lineStyleField ? text(row[chartConfig.lineStyleField] || "") : "";
1802
- appendAggregatedValue(groups, x, series, y, row, lineStyle || null);
1803
- }
1804
- const output = [...groups.values()].map((group) => {
1805
- let y = 0;
1806
- if (chartConfig.yAggregation === "avg") y = group.values.reduce((sum, value) => sum + value, 0) / group.values.length;
1807
- else if (chartConfig.yAggregation === "min") y = Math.min(...group.values);
1808
- else if (chartConfig.yAggregation === "max") y = Math.max(...group.values);
1809
- else y = group.values.reduce((sum, value) => sum + value, 0);
1810
- return { x: group.x, y, series: group.series, ...(group.lineStyle ? { lineStyle: group.lineStyle } : {}), raw: group.raw };
1811
- });
1812
- return output;
1813
- }
1814
-
1815
- function rawProjectedRows(rows, dataset = activeDataset()) {
1816
- const output = [];
1817
- for (const row of rows) {
1818
- if (usesMeasureNames()) {
1819
- for (const field of compatibleMeasureNameFields(dataset)) {
1820
- const y = number(row[field.key]);
1821
- if (y == null) continue;
1822
- output.push({
1823
- x: row[chartConfig.xField],
1824
- y,
1825
- size: chartConfig.sizeField ? number(row[chartConfig.sizeField]) : null,
1826
- series: field.label,
1827
- ...(chartConfig.labelField ? { label: text(row[chartConfig.labelField] || "") } : {}),
1828
- raw: row,
1829
- });
1830
- }
1831
- continue;
1832
- }
1833
- const y = number(row[chartConfig.yField]);
1834
- if (y == null) continue;
1835
- output.push({
1836
- x: row[chartConfig.xField],
1837
- y,
1838
- size: chartConfig.sizeField ? number(row[chartConfig.sizeField]) : null,
1839
- series: chartConfig.colorField ? text(row[chartConfig.colorField] || "Value") : "Value",
1840
- ...(chartConfig.lineStyleField ? { lineStyle: text(row[chartConfig.lineStyleField] || "") } : {}),
1841
- ...(chartConfig.labelField ? { label: text(row[chartConfig.labelField] || "") } : {}),
1842
- raw: row,
1843
- });
1844
- }
1845
- return output;
1846
- }
1847
-
1848
- function projectedDataset(dataset) {
1849
- const type = widgetVisualizationType(activeVisualizationType);
1850
- const rows = projectedDataForType(dataset, type);
1851
- const renderRows = projectedRenderRows(rows);
1852
- return {
1853
- ...dataset,
1854
- chart_spec: projectedChartSpec(dataset, type, rows),
1855
- data: renderRows,
1856
- table: {
1857
- columns: projectedRenderColumns(rows, dataset),
1858
- rows: renderRows,
1859
- row_count: renderRows.length,
1860
- truncated: false,
1861
- },
1862
- };
1863
- }
1864
-
1865
- function projectedDataForType(dataset, type = activeVisualizationType) {
1866
- type = widgetVisualizationType(type);
1867
- const rows = chartRows(dataset);
1868
- return type === "scatter" || type === "histogram" || type === "boxPlot" ? rawProjectedRows(rows, dataset) : aggregateRows(rows, dataset);
1869
- }
1870
-
1871
- function hasNonNegativeProjectedValues(data) {
1872
- let observed = false;
1873
- for (const point of data) {
1874
- const value = number(point && point.y);
1875
- if (value == null) continue;
1876
- observed = true;
1877
- if (value < 0) return false;
1878
- }
1879
- return observed;
1880
- }
1881
-
1882
- function projectedSeriesCount(data) {
1883
- return groupData(data).size;
1884
- }
1885
-
1886
- function projectedXCount(data) {
1887
- return uniqueX(data).length;
1888
- }
1889
-
1890
- function numericValuesForField(dataset, fieldKey) {
1891
- if (!fieldKey) return [];
1892
- return rawChartRows(dataset)
1893
- .map((row) => number(row && row[fieldKey]))
1894
- .filter((value) => value != null);
1895
- }
1896
-
1897
- function hasDistributionSample(dataset, fieldKey, minimumValues = 10) {
1898
- const values = numericValuesForField(dataset, fieldKey);
1899
- return values.length >= minimumValues && new Set(values.map((value) => String(value))).size >= 3;
1900
- }
1901
-
1902
- function maxNumericValuesPerCategory(dataset, categoryField, valueField) {
1903
- if (!categoryField || !valueField) return 0;
1904
- const counts = new Map();
1905
- for (const row of rawChartRows(dataset)) {
1906
- if (number(row && row[valueField]) == null) continue;
1907
- const category = text(row && row[categoryField]);
1908
- counts.set(category, (counts.get(category) || 0) + 1);
1909
- }
1910
- return Math.max(0, ...counts.values());
1911
- }
1912
-
1913
- function compatibleChartPickerSections(dataset = activeDataset()) {
1914
- return chartPickerSections
1915
- .map((section) => ({
1916
- ...section,
1917
- options: section.options
1918
- .filter((option, index, options) => {
1919
- const canonicalType = widgetVisualizationType(option.type);
1920
- return options.findIndex((candidate) => widgetVisualizationType(candidate.type) === canonicalType) === index;
1921
- })
1922
- .map((option) => ({ ...option, type: widgetVisualizationType(option.type) })),
1923
- }))
1924
- .filter((section) => section.options.length);
1925
- }
1926
-
1927
- function ensureActiveChartTypeIsSupported(dataset = activeDataset()) {
1928
- activeVisualizationType = widgetVisualizationType(activeVisualizationType || "bar");
1929
- normalizeChartConfigForType(dataset, activeVisualizationType);
1930
- }
1931
-
1932
- function renderFieldList(dataset) {
1933
- const root = document.getElementById("field-list");
1934
- if (!root) return;
1935
- root.innerHTML = "";
1936
- const rows = rawChartRows(dataset);
1937
- const keys = chartColumns(rows, dataset);
1938
- const measures = keys.filter((key) => isNumericColumn(rows, key, dataset));
1939
- const dimensions = keys.filter((key) => !measures.includes(key));
1940
- const activeKeys = new Set([chartConfig.xField, chartConfig.yField, chartConfig.colorField, chartConfig.lineStyleField, chartConfig.labelField, chartConfig.sizeField]);
1941
- const filter = text(document.querySelector(".field-filter")?.value).trim().toLowerCase();
1942
-
1943
- function appendGroup(title, fields) {
1944
- fields = fields.filter((key) => !filter || columnLabel(key, dataset).toLowerCase().includes(filter) || key.toLowerCase().includes(filter));
1945
- if (!fields.length) return;
1946
- const group = document.createElement("div");
1947
- group.className = "field-group";
1948
- const heading = document.createElement("p");
1949
- heading.className = "field-group-title";
1950
- heading.textContent = title;
1951
- const list = document.createElement("ul");
1952
- list.className = "field-list";
1953
- for (const key of fields) {
1954
- const item = document.createElement("li");
1955
- item.className = `field-item${activeKeys.has(key) ? " is-active" : ""}`;
1956
- const icon = document.createElement("span");
1957
- icon.className = "field-icon";
1958
- icon.appendChild(lucideIcon(measures.includes(key) ? "hash" : "type", { width: "14", height: "14" }));
1959
- const label = document.createElement("span");
1960
- label.className = "field-label";
1961
- label.textContent = columnLabel(key, dataset);
1962
- item.append(icon, label);
1963
- list.appendChild(item);
1964
- }
1965
- group.append(heading, list);
1966
- root.appendChild(group);
1967
- }
1968
-
1969
- appendGroup("Measures", measures);
1970
- appendGroup("Columns", dimensions);
1971
- }
1972
-
1973
- function setupAppChromeControls() {
1974
- const fieldFilter = document.querySelector(".field-filter");
1975
- if (!fieldFilter) return;
1976
- fieldFilter.addEventListener("input", () => renderFieldList(activeDataset()));
1977
- }
1978
-
1979
- function renderDataPreview(dataset) {
1980
- const root = document.getElementById("data-preview");
1981
- if (!root) return;
1982
- destroyDataTable(root);
1983
- root.innerHTML = "";
1984
- const rows = chartRows(dataset);
1985
- renderDataTable(root, {
1986
- columns: tableColumns(dataset, rows),
1987
- emptyLabel: "No rows match the selected filters.",
1988
- maxRows: 80,
1989
- pageSize: isDetailDisplayMode() ? 12 : 8,
1990
- rows,
1991
- });
1992
- }
1993
-
1994
- function appendSourceRow(root, label, value, options = {}) {
1995
- if (value == null || value === "") return;
1996
- if (options.code) {
1997
- const details = document.createElement("details");
1998
- details.className = "source-disclosure";
1999
- const summary = document.createElement("summary");
2000
- summary.textContent = label;
2001
- const code = document.createElement("pre");
2002
- code.className = "source-code";
2003
- code.dataset.language = options.language || label;
2004
- renderSourceCode(code, value, code.dataset.language);
2005
- details.append(summary, code);
2006
- root.appendChild(details);
2007
- return;
2008
- }
2009
- const row = document.createElement("div");
2010
- row.className = "source-row";
2011
- const labelEl = document.createElement("span");
2012
- labelEl.textContent = label;
2013
- const valueEl = document.createElement("strong");
2014
- if (options.multiline) {
2015
- valueEl.className = "source-row-list";
2016
- for (const item of sourceDetailList(value)) {
2017
- const line = document.createElement("span");
2018
- line.textContent = text(item);
2019
- valueEl.appendChild(line);
2020
- }
2021
- }
2022
- else {
2023
- valueEl.textContent = text(value);
2024
- }
2025
- row.append(labelEl, valueEl);
2026
- root.appendChild(row);
2027
- }
2028
-
2029
- function appendSourcePreviewRows(root, dataset) {
2030
- const table = resultTableFor(dataset) || {};
2031
- const rows = asArray(table.rows).filter((row) => row && typeof row === "object").slice(0, 10);
2032
- if (!rows.length) {
2033
- appendSourceRow(root, "Preview rows", "No preview rows available");
2034
- return;
2035
- }
2036
- const columns = [];
2037
- const seen = new Set();
2038
- for (const column of asArray(table.columns)) {
2039
- const key = column && (column.key || column.field);
2040
- if (key && !seen.has(key)) {
2041
- seen.add(key);
2042
- columns.push(key);
2043
- }
2044
- }
2045
- for (const row of rows) {
2046
- for (const key of Object.keys(row)) {
2047
- if (!seen.has(key)) {
2048
- seen.add(key);
2049
- columns.push(key);
2050
- }
2051
- }
2052
- }
2053
- const wrapper = document.createElement("div");
2054
- wrapper.className = "source-row source-row-table";
2055
- const labelEl = document.createElement("span");
2056
- labelEl.textContent = "Preview rows";
2057
- const tableShell = document.createElement("div");
2058
- tableShell.className = "source-preview-shell";
2059
- const previewTable = document.createElement("table");
2060
- previewTable.className = "source-preview-table";
2061
- const thead = document.createElement("thead");
2062
- const headRow = document.createElement("tr");
2063
- for (const column of columns) {
2064
- const th = document.createElement("th");
2065
- th.textContent = column;
2066
- headRow.appendChild(th);
2067
- }
2068
- thead.appendChild(headRow);
2069
- const tbody = document.createElement("tbody");
2070
- for (const row of rows) {
2071
- const tr = document.createElement("tr");
2072
- for (const column of columns) {
2073
- const td = document.createElement("td");
2074
- td.textContent = text(row[column]);
2075
- tr.appendChild(td);
2076
- }
2077
- tbody.appendChild(tr);
2078
- }
2079
- previewTable.append(thead, tbody);
2080
- tableShell.appendChild(previewTable);
2081
- wrapper.append(labelEl, tableShell);
2082
- root.appendChild(wrapper);
2083
- }
2084
-
2085
- function renderSourceDetails(dataset) {
2086
- const root = document.getElementById("source-details");
2087
- if (!root) return;
2088
- root.innerHTML = "";
2089
- const sourceQuery = sourceQueryFor(dataset) || {};
2090
- const metadataObjects = sourceMetadataObjects(dataset);
2091
- const queryCode = sqlTextFor(dataset);
2092
- const queryLanguage = queryLanguageFor(dataset);
2093
- const explicitTables = sourceTableNamesFromMetadata(metadataObjects);
2094
- const inferredTables = extractTablesFromSql(queryCode);
2095
- const tables = explicitTables.length ? explicitTables : inferredTables;
2096
- const explicitFilters = firstListFromObjects(metadataObjects, [
2097
- "filters",
2098
- "filter_descriptions",
2099
- "filterDescriptions",
2100
- "filter_description",
2101
- "filterDescription",
2102
- ]);
2103
- const activeFilters = activeDataFilterSummary(dataset);
2104
- const filters = explicitFilters.length ? explicitFilters : activeFilters === "None" ? ["None declared"] : [activeFilters];
2105
- const metrics = metricDefinitionList(dataset);
2106
- appendSourceRow(root, "Dataset", valueFor(dataset, "dataset", payload.dataset) || sourceLabelFor(dataset) || "Not declared");
2107
- if (sourceQuery.description) appendSourceRow(root, "Description", sourceQuery.description);
2108
- appendSourceRow(root, "Tables used", sourceDetailList(tables.length ? tables : ["Not declared"]), { multiline: true });
2109
- appendSourceRow(root, "Filters", sourceDetailText(filters));
2110
- appendSourceRow(root, "Metric definitions", sourceDetailList(metrics.length ? metrics : ["Displayed directly from source columns"]), { multiline: true });
2111
- appendSourceRow(root, "Snapshot", sourceQuery.executed_at || valueFor(dataset, "executed_at", payload.executed_at) || "Not declared");
2112
- if (queryCode) appendSourceRow(root, "Source query", queryCode, { code: true, language: queryLanguage });
2113
- appendSourcePreviewRows(root, dataset);
2114
- if (!root.childElementCount) {
2115
- const empty = document.createElement("p");
2116
- empty.className = "source-note";
2117
- empty.textContent = "Static reviewed data.";
2118
- root.appendChild(empty);
2119
- }
2120
- }
2121
-
2122
- function renderSqlPreview(dataset) {
2123
- const root = document.getElementById("sql-preview");
2124
- if (!root) return;
2125
- root.innerHTML = "";
2126
- const bottomSplit = document.getElementById("bottom-split");
2127
- const sql = sqlTextFor(dataset);
2128
- if (bottomSplit) bottomSplit.classList.toggle("has-sql", Boolean(sql));
2129
- if (!sql) {
2130
- const empty = document.createElement("div");
2131
- empty.className = "empty";
2132
- empty.setAttribute("role", "status");
2133
- const heading = document.createElement("strong");
2134
- heading.textContent = "No query available";
2135
- const detail = document.createElement("span");
2136
- detail.textContent = "This chart payload did not include SQL or Python source code.";
2137
- empty.append(heading, detail);
2138
- root.appendChild(empty);
2139
- return;
2140
- }
2141
-
2142
- const code = document.createElement("pre");
2143
- code.className = "source-code";
2144
- code.dataset.language = queryLanguageFor(dataset);
2145
- renderSourceCode(code, sql, code.dataset.language);
2146
- root.appendChild(code);
2147
- }
2148
-
2149
- function renderAppChrome(dataset) {
2150
- renderSourceDetails(dataset);
2151
- renderFieldList(dataset);
2152
- renderDataPreview(dataset);
2153
- renderSqlPreview(dataset);
2154
- }
2155
-
2156
- function groupData(data) {
2157
- const groups = new Map();
2158
- for (const point of data) {
2159
- const y = number(point && point.y);
2160
- if (y == null) continue;
2161
- const series = text(point.series || "Value");
2162
- if (!groups.has(series)) groups.set(series, []);
2163
- groups.get(series).push({ x: text(point.x), y, label: point.label, raw: point });
2164
- }
2165
- return groups;
2166
- }
2167
-
2168
- function rechartsLegendNamesForDataset(dataset, type) {
2169
- if (type === "histogram" || type === "heatmap" || type === "boxPlot") return [];
2170
- const chartSpec = chartSpecFor(dataset);
2171
- const encodings = chartSpec && typeof chartSpec.encodings === "object" && !Array.isArray(chartSpec.encodings)
2172
- ? chartSpec.encodings
2173
- : {};
2174
- const yFields = asArray(encodings.y && encodings.y.fields).map(text).filter(Boolean);
2175
- if (yFields.length) return yFields;
2176
- const colorField = text(encodings.color && encodings.color.field);
2177
- if (colorField) {
2178
- return uniqueProjectedSeriesValues(chartRows(dataset)).map((value, index) => syntheticSeriesField(value, index));
2179
- }
2180
- const yField = text(encodings.y && encodings.y.field);
2181
- return yField ? [yField] : [];
2182
- }
2183
-
2184
- function uniqueX(data) {
2185
- const seen = new Set();
2186
- const values = [];
2187
- for (const point of data) {
2188
- const x = text(point && point.x);
2189
- if (seen.has(x)) continue;
2190
- seen.add(x);
2191
- values.push(x);
2192
- }
2193
- return values;
2194
- }
2195
-
2196
- function svgEl(name, attrs = {}) {
2197
- const element = document.createElementNS("http://www.w3.org/2000/svg", name);
2198
- for (const [key, value] of Object.entries(attrs)) {
2199
- if (value != null) element.setAttribute(key, String(value));
2200
- }
2201
- return element;
2202
- }
2203
-
2204
- function lucideIcon(name, attrs = {}) {
2205
- const svg = svgEl("svg", {
2206
- viewBox: "0 0 24 24",
2207
- fill: "none",
2208
- "aria-hidden": "true",
2209
- focusable: "false",
2210
- ...attrs,
2211
- });
2212
- svg.setAttribute("stroke", "currentColor");
2213
- svg.setAttribute("stroke-width", "2");
2214
- svg.setAttribute("stroke-linecap", "round");
2215
- svg.setAttribute("stroke-linejoin", "round");
2216
- const icons = {
2217
- check: [["path", { d: "M20 6 9 17l-5-5" }]],
2218
- chevronDown: [["path", { d: "m6 9 6 6 6-6" }]],
2219
- chevronRight: [["path", { d: "m9 18 6-6-6-6" }]],
2220
- fileDown: [
2221
- ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }],
2222
- ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }],
2223
- ["path", { d: "M12 18v-6" }],
2224
- ["path", { d: "m9 15 3 3 3-3" }],
2225
- ],
2226
- fileText: [
2227
- ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }],
2228
- ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }],
2229
- ["path", { d: "M10 9H8" }],
2230
- ["path", { d: "M16 13H8" }],
2231
- ["path", { d: "M16 17H8" }],
2232
- ],
2233
- printer: [
2234
- ["path", { d: "M6 9V2h12v7" }],
2235
- ["path", { d: "M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2" }],
2236
- ["path", { d: "M6 14h12v8H6z" }],
2237
- ],
2238
- hash: [
2239
- ["line", { x1: "4", x2: "20", y1: "9", y2: "9" }],
2240
- ["line", { x1: "4", x2: "20", y1: "15", y2: "15" }],
2241
- ["line", { x1: "10", x2: "8", y1: "3", y2: "21" }],
2242
- ["line", { x1: "16", x2: "14", y1: "3", y2: "21" }],
2243
- ],
2244
- calendar: [
2245
- ["path", { d: "M8 2v4" }],
2246
- ["path", { d: "M16 2v4" }],
2247
- ["rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }],
2248
- ["path", { d: "M3 10h18" }],
2249
- ],
2250
- maximize2: [
2251
- ["polyline", { points: "15 3 21 3 21 9" }],
2252
- ["polyline", { points: "9 21 3 21 3 15" }],
2253
- ["line", { x1: "21", x2: "14", y1: "3", y2: "10" }],
2254
- ["line", { x1: "3", x2: "10", y1: "21", y2: "14" }],
2255
- ],
2256
- minimize2: [
2257
- ["polyline", { points: "4 14 10 14 10 20" }],
2258
- ["polyline", { points: "20 10 14 10 14 4" }],
2259
- ["line", { x1: "14", x2: "21", y1: "10", y2: "3" }],
2260
- ["line", { x1: "3", x2: "10", y1: "21", y2: "14" }],
2261
- ],
2262
- presentation: [
2263
- ["path", { d: "M2 3h20" }],
2264
- ["path", { d: "M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" }],
2265
- ["path", { d: "m7 21 5-5 5 5" }],
2266
- ],
2267
- refreshCw: [
2268
- ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }],
2269
- ["path", { d: "M21 3v5h-5" }],
2270
- ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }],
2271
- ["path", { d: "M8 16H3v5" }],
2272
- ],
2273
- type: [
2274
- ["polyline", { points: "4 7 4 4 20 4 20 7" }],
2275
- ["line", { x1: "9", x2: "15", y1: "20", y2: "20" }],
2276
- ["line", { x1: "12", x2: "12", y1: "4", y2: "20" }],
2277
- ],
2278
- };
2279
- for (const [tag, attrs] of icons[name] || icons.chevronDown) {
2280
- svg.appendChild(svgEl(tag, attrs));
2281
- }
2282
- return svg;
2283
- }
2284
-
2285
- function renderEmpty(message, title = "Chart cannot be rendered") {
2286
- destroyChartInstance();
2287
- const chartRoot = document.getElementById("chart");
2288
- const empty = document.createElement("div");
2289
- empty.className = "empty";
2290
- empty.setAttribute("role", "status");
2291
- const heading = document.createElement("strong");
2292
- heading.textContent = title;
2293
- const detail = document.createElement("span");
2294
- detail.textContent = message;
2295
- empty.append(heading, detail);
2296
- chartRoot.appendChild(empty);
2297
- }
2298
-
2299
- function destroyChartInstance() {
2300
- destroyRechartsChart(document.getElementById("chart"));
2301
- destroyDataTable(document.getElementById("chart"));
2302
- chartInstance = null;
2303
- }
2304
-
2305
- function hideExternalLegend() {
2306
- const root = document.getElementById("legend");
2307
- const legendShell = document.getElementById("legend-shell");
2308
- if (root) root.innerHTML = "";
2309
- if (legendShell) {
2310
- legendShell.hidden = true;
2311
- legendShell.classList.add("is-empty");
2312
- legendShell.setAttribute("aria-hidden", "true");
2313
- }
2314
- }
2315
-
2316
- function chartPalette() {
2317
- return resolveCssColors(colors).filter(Boolean);
2318
- }
2319
-
2320
- function chartBorderPalette() {
2321
- return resolveCssColors(colorEdges).filter(Boolean);
2322
- }
2323
-
2324
- function resizeChartInstance() {
2325
- // Recharts responds to the container resize; the layout code only needs
2326
- // to reserve the region, not recreate the chart.
2327
- }
2328
-
2329
- function renderChart() {
2330
- const dataset = projectedDataset(activeDataset());
2331
- const chartRoot = document.getElementById("chart");
2332
- destroyChartInstance();
2333
- chartRoot.innerHTML = "";
2334
- activeVisualizationType = widgetVisualizationType(activeVisualizationType || "bar");
2335
- applyChartSettingsAttributes();
2336
- const type = rendererVisualizationType(activeVisualizationType);
2337
- const legendNames = rechartsLegendNamesForDataset(dataset, type);
2338
- reconcileVisibleSeries(legendNames);
2339
- try {
2340
- renderRechartsChart(chartRoot, dataset, type, {
2341
- colors: chartPalette(),
2342
- borderColors: chartBorderPalette(),
2343
- height: isDetailDisplayMode() ? "100%" : undefined,
2344
- onVisibleSeriesChange(nextVisibleSeries) {
2345
- visibleSeries = orderedVisibleSeries(legendNames, nextVisibleSeries);
2346
- renderChart();
2347
- },
2348
- visibleSeries,
2349
- timeUnit: chartConfig.timeUnit,
2350
- unit: unitFor(dataset),
2351
- baseline: number(specValue(dataset, "baseline", payload.baseline)),
2352
- settings: {
2353
- orientation: chartSettings.barOrientation,
2354
- group_mode: chartSettings.barGroupMode,
2355
- },
2356
- surface: isDetailDisplayMode() ? "explorer" : "compact",
2357
- title: text(valueFor(dataset, "title", payload.title || "Data Analytics chart")),
2358
- });
2359
- hideExternalLegend();
2360
- } catch (error) {
2361
- chartRoot.innerHTML = "";
2362
- renderEmpty(error && error.message ? `Choose compatible fields or reset the chart configuration. ${error.message}` : "Choose compatible fields or reset the chart configuration.");
2363
- hideExternalLegend();
2364
- }
2365
- }
2366
-
2367
- function renderTableFallback(dataset) {
2368
- destroyChartInstance();
2369
- const chartRoot = document.getElementById("chart");
2370
- const controls = document.getElementById("controls");
2371
- const legendShell = document.getElementById("legend-shell");
2372
- const dataPreview = document.getElementById("data-preview");
2373
- controls.hidden = true;
2374
- if (legendShell) legendShell.hidden = true;
2375
- if (dataPreview) {
2376
- destroyDataTable(dataPreview);
2377
- dataPreview.innerHTML = "";
2378
- }
2379
- destroyDataTable(chartRoot);
2380
- chartRoot.innerHTML = "";
2381
-
2382
- const resultTable = resultTableFor(dataset);
2383
- const rows = resultTable ? chartRows(dataset) : [];
2384
- if (!rows.length) {
2385
- renderEmpty("No table rows to render.", "No table data");
2386
- return;
2387
- }
2388
-
2389
- const maxRows = Math.max(1, Number(valueFor(dataset, "max_rows", payload.max_rows)) || 50);
2390
- const columns = tableColumns(dataset, rows);
2391
- renderDataTable(chartRoot, {
2392
- columns,
2393
- emptyLabel: "No table rows to render.",
2394
- maxRows,
2395
- pageSize: isDetailDisplayMode() ? 12 : 8,
2396
- rows,
2397
- });
2398
- }
2399
-
2400
- function renderLegend(groupNames) {
2401
- const root = document.getElementById("legend");
2402
- const legendShell = document.getElementById("legend-shell");
2403
- root.innerHTML = "";
2404
- if (legendShell) {
2405
- const empty = groupNames.length <= 1;
2406
- legendShell.hidden = empty && !isDetailDisplayMode();
2407
- legendShell.classList.toggle("is-empty", empty);
2408
- legendShell.setAttribute("aria-hidden", String(empty));
2409
- }
2410
- if (!groupNames.length || groupNames.length <= 1) return;
2411
- groupNames.forEach((name, index) => {
2412
- const item = document.createElement("li");
2413
- item.className = "legend-item";
2414
- item.style.color = colors[index % colors.length];
2415
- const button = document.createElement("button");
2416
- button.type = "button";
2417
- button.className = "legend-button";
2418
- button.setAttribute("aria-pressed", String(visibleSeries.has(name)));
2419
- button.addEventListener("click", () => {
2420
- if (visibleSeries.has(name)) {
2421
- if (visibleSeries.size === 1) return;
2422
- visibleSeries.delete(name);
2423
- }
2424
- else visibleSeries.add(name);
2425
- visibleSeries = orderedVisibleSeries(groupNames, visibleSeries);
2426
- renderChart();
2427
- });
2428
- const swatch = document.createElement("span");
2429
- swatch.className = "swatch";
2430
- const label = document.createElement("span");
2431
- label.textContent = name;
2432
- button.append(swatch, label);
2433
- item.appendChild(button);
2434
- root.appendChild(item);
2435
- });
2436
- }
2437
-
2438
- function chartTypeLabel(type) {
2439
- for (const section of chartPickerSections) {
2440
- for (const option of section.options) {
2441
- if (option.type === type) return option.label;
2442
- }
2443
- }
2444
- return text(type).replaceAll("_", " ");
2445
- }
2446
-
2447
- function activeDataFilterSummary(dataset = activeDataset()) {
2448
- const active = dataFilterSpecs(dataset)
2449
- .map((spec) => {
2450
- const value = selectedDataFilterValue(dataset, spec.key);
2451
- return value === "all" ? null : `${spec.label}: ${value}`;
2452
- })
2453
- .filter(Boolean);
2454
- return active.length ? active.join(", ") : "None";
2455
- }
2456
-
2457
- function promptSourceLines(dataset = activeDataset()) {
2458
- const sourceQuery = sourceQueryFor(dataset) || {};
2459
- const resultTable = resultTableFor(dataset) || {};
2460
- const lines = [];
2461
- if (sourceQuery.label || sourceQuery.id) lines.push(`Source query: ${sourceQuery.label || sourceQuery.id}`);
2462
- if (sourceQuery.engine) lines.push(`Source: ${sourceQuery.engine}`);
2463
- if (sourceQuery.executed_at) lines.push(`Executed at: ${sourceQuery.executed_at}`);
2464
- if (sourceQuery.description) lines.push(`Description: ${sourceQuery.description}`);
2465
- if (Number.isFinite(Number(resultTable.row_count))) {
2466
- lines.push(`Rows: ${formatNumber(Number(resultTable.row_count), 0)}${resultTable.truncated ? " sampled" : ""}`);
2467
- }
2468
- if (sourceQuery.sql) lines.push(`Query:\n${sourceQuery.sql}`);
2469
- return lines.length ? lines.join("\n") : "Source query: not declared in the widget payload.";
2470
- }
2471
-
2472
- function promptContext(dataset = activeDataset()) {
2473
- const title = text(valueFor(dataset, "title", payload.title || "Data Analytics chart"));
2474
- return [
2475
- `Widget URL: ${window.location.href}`,
2476
- `Chart title: ${title}`,
2477
- `Chart type: ${chartTypeLabel(activeVisualizationType)}`,
2478
- `X field: ${fieldLabel(dataset, chartConfig.xField, chartConfig.xField)}`,
2479
- `Y field: ${fieldLabel(dataset, chartConfig.yField, chartConfig.yField)}`,
2480
- `Grouping field: ${chartConfig.colorField ? fieldLabel(dataset, chartConfig.colorField, chartConfig.colorField) : "None"}`,
2481
- `Filters: ${activeDataFilterSummary(dataset)}`,
2482
- promptSourceLines(dataset),
2483
- ].join("\n");
2484
- }
2485
-
2486
- function refreshPrompt(dataset = activeDataset()) {
2487
- return `Refresh this inline chart using the same declared query and the latest available time frame.
2488
-
2489
- Re-run the existing source, update the widget data, preserve the current chart structure unless the latest data makes it invalid, and verify the refreshed chart in the inline and detail views.
2490
-
2491
- ${promptContext(dataset)}`;
2492
- }
2493
-
2494
- function exportPrompt(target, dataset = activeDataset()) {
2495
- const context = promptContext(dataset);
2496
- if (target === "html") {
2497
- return `Export this inline chart detail view as a static, portable HTML artifact. Include the chart title, chart, table preview, active filters, query/source details, and caveats. Omit the interactive top bar and app-only controls from the exported artifact. Verify the HTML before delivery.
2498
-
2499
- ${context}`;
2500
- }
2501
- if (target === "document") {
2502
- return `Create a polished document artifact from this inline chart. Preserve the chart, table preview, active filters, query/source details, and caveats, then verify the document.
2503
-
2504
- ${context}`;
2505
- }
2506
- if (target === "slides") {
2507
- return `Create an executive-ready slide artifact from this inline chart. Preserve the core takeaway, chart, active filters, table preview, and source details, then verify the deck.
2508
-
2509
- ${context}`;
2510
- }
2511
- return `Use the existing data-analytics:report-to-pdf workflow to create a PDF artifact from this inline chart detail view. Use the static HTML/export path when available, preserve the chart title, chart, table preview, active filters, caveats, and source details, omit the interactive top bar, share menus, edit controls, and app-only controls, then verify the PDF before delivery.
2512
-
2513
- ${context}`;
2514
- }
2515
-
2516
- function codexPromptUrl(prompt) {
2517
- const url = new URL("codex://new");
2518
- url.searchParams.set("prompt", prompt);
2519
- url.searchParams.set("originUrl", window.location.href);
2520
- return url.toString();
2521
- }
2522
-
2523
- function sendCodexPromptToHost(prompt) {
2524
- const hostApi = window.openai || {};
2525
- const payload = {
2526
- originUrl: window.location.href,
2527
- prompt,
2528
- };
2529
- const launchers = [hostApi.sendFollowUpMessage, hostApi.openCodexPrompt];
2530
- for (const launch of launchers) {
2531
- if (typeof launch !== "function") continue;
2532
- try {
2533
- Promise.resolve(launch.call(hostApi, payload)).catch(() => {});
2534
- return true;
2535
- } catch (error) {
2536
- // Try the next host launcher if one exists.
2537
- }
2538
- }
2539
- return false;
2540
- }
2541
-
2542
- async function copyTextToClipboard(value) {
2543
- if (!navigator.clipboard || typeof navigator.clipboard.writeText !== "function") return false;
2544
- await navigator.clipboard.writeText(value);
2545
- return true;
2546
- }
2547
-
2548
- function isLocalPreviewHost() {
2549
- return ["localhost", "127.0.0.1", "::1"].includes(window.location.hostname);
2550
- }
2551
-
2552
- function launchCodexDeepLink(url) {
2553
- if (isLocalPreviewHost()) return false;
2554
- let linkClicked = false;
2555
- try {
2556
- const link = document.createElement("a");
2557
- link.href = url;
2558
- link.target = "_top";
2559
- link.rel = "noopener noreferrer";
2560
- link.style.display = "none";
2561
- document.body.appendChild(link);
2562
- link.click();
2563
- link.remove();
2564
- linkClicked = true;
2565
- } catch (error) {
2566
- // Continue to the top-level location path below when available.
2567
- }
2568
- try {
2569
- if (window.top === window) {
2570
- window.location.assign(url);
2571
- return true;
2572
- }
2573
- } catch (fallbackError) {
2574
- // Ignore navigation failures; the copied prompt is the fallback.
2575
- }
2576
- return linkClicked;
2577
- }
2578
-
2579
- function openCodexPrompt(prompt) {
2580
- const url = codexPromptUrl(prompt);
2581
- void copyTextToClipboard(prompt).catch(() => {});
2582
- const sentToHost = sendCodexPromptToHost(prompt);
2583
- if (!sentToHost) launchCodexDeepLink(url);
2584
- }
2585
-
2586
- function removeMenuSurface(menu, { immediate = false } = {}) {
2587
- if (!menu) return;
2588
- if (immediate) {
2589
- menu.remove();
2590
- return;
2591
- }
2592
- menu.classList.remove("opening");
2593
- menu.classList.add("closing");
2594
- window.setTimeout(() => menu.remove(), 100);
2595
- }
2596
-
2597
- function closeShareMenu(options = {}) {
2598
- const menu = document.getElementById("detail-share-menu-list");
2599
- const button = document.getElementById("detail-share-button");
2600
- removeMenuSurface(menu, options);
2601
- if (button) button.setAttribute("aria-expanded", "false");
2602
- shareMenuOpen = false;
2603
- }
2604
-
2605
- function shareMenuItem(label, iconName, onClick) {
2606
- const button = document.createElement("button");
2607
- button.type = "button";
2608
- button.className = "export-menu-item";
2609
- button.setAttribute("role", "menuitem");
2610
- button.append(lucideIcon(iconName, { width: "16", height: "16" }), document.createTextNode(label));
2611
- button.addEventListener("click", (event) => {
2612
- event.stopPropagation();
2613
- closeShareMenu();
2614
- onClick();
2615
- });
2616
- return button;
2617
- }
2618
-
2619
- function openShareMenu() {
2620
- const root = document.getElementById("detail-share-menu");
2621
- const button = document.getElementById("detail-share-button");
2622
- if (!root || !button) return;
2623
- closeFieldMenus();
2624
- closeChartPicker();
2625
- closeDataFilterMenus();
2626
- closeShareMenu({ immediate: true });
2627
- shareMenuOpen = true;
2628
- button.setAttribute("aria-expanded", "true");
2629
- const menu = document.createElement("div");
2630
- menu.id = "detail-share-menu-list";
2631
- menu.className = "export-menu-list menu-surface opening";
2632
- menu.setAttribute("role", "menu");
2633
- menu.append(
2634
- shareMenuItem("Create HTML file", "fileText", () => openCodexPrompt(exportPrompt("html"))),
2635
- shareMenuItem("Create PDF", "fileDown", () => openCodexPrompt(exportPrompt("pdf"))),
2636
- shareMenuItem("Create Google Doc", "fileText", () => openCodexPrompt(exportPrompt("document"))),
2637
- shareMenuItem("Create Google Slides", "presentation", () => openCodexPrompt(exportPrompt("slides"))),
2638
- );
2639
- menu.addEventListener("click", (event) => event.stopPropagation());
2640
- root.appendChild(menu);
2641
- }
2642
-
2643
- function setupDetailRefreshButton() {
2644
- const button = document.getElementById("detail-refresh-button");
2645
- if (!button) return;
2646
- button.addEventListener("click", (event) => {
2647
- event.stopPropagation();
2648
- closeShareMenu();
2649
- openCodexPrompt(refreshPrompt());
2650
- });
2651
- }
2652
-
2653
- function setupDetailShareMenu() {
2654
- const button = document.getElementById("detail-share-button");
2655
- if (!button) return;
2656
- const label = document.createElement("span");
2657
- label.textContent = "Export";
2658
- button.replaceChildren(label, caretIcon());
2659
- button.addEventListener("click", (event) => {
2660
- event.stopPropagation();
2661
- if (shareMenuOpen) closeShareMenu();
2662
- else openShareMenu();
2663
- });
2664
- }
2665
-
2666
- function chartPreviewSvg(kind) {
2667
- const stroke = "currentColor";
2668
- const fill = "color-mix(in srgb, currentColor 28%, transparent)";
2669
- const fillStrong = "color-mix(in srgb, currentColor 48%, transparent)";
2670
- const grid = `<path d="M8 8v48h80" fill="none" stroke="${stroke}" stroke-width="3" stroke-linecap="round" opacity=".44"/>`;
2671
- const frame = (body) => `<svg viewBox="0 0 96 64" aria-hidden="true" fill="none">${grid}${body}</svg>`;
2672
- const rect = (attrs) => `<rect ${attrs} rx="2" fill="${fill}" stroke="${stroke}" stroke-width="2.5"/>`;
2673
- const segment = (attrs, strong = false) => `<rect ${attrs} rx="1.5" fill="${strong ? fillStrong : fill}" stroke="${stroke}" stroke-width="2.2"/>`;
2674
- if (kind === "bar") {
2675
- return frame(
2676
- `${rect('x="18" y="34" width="7" height="22"')}${rect('x="28" y="24" width="7" height="32"')}` +
2677
- `${rect('x="47" y="42" width="7" height="14"')}${rect('x="57" y="18" width="7" height="38"')}` +
2678
- `${rect('x="76" y="29" width="7" height="27"')}`,
2679
- );
2680
- }
2681
- if (kind === "stacked-bar" || kind === "stacked-bar-100") {
2682
- return frame(
2683
- `${segment('x="20" y="38" width="12" height="18"')}${segment('x="20" y="24" width="12" height="14"', true)}` +
2684
- `${segment('x="45" y="32" width="12" height="24"')}${segment('x="45" y="15" width="12" height="17"', true)}` +
2685
- `${segment('x="70" y="43" width="12" height="13"')}${segment('x="70" y="28" width="12" height="15"', true)}`,
2686
- );
2687
- }
2688
- if (kind === "horizontal-bar") {
2689
- return frame(
2690
- `${rect('x="18" y="16" width="22" height="6"')}${rect('x="18" y="25" width="36" height="6"')}` +
2691
- `${rect('x="18" y="40" width="28" height="6"')}${rect('x="18" y="49" width="53" height="6"')}`,
2692
- );
2693
- }
2694
- if (kind === "horizontal-stacked-bar" || kind === "horizontal-stacked-bar-100") {
2695
- return frame(
2696
- `${segment('x="18" y="18" width="30" height="9"')}${segment('x="48" y="18" width="22" height="9"', true)}` +
2697
- `${segment('x="18" y="39" width="22" height="9"')}${segment('x="40" y="39" width="38" height="9"', true)}`,
2698
- );
2699
- }
2700
- if (kind === "line" || kind === "sparkline") {
2701
- return frame(
2702
- `<path d="M18 45 34 29l14 9 17-21 17 15" stroke="${stroke}" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>` +
2703
- `<circle cx="18" cy="45" r="3" fill="${fillStrong}" stroke="${stroke}" stroke-width="2"/><circle cx="34" cy="29" r="3" fill="${fillStrong}" stroke="${stroke}" stroke-width="2"/><circle cx="65" cy="17" r="3" fill="${fillStrong}" stroke="${stroke}" stroke-width="2"/><circle cx="82" cy="32" r="3" fill="${fillStrong}" stroke="${stroke}" stroke-width="2"/>`,
2704
- );
2705
- }
2706
- if (kind === "area" || kind === "stacked-area") {
2707
- return frame(
2708
- `<path d="M18 47 34 35l14 8 18-23 17 10v26H18Z" fill="${fill}"/>` +
2709
- `<path d="M18 47 34 35l14 8 18-23 17 10" stroke="${stroke}" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>`,
2710
- );
2711
- }
2712
- if (kind === "histogram") {
2713
- return frame(
2714
- [16, 27, 38, 49, 60, 71].map((x, i) => {
2715
- const heights = [12, 22, 35, 29, 18, 9];
2716
- return segment(`x="${x}" y="${56 - heights[i]}" width="9" height="${heights[i]}"`, i === 2 || i === 3);
2717
- }).join(""),
2718
- );
2719
- }
2720
- if (kind === "scatter") {
2721
- return frame(
2722
- [[20, 45], [31, 34], [42, 40], [51, 25], [63, 31], [74, 18], [82, 28]]
2723
- .map(([cx, cy]) => `<circle cx="${cx}" cy="${cy}" r="4" fill="${fillStrong}" stroke="${stroke}" stroke-width="2.5"/>`)
2724
- .join(""),
2725
- );
2726
- }
2727
- if (kind === "heatmap") {
2728
- return frame(
2729
- [0, 1, 2].map((row) =>
2730
- [0, 1, 2, 3].map((col) => {
2731
- const strong = (row + col) % 3 === 0;
2732
- const opacity = [".18", ".3", ".46", ".62"][(row * 2 + col) % 4];
2733
- return `<rect x="${18 + col * 16}" y="${16 + row * 12}" width="13" height="10" rx="1.5" fill="${strong ? fillStrong : fill}" opacity="${opacity}" stroke="${stroke}" stroke-width="1.8"/>`;
2734
- }).join("")
2735
- ).join(""),
2736
- );
2737
- }
2738
- if (kind === "pie") {
2739
- return `<svg viewBox="0 0 96 64" aria-hidden="true" fill="none"><path d="M48 8a24 24 0 1 1-20.8 36" fill="${fill}" stroke="${stroke}" stroke-width="3" stroke-linejoin="round"/><path d="M48 8v24l20.8 12A24 24 0 0 0 48 8Z" fill="${fillStrong}" stroke="${stroke}" stroke-width="3" stroke-linejoin="round"/><path d="M48 32 27.2 44" stroke="${stroke}" stroke-width="3" stroke-linecap="round"/></svg>`;
2740
- }
2741
- if (kind === "funnel") {
2742
- return `<svg viewBox="0 0 96 64" aria-hidden="true" fill="none"><path d="M18 10h60L68 24H28Z" fill="${fillStrong}" stroke="${stroke}" stroke-width="3"/><path d="M30 28h36L58 42H38Z" fill="${fill}" stroke="${stroke}" stroke-width="3"/><path d="M40 46h16l-4 10h-8Z" fill="${fill}" stroke="${stroke}" stroke-width="3"/></svg>`;
2743
- }
2744
- if (kind === "waterfall") {
2745
- return frame(
2746
- `${segment('x="18" y="40" width="12" height="16"')}${segment('x="38" y="26" width="12" height="30"', true)}${segment('x="58" y="34" width="12" height="22"')}${segment('x="78" y="20" width="8" height="36"', true)}`,
2747
- );
2748
- }
2749
- if (kind === "leaderboard") {
2750
- return frame(
2751
- `${segment('x="18" y="17" width="58" height="8"', true)}${segment('x="18" y="31" width="44" height="8"')}${segment('x="18" y="45" width="30" height="8"')}`,
2752
- );
2753
- }
2754
- if (kind === "box-plot") {
2755
- return frame(
2756
- `<path d="M22 24h52M22 42h42" stroke="${stroke}" stroke-width="3" stroke-linecap="round"/><rect x="34" y="18" width="24" height="12" rx="2" fill="${fill}" stroke="${stroke}" stroke-width="2.5"/><rect x="30" y="36" width="22" height="12" rx="2" fill="${fillStrong}" stroke="${stroke}" stroke-width="2.5"/>`,
2757
- );
2758
- }
2759
- return frame(rect('x="20" y="22" width="52" height="28"'));
2760
- }
2761
-
2762
- function caretIcon(extraClass = "") {
2763
- const caret = document.createElement("span");
2764
- caret.className = `dropdown-caret${extraClass ? ` ${extraClass}` : ""}`;
2765
- caret.setAttribute("aria-hidden", "true");
2766
- caret.appendChild(lucideIcon("chevronDown"));
2767
- return caret;
2768
- }
2769
-
2770
- function closeChartPicker() {
2771
- const popover = document.getElementById("chart-picker-popover");
2772
- const button = document.getElementById("chart-picker-button");
2773
- if (popover) {
2774
- popover.hidden = true;
2775
- popover.classList.remove("opening");
2776
- }
2777
- if (button) button.setAttribute("aria-expanded", "false");
2778
- }
2779
-
2780
- function renderChartPicker(root) {
2781
- const picker = document.createElement("div");
2782
- picker.className = "chart-picker";
2783
- const button = document.createElement("button");
2784
- button.id = "chart-picker-button";
2785
- button.className = "chart-picker-button";
2786
- button.type = "button";
2787
- button.setAttribute("aria-haspopup", "true");
2788
- button.setAttribute("aria-expanded", "false");
2789
- button.setAttribute("aria-label", `Chart type ${chartTypeLabel(activeVisualizationType)}`);
2790
- const buttonLabel = document.createElement("span");
2791
- buttonLabel.textContent =
2792
- isDetailDisplayMode()
2793
- ? "Chart type"
2794
- : chartTypeLabel(activeVisualizationType);
2795
- button.append(buttonLabel, caretIcon());
2796
-
2797
- const popover = document.createElement("div");
2798
- popover.id = "chart-picker-popover";
2799
- popover.className = "chart-picker-popover menu-surface";
2800
- popover.hidden = true;
2801
- for (const section of compatibleChartPickerSections(activeDataset())) {
2802
- const sectionEl = document.createElement("section");
2803
- sectionEl.className = "chart-picker-section";
2804
- if (section.title) {
2805
- const heading = document.createElement("h2");
2806
- heading.className = "chart-picker-heading";
2807
- heading.textContent = section.title;
2808
- sectionEl.appendChild(heading);
2809
- }
2810
- const grid = document.createElement("div");
2811
- grid.className = "chart-option-grid";
2812
- for (const option of section.options) {
2813
- const optionButton = document.createElement("button");
2814
- optionButton.type = "button";
2815
- optionButton.className = "chart-option";
2816
- optionButton.setAttribute("aria-pressed", String(option.type === activeVisualizationType));
2817
- const preview = document.createElement("span");
2818
- preview.className = "chart-option-preview";
2819
- preview.innerHTML = chartPreviewSvg(option.preview);
2820
- const label = document.createElement("span");
2821
- label.className = "chart-option-label";
2822
- label.textContent = option.label;
2823
- const check = document.createElement("span");
2824
- check.className = "chart-option-check";
2825
- check.appendChild(lucideIcon("check"));
2826
- optionButton.append(preview, label, check);
2827
- optionButton.addEventListener("click", () => {
2828
- activeVisualizationType = option.type;
2829
- if (viewMode === "table") setViewMode("both");
2830
- closeChartPicker();
2831
- render();
2832
- notifyChartSpecChange();
2833
- });
2834
- grid.appendChild(optionButton);
2835
- }
2836
- sectionEl.appendChild(grid);
2837
- popover.appendChild(sectionEl);
2838
- }
2839
-
2840
- button.addEventListener("click", (event) => {
2841
- event.stopPropagation();
2842
- closeFieldMenus();
2843
- closeDataFilterMenus();
2844
- closeShareMenu();
2845
- const nextHidden = !popover.hidden;
2846
- popover.hidden = nextHidden;
2847
- popover.classList.toggle("opening", !nextHidden);
2848
- button.setAttribute("aria-expanded", String(!nextHidden));
2849
- });
2850
- picker.addEventListener("click", (event) => {
2851
- event.stopPropagation();
2852
- });
2853
- picker.append(button, popover);
2854
- root.appendChild(picker);
2855
- }
2856
-
2857
- function closeFieldMenus(options = {}) {
2858
- document.querySelectorAll(".field-menu").forEach((menu) => removeMenuSurface(menu, options));
2859
- document.querySelectorAll(".field-pill[aria-expanded='true']").forEach((button) => button.setAttribute("aria-expanded", "false"));
2860
- }
2861
-
2862
- function closeDataFilterMenus(options = {}) {
2863
- document.querySelectorAll(".filter-menu-list").forEach((menu) => removeMenuSurface(menu, options));
2864
- document.querySelectorAll(".filter-menu-button[aria-expanded='true']").forEach((button) => button.setAttribute("aria-expanded", "false"));
2865
- }
2866
-
2867
- function setDataFilter(dataset, fieldKey, value) {
2868
- const key = dataFilterKey(dataset, fieldKey);
2869
- if (!value || value === "all") delete selectedDataFilters[key];
2870
- else selectedDataFilters[key] = value;
2871
- pruneDataFilters(dataset);
2872
- closeDataFilterMenus();
2873
- resetVisibleSeries();
2874
- render();
2875
- }
2876
-
2877
- function filterOptionLabel(value) {
2878
- return value === "all" ? "All" : value;
2879
- }
2880
-
2881
- function activeDataFilterCount(dataset = activeDataset()) {
2882
- return dataFilterSpecs(dataset).filter((spec) => selectedDataFilterValue(dataset, spec.key) !== "all").length;
2883
- }
2884
-
2885
- function positionDataFilterMenu(anchor, menu) {
2886
- const boundsLeft = 12;
2887
- const boundsRight = window.innerWidth - 12;
2888
- menu.style.left = "0px";
2889
- menu.style.right = "auto";
2890
- const rect = menu.getBoundingClientRect();
2891
- let leftOffset = 0;
2892
- if (rect.right > boundsRight) leftOffset -= rect.right - boundsRight;
2893
- if (rect.left + leftOffset < boundsLeft) leftOffset += boundsLeft - (rect.left + leftOffset);
2894
- menu.style.left = `${Math.round(leftOffset)}px`;
2895
- }
2896
-
2897
- function openDataFilterMenu(anchor, spec) {
2898
- const existing = anchor.parentElement.querySelector(".filter-menu-list");
2899
- closeFieldMenus();
2900
- closeChartPicker();
2901
- if (existing) {
2902
- closeDataFilterMenus();
2903
- closeShareMenu();
2904
- return;
2905
- }
2906
- closeDataFilterMenus({ immediate: true });
2907
- closeShareMenu({ immediate: true });
2908
- anchor.setAttribute("aria-expanded", "true");
2909
- const menu = document.createElement("div");
2910
- menu.className = "filter-menu-list menu-surface opening";
2911
- menu.setAttribute("role", "menu");
2912
- const current = selectedDataFilterValue(activeDataset(), spec.key);
2913
- const options = ["all", ...filterOptionsForSpec(activeDataset(), spec)];
2914
- for (const option of options) {
2915
- const item = document.createElement("button");
2916
- const selected = option === current;
2917
- item.type = "button";
2918
- item.className = "filter-menu-item";
2919
- item.setAttribute("role", "menuitemradio");
2920
- item.setAttribute("aria-checked", String(selected));
2921
- const label = document.createElement("span");
2922
- label.textContent = filterOptionLabel(option);
2923
- item.appendChild(label);
2924
- if (selected) item.appendChild(lucideIcon("check", { width: "14", height: "14" }));
2925
- item.addEventListener("click", (event) => {
2926
- event.stopPropagation();
2927
- setDataFilter(activeDataset(), spec.key, option);
2928
- });
2929
- menu.appendChild(item);
2930
- }
2931
- menu.addEventListener("click", (event) => event.stopPropagation());
2932
- anchor.parentElement.appendChild(menu);
2933
- positionDataFilterMenu(anchor, menu);
2934
- }
2935
-
2936
- function dataFilterChip(spec) {
2937
- const wrapper = document.createElement("div");
2938
- wrapper.className = "filter-menu";
2939
- const button = document.createElement("button");
2940
- button.type = "button";
2941
- button.className = "filter-menu-button";
2942
- button.setAttribute("aria-haspopup", "menu");
2943
- button.setAttribute("aria-expanded", "false");
2944
- const label = document.createElement("span");
2945
- label.className = "filter-menu-label";
2946
- label.textContent = spec.label;
2947
- const value = document.createElement("span");
2948
- value.className = "filter-menu-value";
2949
- const selectedValue = selectedDataFilterValue(activeDataset(), spec.key);
2950
- if (selectedValue !== "all") {
2951
- label.textContent = `${spec.label}:`;
2952
- value.textContent = filterOptionLabel(selectedValue);
2953
- button.dataset.hasFilter = "true";
2954
- button.append(label, caretIcon());
2955
- } else {
2956
- button.append(label, caretIcon());
2957
- }
2958
- button.addEventListener("click", (event) => {
2959
- event.stopPropagation();
2960
- openDataFilterMenu(button, spec);
2961
- });
2962
- wrapper.appendChild(button);
2963
- return wrapper;
2964
- }
2965
-
2966
- function dataFilterOptionsPanel(spec) {
2967
- const panel = document.createElement("div");
2968
- panel.className = "menu-submenu-panel menu-surface";
2969
- panel.setAttribute("role", "menu");
2970
- const current = selectedDataFilterValue(activeDataset(), spec.key);
2971
- const options = ["all", ...filterOptionsForSpec(activeDataset(), spec)];
2972
- for (const option of options) {
2973
- const selected = option === current;
2974
- const row = menuRow(filterOptionLabel(option), selected ? lucideIcon("check", { width: "14", height: "14" }) : "", () => {
2975
- setDataFilter(activeDataset(), spec.key, option);
2976
- }, { active: selected });
2977
- row.setAttribute("role", "menuitemradio");
2978
- row.setAttribute("aria-checked", String(selected));
2979
- panel.appendChild(row);
2980
- }
2981
- return panel;
2982
- }
2983
-
2984
- function openFiltersMenu(anchor, specs) {
2985
- const existing = anchor.parentElement.querySelector(".filter-menu-list");
2986
- closeFieldMenus();
2987
- closeChartPicker();
2988
- if (existing) {
2989
- closeDataFilterMenus();
2990
- closeShareMenu();
2991
- return;
2992
- }
2993
- closeDataFilterMenus({ immediate: true });
2994
- closeShareMenu({ immediate: true });
2995
- anchor.setAttribute("aria-expanded", "true");
2996
- const menu = document.createElement("div");
2997
- menu.className = "filter-menu-list menu-surface opening";
2998
- menu.setAttribute("role", "menu");
2999
- for (const spec of specs) {
3000
- const selected = selectedDataFilterValue(activeDataset(), spec.key);
3001
- menu.appendChild(
3002
- menuSubmenu(
3003
- spec.label,
3004
- selected !== "all" ? filterOptionLabel(selected) : "",
3005
- dataFilterOptionsPanel(spec),
3006
- { active: selected !== "all" },
3007
- ),
3008
- );
3009
- }
3010
- const count = activeDataFilterCount(activeDataset());
3011
- if (count) {
3012
- menu.appendChild(document.createElement("div")).className = "menu-divider";
3013
- menu.appendChild(menuRow("Reset filters", "", () => {
3014
- selectedDataFilters = {};
3015
- closeDataFilterMenus();
3016
- resetVisibleSeries();
3017
- render();
3018
- }));
3019
- }
3020
- menu.addEventListener("click", (event) => event.stopPropagation());
3021
- anchor.parentElement.appendChild(menu);
3022
- positionDataFilterMenu(anchor, menu);
3023
- }
3024
-
3025
- function filtersMenuChip(specs) {
3026
- const wrapper = document.createElement("div");
3027
- wrapper.className = "filter-menu";
3028
- const button = document.createElement("button");
3029
- button.type = "button";
3030
- button.className = "filter-menu-button";
3031
- button.setAttribute("aria-haspopup", "menu");
3032
- button.setAttribute("aria-expanded", "false");
3033
- const label = document.createElement("span");
3034
- label.className = "filter-menu-label";
3035
- label.textContent = "Filters";
3036
- const count = activeDataFilterCount(activeDataset());
3037
- const value = document.createElement("span");
3038
- value.className = "filter-menu-value";
3039
- value.textContent = activeDataFilterSummary(activeDataset());
3040
- value.title = value.textContent;
3041
- button.setAttribute("aria-label", `Filters ${value.textContent}`);
3042
- if (count) button.dataset.hasFilter = "true";
3043
- button.append(label, caretIcon());
3044
- button.addEventListener("click", (event) => {
3045
- event.stopPropagation();
3046
- openFiltersMenu(button, specs);
3047
- });
3048
- wrapper.appendChild(button);
3049
- return wrapper;
3050
- }
3051
-
3052
- function menuRow(label, value, onClick, options = {}) {
3053
- const button = document.createElement("button");
3054
- button.type = "button";
3055
- button.className = `menu-row${options.active ? " is-active" : ""}${options.danger ? " is-danger" : ""}`;
3056
- const labelEl = document.createElement("span");
3057
- labelEl.textContent = label;
3058
- button.appendChild(labelEl);
3059
- if (value) {
3060
- const valueEl = document.createElement("span");
3061
- valueEl.className = "menu-row-value";
3062
- if (value instanceof Node) valueEl.appendChild(value);
3063
- else valueEl.textContent = value;
3064
- button.appendChild(valueEl);
3065
- }
3066
- button.addEventListener("click", (event) => {
3067
- event.stopPropagation();
3068
- onClick?.();
3069
- });
3070
- return button;
3071
- }
3072
-
3073
- function menuValue(value, iconName = "chevronRight") {
3074
- const wrapper = document.createElement("span");
3075
- wrapper.className = "menu-row-value-with-icon";
3076
- if (value) {
3077
- const valueEl = document.createElement("span");
3078
- valueEl.textContent = value;
3079
- wrapper.appendChild(valueEl);
3080
- }
3081
- wrapper.appendChild(lucideIcon(iconName));
3082
- return wrapper;
3083
- }
3084
-
3085
- function positionSubmenuPanel(wrapper) {
3086
- const panel = wrapper.querySelector(".menu-submenu-panel");
3087
- if (!panel) return;
3088
- panel.style.left = "calc(100% + 8px)";
3089
- panel.style.right = "auto";
3090
- panel.style.visibility = "hidden";
3091
- panel.style.display = "grid";
3092
- const rect = panel.getBoundingClientRect();
3093
- if (rect.right > window.innerWidth - 12) {
3094
- panel.style.left = "auto";
3095
- panel.style.right = "calc(100% + 8px)";
3096
- }
3097
- panel.style.display = "";
3098
- panel.style.visibility = "";
3099
- }
3100
-
3101
- function menuSubmenu(label, value, panel, options = {}) {
3102
- const wrapper = document.createElement("div");
3103
- wrapper.className = "menu-submenu";
3104
- const trigger = menuRow(label, menuValue(value), null, options);
3105
- trigger.classList.add("menu-submenu-trigger");
3106
- trigger.setAttribute("aria-haspopup", "menu");
3107
- trigger.setAttribute("aria-expanded", "false");
3108
- trigger.addEventListener("click", (event) => {
3109
- event.stopPropagation();
3110
- const open = !wrapper.classList.contains("is-open");
3111
- wrapper.parentElement?.querySelectorAll(".menu-submenu.is-open").forEach((item) => {
3112
- if (item === wrapper) return;
3113
- item.classList.remove("is-open");
3114
- item.querySelector(".menu-submenu-trigger")?.setAttribute("aria-expanded", "false");
3115
- });
3116
- wrapper.classList.toggle("is-open", open);
3117
- trigger.setAttribute("aria-expanded", String(open));
3118
- if (open) positionSubmenuPanel(wrapper);
3119
- });
3120
- wrapper.addEventListener("mouseenter", () => positionSubmenuPanel(wrapper));
3121
- wrapper.addEventListener("focusin", () => positionSubmenuPanel(wrapper));
3122
- wrapper.append(trigger, panel);
3123
- return wrapper;
3124
- }
3125
-
3126
- function menuHeader(label, value = "") {
3127
- const row = document.createElement("div");
3128
- row.className = "menu-heading";
3129
- const labelEl = document.createElement("span");
3130
- labelEl.textContent = label;
3131
- row.appendChild(labelEl);
3132
- if (value) {
3133
- const valueEl = document.createElement("span");
3134
- valueEl.textContent = value;
3135
- row.appendChild(valueEl);
3136
- }
3137
- return row;
3138
- }
3139
-
3140
- function setChartField(role, key) {
3141
- if (role === "x") chartConfig.xField = key;
3142
- if (role === "y") chartConfig.yField = key;
3143
- if (role === "size") chartConfig.sizeField = key;
3144
- if (role === "lineStyle") chartConfig.lineStyleField = key;
3145
- if (role === "label") chartConfig.labelField = key;
3146
- if (role === "color") {
3147
- chartConfig.colorField = key;
3148
- revealAllSeriesAfterGroupingChange = true;
3149
- }
3150
- if (role === "x" && !fieldLooksDateLike(activeDataset(), key)) chartConfig.timeUnit = "none";
3151
- if (
3152
- role === "y" &&
3153
- chartConfig.colorField === measureNamesField &&
3154
- compatibleMeasureNameFields(activeDataset()).length <= 1
3155
- ) {
3156
- chartConfig.colorField = "";
3157
- }
3158
- resetVisibleSeries();
3159
- closeFieldMenus();
3160
- render();
3161
- notifyChartSpecChange();
3162
- }
3163
-
3164
- function clearChartFields() {
3165
- selectedDataFilters = {};
3166
- chartConfig = { xField: "x", yField: "y", colorField: "series", lineStyleField: "", labelField: "", sizeField: "", timeUnit: "none", yAggregation: "sum" };
3167
- resetChartSettings(activeDataset());
3168
- resetChartConfig(activeDataset());
3169
- visibleSeries = {};
3170
- resetVisibleSeries();
3171
- closeFieldMenus();
3172
- closeDataFilterMenus();
3173
- closeChartPicker();
3174
- render();
3175
- notifyChartSpecReset();
3176
- }
3177
-
3178
- function fieldIconName(dataset, field) {
3179
- if (fieldLooksDateLike(dataset, field.key)) return "calendar";
3180
- if (field.numeric) return "hash";
3181
- return "type";
3182
- }
3183
-
3184
- function fieldGroupLabel(field) {
3185
- if (field.synthetic) return "Calculated";
3186
- return field.numeric ? "Measures" : "Dimensions";
3187
- }
3188
-
3189
- function fieldIsSelectedForRole(role, key) {
3190
- if (role === "x") return chartConfig.xField === key;
3191
- if (role === "y") return chartConfig.yField === key;
3192
- if (role === "size") return chartConfig.sizeField === key;
3193
- if (role === "label") return chartConfig.labelField === key;
3194
- if (role === "color") return chartConfig.colorField === key;
3195
- return false;
3196
- }
3197
-
3198
- function fieldMenuRow(dataset, role, field, options = {}) {
3199
- const row = menuRow("", "", () => setChartField(role, field.key), {
3200
- active: fieldIsSelectedForRole(role, field.key),
3201
- });
3202
- row.innerHTML = "";
3203
- const label = document.createElement("span");
3204
- label.className = "menu-field-label";
3205
- if (options.icons !== false) {
3206
- const icon = document.createElement("span");
3207
- icon.className = "menu-field-icon";
3208
- icon.appendChild(lucideIcon(fieldIconName(dataset, field), { width: "14", height: "14" }));
3209
- label.appendChild(icon);
3210
- }
3211
- const textEl = document.createElement("span");
3212
- textEl.textContent = field.label;
3213
- label.appendChild(textEl);
3214
- row.appendChild(label);
3215
- return row;
3216
- }
3217
-
3218
- function renderFieldRows(panel, role, options = {}) {
3219
- const dataset = activeDataset();
3220
- const fields = compatibleFieldsForRole(dataset, role);
3221
- const scroll = document.createElement("div");
3222
- scroll.className = "menu-scroll";
3223
- const showSearch = options.search !== false && fields.length > fieldMenuSearchThreshold;
3224
- if (showSearch) {
3225
- const search = document.createElement("input");
3226
- search.className = "menu-search";
3227
- search.type = "search";
3228
- search.placeholder = "Filter...";
3229
- panel.append(search);
3230
- search.addEventListener("input", () => paint(search.value));
3231
- }
3232
- panel.append(scroll);
3233
-
3234
- const paint = (query = "") => {
3235
- const filter = query.trim().toLowerCase();
3236
- scroll.innerHTML = "";
3237
- let lastGroup = "";
3238
- if (options.includeNone) {
3239
- const noneRow = menuRow("None", "", () => setChartField(role, ""), {
3240
- active: !fieldKeyForRole(role),
3241
- });
3242
- scroll.appendChild(noneRow);
3243
- }
3244
- for (const field of fields) {
3245
- if (filter && !field.label.toLowerCase().includes(filter) && !field.key.toLowerCase().includes(filter)) continue;
3246
- const group = fieldGroupLabel(field);
3247
- if (options.grouped !== false && group !== lastGroup) {
3248
- scroll.appendChild(menuHeader(group));
3249
- lastGroup = group;
3250
- }
3251
- scroll.appendChild(fieldMenuRow(dataset, role, field, options));
3252
- }
3253
- };
3254
- paint();
3255
- }
3256
-
3257
- function appendTimeGrainRows(panel, dataset) {
3258
- for (const option of timeUnitOptionsForDataset(dataset)) {
3259
- panel.appendChild(
3260
- menuRow(option, canonicalTimeUnit(option) === chartConfig.timeUnit ? lucideIcon("check", { width: "14", height: "14" }) : "", () => {
3261
- chartConfig.timeUnit = canonicalTimeUnit(option);
3262
- closeFieldMenus();
3263
- render();
3264
- notifyChartSpecChange();
3265
- }, { active: canonicalTimeUnit(option) === chartConfig.timeUnit }),
3266
- );
3267
- }
3268
- }
3269
-
3270
- function appendAggregationRows(panel) {
3271
- for (const option of aggregationOptions) {
3272
- panel.appendChild(
3273
- menuRow(option, canonicalAggregation(option) === chartConfig.yAggregation ? lucideIcon("check", { width: "14", height: "14" }) : "", () => {
3274
- chartConfig.yAggregation = canonicalAggregation(option);
3275
- closeFieldMenus();
3276
- render();
3277
- notifyChartSpecChange();
3278
- }, { active: canonicalAggregation(option) === chartConfig.yAggregation }),
3279
- );
3280
- }
3281
- }
3282
-
3283
- function positionFieldMenu(anchor, menu) {
3284
- const panel = document.querySelector(".app-main");
3285
- const panelRect = panel?.getBoundingClientRect();
3286
- const boundsLeft = Math.max(12, panelRect ? panelRect.left + 12 : 12);
3287
- const boundsRight = Math.min(window.innerWidth - 12, panelRect ? panelRect.right - 12 : window.innerWidth - 12);
3288
- menu.style.left = "0px";
3289
- menu.style.right = "auto";
3290
- menu.style.maxWidth = `${Math.max(180, Math.min(240, boundsRight - boundsLeft))}px`;
3291
-
3292
- const menuRect = menu.getBoundingClientRect();
3293
- let leftOffset = 0;
3294
- if (menuRect.right > boundsRight) {
3295
- leftOffset -= menuRect.right - boundsRight;
3296
- }
3297
- if (menuRect.left + leftOffset < boundsLeft) {
3298
- leftOffset += boundsLeft - (menuRect.left + leftOffset);
3299
- }
3300
- menu.style.left = `${Math.round(leftOffset)}px`;
3301
- }
3302
-
3303
- function openFieldMenu(anchor, spec) {
3304
- const role = spec.role;
3305
- const existing = anchor.parentElement.querySelector(".field-menu");
3306
- closeFieldMenus();
3307
- closeChartPicker();
3308
- closeDataFilterMenus();
3309
- closeShareMenu();
3310
- if (existing) return;
3311
- anchor.setAttribute("aria-expanded", "true");
3312
- const menu = document.createElement("div");
3313
- menu.className = "field-menu menu-surface opening";
3314
- menu.addEventListener("click", (event) => {
3315
- event.stopPropagation();
3316
- });
3317
- const panel = document.createElement("div");
3318
- panel.className = "field-menu-panel";
3319
- const dataset = activeDataset();
3320
-
3321
- if (role === "x") {
3322
- const supportsTime = spec.time !== false;
3323
- renderFieldRows(panel, role, { grouped: false, icons: false });
3324
- if (supportsTime && fieldLooksDateLike(dataset, chartConfig.xField)) {
3325
- panel.appendChild(document.createElement("div")).className = "menu-divider";
3326
- panel.appendChild(menuHeader("Time grain"));
3327
- appendTimeGrainRows(panel, dataset);
3328
- }
3329
- } else if (role === "y") {
3330
- const supportsAggregation = spec.aggregate !== false;
3331
- renderFieldRows(panel, role, { grouped: false, icons: false });
3332
- if (supportsAggregation) {
3333
- panel.appendChild(document.createElement("div")).className = "menu-divider";
3334
- panel.appendChild(menuHeader("Aggregation"));
3335
- appendAggregationRows(panel);
3336
- }
3337
- } else {
3338
- renderFieldRows(panel, role, {
3339
- grouped: false,
3340
- icons: false,
3341
- includeNone: !spec.required,
3342
- search: false,
3343
- });
3344
- }
3345
- menu.appendChild(panel);
3346
- anchor.parentElement.appendChild(menu);
3347
- positionFieldMenu(anchor, menu);
3348
- }
3349
-
3350
- function fieldPill(spec) {
3351
- const role = spec.role;
3352
- const dataset = activeDataset();
3353
- const detailChrome = isDetailDisplayMode();
3354
- const well = document.createElement("div");
3355
- well.className = "field-well";
3356
- const button = document.createElement("button");
3357
- button.type = "button";
3358
- button.className = "field-pill";
3359
- button.setAttribute("aria-haspopup", "menu");
3360
- button.setAttribute("aria-expanded", "false");
3361
- const roleEl = document.createElement("span");
3362
- roleEl.className = "field-pill-role";
3363
- roleEl.textContent = detailChrome ? detailControlLabel(spec) : spec.label;
3364
- button.appendChild(roleEl);
3365
- const field = document.createElement("span");
3366
- field.className = "field-pill-field";
3367
- field.textContent =
3368
- role === "x" ? fieldLabel(dataset, chartConfig.xField, spec.fallback) :
3369
- role === "y" ? fieldLabel(dataset, chartConfig.yField, spec.fallback) :
3370
- role === "size" ? chartConfig.sizeField ? fieldLabel(dataset, chartConfig.sizeField, spec.fallback) : spec.fallback :
3371
- role === "label" ? chartConfig.labelField ? fieldLabel(dataset, chartConfig.labelField, spec.fallback) : spec.fallback :
3372
- role === "time" ? timeUnitLabel(chartConfig.timeUnit) :
3373
- chartConfig.colorField ? fieldLabel(dataset, chartConfig.colorField, spec.fallback) : spec.fallback;
3374
- button.setAttribute("aria-label", `${roleEl.textContent}: ${field.textContent}`);
3375
- if (!detailChrome) button.appendChild(field);
3376
- const modifier = document.createElement("span");
3377
- modifier.className = "field-pill-modifier";
3378
- if (role === "x" && spec.time !== false && fieldLooksDateLike(dataset, chartConfig.xField)) {
3379
- modifier.textContent = timeUnitLabel(chartConfig.timeUnit === "none" ? "" : chartConfig.timeUnit);
3380
- }
3381
- if (role === "y" && spec.aggregate !== false) modifier.textContent = aggregationLabel(chartConfig.yAggregation);
3382
- if (modifier.textContent && !detailChrome) button.appendChild(modifier);
3383
- button.appendChild(caretIcon("field-pill-caret"));
3384
- button.addEventListener("click", (event) => {
3385
- event.stopPropagation();
3386
- openFieldMenu(button, spec);
3387
- });
3388
- well.appendChild(button);
3389
- return well;
3390
- }
3391
-
3392
- function barSettingsHaveSeries(dataset = activeDataset()) {
3393
- if (!chartConfig.colorField) return false;
3394
- return projectedSeriesCount(projectedDataForType(dataset, "bar")) > 1;
3395
- }
3396
-
3397
- function openSettingMenu(anchor, label, value, options, onChange) {
3398
- const existing = anchor.parentElement.querySelector(".field-menu");
3399
- closeFieldMenus();
3400
- closeChartPicker();
3401
- closeDataFilterMenus();
3402
- closeShareMenu();
3403
- if (existing) return;
3404
- anchor.setAttribute("aria-expanded", "true");
3405
- const menu = document.createElement("div");
3406
- menu.className = "field-menu menu-surface opening";
3407
- menu.addEventListener("click", (event) => {
3408
- event.stopPropagation();
3409
- });
3410
- const panel = document.createElement("div");
3411
- panel.className = "field-menu-panel";
3412
- for (const option of options) {
3413
- const selected = option.value === value;
3414
- const row = menuRow(option.label, selected ? lucideIcon("check", { width: "14", height: "14" }) : "", () => {
3415
- onChange(option.value);
3416
- render();
3417
- closeFieldMenus({ immediate: true });
3418
- notifyChartSpecChange();
3419
- }, { active: selected });
3420
- row.setAttribute("role", "menuitemradio");
3421
- row.setAttribute("aria-checked", String(selected));
3422
- panel.appendChild(row);
3423
- }
3424
- menu.appendChild(panel);
3425
- anchor.parentElement.appendChild(menu);
3426
- positionFieldMenu(anchor, menu);
3427
- }
3428
-
3429
- function settingDropdownChip(label, value, options, onChange) {
3430
- const selected = options.find((option) => option.value === value) || options[0];
3431
- const well = document.createElement("div");
3432
- well.className = "field-well chart-setting-control";
3433
- const button = document.createElement("button");
3434
- button.type = "button";
3435
- button.className = "field-pill chart-setting-option";
3436
- button.setAttribute("aria-haspopup", "menu");
3437
- button.setAttribute("aria-expanded", "false");
3438
- const labelEl = document.createElement("span");
3439
- labelEl.className = "field-pill-role";
3440
- labelEl.textContent = label;
3441
- const valueEl = document.createElement("span");
3442
- valueEl.className = "field-pill-field";
3443
- valueEl.textContent = selected?.label || "";
3444
- button.setAttribute("aria-label", `${label}: ${valueEl.textContent}`);
3445
- button.append(labelEl, caretIcon("field-pill-caret"));
3446
- button.addEventListener("click", (event) => {
3447
- event.stopPropagation();
3448
- openSettingMenu(button, label, value, options, onChange);
3449
- });
3450
- well.appendChild(button);
3451
- return well;
3452
- }
3453
-
3454
- function renderExpandedSettingsControls(root) {
3455
- if (!isDetailDisplayMode()) return;
3456
- const wells = document.createElement("div");
3457
- wells.className = "chart-settings-wells";
3458
- if (activeVisualizationType === "bar") {
3459
- wells.appendChild(
3460
- settingDropdownChip(
3461
- "Orientation",
3462
- chartSettings.barOrientation,
3463
- [
3464
- { label: "Vertical", value: "vertical" },
3465
- { label: "Horizontal", value: "horizontal" },
3466
- ],
3467
- (value) => {
3468
- chartSettings.barOrientation = canonicalBarOrientation(value);
3469
- },
3470
- ),
3471
- );
3472
- if (barSettingsHaveSeries()) {
3473
- const selectedMode = chartSettings.barGroupMode === "single" ? "grouped" : chartSettings.barGroupMode;
3474
- wells.appendChild(
3475
- settingDropdownChip(
3476
- "Mode",
3477
- selectedMode,
3478
- [
3479
- { label: "Grouped", value: "grouped" },
3480
- { label: "Stacked", value: "stacked" },
3481
- { label: "100%", value: "stacked100" },
3482
- ],
3483
- (value) => {
3484
- chartSettings.barGroupMode = canonicalBarGroupMode(value);
3485
- },
3486
- ),
3487
- );
3488
- }
3489
- }
3490
- root.appendChild(wells);
3491
- }
3492
-
3493
- function renderControls() {
3494
- const root = document.getElementById("controls");
3495
- const showControls = Boolean(specValue(activeDataset(), "show_controls", payload.show_controls));
3496
- const appMode = isDetailDisplayMode();
3497
- pruneDataFilters(activeDataset());
3498
- root.innerHTML = "";
3499
- root.hidden = !appMode && !showControls;
3500
-
3501
- if (showControls || appMode) {
3502
- const typeGroup = document.createElement("div");
3503
- typeGroup.className = "control-group";
3504
- const typeLabel = document.createElement("span");
3505
- typeLabel.className = "control-label";
3506
- typeLabel.textContent = "View";
3507
- typeGroup.appendChild(typeLabel);
3508
- renderChartPicker(typeGroup);
3509
- root.appendChild(typeGroup);
3510
-
3511
- const wells = document.createElement("div");
3512
- wells.className = "field-wells";
3513
- wells.append(...chartRoleSpecs().map((spec) => fieldPill(spec)));
3514
- root.appendChild(wells);
3515
-
3516
- renderExpandedSettingsControls(root);
3517
-
3518
- const clear = document.createElement("button");
3519
- clear.type = "button";
3520
- clear.className = "clear-button";
3521
- clear.textContent = appMode ? "Reset" : "Clear all";
3522
- clear.addEventListener("click", clearChartFields);
3523
- root.appendChild(clear);
3524
- }
3525
- }
3526
-
3527
- function render() {
3528
- const dataset = activeDataset();
3529
- ensureActiveChartTypeIsSupported(dataset);
3530
- const subtitle = specValue(dataset, "subtitle", payload.subtitle);
3531
- const title = specValue(dataset, "title", payload.title || "Data Analytics chart");
3532
- document.getElementById("title").textContent = text(title);
3533
- document.getElementById("subtitle").textContent = text(subtitle);
3534
- document.getElementById("subtitle").hidden = !subtitle;
3535
- renderDetailHeader(dataset, title, subtitle);
3536
- setViewMode(viewMode);
3537
- setDataMode(dataMode);
3538
- renderQueryControls(dataset);
3539
-
3540
- if (isTablePayload(dataset)) {
3541
- renderTableFallback(dataset);
3542
- } else {
3543
- renderAppChrome(dataset);
3544
- renderControls();
3545
- renderChart();
3546
- }
3547
- applySplitLayout();
3548
- }
3549
-
3550
- window.addEventListener("message", (event) => {
3551
- if (!messageTargetsThisWidget(event.data)) return;
3552
- applyHostState(event.data);
3553
- applyPayload(event.data, event.data);
3554
- });
3555
-
3556
- window.addEventListener("openai:set_globals", (event) => {
3557
- const globals = event.detail && event.detail.globals;
3558
- if (!messageTargetsThisWidget(globals)) return;
3559
- applyHostState(globals);
3560
- if (globals && applyPayload(globals.toolOutput, globals)) return;
3561
- if (!hasExternalPayload) applyPayload(currentHostPayload(), null);
3562
- });
3563
-
3564
- setupDisplayModeControls();
3565
- setupViewModeControls();
3566
- setupDataModeControls();
3567
- setupMenuDismissal();
3568
- setupAppChromeControls();
3569
- setupDetailRefreshButton();
3570
- setupDetailShareMenu();
3571
- setupSplitResizer();
3572
- window.addEventListener("resize", applySplitLayout);
3573
- if (!applyPayload(currentHostPayload())) {
3574
- const initialPayload = window.openai ? hostedEmptyPayload() : fallbackPayload;
3575
- normalizePayload(initialPayload);
3576
- lastPayloadSignature = payloadSignature(initialPayload);
3577
- render();
3578
- }
3579
-
3580
- let hostPollAttempts = 0;
3581
- const hostPoll = window.setInterval(() => {
3582
- hostPollAttempts += 1;
3583
- setDisplayMode(currentHostDisplayMode());
3584
- if (!hasExternalPayload) {
3585
- applyPayload(currentHostPayload(), null);
3586
- }
3587
- if (hostPollAttempts >= 80) window.clearInterval(hostPoll);
3588
- }, 250);