create-yss-spec 1.1.1 → 1.1.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 (257) hide show
  1. package/package.json +1 -1
  2. package/template/.agents/skills/high-fidelity-html-prototype/SKILL.md +102 -0
  3. package/template/.codex/skills/data-analytics/.app.json +84 -0
  4. package/template/.codex/skills/data-analytics/.codex-plugin/plugin.json +66 -0
  5. package/template/.codex/skills/data-analytics/.mcp.json +30 -0
  6. package/template/.codex/skills/data-analytics/AGENTS.md +20 -0
  7. package/template/.codex/skills/data-analytics/DEPENDENCIES.MD +27 -0
  8. package/template/.codex/skills/data-analytics/README.md +60 -0
  9. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html +16 -0
  10. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part001 +1 -0
  11. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part002 +1 -0
  12. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part003 +1 -0
  13. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part004 +1 -0
  14. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part005 +1 -0
  15. package/template/.codex/skills/data-analytics/assets/datascience-artifact-widget.html.gz.b64.part006 +1 -0
  16. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html +16 -0
  17. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part001 +1 -0
  18. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part002 +1 -0
  19. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part003 +1 -0
  20. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part004 +1 -0
  21. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part005 +1 -0
  22. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part006 +1 -0
  23. package/template/.codex/skills/data-analytics/assets/datascience-chart-widget.html.gz.b64.part007 +1 -0
  24. package/template/.codex/skills/data-analytics/assets/datascience-small.svg +5 -0
  25. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html +16 -0
  26. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part001 +1 -0
  27. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part002 +1 -0
  28. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part003 +1 -0
  29. package/template/.codex/skills/data-analytics/assets/datascience-table-widget.html.gz.b64.part004 +1 -0
  30. package/template/.codex/skills/data-analytics/assets/datascience.png +0 -0
  31. package/template/.codex/skills/data-analytics/assets/datascience.svg +10 -0
  32. package/template/.codex/skills/data-analytics/mcp/server.cjs +2963 -0
  33. package/template/.codex/skills/data-analytics/package-lock.json +3048 -0
  34. package/template/.codex/skills/data-analytics/package.json +43 -0
  35. package/template/.codex/skills/data-analytics/scripts/normalize-widget-assets.mjs +75 -0
  36. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +160 -0
  37. package/template/.codex/skills/data-analytics/skills/analyze-data-quality/agents/openai.yaml +6 -0
  38. package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +148 -0
  39. package/template/.codex/skills/data-analytics/skills/build-dashboard/agents/openai.yaml +6 -0
  40. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/bi-platform-dashboard.md +18 -0
  41. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/html-dashboard.md +24 -0
  42. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/mcp-artifact-dashboard.md +71 -0
  43. package/template/.codex/skills/data-analytics/skills/build-dashboard/specifications/streamlit-dashboard.md +85 -0
  44. package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +207 -0
  45. package/template/.codex/skills/data-analytics/skills/build-report/agents/openai.yaml +6 -0
  46. package/template/.codex/skills/data-analytics/skills/build-report/assets/executive-report-shell.html +70 -0
  47. package/template/.codex/skills/data-analytics/skills/build-report/assets/technical-report-shell.html +66 -0
  48. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/SKILL.md +118 -0
  49. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/agents/openai.yaml +6 -0
  50. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/__init__.py +1 -0
  51. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/cli.py +57 -0
  52. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/constants.py +61 -0
  53. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/docx_writer.py +362 -0
  54. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/html_parser.py +829 -0
  55. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/model.py +30 -0
  56. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/plan.py +129 -0
  57. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/quality.py +374 -0
  58. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/rendering.py +613 -0
  59. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/table_utils.py +54 -0
  60. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc/utils.py +16 -0
  61. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-doc/scripts/report_to_google_doc_plan.py +9 -0
  62. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/SKILL.md +78 -0
  63. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/agents/openai.yaml +6 -0
  64. package/template/.codex/skills/data-analytics/skills/build-report/report-to-google-slides/scripts/report_to_google_slides.py +2379 -0
  65. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/SKILL.md +88 -0
  66. package/template/.codex/skills/data-analytics/skills/build-report/report-to-pdf/agents/openai.yaml +6 -0
  67. package/template/.codex/skills/data-analytics/skills/build-report/specifications/executive-report.md +97 -0
  68. package/template/.codex/skills/data-analytics/skills/build-report/specifications/mcp-app-report.md +59 -0
  69. package/template/.codex/skills/data-analytics/skills/build-report/specifications/technical-report.md +75 -0
  70. package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +103 -0
  71. package/template/.codex/skills/data-analytics/skills/design-kpis/agents/openai.yaml +6 -0
  72. package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +68 -0
  73. package/template/.codex/skills/data-analytics/skills/gather-business-context/agents/openai.yaml +6 -0
  74. package/template/.codex/skills/data-analytics/skills/index/SKILL.md +251 -0
  75. package/template/.codex/skills/data-analytics/skills/index/agents/openai.yaml +6 -0
  76. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +131 -0
  77. package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/agents/openai.yaml +6 -0
  78. package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +141 -0
  79. package/template/.codex/skills/data-analytics/skills/kpi-reporting/agents/openai.yaml +6 -0
  80. package/template/.codex/skills/data-analytics/skills/kpi-reporting/references/report-templates.md +32 -0
  81. package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +106 -0
  82. package/template/.codex/skills/data-analytics/skills/market-sizing/agents/openai.yaml +6 -0
  83. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +130 -0
  84. package/template/.codex/skills/data-analytics/skills/metric-diagnostics/agents/openai.yaml +6 -0
  85. package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +141 -0
  86. package/template/.codex/skills/data-analytics/skills/product-business-analysis/agents/openai.yaml +6 -0
  87. package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +178 -0
  88. package/template/.codex/skills/data-analytics/skills/spreadsheets/agents/openai.yaml +9 -0
  89. package/template/.codex/skills/data-analytics/skills/spreadsheets/assets/file-spreadsheet.png +0 -0
  90. package/template/.codex/skills/data-analytics/skills/spreadsheets/charts.md +31 -0
  91. package/template/.codex/skills/data-analytics/skills/spreadsheets/references/artifact_tool_api.md +466 -0
  92. package/template/.codex/skills/data-analytics/skills/spreadsheets/style_guidelines.md +99 -0
  93. package/template/.codex/skills/data-analytics/skills/user-context/SKILL.md +195 -0
  94. package/template/.codex/skills/data-analytics/skills/user-context/agents/openai.yaml +6 -0
  95. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/automation-config.md +26 -0
  96. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/source-category-config.json +51 -0
  97. package/template/.codex/skills/data-analytics/skills/user-context/plugin-author-config/user-context-config.md +66 -0
  98. package/template/.codex/skills/data-analytics/skills/user-context/references/automation.md +69 -0
  99. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-examples.md +204 -0
  100. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding-state-template.json +87 -0
  101. package/template/.codex/skills/data-analytics/skills/user-context/references/onboarding.md +497 -0
  102. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/connector-playbook.md +74 -0
  103. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/setup.md +65 -0
  104. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/skill-template.md +160 -0
  105. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/source-intake.md +75 -0
  106. package/template/.codex/skills/data-analytics/skills/user-context/references/semantic-layer/weekly-polling-automation.md +96 -0
  107. package/template/.codex/skills/data-analytics/skills/user-context/references/source-category-runtime.md +263 -0
  108. package/template/.codex/skills/data-analytics/skills/user-context/scripts/data_analytics_preflight.py +1460 -0
  109. package/template/.codex/skills/data-analytics/skills/user-context/scripts/init_user_context_state.py +128 -0
  110. package/template/.codex/skills/data-analytics/skills/user-context/scripts/reset_user_context_state.py +101 -0
  111. package/template/.codex/skills/data-analytics/skills/user-context/scripts/validate_user_context_preflight.py +499 -0
  112. package/template/.codex/skills/data-analytics/skills/user-context/tests/test_state_helpers.py +978 -0
  113. package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +190 -0
  114. package/template/.codex/skills/data-analytics/skills/validate-data/agents/openai.yaml +6 -0
  115. package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +157 -0
  116. package/template/.codex/skills/data-analytics/skills/visualize-data/agents/openai.yaml +6 -0
  117. package/template/.codex/skills/data-analytics/skills/visualize-data/references/seaborn-templates.md +774 -0
  118. package/template/.codex/skills/data-analytics/src/DESIGN.md +222 -0
  119. package/template/.codex/skills/data-analytics/src/analytics-app/App.tsx +3666 -0
  120. package/template/.codex/skills/data-analytics/src/analytics-app/analytics-layout.test.mjs +136 -0
  121. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartFrame.tsx +54 -0
  122. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartLegend.tsx +96 -0
  123. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartRenderer.tsx +1647 -0
  124. package/template/.codex/skills/data-analytics/src/analytics-app/charting/ChartTooltip.tsx +245 -0
  125. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-app-helpers.tsx +462 -0
  126. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-capabilities.ts +107 -0
  127. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-compatibility.ts +164 -0
  128. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-contract.ts +192 -0
  129. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-theme.ts +203 -0
  130. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-tokens.css +619 -0
  131. package/template/.codex/skills/data-analytics/src/analytics-app/charting/chart-transforms.ts +402 -0
  132. package/template/.codex/skills/data-analytics/src/analytics-app/fonts/SystemSansVariableVF.woff2 +0 -0
  133. package/template/.codex/skills/data-analytics/src/analytics-app/imageExport.ts +370 -0
  134. package/template/.codex/skills/data-analytics/src/analytics-app/layout/AnalyticsLayoutCanvas.tsx +536 -0
  135. package/template/.codex/skills/data-analytics/src/analytics-app/layout/RichMarkdown.tsx +681 -0
  136. package/template/.codex/skills/data-analytics/src/analytics-app/layout/analyticsLayoutCore.ts +164 -0
  137. package/template/.codex/skills/data-analytics/src/analytics-app/main.tsx +21 -0
  138. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/chart_contract.py +472 -0
  139. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/check_analytics_app_runtime_links.py +75 -0
  140. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/design_contract.py +142 -0
  141. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/package_utils.py +142 -0
  142. package/template/.codex/skills/data-analytics/src/analytics-app/scripts/tests/test_package_utils.py +377 -0
  143. package/template/.codex/skills/data-analytics/src/analytics-app/styles.css +3468 -0
  144. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.d.ts +44 -0
  145. package/template/.codex/skills/data-analytics/src/analytics-app/tables/DataTable.jsx +639 -0
  146. package/template/.codex/skills/data-analytics/src/analytics-app/tables/data-table.css +327 -0
  147. package/template/.codex/skills/data-analytics/src/analytics-app/tokens.css +225 -0
  148. package/template/.codex/skills/data-analytics/src/analytics-app/types.ts +204 -0
  149. package/template/.codex/skills/data-analytics/src/analytics-app-core.md +85 -0
  150. package/template/.codex/skills/data-analytics/src/codex-style-contract.md +30 -0
  151. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.html +12 -0
  152. package/template/.codex/skills/data-analytics/src/datascience-artifact-widget.jsx +1008 -0
  153. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.css +2117 -0
  154. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.html +122 -0
  155. package/template/.codex/skills/data-analytics/src/datascience-chart-widget.js +3588 -0
  156. package/template/.codex/skills/data-analytics/src/datascience-table-widget.css +483 -0
  157. package/template/.codex/skills/data-analytics/src/datascience-table-widget.html +32 -0
  158. package/template/.codex/skills/data-analytics/src/datascience-table-widget.js +333 -0
  159. package/template/.codex/skills/data-analytics/src/mcp-host.js +153 -0
  160. package/template/.codex/skills/data-analytics/src/recharts-config.js +315 -0
  161. package/template/.codex/skills/data-analytics/src/recharts-renderer.jsx +294 -0
  162. package/template/.codex/skills/data-analytics/src/sql-source-view.css +33 -0
  163. package/template/.codex/skills/data-analytics/src/sql-source-view.js +157 -0
  164. package/template/.codex/skills/data-analytics/src/styles/codex-theme.css +121 -0
  165. package/template/.codex/skills/data-analytics/src/table-renderer.jsx +28 -0
  166. package/template/.codex/skills/data-analytics/tests/chart-transforms.test.mjs +87 -0
  167. package/template/.codex/skills/data-analytics/tests/funnel-smoke.html +105 -0
  168. package/template/.codex/skills/data-analytics/tests/inline-widget-compare.html +233 -0
  169. package/template/.codex/skills/data-analytics/tests/mcp-server.test.mjs +1500 -0
  170. package/template/.codex/skills/data-analytics/tests/native-style-contract.test.mjs +219 -0
  171. package/template/.codex/skills/data-analytics/tests/recharts-config.test.mjs +144 -0
  172. package/template/.codex/skills/data-analytics/tests/recharts-renderer.test.mjs +11 -0
  173. package/template/.codex/skills/data-analytics/tests/rich-markdown.test.mjs +54 -0
  174. package/template/.codex/skills/data-analytics/tests/widget-render-harness.html +321 -0
  175. package/template/.codex/skills/data-analytics/tsconfig.json +19 -0
  176. package/template/.codex/skills/data-analytics/vite.config.ts +46 -0
  177. package/template/.codex/skills/high-fidelity-html-prototype/SKILL.md +102 -0
  178. package/template/.codex/skills/high-fidelity-html-prototype/agents/openai.yaml +5 -0
  179. package/template/.codex/skills/product-design/.app.json +7 -0
  180. package/template/.codex/skills/product-design/.codex-plugin/plugin.json +50 -0
  181. package/template/.codex/skills/product-design/README.md +50 -0
  182. package/template/.codex/skills/product-design/agents/openai.yaml +4 -0
  183. package/template/.codex/skills/product-design/assets/composerIcon.svg +9 -0
  184. package/template/.codex/skills/product-design/assets/logo.png +0 -0
  185. package/template/.codex/skills/product-design/package.json +11 -0
  186. package/template/.codex/skills/product-design/references/browser-order.md +9 -0
  187. package/template/.codex/skills/product-design/references/communication-protocol.md +45 -0
  188. package/template/.codex/skills/product-design/references/critical-overrides.md +58 -0
  189. package/template/.codex/skills/product-design/references/local-prototype-preflight.md +16 -0
  190. package/template/.codex/skills/product-design/scripts/bootstrap-prototype.mjs +147 -0
  191. package/template/.codex/skills/product-design/skills/audit/SKILL.md +159 -0
  192. package/template/.codex/skills/product-design/skills/audit/agents/openai.yaml +6 -0
  193. package/template/.codex/skills/product-design/skills/audit/references/design-audit-framework.md +73 -0
  194. package/template/.codex/skills/product-design/skills/design-qa/SKILL.md +133 -0
  195. package/template/.codex/skills/product-design/skills/design-qa/agents/openai.yaml +6 -0
  196. package/template/.codex/skills/product-design/skills/design-qa/references/qa-rubric.md +61 -0
  197. package/template/.codex/skills/product-design/skills/get-context/SKILL.md +81 -0
  198. package/template/.codex/skills/product-design/skills/get-context/agents/openai.yaml +6 -0
  199. package/template/.codex/skills/product-design/skills/ideate/SKILL.md +179 -0
  200. package/template/.codex/skills/product-design/skills/ideate/agents/openai.yaml +6 -0
  201. package/template/.codex/skills/product-design/skills/image-to-code/SKILL.md +105 -0
  202. package/template/.codex/skills/product-design/skills/image-to-code/agents/openai.yaml +6 -0
  203. package/template/.codex/skills/product-design/skills/index/SKILL.md +126 -0
  204. package/template/.codex/skills/product-design/skills/index/agents/openai.yaml +6 -0
  205. package/template/.codex/skills/product-design/skills/prototype/SKILL.md +128 -0
  206. package/template/.codex/skills/product-design/skills/prototype/agents/openai.yaml +6 -0
  207. package/template/.codex/skills/product-design/skills/prototype/references/existing-codebase-edits.md +18 -0
  208. package/template/.codex/skills/product-design/skills/research/SKILL.md +92 -0
  209. package/template/.codex/skills/product-design/skills/research/agents/openai.yaml +6 -0
  210. package/template/.codex/skills/product-design/skills/share/SKILL.md +41 -0
  211. package/template/.codex/skills/product-design/skills/share/agents/openai.yaml +6 -0
  212. package/template/.codex/skills/product-design/skills/url-to-code/SKILL.md +124 -0
  213. package/template/.codex/skills/product-design/skills/url-to-code/agents/openai.yaml +6 -0
  214. package/template/.codex/skills/product-design/skills/user-context/SKILL.md +147 -0
  215. package/template/.codex/skills/product-design/skills/user-context/agents/openai.yaml +6 -0
  216. package/template/.codex/skills/product-design/skills/user-context/plugin-author-config/user-context-template.md +63 -0
  217. package/template/.codex/skills/product-design/skills/user-context/references/onboarding.md +66 -0
  218. package/template/.codex/skills/product-design/skills/user-context/scripts/init_user_context.py +86 -0
  219. package/template/.codex/skills/product-design/skills/user-context/scripts/user_context_preflight.py +204 -0
  220. package/template/.codex/skills/product-design/templates/prototype/AGENTS.md +7 -0
  221. package/template/.codex/skills/product-design/templates/prototype/index.html +12 -0
  222. package/template/.codex/skills/product-design/templates/prototype/package.json +18 -0
  223. package/template/.codex/skills/product-design/templates/prototype/src/App.jsx +5 -0
  224. package/template/.codex/skills/product-design/templates/prototype/src/main.jsx +10 -0
  225. package/template/.codex/skills/product-design/templates/prototype/src/styles.css +13 -0
  226. package/template/.codex/skills/product-design/templates/prototype/vite.config.mjs +14 -0
  227. package/template/.codex/skills/product-design-prototype/SKILL.md +2 -4
  228. package/template/.codex/skills/prototype-review/SKILL.md +7 -7
  229. package/template/.codex/skills/yss-design-system/SKILL.md +4 -2
  230. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +10 -7
  231. package/template/.codex/skills/yss-product-lifecycle/references/artifact-checklist.md +1 -0
  232. package/template/.codex/skills/yss-product-lifecycle/references/stage-routing.md +1 -1
  233. package/template/.codex/skills/yss-ui/SKILL.md +4 -10
  234. package/template/.codex/skills/yss-ui/references/quick-recipes.md +1 -1
  235. package/template/AGENTS.md +9 -3
  236. package/template/CONTEXT.md +1 -0
  237. package/template/docs/adr/adr-product-design-prototype-entrypoint.md +54 -0
  238. package/template/docs/api/templates/openapi-draft-review-checklist.md +5 -1
  239. package/template/docs/design/README.md +13 -6
  240. package/template/docs/design/prototypes/.gitkeep +1 -0
  241. package/template/docs/design/templates/interaction-spec-template.md +8 -3
  242. package/template/docs/design/templates/product-overview-design-template.md +34 -5
  243. package/template/docs/design/templates/prototype-confirmation-template.md +57 -0
  244. package/template/docs/design/templates/prototype-review-checklist.md +7 -3
  245. package/template/docs/design/templates/state-matrix-template.md +1 -1
  246. package/template/docs/process/harness-executive-blueprint.md +6 -1
  247. package/template/docs/process/harness-process-tailoring.md +3 -3
  248. package/template/docs/process/harness-work-unit-map.md +1 -1
  249. package/template/docs/process/lifecycle-artifact-map.md +7 -5
  250. package/template/docs/process/product-prototype-workflow.md +82 -0
  251. package/template/docs/templates/requirement-freeze-template.md +4 -3
  252. package/template/docs/user-guide/product-lifecycle-workflow.md +2 -2
  253. package/template/docs/user-guide/product-rd-lifecycle-best-practices.md +54 -24
  254. package/template/.codex/skills/component-story-prototype/SKILL.md +0 -55
  255. package/template/.codex/skills/component-story-prototype/agents/openai.yaml +0 -4
  256. package/template/.codex/skills/mock-api-prototype/SKILL.md +0 -48
  257. package/template/.codex/skills/mock-api-prototype/agents/openai.yaml +0 -4
@@ -0,0 +1,681 @@
1
+ import {
2
+ $convertFromMarkdownString,
3
+ $convertToMarkdownString,
4
+ BOLD_STAR,
5
+ HEADING,
6
+ INLINE_CODE,
7
+ LINK,
8
+ ORDERED_LIST,
9
+ QUOTE,
10
+ UNORDERED_LIST,
11
+ type Transformer
12
+ } from "@lexical/markdown";
13
+ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
14
+ import { LexicalComposer } from "@lexical/react/LexicalComposer";
15
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
16
+ import { ContentEditable } from "@lexical/react/LexicalContentEditable";
17
+ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
18
+ import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
19
+ import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
20
+ import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
21
+ import { LinkNode } from "@lexical/link";
22
+ import { ListItemNode, ListNode } from "@lexical/list";
23
+ import { HeadingNode, QuoteNode } from "@lexical/rich-text";
24
+ import {
25
+ $getSelection,
26
+ $isRangeSelection,
27
+ COMMAND_PRIORITY_HIGH,
28
+ KEY_ESCAPE_COMMAND,
29
+ PASTE_COMMAND
30
+ } from "lexical";
31
+ import {
32
+ type CSSProperties,
33
+ type KeyboardEvent as ReactKeyboardEvent,
34
+ type MouseEvent as ReactMouseEvent,
35
+ type ReactNode,
36
+ useCallback,
37
+ useEffect,
38
+ useId,
39
+ useMemo,
40
+ useRef,
41
+ useState
42
+ } from "react";
43
+
44
+ const MARKDOWN_TRANSFORMERS: Transformer[] = [
45
+ HEADING,
46
+ QUOTE,
47
+ UNORDERED_LIST,
48
+ ORDERED_LIST,
49
+ BOLD_STAR,
50
+ INLINE_CODE,
51
+ LINK
52
+ ];
53
+ const MARKDOWN_COMMIT_DEBOUNCE_MS = 180;
54
+
55
+ type RichMarkdownFocusPoint = {
56
+ clientX: number;
57
+ clientY: number;
58
+ };
59
+
60
+ type CaretPositionDocument = Document & {
61
+ caretPositionFromPoint?: (
62
+ x: number,
63
+ y: number
64
+ ) => { offset: number; offsetNode: Node } | null;
65
+ caretRangeFromPoint?: (x: number, y: number) => Range | null;
66
+ };
67
+
68
+ export type RichMarkdownActivation = "click" | "doubleClick" | "manual";
69
+ export type RichMarkdownVariant = "cellHeader" | "pageHeader" | "reportBlock";
70
+
71
+ export type RichMarkdownProps = {
72
+ activation?: RichMarkdownActivation;
73
+ ariaLabel: string;
74
+ className?: string;
75
+ isEditMode?: boolean;
76
+ markdown: string;
77
+ minRows?: number;
78
+ onCommit?: (nextMarkdown: string) => void;
79
+ onMarkdownChange: (nextMarkdown: string) => void;
80
+ onRequestEditMode?: () => void;
81
+ placeholder: string;
82
+ variant?: RichMarkdownVariant;
83
+ };
84
+
85
+ const richMarkdownTheme = {
86
+ heading: {
87
+ h1: "rich-markdown-heading rich-markdown-heading-1",
88
+ h2: "rich-markdown-heading rich-markdown-heading-2",
89
+ h3: "rich-markdown-heading rich-markdown-heading-3"
90
+ },
91
+ link: "rich-markdown-link",
92
+ list: {
93
+ listitem: "rich-markdown-list-item",
94
+ ol: "rich-markdown-list",
95
+ ul: "rich-markdown-list"
96
+ },
97
+ quote: "rich-markdown-quote",
98
+ text: {
99
+ bold: "rich-markdown-bold",
100
+ code: "rich-markdown-inline-code"
101
+ }
102
+ };
103
+
104
+ function renderMarkdownInline(text: string) {
105
+ const parts = text.split(/(`[^`]+`|\*\*[^*]+\*\*|\[[^\]]+\]\([^)]+\))/g);
106
+ return parts.map((part, index) => {
107
+ if (part.startsWith("**") && part.endsWith("**") && part.length > 4) {
108
+ return <strong key={`${part}-${index}`}>{part.slice(2, -2)}</strong>;
109
+ }
110
+ if (part.startsWith("`") && part.endsWith("`") && part.length > 2) {
111
+ return <code key={`${part}-${index}`}>{part.slice(1, -1)}</code>;
112
+ }
113
+ const linkMatch = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(part);
114
+ if (linkMatch) {
115
+ return (
116
+ <a href={linkMatch[2]} key={`${part}-${index}`} rel="noreferrer" target="_blank">
117
+ {linkMatch[1]}
118
+ </a>
119
+ );
120
+ }
121
+ return <span key={`${part}-${index}`}>{part}</span>;
122
+ });
123
+ }
124
+
125
+ type MarkdownTableAlignment = "center" | "left" | "right";
126
+
127
+ function splitMarkdownTableRow(line: string) {
128
+ const trimmed = line.trim();
129
+ const withoutLeadingPipe = trimmed.startsWith("|") ? trimmed.slice(1) : trimmed;
130
+ const withoutOuterPipes = withoutLeadingPipe.endsWith("|")
131
+ ? withoutLeadingPipe.slice(0, -1)
132
+ : withoutLeadingPipe;
133
+ return withoutOuterPipes
134
+ .split(/(?<!\\)\|/g)
135
+ .map((cell) => cell.replace(/\\\|/g, "|").trim());
136
+ }
137
+
138
+ function parseMarkdownTableSeparator(line: string): MarkdownTableAlignment[] | null {
139
+ const cells = splitMarkdownTableRow(line);
140
+ if (!cells.length) return null;
141
+ const alignments: MarkdownTableAlignment[] = [];
142
+ for (const cell of cells) {
143
+ if (!/^:?-{3,}:?$/.test(cell)) return null;
144
+ if (cell.startsWith(":") && cell.endsWith(":")) alignments.push("center");
145
+ else if (cell.endsWith(":")) alignments.push("right");
146
+ else alignments.push("left");
147
+ }
148
+ return alignments;
149
+ }
150
+
151
+ function isMarkdownTableStart(lines: string[], index: number) {
152
+ const header = lines[index]?.trim() ?? "";
153
+ const separator = lines[index + 1]?.trim() ?? "";
154
+ return header.includes("|") && Boolean(parseMarkdownTableSeparator(separator));
155
+ }
156
+
157
+ function isMarkdownTableRow(line: string) {
158
+ const trimmed = line.trim();
159
+ return Boolean(trimmed) && trimmed.includes("|");
160
+ }
161
+
162
+ export function normalizeInlineOrderedListMarkers(markdown: string) {
163
+ return markdown
164
+ .replace(/\u00a0/g, " ")
165
+ .split(/\r?\n/)
166
+ .map((line) => {
167
+ if (!/^\s*\d+[.)]\s+/.test(line)) return line;
168
+ return line.replace(/([^\n])[\t ]+(?=\d+[.)]\s+)/g, "$1\n");
169
+ })
170
+ .join("\n");
171
+ }
172
+
173
+ export function RichMarkdownPreview({ markdown }: { markdown: string }) {
174
+ const lines = normalizeInlineOrderedListMarkers(markdown).split(/\r?\n/);
175
+ const nodes: ReactNode[] = [];
176
+ let paragraph: string[] = [];
177
+ let listItems: string[] = [];
178
+ let orderedListItems: string[] = [];
179
+ let blockquote: string[] = [];
180
+
181
+ function flushParagraph() {
182
+ if (!paragraph.length) return;
183
+ nodes.push(<p key={`p-${nodes.length}`}>{renderMarkdownInline(paragraph.join(" "))}</p>);
184
+ paragraph = [];
185
+ }
186
+
187
+ function flushList() {
188
+ if (!listItems.length) return;
189
+ nodes.push(
190
+ <ul key={`ul-${nodes.length}`}>
191
+ {listItems.map((item, index) => (
192
+ <li key={`${item}-${index}`}>{renderMarkdownInline(item)}</li>
193
+ ))}
194
+ </ul>
195
+ );
196
+ listItems = [];
197
+ }
198
+
199
+ function flushOrderedList() {
200
+ if (!orderedListItems.length) return;
201
+ nodes.push(
202
+ <ol key={`ol-${nodes.length}`}>
203
+ {orderedListItems.map((item, index) => (
204
+ <li key={`${item}-${index}`}>{renderMarkdownInline(item)}</li>
205
+ ))}
206
+ </ol>
207
+ );
208
+ orderedListItems = [];
209
+ }
210
+
211
+ function flushBlockquote() {
212
+ if (!blockquote.length) return;
213
+ nodes.push(
214
+ <blockquote key={`blockquote-${nodes.length}`}>
215
+ {blockquote.map((item, index) => (
216
+ <p key={`${item}-${index}`}>{renderMarkdownInline(item)}</p>
217
+ ))}
218
+ </blockquote>
219
+ );
220
+ blockquote = [];
221
+ }
222
+
223
+ function flushTextRuns() {
224
+ flushParagraph();
225
+ flushList();
226
+ flushOrderedList();
227
+ flushBlockquote();
228
+ }
229
+
230
+ for (let index = 0; index < lines.length; index += 1) {
231
+ const line = lines[index];
232
+ const trimmed = line.trim();
233
+ if (!trimmed) {
234
+ flushParagraph();
235
+ flushBlockquote();
236
+ continue;
237
+ }
238
+ if (isMarkdownTableStart(lines, index)) {
239
+ flushTextRuns();
240
+ const headers = splitMarkdownTableRow(lines[index]);
241
+ const alignments = parseMarkdownTableSeparator(lines[index + 1]) ?? [];
242
+ const rows: string[][] = [];
243
+ index += 2;
244
+ while (index < lines.length && isMarkdownTableRow(lines[index])) {
245
+ rows.push(splitMarkdownTableRow(lines[index]));
246
+ index += 1;
247
+ }
248
+ index -= 1;
249
+ nodes.push(
250
+ <div className="rich-markdown-table-scroll" key={`table-${nodes.length}`}>
251
+ <table className="rich-markdown-table">
252
+ <thead>
253
+ <tr>
254
+ {headers.map((header, headerIndex) => (
255
+ <th
256
+ key={`${header}-${headerIndex}`}
257
+ style={{ textAlign: alignments[headerIndex] ?? "left" }}
258
+ >
259
+ {renderMarkdownInline(header)}
260
+ </th>
261
+ ))}
262
+ </tr>
263
+ </thead>
264
+ <tbody>
265
+ {rows.map((row, rowIndex) => (
266
+ <tr key={`row-${rowIndex}`}>
267
+ {headers.map((_, cellIndex) => (
268
+ <td
269
+ key={`cell-${rowIndex}-${cellIndex}`}
270
+ style={{ textAlign: alignments[cellIndex] ?? "left" }}
271
+ >
272
+ {renderMarkdownInline(row[cellIndex] ?? "")}
273
+ </td>
274
+ ))}
275
+ </tr>
276
+ ))}
277
+ </tbody>
278
+ </table>
279
+ </div>
280
+ );
281
+ continue;
282
+ }
283
+ if (/^(-{3,}|\*{3,}|_{3,})$/.test(trimmed)) {
284
+ flushTextRuns();
285
+ nodes.push(<hr key={`hr-${nodes.length}`} />);
286
+ continue;
287
+ }
288
+ const headingMatch = /^(#{1,3})\s+(.+)$/.exec(trimmed);
289
+ if (headingMatch) {
290
+ flushTextRuns();
291
+ const level = headingMatch[1].length;
292
+ const content = renderMarkdownInline(headingMatch[2]);
293
+ if (level === 1) nodes.push(<h1 key={`h-${nodes.length}`}>{content}</h1>);
294
+ else if (level === 2) nodes.push(<h2 key={`h-${nodes.length}`}>{content}</h2>);
295
+ else nodes.push(<h3 key={`h-${nodes.length}`}>{content}</h3>);
296
+ continue;
297
+ }
298
+ const blockquoteMatch = /^>\s?(.*)$/.exec(trimmed);
299
+ if (blockquoteMatch) {
300
+ flushParagraph();
301
+ flushList();
302
+ flushOrderedList();
303
+ const content = blockquoteMatch[1].trim();
304
+ if (content) blockquote.push(content);
305
+ continue;
306
+ }
307
+ const listMatch = /^[-*]\s+(.+)$/.exec(trimmed);
308
+ if (listMatch) {
309
+ flushParagraph();
310
+ flushOrderedList();
311
+ flushBlockquote();
312
+ listItems.push(listMatch[1]);
313
+ continue;
314
+ }
315
+ const orderedListMatch = /^\d+[.)]\s+(.+)$/.exec(trimmed);
316
+ if (orderedListMatch) {
317
+ flushParagraph();
318
+ flushList();
319
+ flushBlockquote();
320
+ orderedListItems.push(orderedListMatch[1]);
321
+ continue;
322
+ }
323
+ flushList();
324
+ flushOrderedList();
325
+ flushBlockquote();
326
+ paragraph.push(trimmed);
327
+ }
328
+
329
+ flushTextRuns();
330
+
331
+ return <>{nodes.length ? nodes : <p className="markdown-placeholder">Double-click to edit text.</p>}</>;
332
+ }
333
+
334
+ function normalizeEditorMarkdown(markdown: string) {
335
+ return normalizeInlineOrderedListMarkers(markdown)
336
+ .replace(/[ \t]+\n/g, "\n")
337
+ .replace(/\n{3,}/g, "\n\n")
338
+ .trim();
339
+ }
340
+
341
+ function htmlToMarkdown(html: string) {
342
+ if (typeof DOMParser === "undefined") return "";
343
+ const document = new DOMParser().parseFromString(html, "text/html");
344
+
345
+ function childrenToMarkdown(node: Node): string {
346
+ return Array.from(node.childNodes).map(nodeToMarkdown).join("");
347
+ }
348
+
349
+ function nodeToMarkdown(node: Node): string {
350
+ if (node.nodeType === Node.TEXT_NODE) return node.textContent ?? "";
351
+ if (!(node instanceof HTMLElement)) return childrenToMarkdown(node);
352
+
353
+ const content = childrenToMarkdown(node).trim();
354
+ switch (node.tagName.toLowerCase()) {
355
+ case "a": {
356
+ const href = node.getAttribute("href");
357
+ return href && content ? `[${content}](${href})` : content;
358
+ }
359
+ case "b":
360
+ case "strong":
361
+ return content ? `**${content}**` : "";
362
+ case "br":
363
+ return "\n";
364
+ case "code":
365
+ return content ? `\`${content.replaceAll("`", "'")}\`` : "";
366
+ case "h1":
367
+ return content ? `# ${content}\n\n` : "";
368
+ case "h2":
369
+ return content ? `## ${content}\n\n` : "";
370
+ case "h3":
371
+ return content ? `### ${content}\n\n` : "";
372
+ case "li":
373
+ return content ? `- ${content}\n` : "";
374
+ case "ol":
375
+ case "ul":
376
+ return `${childrenToMarkdown(node).trim()}\n\n`;
377
+ case "div":
378
+ case "p":
379
+ case "section":
380
+ return content ? `${content}\n\n` : "";
381
+ default:
382
+ return content;
383
+ }
384
+ }
385
+
386
+ return normalizeEditorMarkdown(childrenToMarkdown(document.body));
387
+ }
388
+
389
+ function MarkdownChangePlugin({ onDraftChange }: { onDraftChange: (nextMarkdown: string) => void }) {
390
+ return (
391
+ <OnChangePlugin
392
+ ignoreSelectionChange
393
+ onChange={(editorState) => {
394
+ editorState.read(() => {
395
+ onDraftChange(normalizeEditorMarkdown($convertToMarkdownString(MARKDOWN_TRANSFORMERS)));
396
+ });
397
+ }}
398
+ />
399
+ );
400
+ }
401
+
402
+ function rangeFromPoint(
403
+ document: CaretPositionDocument,
404
+ point: RichMarkdownFocusPoint
405
+ ): Range | null {
406
+ const position = document.caretPositionFromPoint?.(point.clientX, point.clientY);
407
+ if (position) {
408
+ const range = document.createRange();
409
+ range.setStart(position.offsetNode, position.offset);
410
+ range.collapse(true);
411
+ return range;
412
+ }
413
+ return document.caretRangeFromPoint?.(point.clientX, point.clientY) ?? null;
414
+ }
415
+
416
+ function focusContentEditableAtPoint(
417
+ rootElement: HTMLElement,
418
+ point: RichMarkdownFocusPoint
419
+ ): boolean {
420
+ const ownerDocument = rootElement.ownerDocument as CaretPositionDocument;
421
+ const range = rangeFromPoint(ownerDocument, point);
422
+ if (!range || !rootElement.contains(range.startContainer)) return false;
423
+ const selection = ownerDocument.defaultView?.getSelection();
424
+ if (!selection) return false;
425
+
426
+ rootElement.focus({ preventScroll: true });
427
+ selection.removeAllRanges();
428
+ selection.addRange(range);
429
+ return true;
430
+ }
431
+
432
+ function FocusOnMountPlugin({ focusPoint }: { focusPoint: RichMarkdownFocusPoint | null }) {
433
+ const [editor] = useLexicalComposerContext();
434
+
435
+ useEffect(() => {
436
+ const frame = window.requestAnimationFrame(() => {
437
+ const rootElement = editor.getRootElement();
438
+ if (rootElement && focusPoint && focusContentEditableAtPoint(rootElement, focusPoint)) {
439
+ return;
440
+ }
441
+ editor.focus(() => {}, { defaultSelection: "rootEnd" });
442
+ });
443
+ return () => window.cancelAnimationFrame(frame);
444
+ }, [editor, focusPoint]);
445
+
446
+ return null;
447
+ }
448
+
449
+ function CommitOnEscapePlugin({ onCommit }: { onCommit: () => void }) {
450
+ const [editor] = useLexicalComposerContext();
451
+
452
+ useEffect(() => {
453
+ return editor.registerCommand(
454
+ KEY_ESCAPE_COMMAND,
455
+ (event) => {
456
+ event?.preventDefault();
457
+ onCommit();
458
+ return true;
459
+ },
460
+ COMMAND_PRIORITY_HIGH
461
+ );
462
+ }, [editor, onCommit]);
463
+
464
+ return null;
465
+ }
466
+
467
+ function PasteSanitizerPlugin() {
468
+ const [editor] = useLexicalComposerContext();
469
+
470
+ useEffect(() => {
471
+ return editor.registerCommand(
472
+ PASTE_COMMAND,
473
+ (event) => {
474
+ if (!("clipboardData" in event) || !event.clipboardData) return false;
475
+ const html = event.clipboardData.getData("text/html");
476
+ if (!html) return false;
477
+ const markdown = htmlToMarkdown(html);
478
+ if (!markdown) return false;
479
+ event.preventDefault();
480
+ editor.update(() => {
481
+ const selection = $getSelection();
482
+ if ($isRangeSelection(selection)) selection.insertText(markdown);
483
+ });
484
+ return true;
485
+ },
486
+ COMMAND_PRIORITY_HIGH
487
+ );
488
+ }, [editor]);
489
+
490
+ return null;
491
+ }
492
+
493
+ export function RichMarkdown({
494
+ activation = "doubleClick",
495
+ ariaLabel,
496
+ className,
497
+ isEditMode = false,
498
+ markdown,
499
+ minRows = 1,
500
+ onCommit,
501
+ onMarkdownChange,
502
+ onRequestEditMode,
503
+ placeholder,
504
+ variant = "reportBlock"
505
+ }: RichMarkdownProps) {
506
+ const editorId = useId();
507
+ const [isEditing, setIsEditing] = useState(false);
508
+ const [editSessionKey, setEditSessionKey] = useState(0);
509
+ const initialMarkdownRef = useRef(markdown);
510
+ const latestDraftRef = useRef(markdown);
511
+ const lastEmittedRef = useRef(markdown);
512
+ const debounceRef = useRef<number | null>(null);
513
+ const focusPointRef = useRef<RichMarkdownFocusPoint | null>(null);
514
+ const isEditingRef = useRef(isEditing);
515
+ const flushRef = useRef<() => void>(() => {});
516
+ const displayMarkdown = markdown.trim() ? markdown : placeholder;
517
+ const variantClassName = `rich-markdown-${variant}`;
518
+
519
+ useEffect(() => {
520
+ if (!isEditing) {
521
+ latestDraftRef.current = markdown;
522
+ lastEmittedRef.current = markdown;
523
+ }
524
+ }, [isEditing, markdown]);
525
+
526
+ useEffect(() => {
527
+ isEditingRef.current = isEditing;
528
+ }, [isEditing]);
529
+
530
+ const emitMarkdownChange = useCallback((nextMarkdown: string) => {
531
+ if (nextMarkdown === lastEmittedRef.current) return;
532
+ lastEmittedRef.current = nextMarkdown;
533
+ onMarkdownChange(nextMarkdown);
534
+ }, [onMarkdownChange]);
535
+
536
+ const flushMarkdownChange = useCallback(() => {
537
+ if (debounceRef.current !== null) {
538
+ window.clearTimeout(debounceRef.current);
539
+ debounceRef.current = null;
540
+ }
541
+ const nextMarkdown = latestDraftRef.current;
542
+ emitMarkdownChange(nextMarkdown);
543
+ onCommit?.(nextMarkdown);
544
+ }, [emitMarkdownChange, onCommit]);
545
+
546
+ flushRef.current = flushMarkdownChange;
547
+
548
+ useEffect(() => {
549
+ return () => {
550
+ if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
551
+ if (isEditingRef.current) flushRef.current();
552
+ };
553
+ }, []);
554
+
555
+ const scheduleMarkdownChange = useCallback((nextMarkdown: string) => {
556
+ latestDraftRef.current = nextMarkdown;
557
+ if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
558
+ debounceRef.current = window.setTimeout(() => {
559
+ debounceRef.current = null;
560
+ emitMarkdownChange(latestDraftRef.current);
561
+ }, MARKDOWN_COMMIT_DEBOUNCE_MS);
562
+ }, [emitMarkdownChange]);
563
+
564
+ const startEditing = useCallback((focusPoint: RichMarkdownFocusPoint | null = null) => {
565
+ initialMarkdownRef.current = markdown;
566
+ latestDraftRef.current = markdown;
567
+ lastEmittedRef.current = markdown;
568
+ focusPointRef.current = focusPoint;
569
+ setEditSessionKey((current) => current + 1);
570
+ setIsEditing(true);
571
+ }, [markdown]);
572
+
573
+ const handlePreviewClick = useCallback((event: ReactMouseEvent<HTMLDivElement>) => {
574
+ if (!isEditMode || activation === "manual") return;
575
+ event.preventDefault();
576
+ window.getSelection()?.removeAllRanges();
577
+ startEditing({ clientX: event.clientX, clientY: event.clientY });
578
+ }, [activation, isEditMode, startEditing]);
579
+
580
+ const handlePreviewDoubleClick = useCallback((event: ReactMouseEvent<HTMLDivElement>) => {
581
+ if (activation === "manual" && isEditMode) return;
582
+ event.preventDefault();
583
+ event.stopPropagation();
584
+ window.getSelection()?.removeAllRanges();
585
+ if (!isEditMode) onRequestEditMode?.();
586
+ startEditing({ clientX: event.clientX, clientY: event.clientY });
587
+ }, [activation, isEditMode, onRequestEditMode, startEditing]);
588
+
589
+ const handlePreviewMouseDownCapture = useCallback((event: ReactMouseEvent<HTMLDivElement>) => {
590
+ if (!isEditMode && activation !== "manual" && event.detail > 1) {
591
+ event.preventDefault();
592
+ event.stopPropagation();
593
+ window.getSelection()?.removeAllRanges();
594
+ onRequestEditMode?.();
595
+ startEditing({ clientX: event.clientX, clientY: event.clientY });
596
+ }
597
+ }, [activation, isEditMode, onRequestEditMode, startEditing]);
598
+
599
+ const commitAndClose = useCallback(() => {
600
+ flushMarkdownChange();
601
+ setIsEditing(false);
602
+ }, [flushMarkdownChange]);
603
+
604
+ const initialConfig = useMemo(() => ({
605
+ editorState: () => {
606
+ $convertFromMarkdownString(initialMarkdownRef.current, MARKDOWN_TRANSFORMERS);
607
+ },
608
+ namespace: `DataScienceRichMarkdown-${editorId}-${editSessionKey}`,
609
+ nodes: [HeadingNode, QuoteNode, ListNode, ListItemNode, LinkNode],
610
+ onError(error: Error) {
611
+ throw error;
612
+ },
613
+ theme: richMarkdownTheme
614
+ }), [editSessionKey, editorId]);
615
+
616
+ const interactiveProps =
617
+ isEditMode && activation !== "manual"
618
+ ? { onClick: handlePreviewClick }
619
+ : activation !== "manual" || onRequestEditMode
620
+ ? { onDoubleClick: handlePreviewDoubleClick }
621
+ : {};
622
+
623
+ if (isEditing) {
624
+ return (
625
+ <div
626
+ className={`rich-markdown-editor-shell viz-card__no-drag ${variantClassName}`.trim()}
627
+ style={{ "--rich-markdown-min-height": `${Math.max(1, minRows) * 22}px` } as CSSProperties}
628
+ >
629
+ <LexicalComposer initialConfig={initialConfig} key={editSessionKey}>
630
+ <RichTextPlugin
631
+ contentEditable={
632
+ <ContentEditable
633
+ aria-label={ariaLabel}
634
+ className={`markdown-render markdown-edit-target rich-markdown rich-markdown-editor viz-card__no-drag ${variantClassName} ${className ?? ""}`.trim()}
635
+ onBlur={commitAndClose}
636
+ spellCheck
637
+ />
638
+ }
639
+ ErrorBoundary={LexicalErrorBoundary}
640
+ placeholder={
641
+ <div className={`markdown-render rich-markdown-placeholder ${variantClassName} ${className ?? ""}`.trim()}>
642
+ <RichMarkdownPreview markdown={placeholder} />
643
+ </div>
644
+ }
645
+ />
646
+ <HistoryPlugin />
647
+ <FocusOnMountPlugin focusPoint={focusPointRef.current} />
648
+ <MarkdownShortcutPlugin transformers={MARKDOWN_TRANSFORMERS} />
649
+ <MarkdownChangePlugin onDraftChange={scheduleMarkdownChange} />
650
+ <CommitOnEscapePlugin onCommit={commitAndClose} />
651
+ <PasteSanitizerPlugin />
652
+ </LexicalComposer>
653
+ </div>
654
+ );
655
+ }
656
+
657
+ function handleKeyDown(event: ReactKeyboardEvent<HTMLDivElement>) {
658
+ if (event.key === "Enter" || event.key === " ") {
659
+ event.preventDefault();
660
+ if (!isEditMode) onRequestEditMode?.();
661
+ startEditing();
662
+ }
663
+ }
664
+
665
+ return (
666
+ <div
667
+ aria-label={ariaLabel}
668
+ className={`markdown-render markdown-edit-target rich-markdown rich-markdown-preview viz-card__no-drag ${variantClassName} ${className ?? ""}`.trim()}
669
+ data-rich-markdown-activation={activation}
670
+ data-rich-markdown-edit-mode={isEditMode ? "true" : "false"}
671
+ data-rich-markdown-variant={variant}
672
+ onKeyDown={handleKeyDown}
673
+ onMouseDownCapture={handlePreviewMouseDownCapture}
674
+ role="button"
675
+ tabIndex={0}
676
+ {...interactiveProps}
677
+ >
678
+ <RichMarkdownPreview markdown={displayMarkdown} />
679
+ </div>
680
+ );
681
+ }