openplanr 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (530) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/dist/cli/commands/backlog-commands.d.ts +3 -0
  3. package/dist/cli/commands/backlog-commands.d.ts.map +1 -0
  4. package/dist/cli/commands/backlog-commands.js +60 -0
  5. package/dist/cli/commands/backlog-commands.js.map +1 -0
  6. package/dist/cli/commands/company.d.ts.map +1 -1
  7. package/dist/cli/commands/company.js +17 -0
  8. package/dist/cli/commands/company.js.map +1 -1
  9. package/dist/cli/commands/design.d.ts +3 -0
  10. package/dist/cli/commands/design.d.ts.map +1 -0
  11. package/dist/cli/commands/design.js +137 -0
  12. package/dist/cli/commands/design.js.map +1 -0
  13. package/dist/cli/commands/groups/operations.d.ts.map +1 -1
  14. package/dist/cli/commands/groups/operations.js +2 -0
  15. package/dist/cli/commands/groups/operations.js.map +1 -1
  16. package/dist/cli/commands/planning-artifacts.d.ts.map +1 -1
  17. package/dist/cli/commands/planning-artifacts.js +13 -5
  18. package/dist/cli/commands/planning-artifacts.js.map +1 -1
  19. package/dist/dashboard/.vite/manifest.json +1 -1
  20. package/dist/dashboard/assets/{index-CvQAlBy3.js → index-C_OquZVk.js} +2 -2
  21. package/dist/dashboard/assets/{index-CvQAlBy3.js.map → index-C_OquZVk.js.map} +1 -1
  22. package/dist/dashboard/dashboard-manifest.json +13 -13
  23. package/dist/dashboard/index.html +1 -1
  24. package/dist/services/artifact-service.d.ts.map +1 -1
  25. package/dist/services/artifact-service.js +13 -6
  26. package/dist/services/artifact-service.js.map +1 -1
  27. package/dist/services/company-export-service.d.ts +41 -0
  28. package/dist/services/company-export-service.d.ts.map +1 -0
  29. package/dist/services/company-export-service.js +429 -0
  30. package/dist/services/company-export-service.js.map +1 -0
  31. package/dist/services/company-sync-service.d.ts +24 -1
  32. package/dist/services/company-sync-service.d.ts.map +1 -1
  33. package/dist/services/company-sync-service.js +36 -19
  34. package/dist/services/company-sync-service.js.map +1 -1
  35. package/dist/services/design-artifact-service.d.ts +22 -0
  36. package/dist/services/design-artifact-service.d.ts.map +1 -1
  37. package/dist/services/design-artifact-service.js +76 -16
  38. package/dist/services/design-artifact-service.js.map +1 -1
  39. package/dist/services/runtime-manager/runtime-package.js +1 -1
  40. package/dist/services/runtime-manager/runtime-package.js.map +1 -1
  41. package/docs/CLI.md +126 -1
  42. package/lib/backlog-item.d.mts +45 -1
  43. package/lib/backlog-item.mjs +302 -34
  44. package/lib/host-packages/adapter-registry.json +5 -5
  45. package/lib/host-packages/capability-map.json +1 -1
  46. package/lib/host-packages/claude/.claude-plugin/marketplace.json +2 -2
  47. package/lib/host-packages/claude/openplanr/.claude-plugin/icon.png +0 -0
  48. package/lib/host-packages/claude/openplanr/.claude-plugin/plugin.json +1 -1
  49. package/lib/host-packages/claude/openplanr/.openplanr-content.json +97 -413
  50. package/lib/host-packages/claude/openplanr/README.md +8 -8
  51. package/lib/host-packages/claude/openplanr/skills/artifact/references/design-sharing.md +2 -2
  52. package/lib/host-packages/claude/openplanr/skills/ceo-review/references/note-checklist.md +131 -0
  53. package/lib/host-packages/claude/openplanr/skills/ceo-review/references/operate-advisor-contract.md +2 -7
  54. package/lib/host-packages/claude/openplanr/skills/chair-review/SKILL.md +2 -7
  55. package/lib/host-packages/claude/openplanr/skills/chair-review/references/note-checklist.md +131 -0
  56. package/lib/host-packages/claude/openplanr/skills/challenger-review/SKILL.md +3 -7
  57. package/lib/host-packages/claude/openplanr/skills/challenger-review/references/note-checklist.md +131 -0
  58. package/lib/host-packages/claude/openplanr/skills/cmo-review/references/note-checklist.md +131 -0
  59. package/lib/host-packages/claude/openplanr/skills/cmo-review/references/operate-advisor-contract.md +2 -7
  60. package/lib/host-packages/claude/openplanr/skills/coo-review/references/note-checklist.md +131 -0
  61. package/lib/host-packages/claude/openplanr/skills/coo-review/references/operate-advisor-contract.md +2 -7
  62. package/lib/host-packages/claude/openplanr/skills/cpo-review/references/note-checklist.md +131 -0
  63. package/lib/host-packages/claude/openplanr/skills/cpo-review/references/operate-advisor-contract.md +2 -7
  64. package/lib/host-packages/claude/openplanr/skills/cto-review/references/note-checklist.md +131 -0
  65. package/lib/host-packages/claude/openplanr/skills/cto-review/references/operate-advisor-contract.md +2 -7
  66. package/lib/host-packages/claude/openplanr/skills/design/SKILL.md +23 -12
  67. package/lib/host-packages/claude/openplanr/skills/design/references/design-system.md +2 -2
  68. package/lib/host-packages/claude/openplanr/skills/design/references/generation.md +3 -3
  69. package/lib/host-packages/claude/openplanr/skills/design/references/hosted-canvas.md +6 -3
  70. package/lib/host-packages/claude/openplanr/skills/design/references/review.md +6 -6
  71. package/lib/host-packages/claude/openplanr/skills/design/references/team-review.md +3 -4
  72. package/lib/host-packages/claude/openplanr/skills/design/references/utilities.md +78 -38
  73. package/lib/host-packages/claude/openplanr/skills/design-loop/SKILL.md +15 -7
  74. package/lib/host-packages/claude/openplanr/skills/design-loop/references/design-system.md +2 -2
  75. package/lib/host-packages/claude/openplanr/skills/design-loop/references/generation.md +3 -3
  76. package/lib/host-packages/claude/openplanr/skills/design-loop/references/review.md +6 -6
  77. package/lib/host-packages/claude/openplanr/skills/design-loop/references/team-review.md +3 -4
  78. package/lib/host-packages/claude/openplanr/skills/design-loop/references/utilities.md +78 -38
  79. package/lib/host-packages/claude/openplanr/skills/design-review/SKILL.md +25 -5
  80. package/lib/host-packages/claude/openplanr/skills/design-review/references/design-system.md +2 -2
  81. package/lib/host-packages/claude/openplanr/skills/design-review/references/generation.md +3 -3
  82. package/lib/host-packages/claude/openplanr/skills/design-review/references/review.md +6 -6
  83. package/lib/host-packages/claude/openplanr/skills/design-review/references/team-review.md +3 -4
  84. package/lib/host-packages/claude/openplanr/skills/design-review/references/utilities.md +78 -38
  85. package/lib/host-packages/claude/openplanr/skills/investigate/SKILL.md +2 -3
  86. package/lib/host-packages/claude/openplanr/skills/investigate/references/backlog-filing.md +102 -0
  87. package/lib/host-packages/claude/openplanr/skills/investigate/references/findings.md +8 -16
  88. package/lib/host-packages/claude/openplanr/skills/operate/SKILL.md +73 -84
  89. package/lib/host-packages/claude/openplanr/skills/operate/references/note-checklist.md +131 -0
  90. package/lib/host-packages/claude/openplanr/skills/operate/references/return-contract.md +10 -0
  91. package/lib/host-packages/claude/openplanr/skills/plan/SKILL.md +23 -20
  92. package/lib/host-packages/claude/openplanr/skills/plan/references/planning-ids.md +53 -0
  93. package/lib/host-packages/claude/openplanr/skills/plan/references/return-contract.md +10 -0
  94. package/lib/host-packages/claude/openplanr/skills/plan-review/SKILL.md +6 -6
  95. package/lib/host-packages/claude/openplanr/skills/plan-review/references/return-contract.md +10 -0
  96. package/lib/host-packages/claude/openplanr/skills/release/SKILL.md +40 -139
  97. package/lib/host-packages/claude/openplanr/skills/release/references/cadence.md +10 -0
  98. package/lib/host-packages/claude/openplanr/skills/release/references/release-notes.md +1 -1
  99. package/lib/host-packages/claude/openplanr/skills/release/references/return-contract.md +10 -0
  100. package/lib/host-packages/claude/openplanr/skills/release/references/schemes.md +4 -2
  101. package/lib/host-packages/claude/openplanr/skills/ship/SKILL.md +37 -24
  102. package/lib/host-packages/claude/openplanr/skills/ship/references/backlog-filing.md +102 -0
  103. package/lib/host-packages/claude/openplanr/skills/ship/references/findings.md +8 -16
  104. package/lib/host-packages/claude/openplanr/skills/ship/references/return-contract.md +10 -0
  105. package/lib/host-packages/claude/openplanr/skills/spec/SKILL.md +9 -9
  106. package/lib/host-packages/claude/openplanr/skills/spec/references/return-contract.md +10 -0
  107. package/lib/host-packages/cursor/openplanr/.openplanr-content.json +94 -410
  108. package/lib/host-packages/cursor/openplanr/manifest.json +1 -1
  109. package/lib/host-packages/cursor/openplanr/rules/planr-artifact/references/design-sharing.md +2 -2
  110. package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/references/note-checklist.md +131 -0
  111. package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/references/operate-advisor-contract.md +2 -7
  112. package/lib/host-packages/cursor/openplanr/rules/planr-chair-review/references/note-checklist.md +131 -0
  113. package/lib/host-packages/cursor/openplanr/rules/planr-chair-review.mdc +2 -7
  114. package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review/references/note-checklist.md +131 -0
  115. package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review.mdc +3 -7
  116. package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/references/note-checklist.md +131 -0
  117. package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/references/operate-advisor-contract.md +2 -7
  118. package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/references/note-checklist.md +131 -0
  119. package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/references/operate-advisor-contract.md +2 -7
  120. package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/references/note-checklist.md +131 -0
  121. package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/references/operate-advisor-contract.md +2 -7
  122. package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/references/note-checklist.md +131 -0
  123. package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/references/operate-advisor-contract.md +2 -7
  124. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/design-system.md +2 -2
  125. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/generation.md +3 -3
  126. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/hosted-canvas.md +6 -3
  127. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/review.md +6 -6
  128. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/team-review.md +3 -4
  129. package/lib/host-packages/cursor/openplanr/rules/planr-design/references/utilities.md +78 -38
  130. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/design-system.md +2 -2
  131. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/generation.md +3 -3
  132. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/review.md +6 -6
  133. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/team-review.md +3 -4
  134. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/references/utilities.md +78 -38
  135. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop.mdc +15 -7
  136. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/design-system.md +2 -2
  137. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/generation.md +3 -3
  138. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/review.md +6 -6
  139. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/team-review.md +3 -4
  140. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/references/utilities.md +78 -38
  141. package/lib/host-packages/cursor/openplanr/rules/planr-design-review.mdc +25 -5
  142. package/lib/host-packages/cursor/openplanr/rules/planr-design.mdc +23 -12
  143. package/lib/host-packages/cursor/openplanr/rules/planr-investigate/references/backlog-filing.md +102 -0
  144. package/lib/host-packages/cursor/openplanr/rules/planr-investigate/references/findings.md +8 -16
  145. package/lib/host-packages/cursor/openplanr/rules/planr-investigate.mdc +2 -3
  146. package/lib/host-packages/cursor/openplanr/rules/planr-operate/references/note-checklist.md +131 -0
  147. package/lib/host-packages/cursor/openplanr/rules/planr-operate/references/return-contract.md +10 -0
  148. package/lib/host-packages/cursor/openplanr/rules/planr-operate.mdc +73 -84
  149. package/lib/host-packages/cursor/openplanr/rules/planr-plan/references/planning-ids.md +53 -0
  150. package/lib/host-packages/cursor/openplanr/rules/planr-plan/references/return-contract.md +10 -0
  151. package/lib/host-packages/cursor/openplanr/rules/planr-plan-review/references/return-contract.md +10 -0
  152. package/lib/host-packages/cursor/openplanr/rules/planr-plan-review.mdc +6 -6
  153. package/lib/host-packages/cursor/openplanr/rules/planr-plan.mdc +23 -20
  154. package/lib/host-packages/cursor/openplanr/rules/planr-release/references/cadence.md +10 -0
  155. package/lib/host-packages/cursor/openplanr/rules/planr-release/references/release-notes.md +1 -1
  156. package/lib/host-packages/cursor/openplanr/rules/planr-release/references/return-contract.md +10 -0
  157. package/lib/host-packages/cursor/openplanr/rules/planr-release/references/schemes.md +4 -2
  158. package/lib/host-packages/cursor/openplanr/rules/planr-release.mdc +40 -139
  159. package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/backlog-filing.md +102 -0
  160. package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/findings.md +8 -16
  161. package/lib/host-packages/cursor/openplanr/rules/planr-ship/references/return-contract.md +10 -0
  162. package/lib/host-packages/cursor/openplanr/rules/planr-ship.mdc +37 -24
  163. package/lib/host-packages/cursor/openplanr/rules/planr-spec/references/return-contract.md +10 -0
  164. package/lib/host-packages/cursor/openplanr/rules/planr-spec.mdc +9 -9
  165. package/lib/host-packages/openai/.claude-plugin/marketplace.json +2 -2
  166. package/lib/host-packages/openai/openplanr/.codex-plugin/plugin.json +1 -1
  167. package/lib/host-packages/openai/openplanr/.openplanr-content.json +112 -428
  168. package/lib/host-packages/openai/openplanr/skills/artifact/references/design-sharing.md +2 -2
  169. package/lib/host-packages/openai/openplanr/skills/ceo-review/openplanr.skill.json +4 -4
  170. package/lib/host-packages/openai/openplanr/skills/ceo-review/references/note-checklist.md +131 -0
  171. package/lib/host-packages/openai/openplanr/skills/ceo-review/references/operate-advisor-contract.md +2 -7
  172. package/lib/host-packages/openai/openplanr/skills/chair-review/SKILL.md +2 -7
  173. package/lib/host-packages/openai/openplanr/skills/chair-review/openplanr.skill.json +4 -4
  174. package/lib/host-packages/openai/openplanr/skills/chair-review/references/note-checklist.md +131 -0
  175. package/lib/host-packages/openai/openplanr/skills/challenger-review/SKILL.md +3 -7
  176. package/lib/host-packages/openai/openplanr/skills/challenger-review/openplanr.skill.json +4 -4
  177. package/lib/host-packages/openai/openplanr/skills/challenger-review/references/note-checklist.md +131 -0
  178. package/lib/host-packages/openai/openplanr/skills/cmo-review/openplanr.skill.json +4 -4
  179. package/lib/host-packages/openai/openplanr/skills/cmo-review/references/note-checklist.md +131 -0
  180. package/lib/host-packages/openai/openplanr/skills/cmo-review/references/operate-advisor-contract.md +2 -7
  181. package/lib/host-packages/openai/openplanr/skills/coo-review/openplanr.skill.json +4 -4
  182. package/lib/host-packages/openai/openplanr/skills/coo-review/references/note-checklist.md +131 -0
  183. package/lib/host-packages/openai/openplanr/skills/coo-review/references/operate-advisor-contract.md +2 -7
  184. package/lib/host-packages/openai/openplanr/skills/cpo-review/openplanr.skill.json +4 -4
  185. package/lib/host-packages/openai/openplanr/skills/cpo-review/references/note-checklist.md +131 -0
  186. package/lib/host-packages/openai/openplanr/skills/cpo-review/references/operate-advisor-contract.md +2 -7
  187. package/lib/host-packages/openai/openplanr/skills/cto-review/openplanr.skill.json +4 -4
  188. package/lib/host-packages/openai/openplanr/skills/cto-review/references/note-checklist.md +131 -0
  189. package/lib/host-packages/openai/openplanr/skills/cto-review/references/operate-advisor-contract.md +2 -7
  190. package/lib/host-packages/openai/openplanr/skills/design/SKILL.md +23 -12
  191. package/lib/host-packages/openai/openplanr/skills/design/openplanr.skill.json +0 -11
  192. package/lib/host-packages/openai/openplanr/skills/design/references/design-system.md +2 -2
  193. package/lib/host-packages/openai/openplanr/skills/design/references/generation.md +3 -3
  194. package/lib/host-packages/openai/openplanr/skills/design/references/hosted-canvas.md +6 -3
  195. package/lib/host-packages/openai/openplanr/skills/design/references/review.md +6 -6
  196. package/lib/host-packages/openai/openplanr/skills/design/references/team-review.md +3 -4
  197. package/lib/host-packages/openai/openplanr/skills/design/references/utilities.md +78 -38
  198. package/lib/host-packages/openai/openplanr/skills/design-loop/SKILL.md +15 -7
  199. package/lib/host-packages/openai/openplanr/skills/design-loop/openplanr.skill.json +0 -11
  200. package/lib/host-packages/openai/openplanr/skills/design-loop/references/design-system.md +2 -2
  201. package/lib/host-packages/openai/openplanr/skills/design-loop/references/generation.md +3 -3
  202. package/lib/host-packages/openai/openplanr/skills/design-loop/references/review.md +6 -6
  203. package/lib/host-packages/openai/openplanr/skills/design-loop/references/team-review.md +3 -4
  204. package/lib/host-packages/openai/openplanr/skills/design-loop/references/utilities.md +78 -38
  205. package/lib/host-packages/openai/openplanr/skills/design-review/SKILL.md +25 -5
  206. package/lib/host-packages/openai/openplanr/skills/design-review/openplanr.skill.json +0 -11
  207. package/lib/host-packages/openai/openplanr/skills/design-review/references/design-system.md +2 -2
  208. package/lib/host-packages/openai/openplanr/skills/design-review/references/generation.md +3 -3
  209. package/lib/host-packages/openai/openplanr/skills/design-review/references/review.md +6 -6
  210. package/lib/host-packages/openai/openplanr/skills/design-review/references/team-review.md +3 -4
  211. package/lib/host-packages/openai/openplanr/skills/design-review/references/utilities.md +78 -38
  212. package/lib/host-packages/openai/openplanr/skills/investigate/SKILL.md +2 -3
  213. package/lib/host-packages/openai/openplanr/skills/investigate/openplanr.skill.json +4 -4
  214. package/lib/host-packages/openai/openplanr/skills/investigate/references/backlog-filing.md +102 -0
  215. package/lib/host-packages/openai/openplanr/skills/investigate/references/findings.md +8 -16
  216. package/lib/host-packages/openai/openplanr/skills/operate/SKILL.md +73 -84
  217. package/lib/host-packages/openai/openplanr/skills/operate/openplanr.skill.json +15 -4
  218. package/lib/host-packages/openai/openplanr/skills/operate/references/note-checklist.md +131 -0
  219. package/lib/host-packages/openai/openplanr/skills/operate/references/return-contract.md +10 -0
  220. package/lib/host-packages/openai/openplanr/skills/plan/SKILL.md +23 -20
  221. package/lib/host-packages/openai/openplanr/skills/plan/openplanr.skill.json +8 -8
  222. package/lib/host-packages/openai/openplanr/skills/plan/references/planning-ids.md +53 -0
  223. package/lib/host-packages/openai/openplanr/skills/plan/references/return-contract.md +10 -0
  224. package/lib/host-packages/openai/openplanr/skills/plan-review/SKILL.md +6 -6
  225. package/lib/host-packages/openai/openplanr/skills/plan-review/openplanr.skill.json +12 -1
  226. package/lib/host-packages/openai/openplanr/skills/plan-review/references/return-contract.md +10 -0
  227. package/lib/host-packages/openai/openplanr/skills/release/SKILL.md +40 -139
  228. package/lib/host-packages/openai/openplanr/skills/release/openplanr.skill.json +4 -4
  229. package/lib/host-packages/openai/openplanr/skills/release/references/cadence.md +10 -0
  230. package/lib/host-packages/openai/openplanr/skills/release/references/release-notes.md +1 -1
  231. package/lib/host-packages/openai/openplanr/skills/release/references/return-contract.md +10 -0
  232. package/lib/host-packages/openai/openplanr/skills/release/references/schemes.md +4 -2
  233. package/lib/host-packages/openai/openplanr/skills/ship/SKILL.md +37 -24
  234. package/lib/host-packages/openai/openplanr/skills/ship/openplanr.skill.json +9 -9
  235. package/lib/host-packages/openai/openplanr/skills/ship/references/backlog-filing.md +102 -0
  236. package/lib/host-packages/openai/openplanr/skills/ship/references/findings.md +8 -16
  237. package/lib/host-packages/openai/openplanr/skills/ship/references/return-contract.md +10 -0
  238. package/lib/host-packages/openai/openplanr/skills/spec/SKILL.md +9 -9
  239. package/lib/host-packages/openai/openplanr/skills/spec/openplanr.skill.json +12 -1
  240. package/lib/host-packages/openai/openplanr/skills/spec/references/return-contract.md +10 -0
  241. package/package.json +2 -2
  242. package/lib/host-packages/claude/openplanr/.claude-plugin/icon.svg +0 -11
  243. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
  244. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
  245. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
  246. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
  247. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
  248. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
  249. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
  250. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
  251. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
  252. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
  253. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
  254. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
  255. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
  256. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
  257. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
  258. package/lib/host-packages/claude/openplanr/runtime/design/scripts/design.mjs +0 -1500
  259. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
  260. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
  261. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
  262. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
  263. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
  264. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
  265. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
  266. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
  267. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
  268. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
  269. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
  270. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
  271. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
  272. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
  273. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
  274. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
  275. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
  276. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
  277. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
  278. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
  279. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
  280. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
  281. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
  282. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
  283. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
  284. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
  285. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
  286. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
  287. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
  288. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
  289. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
  290. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
  291. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
  292. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
  293. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
  294. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
  295. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
  296. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
  297. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
  298. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  299. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
  300. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
  301. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
  302. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
  303. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
  304. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
  305. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
  306. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
  307. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
  308. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
  309. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
  310. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
  311. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
  312. package/lib/host-packages/claude/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
  313. package/lib/host-packages/claude/openplanr/runtime/operate/scripts/errors.mjs +0 -8
  314. package/lib/host-packages/claude/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
  315. package/lib/host-packages/claude/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
  316. package/lib/host-packages/claude/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
  317. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
  318. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/design.mjs +0 -2611
  319. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
  320. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
  321. package/lib/host-packages/claude/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  322. package/lib/host-packages/claude/openplanr/skills/ceo-review/scripts/validate-note.mjs +0 -12
  323. package/lib/host-packages/claude/openplanr/skills/chair-review/scripts/validate-note.mjs +0 -12
  324. package/lib/host-packages/claude/openplanr/skills/challenger-review/scripts/validate-note.mjs +0 -12
  325. package/lib/host-packages/claude/openplanr/skills/cmo-review/scripts/validate-note.mjs +0 -12
  326. package/lib/host-packages/claude/openplanr/skills/coo-review/scripts/validate-note.mjs +0 -12
  327. package/lib/host-packages/claude/openplanr/skills/cpo-review/scripts/validate-note.mjs +0 -12
  328. package/lib/host-packages/claude/openplanr/skills/cto-review/scripts/validate-note.mjs +0 -12
  329. package/lib/host-packages/claude/openplanr/skills/design/scripts/design.mjs +0 -12
  330. package/lib/host-packages/claude/openplanr/skills/design-loop/scripts/design.mjs +0 -12
  331. package/lib/host-packages/claude/openplanr/skills/design-review/scripts/design.mjs +0 -12
  332. package/lib/host-packages/claude/openplanr/skills/investigate/scripts/backlog-item.mjs +0 -339
  333. package/lib/host-packages/claude/openplanr/skills/operate/scripts/validate-note.mjs +0 -12
  334. package/lib/host-packages/claude/openplanr/skills/plan/scripts/design.mjs +0 -12
  335. package/lib/host-packages/claude/openplanr/skills/plan/scripts/planning-ids.mjs +0 -166
  336. package/lib/host-packages/claude/openplanr/skills/release/scripts/collect_changes.sh +0 -85
  337. package/lib/host-packages/claude/openplanr/skills/ship/scripts/backlog-item.mjs +0 -339
  338. package/lib/host-packages/claude/openplanr/skills/ship/scripts/discover-verification.mjs +0 -108
  339. package/lib/host-packages/cursor/openplanr/rules/planr-ceo-review/scripts/validate-note.mjs +0 -12
  340. package/lib/host-packages/cursor/openplanr/rules/planr-chair-review/scripts/validate-note.mjs +0 -12
  341. package/lib/host-packages/cursor/openplanr/rules/planr-challenger-review/scripts/validate-note.mjs +0 -12
  342. package/lib/host-packages/cursor/openplanr/rules/planr-cmo-review/scripts/validate-note.mjs +0 -12
  343. package/lib/host-packages/cursor/openplanr/rules/planr-coo-review/scripts/validate-note.mjs +0 -12
  344. package/lib/host-packages/cursor/openplanr/rules/planr-cpo-review/scripts/validate-note.mjs +0 -12
  345. package/lib/host-packages/cursor/openplanr/rules/planr-cto-review/scripts/validate-note.mjs +0 -12
  346. package/lib/host-packages/cursor/openplanr/rules/planr-design/scripts/design.mjs +0 -12
  347. package/lib/host-packages/cursor/openplanr/rules/planr-design-loop/scripts/design.mjs +0 -12
  348. package/lib/host-packages/cursor/openplanr/rules/planr-design-review/scripts/design.mjs +0 -12
  349. package/lib/host-packages/cursor/openplanr/rules/planr-investigate/scripts/backlog-item.mjs +0 -339
  350. package/lib/host-packages/cursor/openplanr/rules/planr-operate/scripts/validate-note.mjs +0 -12
  351. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/design.mjs +0 -12
  352. package/lib/host-packages/cursor/openplanr/rules/planr-plan/scripts/planning-ids.mjs +0 -166
  353. package/lib/host-packages/cursor/openplanr/rules/planr-release/scripts/collect_changes.sh +0 -85
  354. package/lib/host-packages/cursor/openplanr/rules/planr-ship/scripts/backlog-item.mjs +0 -339
  355. package/lib/host-packages/cursor/openplanr/rules/planr-ship/scripts/discover-verification.mjs +0 -108
  356. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
  357. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
  358. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
  359. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
  360. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
  361. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
  362. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
  363. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
  364. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
  365. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
  366. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
  367. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
  368. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
  369. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
  370. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
  371. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/design.mjs +0 -1500
  372. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
  373. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
  374. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
  375. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
  376. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
  377. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
  378. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
  379. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
  380. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
  381. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
  382. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
  383. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
  384. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
  385. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
  386. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
  387. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
  388. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
  389. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
  390. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
  391. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
  392. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
  393. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
  394. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
  395. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
  396. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
  397. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
  398. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
  399. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
  400. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
  401. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
  402. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
  403. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
  404. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
  405. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
  406. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
  407. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
  408. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
  409. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
  410. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
  411. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  412. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
  413. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
  414. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
  415. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
  416. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
  417. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
  418. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
  419. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
  420. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
  421. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
  422. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
  423. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
  424. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
  425. package/lib/host-packages/cursor/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
  426. package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/errors.mjs +0 -8
  427. package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
  428. package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
  429. package/lib/host-packages/cursor/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
  430. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
  431. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/design.mjs +0 -2611
  432. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
  433. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
  434. package/lib/host-packages/cursor/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  435. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-artifact-shell.mjs +0 -1772
  436. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-document.mjs +0 -2937
  437. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-entities.mjs +0 -439
  438. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-handoff.mjs +0 -2370
  439. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-loopback-server.mjs +0 -359
  440. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-parse5-parser.mjs +0 -3970
  441. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-parse5-tokenizer.mjs +0 -3501
  442. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-review-state-store.mjs +0 -45
  443. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-review.mjs +0 -3376
  444. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-runtime-home.mjs +0 -38
  445. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-sandbox-guards.mjs +0 -1102
  446. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-share-cli-bridge.mjs +0 -321
  447. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-artifact-support-dependencies-design-support-protocol-contracts-d707e49b.mjs +0 -1652
  448. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-artifact-support-protocol-contracts-9f6918b0.mjs +0 -1037
  449. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design-shared-design-support-0bac378d.mjs +0 -137
  450. package/lib/host-packages/openai/openplanr/runtime/design/scripts/design.mjs +0 -1500
  451. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -2023
  452. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/lib/artifact/ui/studio-shell.css +0 -2
  453. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/01-module-runtime.js +0 -18
  454. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/02-artifact-review-ui.js +0 -814
  455. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/03-protocol-contracts.js +0 -10
  456. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources/04-artifact-review-ui.js +0 -2568
  457. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/artifact/templates/artifact-review-stage.js.sources.json +0 -60
  458. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/package.json +0 -65
  459. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/enhancements.css +0 -388
  460. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/handoff-center.css +0 -81
  461. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.css +0 -1
  462. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/share.js +0 -139
  463. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.css +0 -1159
  464. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/01-module-runtime.js +0 -30
  465. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/02-react.js +0 -900
  466. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/03-react-dom-client-fiber-foundations.js +0 -2261
  467. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/04-react-dom-client-reconciliation-hooks.js +0 -1595
  468. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/05-react-dom-client-render-phases.js +0 -1467
  469. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/06-react-dom-client-commit-effects.js +0 -1860
  470. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/07-react-dom-client-work-loop-scheduling.js +0 -914
  471. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/08-react-dom-client-dom-events-properties-hydration.js +0 -2578
  472. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/09-react-dom-client-public-roots.js +0 -582
  473. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/10-react.js +0 -17
  474. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/11-artifact-review-ui.js +0 -12
  475. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/12-protocol-contracts.js +0 -285
  476. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/13-design-review-experience.js +0 -2773
  477. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/14-studio-shell.js +0 -134
  478. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/15-canvas-engine.js +0 -2207
  479. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/16-design-review-experience.js +0 -16
  480. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/17-studio-shell.js +0 -8
  481. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/18-interface-primitives.js +0 -3883
  482. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/19-interface-primitives.js +0 -966
  483. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/20-studio-shell.js +0 -520
  484. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources/21-design-studio.js +0 -1597
  485. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/design/templates/studio/studio.js.sources.json +0 -311
  486. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/package.json +0 -163
  487. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/registries/artifact-theme.json +0 -64
  488. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.0.0/design-manifest.schema.json +0 -90
  489. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-envelope.schema.json +0 -142
  490. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  491. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-bundle.schema.json +0 -584
  492. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-context.schema.json +0 -191
  493. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-handoff.schema.json +0 -407
  494. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.10.0/design-review-metadata-payload.schema.json +0 -106
  495. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-handoff-readiness.schema.json +0 -398
  496. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-implementation-handoff.schema.json +0 -518
  497. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-planning-lineage.schema.json +0 -106
  498. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.11.0/design-review-metadata-payload.schema.json +0 -107
  499. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.14.0/artifact-theme.schema.json +0 -316
  500. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/artifact-envelope.schema.json +0 -408
  501. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.16.0/design-review-bundle.schema.json +0 -590
  502. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-document.schema.json +0 -506
  503. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.22.0/design-review-bundle.schema.json +0 -822
  504. package/lib/host-packages/openai/openplanr/runtime/design/scripts/runtime/packages/protocol/schemas/v1.9.0/design-review-bundle.schema.json +0 -346
  505. package/lib/host-packages/openai/openplanr/runtime/operate/scripts/errors.mjs +0 -8
  506. package/lib/host-packages/openai/openplanr/runtime/operate/scripts/operate-review-contract.mjs +0 -428
  507. package/lib/host-packages/openai/openplanr/runtime/operate/scripts/operate-review-note.mjs +0 -686
  508. package/lib/host-packages/openai/openplanr/runtime/operate/scripts/validate-note.mjs +0 -39
  509. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design-protocol-contracts.mjs +0 -1232
  510. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/design.mjs +0 -2611
  511. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/notices/THIRD_PARTY_NOTICES.txt +0 -21
  512. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/package.json +0 -163
  513. package/lib/host-packages/openai/openplanr/runtime/plan/scripts/runtime/packages/protocol/schemas/v1.1.0/artifact-review.schema.json +0 -97
  514. package/lib/host-packages/openai/openplanr/skills/ceo-review/scripts/validate-note.mjs +0 -12
  515. package/lib/host-packages/openai/openplanr/skills/chair-review/scripts/validate-note.mjs +0 -12
  516. package/lib/host-packages/openai/openplanr/skills/challenger-review/scripts/validate-note.mjs +0 -12
  517. package/lib/host-packages/openai/openplanr/skills/cmo-review/scripts/validate-note.mjs +0 -12
  518. package/lib/host-packages/openai/openplanr/skills/coo-review/scripts/validate-note.mjs +0 -12
  519. package/lib/host-packages/openai/openplanr/skills/cpo-review/scripts/validate-note.mjs +0 -12
  520. package/lib/host-packages/openai/openplanr/skills/cto-review/scripts/validate-note.mjs +0 -12
  521. package/lib/host-packages/openai/openplanr/skills/design/scripts/design.mjs +0 -12
  522. package/lib/host-packages/openai/openplanr/skills/design-loop/scripts/design.mjs +0 -12
  523. package/lib/host-packages/openai/openplanr/skills/design-review/scripts/design.mjs +0 -12
  524. package/lib/host-packages/openai/openplanr/skills/investigate/scripts/backlog-item.mjs +0 -339
  525. package/lib/host-packages/openai/openplanr/skills/operate/scripts/validate-note.mjs +0 -12
  526. package/lib/host-packages/openai/openplanr/skills/plan/scripts/design.mjs +0 -12
  527. package/lib/host-packages/openai/openplanr/skills/plan/scripts/planning-ids.mjs +0 -166
  528. package/lib/host-packages/openai/openplanr/skills/release/scripts/collect_changes.sh +0 -85
  529. package/lib/host-packages/openai/openplanr/skills/ship/scripts/backlog-item.mjs +0 -339
  530. package/lib/host-packages/openai/openplanr/skills/ship/scripts/discover-verification.mjs +0 -108
@@ -1,2568 +0,0 @@
1
-
2
-
3
- // lib/artifact/ui/feedback-rail.mjs
4
- var ARTIFACT_REVIEW_CHANGE_EVENT = "planr:artifact-review-change";
5
- var ARTIFACT_REVIEW_SELECT_EVENT = "planr:artifact-review-select";
6
- var ARTIFACT_REVIEW_DRAFT_CHANGE_EVENT = "planr:artifact-review-draft-change";
7
- var ARTIFACT_REVIEW_LIMITS = Object.freeze({
8
- id: 128,
9
- authorName: 256,
10
- artifactId: 128,
11
- variant: 128,
12
- anchor: 512,
13
- screen: 128,
14
- text: 65536,
15
- pins: 1e4,
16
- replies: 1e4,
17
- viewport: 16384
18
- });
19
- var ARTIFACT_REVIEW_DECISIONS = Object.freeze([
20
- "pending",
21
- "approved",
22
- "changes_requested"
23
- ]);
24
- var ARTIFACT_REVIEW_INTENTS = Object.freeze(["fix", "improve", "question"]);
25
- var ARTIFACT_REVIEW_STATUSES = Object.freeze(["open", "addressed", "resolved"]);
26
- var REVIEW_OF_RE = /^[a-f0-9]{64}$/;
27
- var ArtifactReviewStateError = class extends Error {
28
- constructor(code, message) {
29
- super(message);
30
- this.name = "ArtifactReviewStateError";
31
- this.code = code;
32
- }
33
- };
34
- function invalid(message) {
35
- throw new ArtifactReviewStateError("E_ARTIFACT_REVIEW_INVALID", message);
36
- }
37
- function identityRequired() {
38
- throw new ArtifactReviewStateError(
39
- "E_ARTIFACT_REVIEW_IDENTITY_REQUIRED",
40
- "Enter your name before adding a comment."
41
- );
42
- }
43
- function clonePlain(value) {
44
- if (Array.isArray(value)) return value.map(clonePlain);
45
- if (!value || typeof value !== "object") return value;
46
- const clone = {};
47
- for (const [key, entry] of Object.entries(value)) clone[key] = clonePlain(entry);
48
- return clone;
49
- }
50
- function cloneFrozenArtifactReview(review) {
51
- return deepFreeze(clonePlain(review));
52
- }
53
- function boundedString(value, label, { min = 0, max, trim = false, pattern } = {}) {
54
- if (typeof value !== "string") invalid(`${label} must be a string.`);
55
- const normalized3 = trim ? value.trim() : value;
56
- if (normalized3.length < min || max !== void 0 && normalized3.length > max) {
57
- invalid(`${label} must contain ${min} through ${max ?? "unlimited"} characters.`);
58
- }
59
- if (pattern && !pattern.test(normalized3)) invalid(`${label} has an invalid format.`);
60
- return normalized3;
61
- }
62
- function optionalString(value, label, options) {
63
- if (value === void 0) return void 0;
64
- return boundedString(value, label, options);
65
- }
66
- function enumValue(value, values, label) {
67
- if (!values.includes(value)) invalid(`${label} must be one of: ${values.join(", ")}.`);
68
- return value;
69
- }
70
- function isoTimestamp(value, label) {
71
- const timestamp = value instanceof Date ? value.toISOString() : value;
72
- if (typeof timestamp !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/.test(timestamp) || !Number.isFinite(Date.parse(timestamp))) {
73
- invalid(`${label} must be an ISO-8601 date-time.`);
74
- }
75
- return timestamp;
76
- }
77
- function dependencyTimestamp(now) {
78
- return isoTimestamp(now(), "now()");
79
- }
80
- function defaultNow() {
81
- return (/* @__PURE__ */ new Date()).toISOString();
82
- }
83
- function createSecureArtifactReviewId(cryptoProvider = globalThis.crypto) {
84
- const randomUuid = cryptoProvider?.randomUUID?.();
85
- if (randomUuid) return randomUuid;
86
- const bytes = new Uint8Array(16);
87
- if (typeof cryptoProvider?.getRandomValues !== "function") {
88
- throw new ArtifactReviewStateError(
89
- "E_ARTIFACT_REVIEW_UUID_UNAVAILABLE",
90
- "Secure UUID generation is unavailable in this browser."
91
- );
92
- }
93
- cryptoProvider.getRandomValues(bytes);
94
- bytes[6] = bytes[6] & 15 | 64;
95
- bytes[8] = bytes[8] & 63 | 128;
96
- const hex = [...bytes].map((value) => value.toString(16).padStart(2, "0")).join("");
97
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
98
- }
99
- function defaultCreateId() {
100
- return createSecureArtifactReviewId();
101
- }
102
- function dependencyId(createId, kind) {
103
- return boundedString(createId(kind), `${kind} id`, {
104
- min: 1,
105
- max: ARTIFACT_REVIEW_LIMITS.id,
106
- trim: true
107
- });
108
- }
109
- function uniqueDependencyId(createId, kind, existingIds) {
110
- for (let attempt = 0; attempt < 100; attempt += 1) {
111
- const candidate = dependencyId(createId, kind);
112
- if (!existingIds.has(candidate)) return candidate;
113
- }
114
- throw new ArtifactReviewStateError(
115
- "E_ARTIFACT_REVIEW_ID_COLLISION",
116
- `Could not create a unique ${kind} id.`
117
- );
118
- }
119
- function normalizeArtifactReviewIdentity(value, { allowEmpty = false } = {}) {
120
- if (value === null || value === void 0 || value === "") {
121
- if (allowEmpty) return null;
122
- identityRequired();
123
- }
124
- const source = typeof value === "string" ? { name: value } : value;
125
- if (!source || typeof source !== "object" || Array.isArray(source)) identityRequired();
126
- const name = boundedString(source.name, "author.name", {
127
- min: 1,
128
- max: ARTIFACT_REVIEW_LIMITS.authorName,
129
- trim: true
130
- });
131
- const id = optionalString(source.id, "author.id", {
132
- min: 1,
133
- max: ARTIFACT_REVIEW_LIMITS.id,
134
- trim: true
135
- });
136
- return deepFreeze(id === void 0 ? { name } : { id, name });
137
- }
138
- function normalizeRegion(region) {
139
- if (!region || typeof region !== "object" || Array.isArray(region)) {
140
- invalid("pin.region must be an object.");
141
- }
142
- const finite3 = (value, label) => {
143
- if (typeof value !== "number" || !Number.isFinite(value)) invalid(`${label} must be finite.`);
144
- return value;
145
- };
146
- const x = finite3(region.x, "pin.region.x");
147
- const y = finite3(region.y, "pin.region.y");
148
- const w = finite3(region.w, "pin.region.w");
149
- const h = finite3(region.h, "pin.region.h");
150
- if (x >= 0 && y >= 0 && w >= 0 && h >= 0 && x + w <= 1 && y + h <= 1) {
151
- return { x, y, w, h };
152
- }
153
- const unit = (value) => Math.round(Math.min(1, Math.max(0, value)) * 1e6) / 1e6;
154
- const boundedX = unit(x);
155
- const boundedY = unit(y);
156
- return {
157
- x: boundedX,
158
- y: boundedY,
159
- w: Math.round(Math.min(unit(w), 1 - boundedX) * 1e6) / 1e6,
160
- h: Math.round(Math.min(unit(h), 1 - boundedY) * 1e6) / 1e6
161
- };
162
- }
163
- function normalizeViewport(viewport) {
164
- if (!viewport || typeof viewport !== "object" || Array.isArray(viewport)) {
165
- invalid("pin.viewport must be an object.");
166
- }
167
- const dimension = (value, label) => {
168
- if (!Number.isInteger(value) || value < 1 || value > ARTIFACT_REVIEW_LIMITS.viewport) {
169
- invalid(`${label} must be an integer from 1 through ${ARTIFACT_REVIEW_LIMITS.viewport}.`);
170
- }
171
- return value;
172
- };
173
- return {
174
- width: dimension(viewport.width, "pin.viewport.width"),
175
- height: dimension(viewport.height, "pin.viewport.height")
176
- };
177
- }
178
- function normalizeAnchor(anchor) {
179
- if (anchor === void 0 || anchor === null) return void 0;
180
- if (typeof anchor !== "object" || Array.isArray(anchor)) invalid("pin.anchor must be an object.");
181
- const planrId = boundedString(anchor.planrId, "pin.anchor.planrId", {
182
- min: 1,
183
- max: ARTIFACT_REVIEW_LIMITS.anchor,
184
- trim: true
185
- });
186
- const screen = optionalString(anchor.screen, "pin.anchor.screen", {
187
- min: 1,
188
- max: ARTIFACT_REVIEW_LIMITS.screen,
189
- trim: true
190
- });
191
- return screen === void 0 ? { planrId } : { planrId, screen };
192
- }
193
- function normalizeReply(reply, label = "reply") {
194
- if (!reply || typeof reply !== "object" || Array.isArray(reply))
195
- invalid(`${label} must be an object.`);
196
- return {
197
- id: boundedString(reply.id, `${label}.id`, {
198
- min: 1,
199
- max: ARTIFACT_REVIEW_LIMITS.id,
200
- trim: true
201
- }),
202
- author: normalizeArtifactReviewIdentity(reply.author),
203
- comment: boundedString(reply.comment, `${label}.comment`, {
204
- min: 1,
205
- max: ARTIFACT_REVIEW_LIMITS.text,
206
- trim: true
207
- }),
208
- createdAt: isoTimestamp(reply.createdAt, `${label}.createdAt`)
209
- };
210
- }
211
- function compareTimestampThenId(left, right) {
212
- const byTime = left.createdAt.localeCompare(right.createdAt);
213
- return byTime === 0 ? left.id.localeCompare(right.id) : byTime;
214
- }
215
- function normalizePin(pin, label = "pin") {
216
- if (!pin || typeof pin !== "object" || Array.isArray(pin)) invalid(`${label} must be an object.`);
217
- if (!Array.isArray(pin.replies) || pin.replies.length > ARTIFACT_REVIEW_LIMITS.replies) {
218
- invalid(`${label}.replies must contain no more than ${ARTIFACT_REVIEW_LIMITS.replies} items.`);
219
- }
220
- const replies = pin.replies.map(
221
- (reply, index) => normalizeReply(reply, `${label}.replies[${index}]`)
222
- );
223
- const replyIds = new Set(replies.map(({ id }) => id));
224
- if (replyIds.size !== replies.length) invalid(`${label}.replies must have unique ids.`);
225
- const normalized3 = {
226
- id: boundedString(pin.id, `${label}.id`, {
227
- min: 1,
228
- max: ARTIFACT_REVIEW_LIMITS.id,
229
- trim: true
230
- }),
231
- author: normalizeArtifactReviewIdentity(pin.author),
232
- artifactId: boundedString(pin.artifactId, `${label}.artifactId`, {
233
- min: 1,
234
- max: ARTIFACT_REVIEW_LIMITS.artifactId,
235
- trim: true
236
- }),
237
- region: normalizeRegion(pin.region),
238
- viewport: normalizeViewport(pin.viewport),
239
- intent: enumValue(pin.intent, ARTIFACT_REVIEW_INTENTS, `${label}.intent`),
240
- status: enumValue(pin.status, ARTIFACT_REVIEW_STATUSES, `${label}.status`),
241
- comment: boundedString(pin.comment, `${label}.comment`, {
242
- min: 1,
243
- max: ARTIFACT_REVIEW_LIMITS.text,
244
- trim: true
245
- }),
246
- replies: replies.sort(compareTimestampThenId),
247
- createdAt: isoTimestamp(pin.createdAt, `${label}.createdAt`),
248
- updatedAt: isoTimestamp(pin.updatedAt, `${label}.updatedAt`)
249
- };
250
- const variant = optionalString(pin.variant, `${label}.variant`, {
251
- min: 1,
252
- max: ARTIFACT_REVIEW_LIMITS.variant,
253
- trim: true
254
- });
255
- const anchor = normalizeAnchor(pin.anchor);
256
- if (variant !== void 0) normalized3.variant = variant;
257
- if (anchor !== void 0) normalized3.anchor = anchor;
258
- return normalized3;
259
- }
260
- function normalizeArtifactReview(review) {
261
- if (!review || typeof review !== "object" || Array.isArray(review)) {
262
- invalid("Artifact review must be an object.");
263
- }
264
- if (!Array.isArray(review.pins) || review.pins.length > ARTIFACT_REVIEW_LIMITS.pins) {
265
- invalid(`review.pins must contain no more than ${ARTIFACT_REVIEW_LIMITS.pins} items.`);
266
- }
267
- const pins = review.pins.map((pin, index) => normalizePin(pin, `review.pins[${index}]`));
268
- const pinIds = new Set(pins.map(({ id }) => id));
269
- if (pinIds.size !== pins.length) invalid("review.pins must have unique ids.");
270
- const normalized3 = {
271
- schemaVersion: enumValue(review.schemaVersion, ["1.0.0"], "review.schemaVersion"),
272
- reviewId: boundedString(review.reviewId, "review.reviewId", {
273
- min: 1,
274
- max: ARTIFACT_REVIEW_LIMITS.id,
275
- trim: true
276
- }),
277
- reviewOf: boundedString(review.reviewOf, "review.reviewOf", {
278
- min: 64,
279
- max: 64,
280
- pattern: REVIEW_OF_RE
281
- }),
282
- decision: enumValue(review.decision, ARTIFACT_REVIEW_DECISIONS, "review.decision"),
283
- overall: boundedString(review.overall, "review.overall", {
284
- max: ARTIFACT_REVIEW_LIMITS.text
285
- }),
286
- pins: pins.sort(compareTimestampThenId)
287
- };
288
- if (review.createdAt !== void 0)
289
- normalized3.createdAt = isoTimestamp(review.createdAt, "review.createdAt");
290
- if (review.updatedAt !== void 0)
291
- normalized3.updatedAt = isoTimestamp(review.updatedAt, "review.updatedAt");
292
- return deepFreeze(normalized3);
293
- }
294
- function createArtifactReview({ reviewId, reviewOf, createId = defaultCreateId } = {}) {
295
- const normalizedReviewId = reviewId === void 0 ? dependencyId(createId, "review") : boundedString(reviewId, "reviewId", {
296
- min: 1,
297
- max: ARTIFACT_REVIEW_LIMITS.id,
298
- trim: true
299
- });
300
- return normalizeArtifactReview({
301
- schemaVersion: "1.0.0",
302
- reviewId: normalizedReviewId,
303
- reviewOf: boundedString(reviewOf, "reviewOf", {
304
- min: 64,
305
- max: 64,
306
- pattern: REVIEW_OF_RE
307
- }),
308
- decision: "pending",
309
- overall: "",
310
- pins: []
311
- });
312
- }
313
- function replacePin(review, pin) {
314
- return review.pins.map((candidate) => candidate.id === pin.id ? pin : candidate);
315
- }
316
- function findPin(review, pinId) {
317
- const normalizedId = boundedString(pinId, "pinId", {
318
- min: 1,
319
- max: ARTIFACT_REVIEW_LIMITS.id,
320
- trim: true
321
- });
322
- const pin = review.pins.find(({ id }) => id === normalizedId);
323
- if (!pin) {
324
- throw new ArtifactReviewStateError(
325
- "E_ARTIFACT_REVIEW_PIN_NOT_FOUND",
326
- `Unknown feedback pin: ${normalizedId}`
327
- );
328
- }
329
- return pin;
330
- }
331
- function preserveOptionalReviewTimestamps(review, next, timestamp) {
332
- if (review.createdAt !== void 0) next.createdAt = review.createdAt;
333
- if (review.updatedAt !== void 0) next.updatedAt = timestamp;
334
- return next;
335
- }
336
- function reduceArtifactReview(review, action, { createId = defaultCreateId, now = defaultNow } = {}) {
337
- const current = normalizeArtifactReview(review);
338
- if (!action || typeof action !== "object" || Array.isArray(action))
339
- invalid("Review action must be an object.");
340
- const timestamp = dependencyTimestamp(now);
341
- let next;
342
- switch (action.type) {
343
- case "add-pin": {
344
- if (current.pins.length >= ARTIFACT_REVIEW_LIMITS.pins) {
345
- invalid(`A review can contain at most ${ARTIFACT_REVIEW_LIMITS.pins} pins.`);
346
- }
347
- const author = normalizeArtifactReviewIdentity(action.author);
348
- const pinInput = action.pin && typeof action.pin === "object" ? action.pin : {};
349
- const pinId = pinInput.id === void 0 ? uniqueDependencyId(createId, "pin", new Set(current.pins.map(({ id }) => id))) : boundedString(pinInput.id, "pin.id", {
350
- min: 1,
351
- max: ARTIFACT_REVIEW_LIMITS.id,
352
- trim: true
353
- });
354
- if (current.pins.some(({ id }) => id === pinId)) {
355
- throw new ArtifactReviewStateError(
356
- "E_ARTIFACT_REVIEW_ID_COLLISION",
357
- `Duplicate pin id: ${pinId}`
358
- );
359
- }
360
- const pin = normalizePin({
361
- ...pinInput,
362
- id: pinId,
363
- author,
364
- status: pinInput.status ?? "open",
365
- replies: [],
366
- createdAt: pinInput.createdAt ?? timestamp,
367
- updatedAt: pinInput.updatedAt ?? timestamp
368
- });
369
- next = preserveOptionalReviewTimestamps(
370
- current,
371
- {
372
- ...current,
373
- pins: [...current.pins, pin]
374
- },
375
- timestamp
376
- );
377
- break;
378
- }
379
- case "add-reply": {
380
- const pin = findPin(current, action.pinId);
381
- if (pin.replies.length >= ARTIFACT_REVIEW_LIMITS.replies) {
382
- invalid(`A feedback thread can contain at most ${ARTIFACT_REVIEW_LIMITS.replies} replies.`);
383
- }
384
- const replyId = action.id === void 0 ? uniqueDependencyId(createId, "reply", new Set(pin.replies.map(({ id }) => id))) : boundedString(action.id, "reply.id", {
385
- min: 1,
386
- max: ARTIFACT_REVIEW_LIMITS.id,
387
- trim: true
388
- });
389
- if (pin.replies.some(({ id }) => id === replyId)) {
390
- throw new ArtifactReviewStateError(
391
- "E_ARTIFACT_REVIEW_ID_COLLISION",
392
- `Duplicate reply id: ${replyId}`
393
- );
394
- }
395
- const reply = normalizeReply({
396
- id: replyId,
397
- author: normalizeArtifactReviewIdentity(action.author),
398
- comment: action.comment,
399
- createdAt: action.createdAt ?? timestamp
400
- });
401
- const updatedPin = normalizePin({
402
- ...pin,
403
- replies: [...pin.replies, reply],
404
- updatedAt: timestamp
405
- });
406
- next = preserveOptionalReviewTimestamps(
407
- current,
408
- {
409
- ...current,
410
- pins: replacePin(current, updatedPin)
411
- },
412
- timestamp
413
- );
414
- break;
415
- }
416
- case "set-status": {
417
- const pin = findPin(current, action.pinId);
418
- const updatedPin = normalizePin({
419
- ...pin,
420
- status: enumValue(action.status, ARTIFACT_REVIEW_STATUSES, "status"),
421
- updatedAt: timestamp
422
- });
423
- next = preserveOptionalReviewTimestamps(
424
- current,
425
- {
426
- ...current,
427
- pins: replacePin(current, updatedPin)
428
- },
429
- timestamp
430
- );
431
- break;
432
- }
433
- case "set-overall":
434
- next = preserveOptionalReviewTimestamps(
435
- current,
436
- {
437
- ...current,
438
- overall: boundedString(action.overall, "overall", { max: ARTIFACT_REVIEW_LIMITS.text })
439
- },
440
- timestamp
441
- );
442
- break;
443
- case "set-decision":
444
- next = preserveOptionalReviewTimestamps(
445
- current,
446
- {
447
- ...current,
448
- decision: enumValue(action.decision, ARTIFACT_REVIEW_DECISIONS, "decision")
449
- },
450
- timestamp
451
- );
452
- break;
453
- default:
454
- throw new ArtifactReviewStateError(
455
- "E_ARTIFACT_REVIEW_ACTION_UNKNOWN",
456
- `Unknown artifact review action: ${String(action.type)}`
457
- );
458
- }
459
- return normalizeArtifactReview(next);
460
- }
461
- function createArtifactReviewController({
462
- initialReview = null,
463
- reviewOf,
464
- reviewId,
465
- identity = null,
466
- createId = defaultCreateId,
467
- now = defaultNow
468
- } = {}) {
469
- let review = initialReview === null || initialReview === void 0 ? null : normalizeArtifactReview(initialReview);
470
- if (review && reviewOf !== void 0 && review.reviewOf !== reviewOf) {
471
- throw new ArtifactReviewStateError(
472
- "E_ARTIFACT_REVIEW_DIGEST_MISMATCH",
473
- "The initial review does not match the artifact envelope digest."
474
- );
475
- }
476
- let localIdentity = normalizeArtifactReviewIdentity(identity, { allowEmpty: true });
477
- let activePinId = null;
478
- let destroyed = false;
479
- const listeners = /* @__PURE__ */ new Set();
480
- const assertAlive = () => {
481
- if (destroyed) {
482
- throw new ArtifactReviewStateError(
483
- "E_ARTIFACT_REVIEW_DESTROYED",
484
- "Artifact review controller is destroyed."
485
- );
486
- }
487
- };
488
- const ensureReview = () => {
489
- review ??= createArtifactReview({ reviewId, reviewOf, createId });
490
- return review;
491
- };
492
- const getState = () => deepFreeze({
493
- review,
494
- identity: localIdentity,
495
- activePinId
496
- });
497
- const notify = (change) => {
498
- const state = getState();
499
- for (const listener of [...listeners]) listener(state, deepFreeze({ ...change }));
500
- };
501
- const controller = {
502
- getReview() {
503
- return review;
504
- },
505
- getState,
506
- getIdentity() {
507
- return localIdentity;
508
- },
509
- setIdentity(value) {
510
- assertAlive();
511
- localIdentity = normalizeArtifactReviewIdentity(value, { allowEmpty: true });
512
- notify({ type: "identity" });
513
- return localIdentity;
514
- },
515
- dispatch(action) {
516
- assertAlive();
517
- const authored = action?.type === "add-pin" || action?.type === "add-reply";
518
- const nextAction = authored && action.author === void 0 ? { ...action, author: localIdentity ?? identityRequired() } : action;
519
- const previousPinIds = nextAction?.type === "add-pin" ? new Set(review?.pins.map(({ id }) => id) ?? []) : null;
520
- review = reduceArtifactReview(ensureReview(), nextAction, { createId, now });
521
- if (previousPinIds) {
522
- activePinId = review.pins.find(({ id }) => !previousPinIds.has(id))?.id ?? activePinId;
523
- }
524
- notify({ type: "review", action: nextAction.type });
525
- return review;
526
- },
527
- replaceReview(value) {
528
- assertAlive();
529
- const next = value === null || value === void 0 ? null : normalizeArtifactReview(value);
530
- if (next && reviewOf !== void 0 && next.reviewOf !== reviewOf) {
531
- throw new ArtifactReviewStateError(
532
- "E_ARTIFACT_REVIEW_DIGEST_MISMATCH",
533
- "The replacement review does not match the artifact envelope digest."
534
- );
535
- }
536
- review = next;
537
- if (activePinId && !review?.pins.some(({ id }) => id === activePinId)) activePinId = null;
538
- notify({ type: "review-replaced" });
539
- return review;
540
- },
541
- selectPin(pinId) {
542
- assertAlive();
543
- if (pinId === null || pinId === void 0) {
544
- activePinId = null;
545
- } else {
546
- activePinId = findPin(ensureReview(), pinId).id;
547
- }
548
- notify({ type: "selection" });
549
- return activePinId;
550
- },
551
- subscribe(listener) {
552
- assertAlive();
553
- if (typeof listener !== "function") invalid("Review subscriber must be a function.");
554
- listeners.add(listener);
555
- return () => listeners.delete(listener);
556
- },
557
- destroy() {
558
- if (destroyed) return;
559
- destroyed = true;
560
- listeners.clear();
561
- review = null;
562
- localIdentity = null;
563
- activePinId = null;
564
- }
565
- };
566
- return Object.freeze(controller);
567
- }
568
- function createElement(document2, tagName, { className, text: text2, attributes = {} } = {}) {
569
- const element = document2.createElement(tagName);
570
- if (className) element.className = className;
571
- if (text2 !== void 0) element.textContent = text2;
572
- for (const [name, value] of Object.entries(attributes)) {
573
- if (value !== void 0 && value !== null) element.setAttribute(name, String(value));
574
- }
575
- return element;
576
- }
577
- function artifactReviewThreadDomId(pinId) {
578
- return annotationDomIds(pinId).thread;
579
- }
580
- function displayTimestamp(timestamp) {
581
- const canonical = new Date(timestamp).toISOString();
582
- return `${canonical.slice(0, 10)} ${canonical.slice(11, 16)} UTC`;
583
- }
584
- function renderReply(document2, reply) {
585
- const item = createElement(document2, "li", {
586
- className: "planr-reply",
587
- attributes: { "data-planr-reply-id": reply.id }
588
- });
589
- const heading = createElement(document2, "header");
590
- heading.append(createElement(document2, "strong", { text: reply.author.name }));
591
- const time = createElement(document2, "time", {
592
- text: displayTimestamp(reply.createdAt),
593
- attributes: { datetime: reply.createdAt }
594
- });
595
- heading.append(time);
596
- item.append(heading, createElement(document2, "p", { text: reply.comment }));
597
- return item;
598
- }
599
- function renderReplyForm(document2, pin, expanded = false) {
600
- const fieldId = `${annotationDomIds(pin.id).thread}-reply`;
601
- const wrapper = createElement(document2, "div", { className: "planr-reply-editor" });
602
- const toggle = createElement(document2, "button", {
603
- className: "planr-reply-toggle",
604
- text: expanded ? "− Reply" : "+ Reply",
605
- attributes: {
606
- type: "button",
607
- id: `${fieldId}-toggle`,
608
- "data-planr-reply-toggle": pin.id,
609
- "aria-expanded": String(expanded),
610
- "aria-controls": `${fieldId}-form`,
611
- title: expanded ? "Collapse reply" : "Reply to this comment"
612
- }
613
- });
614
- const form = createElement(document2, "form", {
615
- className: "planr-reply-form",
616
- attributes: {
617
- "data-planr-reply-form": pin.id,
618
- id: `${fieldId}-form`,
619
- ...expanded ? {} : { hidden: "" }
620
- }
621
- });
622
- const label = createElement(document2, "label", {
623
- className: "planr-reply-label",
624
- text: "Reply to thread",
625
- attributes: { for: fieldId }
626
- });
627
- const textarea = createElement(document2, "textarea", {
628
- attributes: {
629
- id: fieldId,
630
- name: "reply",
631
- maxlength: ARTIFACT_REVIEW_LIMITS.text,
632
- rows: 2,
633
- placeholder: "Write a reply…",
634
- required: "",
635
- "aria-describedby": "planr-review-error"
636
- }
637
- });
638
- const controls = createElement(document2, "div", { className: "planr-reply-controls" });
639
- const hint = createElement(document2, "span", {
640
- className: "planr-reply-hint",
641
- text: "Ctrl/⌘ + Enter to send"
642
- });
643
- const submit = createElement(document2, "button", {
644
- className: "planr-reply-send",
645
- attributes: {
646
- type: "submit",
647
- disabled: "",
648
- "aria-label": "Send reply",
649
- title: "Send reply (Ctrl/⌘ + Enter)"
650
- }
651
- });
652
- const svg = document2.createElementNS("http://www.w3.org/2000/svg", "svg");
653
- for (const [name, value] of Object.entries({
654
- viewBox: "0 0 24 24",
655
- width: "16",
656
- height: "16",
657
- fill: "none",
658
- stroke: "currentColor",
659
- "stroke-width": "1.8",
660
- "stroke-linecap": "round",
661
- "stroke-linejoin": "round",
662
- "aria-hidden": "true",
663
- focusable: "false"
664
- }))
665
- svg.setAttribute(name, value);
666
- const path = document2.createElementNS("http://www.w3.org/2000/svg", "path");
667
- path.setAttribute("d", "M12 19V5m-6 6 6-6 6 6");
668
- svg.append(path);
669
- submit.append(svg);
670
- controls.append(hint, submit);
671
- form.append(label, textarea, controls);
672
- wrapper.append(toggle, form);
673
- return wrapper;
674
- }
675
- function renderThread(document2, pin, active, description = {}) {
676
- const intentLabel = typeof description.intentLabel === "string" ? description.intentLabel.slice(0, 128) : pin.intent;
677
- const article = createElement(document2, "article", {
678
- className: `planr-thread${active ? " is-active" : ""}`,
679
- attributes: {
680
- id: artifactReviewThreadDomId(pin.id),
681
- tabindex: "-1",
682
- "data-planr-pin-id": pin.id,
683
- "data-planr-intent": pin.intent,
684
- "data-planr-status": pin.status,
685
- "aria-controls": annotationDomIds(pin.id).pin,
686
- "aria-label": `${intentLabel} comment by ${pin.author.name}`,
687
- "data-planr-unread": description.unread === true ? "true" : "false",
688
- ...description.compact ? { "data-planr-compact": "true" } : {}
689
- }
690
- });
691
- const header = createElement(document2, "header");
692
- const byline = createElement(document2, "div", { className: "planr-review-byline" });
693
- byline.append(
694
- createElement(document2, "strong", { text: pin.author.name }),
695
- createElement(document2, "span", { className: "planr-intent", text: intentLabel })
696
- );
697
- const time = createElement(document2, "time", {
698
- text: description.compact ? `${new Date(pin.createdAt).toISOString().slice(11, 16)} UTC` : displayTimestamp(pin.createdAt),
699
- attributes: { datetime: pin.createdAt, title: displayTimestamp(pin.createdAt) }
700
- });
701
- header.append(byline, time);
702
- const comment = createElement(document2, "p", {
703
- className: "planr-thread-comment",
704
- text: pin.comment
705
- });
706
- const lifecycle = createElement(document2, "div", { className: "planr-thread-actions" });
707
- lifecycle.append(
708
- createElement(document2, "span", {
709
- className: "planr-thread-status",
710
- text: pin.status,
711
- attributes: { title: `Status: ${pin.status}` }
712
- }),
713
- createElement(document2, "button", {
714
- text: pin.status === "resolved" ? "Reopen" : "Resolve",
715
- attributes: {
716
- type: "button",
717
- "data-planr-thread-action": pin.status === "resolved" ? "reopen" : "resolve",
718
- "data-planr-pin-id": pin.id
719
- }
720
- }),
721
- createElement(document2, "button", {
722
- text: "Show pin",
723
- attributes: {
724
- type: "button",
725
- "data-planr-thread-focus": pin.id,
726
- "aria-controls": annotationDomIds(pin.id).pin
727
- }
728
- })
729
- );
730
- const replies = createElement(document2, "ol", {
731
- className: "planr-replies",
732
- attributes: { "aria-label": "Replies" }
733
- });
734
- const earlierReplies = description.compact ? Math.max(0, pin.replies.length - (description.replyLimit || 2)) : 0;
735
- for (const reply of pin.replies.slice(earlierReplies))
736
- replies.append(renderReply(document2, reply));
737
- const editor = renderReplyForm(document2, pin, description.replyExpanded);
738
- article.append(header, comment);
739
- if (description.compact && pin.comment.length > 240) {
740
- comment.dataset.planrCommentCollapsed = String(!description.commentExpanded);
741
- article.append(
742
- createElement(document2, "button", {
743
- className: "planr-comment-expand",
744
- text: description.commentExpanded ? "Show less" : "Read full comment",
745
- attributes: {
746
- type: "button",
747
- id: `${annotationDomIds(pin.id).thread}-expand`,
748
- "data-planr-comment-expand": pin.id,
749
- "aria-expanded": String(Boolean(description.commentExpanded))
750
- }
751
- })
752
- );
753
- }
754
- if (earlierReplies)
755
- article.append(
756
- createElement(document2, "button", {
757
- className: "planr-history-expand",
758
- text: `Show ${earlierReplies} earlier ${earlierReplies === 1 ? "reply" : "replies"}`,
759
- attributes: {
760
- type: "button",
761
- id: `${annotationDomIds(pin.id).thread}-history`,
762
- "data-planr-history-expand": pin.id,
763
- "aria-expanded": "false"
764
- }
765
- })
766
- );
767
- if (pin.replies.length) article.append(replies);
768
- if (description.compact) {
769
- lifecycle.append(editor.querySelector("[data-planr-reply-toggle]"));
770
- article.append(lifecycle, editor);
771
- } else article.append(lifecycle, editor);
772
- return article;
773
- }
774
- function decisionCopy(decision) {
775
- if (decision === "approved") return "Review approved";
776
- if (decision === "changes_requested") return "Changes requested";
777
- return "Decision pending";
778
- }
779
- function mountArtifactFeedbackRail({
780
- root,
781
- document: document2 = root?.ownerDocument,
782
- window = document2?.defaultView,
783
- controller: providedController,
784
- initialReview = null,
785
- reviewOf,
786
- reviewId,
787
- identity = null,
788
- createId = defaultCreateId,
789
- now = defaultNow,
790
- onSelectPin,
791
- presentation: initialPresentation = {}
792
- } = {}) {
793
- if (!root || !document2 || !window) invalid("A browser root, document, and window are required.");
794
- const slot = root.querySelector('[data-planr-slot="feedback-rail"]');
795
- const identityInput = root.querySelector("[data-planr-reviewer-name]");
796
- const identityStatus = root.querySelector("[data-planr-identity-status]");
797
- const overall = root.querySelector("#planr-overall-note");
798
- const decisionStatus = root.querySelector('[data-planr-slot="decision-status"]');
799
- const decisionButtons = [...root.querySelectorAll("[data-planr-decision]")];
800
- if (!slot || !identityInput || !overall || !decisionStatus || decisionButtons.length === 0) {
801
- invalid("Artifact feedback renderer slots are missing.");
802
- }
803
- const ownsController = !providedController;
804
- const controller = providedController ?? createArtifactReviewController({
805
- initialReview,
806
- reviewOf,
807
- reviewId,
808
- identity,
809
- createId,
810
- now
811
- });
812
- let destroyed = false;
813
- let presentation = { ...initialPresentation };
814
- const replyDrafts = /* @__PURE__ */ new Map();
815
- const expandedReplies = /* @__PURE__ */ new Set();
816
- const expandedHistory = /* @__PURE__ */ new Map(), expandedComments = /* @__PURE__ */ new Set();
817
- let visibleLimit = Number.isInteger(initialPresentation.pageSize) ? initialPresentation.pageSize : Infinity;
818
- const extraDrafts = /* @__PURE__ */ new Map();
819
- let overallDirty = false;
820
- let composing = false;
821
- let renderQueued = false;
822
- const captureDrafts = () => {
823
- for (const form of slot.querySelectorAll("[data-planr-reply-form]")) {
824
- const field = form.elements.namedItem("reply");
825
- if (field) replyDrafts.set(form.dataset.planrReplyForm, field.value);
826
- }
827
- for (const field of slot.querySelectorAll("[data-planr-draft-key]")) {
828
- if (typeof field.value === "string")
829
- extraDrafts.set(field.dataset.planrDraftKey, field.value);
830
- }
831
- };
832
- const snapshotDrafts = () => {
833
- captureDrafts();
834
- return cloneFrozenArtifactReview({
835
- reviewOf: controller.getReview()?.reviewOf ?? reviewOf,
836
- replies: Object.fromEntries(replyDrafts),
837
- expandedReplies: [...expandedReplies],
838
- fields: Object.fromEntries(extraDrafts),
839
- overall: { value: overall.value, dirty: overallDirty },
840
- identity: identityInput.value
841
- });
842
- };
843
- const emitDraftChange = () => root.dispatchEvent(
844
- new window.CustomEvent(ARTIFACT_REVIEW_DRAFT_CHANGE_EVENT, {
845
- bubbles: true,
846
- detail: snapshotDrafts()
847
- })
848
- );
849
- const focusDescriptor = () => {
850
- const element = document2.activeElement;
851
- if (!element || !slot.contains(element)) return null;
852
- const thread = element.closest("[data-planr-pin-id]");
853
- return {
854
- id: element.id,
855
- pinId: thread?.dataset.planrPinId,
856
- draftKey: element.dataset.planrDraftKey,
857
- action: element.dataset.planrThreadAction,
858
- showPin: element.dataset.planrThreadFocus,
859
- tag: element.tagName,
860
- name: element.name,
861
- start: element.selectionStart,
862
- end: element.selectionEnd,
863
- direction: element.selectionDirection
864
- };
865
- };
866
- const restoreFocus = (descriptor) => {
867
- if (!descriptor) return;
868
- const thread = descriptor.pinId && document2.getElementById(artifactReviewThreadDomId(descriptor.pinId));
869
- const target = descriptor.id && document2.getElementById(descriptor.id) || descriptor.draftKey && [...slot.querySelectorAll("[data-planr-draft-key]")].find(
870
- (field) => field.dataset.planrDraftKey === descriptor.draftKey
871
- ) || descriptor.action && [...thread?.querySelectorAll("[data-planr-thread-action]") ?? []].find(
872
- (button) => button.dataset.planrThreadAction === descriptor.action
873
- ) || descriptor.showPin && thread?.querySelector("[data-planr-thread-focus]") || [...thread?.querySelectorAll("button,input,select,textarea") ?? []].find(
874
- (field) => field.tagName === descriptor.tag && field.name === descriptor.name
875
- );
876
- target?.focus?.({ preventScroll: true });
877
- if (Number.isInteger(descriptor.start))
878
- target?.setSelectionRange?.(descriptor.start, descriptor.end, descriptor.direction);
879
- };
880
- const showError = (error) => {
881
- const target = root.querySelector("#planr-review-error");
882
- if (!target) return;
883
- target.textContent = error?.message ?? String(error);
884
- target.hidden = false;
885
- };
886
- const clearError = () => {
887
- const target = root.querySelector("#planr-review-error");
888
- if (!target) return;
889
- target.textContent = "";
890
- target.hidden = true;
891
- };
892
- const announce2 = (message) => {
893
- decisionStatus.textContent = message;
894
- const live = root.parentElement?.querySelector('[data-planr-slot="review-announcer"]') ?? document2.querySelector('[data-planr-slot="review-announcer"]');
895
- if (live) live.textContent = message;
896
- };
897
- const updateCounts = (pins) => {
898
- const label = `${pins.length} ${pins.length === 1 ? "comment" : "comments"}`;
899
- for (const count of root.querySelectorAll(
900
- '[data-planr-action="feedback"] .planr-count, .planr-review-rail > header .planr-count'
901
- )) {
902
- count.textContent = String(pins.length);
903
- count.setAttribute("aria-label", label);
904
- }
905
- const commentsButton = root.querySelector('[data-planr-action="feedback"]');
906
- commentsButton?.setAttribute("aria-label", commentsButton.dataset.planrReviewLabel || label);
907
- if (commentsButton?.dataset.planrReviewLabel)
908
- commentsButton.setAttribute("aria-description", label);
909
- };
910
- const render = ({ capture = true } = {}) => {
911
- if (composing) {
912
- renderQueued = true;
913
- return;
914
- }
915
- if (capture) captureDrafts();
916
- const focus = focusDescriptor();
917
- const scroll = [];
918
- for (let node = slot; node && root.contains(node); node = node.parentElement)
919
- scroll.push([node, node.scrollTop, node.scrollLeft]);
920
- const state = controller.getState();
921
- const { review, activePinId } = state;
922
- const allPins = review?.pins ?? [];
923
- const pins = typeof presentation.filterPin === "function" ? allPins.filter((pin) => presentation.filterPin(pin, state)) : allPins;
924
- const activeIndex = pins.findIndex((pin) => pin.id === activePinId);
925
- const visiblePins = pins.slice(0, visibleLimit);
926
- if (activeIndex >= visibleLimit) visiblePins.push(pins[activeIndex]);
927
- const fragment = document2.createDocumentFragment();
928
- const list = createElement(document2, "div", {
929
- className: "planr-thread-list",
930
- attributes: { "aria-label": "Comment threads" }
931
- });
932
- if (pins.length === 0) {
933
- list.append(
934
- createElement(document2, "p", {
935
- className: "planr-review-empty",
936
- text: typeof presentation.emptyMessage === "string" ? presentation.emptyMessage : allPins.length ? "No comments match these filters." : "No comments yet. Choose Add comment, then select a point or region in the artifact."
937
- })
938
- );
939
- } else {
940
- for (const pin of visiblePins) {
941
- const thread = renderThread(document2, pin, pin.id === activePinId, {
942
- ...presentation.describePin?.(pin, state),
943
- compact: presentation.compact === true,
944
- replyLimit: expandedHistory.get(pin.id) || 2,
945
- commentExpanded: expandedComments.has(pin.id),
946
- replyExpanded: expandedReplies.has(pin.id)
947
- });
948
- presentation.decorateThread?.({ element: thread, pin, state, document: document2 });
949
- const reply = thread.querySelector('[name="reply"]');
950
- if (replyDrafts.has(pin.id)) reply.value = replyDrafts.get(pin.id);
951
- thread.querySelector(".planr-reply-send").disabled = !reply.value.trim();
952
- for (const field of thread.querySelectorAll("[data-planr-draft-key]")) {
953
- if (extraDrafts.has(field.dataset.planrDraftKey))
954
- field.value = extraDrafts.get(field.dataset.planrDraftKey);
955
- }
956
- list.append(thread);
957
- }
958
- }
959
- if (pins.length > visibleLimit)
960
- list.append(
961
- createElement(document2, "button", {
962
- className: "planr-threads-more",
963
- text: `Show more comments · ${Math.min(visibleLimit, pins.length)} of ${pins.length}`,
964
- attributes: { type: "button", "data-planr-threads-more": "" }
965
- })
966
- );
967
- fragment.append(list);
968
- slot.replaceChildren(fragment);
969
- const identityName2 = controller.getIdentity()?.name ?? "";
970
- if (document2.activeElement !== identityInput) identityInput.value = identityName2;
971
- if (identityStatus) {
972
- identityStatus.dataset.planrIdentityReady = String(Boolean(identityName2));
973
- identityStatus.textContent = identityName2 ? `Comments will appear as ${identityName2}.` : "Used to sign your comments.";
974
- }
975
- overall.maxLength = ARTIFACT_REVIEW_LIMITS.text;
976
- if (!overallDirty && document2.activeElement !== overall) overall.value = review?.overall ?? "";
977
- for (const button of decisionButtons) {
978
- button.setAttribute(
979
- "aria-pressed",
980
- String(button.dataset.planrDecision === (review?.decision ?? "pending"))
981
- );
982
- }
983
- decisionStatus.textContent = decisionCopy(review?.decision ?? "pending");
984
- updateCounts(allPins);
985
- restoreFocus(focus);
986
- for (const [node, top, left] of scroll) {
987
- node.scrollTop = top;
988
- node.scrollLeft = left;
989
- }
990
- const openMetric = root.querySelector('[data-planr-metric="open"]');
991
- if (openMetric)
992
- openMetric.textContent = `${allPins.filter(({ status }) => status !== "resolved").length} open`;
993
- };
994
- const focusThread = (pinId) => {
995
- const thread = document2.getElementById(artifactReviewThreadDomId(pinId));
996
- thread?.focus({ preventScroll: true });
997
- thread?.scrollIntoView?.({ block: "nearest" });
998
- };
999
- const emitReview = (review) => {
1000
- const detail = cloneFrozenArtifactReview(review);
1001
- root.dispatchEvent(
1002
- new window.CustomEvent(ARTIFACT_REVIEW_CHANGE_EVENT, {
1003
- detail,
1004
- bubbles: true
1005
- })
1006
- );
1007
- };
1008
- const unsubscribe = controller.subscribe((state, change) => {
1009
- if (destroyed) return;
1010
- if (["review", "review-replaced", "selection"].includes(change.type)) render();
1011
- if (["review", "review-replaced"].includes(change.type) && state.review)
1012
- emitReview(state.review);
1013
- });
1014
- const onInput = (event) => {
1015
- if (event.target !== identityInput) return;
1016
- try {
1017
- controller.setIdentity(event.target.value ? { name: event.target.value } : null);
1018
- const name = controller.getIdentity()?.name ?? "";
1019
- if (identityStatus) {
1020
- identityStatus.dataset.planrIdentityReady = String(Boolean(name));
1021
- identityStatus.textContent = name ? `Comments will appear as ${name}.` : "Used to sign your comments.";
1022
- }
1023
- clearError();
1024
- } catch (error) {
1025
- showError(error);
1026
- }
1027
- };
1028
- const setReplyExpanded = (pinId, expanded, { focus = false } = {}) => {
1029
- const thread = document2.getElementById(artifactReviewThreadDomId(pinId));
1030
- const form = thread?.querySelector("[data-planr-reply-form]");
1031
- const toggle = thread?.querySelector("[data-planr-reply-toggle]");
1032
- if (!form || !toggle) return;
1033
- if (expanded) expandedReplies.add(pinId);
1034
- else expandedReplies.delete(pinId);
1035
- form.hidden = !expanded;
1036
- toggle.setAttribute("aria-expanded", String(expanded));
1037
- toggle.textContent = expanded ? "− Reply" : "+ Reply";
1038
- toggle.title = expanded ? "Collapse reply" : "Reply to this comment";
1039
- if (focus)
1040
- (expanded ? form.elements.namedItem("reply") : toggle).focus({ preventScroll: true });
1041
- emitDraftChange();
1042
- };
1043
- const onKeyDown = (event) => {
1044
- const form = event.target?.closest?.("[data-planr-reply-form]");
1045
- if (!form || event.isComposing) return;
1046
- if (event.key === "Escape") {
1047
- event.preventDefault();
1048
- event.stopPropagation();
1049
- setReplyExpanded(form.dataset.planrReplyForm, false, { focus: true });
1050
- } else if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
1051
- event.preventDefault();
1052
- if (form.elements.namedItem("reply").value.trim()) form.requestSubmit();
1053
- }
1054
- };
1055
- const emitSelection = (pinId) => {
1056
- controller.selectPin(pinId);
1057
- root.dispatchEvent(
1058
- new window.CustomEvent(ARTIFACT_REVIEW_SELECT_EVENT, {
1059
- detail: deepFreeze({ pinId, source: "thread" }),
1060
- bubbles: true
1061
- })
1062
- );
1063
- onSelectPin?.(pinId);
1064
- };
1065
- const onClick = (event) => {
1066
- if (event.target.closest?.("[data-planr-threads-more]")) {
1067
- visibleLimit += presentation.pageSize || 40;
1068
- render();
1069
- return;
1070
- }
1071
- const historyToggle = event.target.closest?.("[data-planr-history-expand]");
1072
- if (historyToggle) {
1073
- const id = historyToggle.dataset.planrHistoryExpand;
1074
- expandedHistory.set(id, (expandedHistory.get(id) || 2) + 20);
1075
- render();
1076
- return;
1077
- }
1078
- const commentToggle = event.target.closest?.("[data-planr-comment-expand]");
1079
- if (commentToggle) {
1080
- const id = commentToggle.dataset.planrCommentExpand;
1081
- if (expandedComments.has(id)) expandedComments.delete(id);
1082
- else expandedComments.add(id);
1083
- render();
1084
- return;
1085
- }
1086
- const replyToggle = event.target?.closest?.("[data-planr-reply-toggle]");
1087
- if (replyToggle) {
1088
- setReplyExpanded(
1089
- replyToggle.dataset.planrReplyToggle,
1090
- replyToggle.getAttribute("aria-expanded") !== "true",
1091
- { focus: true }
1092
- );
1093
- return;
1094
- }
1095
- const action = event.target?.closest?.("[data-planr-thread-action]");
1096
- const focus = event.target?.closest?.("[data-planr-thread-focus]");
1097
- if (action) {
1098
- try {
1099
- const pinId = action.dataset.planrPinId;
1100
- controller.dispatch({
1101
- type: "set-status",
1102
- pinId,
1103
- status: action.dataset.planrThreadAction === "resolve" ? "resolved" : "open"
1104
- });
1105
- announce2(
1106
- action.dataset.planrThreadAction === "resolve" ? "Comment resolved" : "Comment reopened"
1107
- );
1108
- focusThread(pinId);
1109
- clearError();
1110
- } catch (error) {
1111
- showError(error);
1112
- }
1113
- return;
1114
- }
1115
- if (focus) emitSelection(focus.dataset.planrThreadFocus);
1116
- };
1117
- const onSubmit = (event) => {
1118
- const form = event.target?.closest?.("[data-planr-reply-form]");
1119
- if (!form) return;
1120
- event.preventDefault();
1121
- const textarea = form.elements.namedItem("reply");
1122
- const comment = textarea.value;
1123
- if (!comment.trim()) return;
1124
- const wasExpanded = expandedReplies.has(form.dataset.planrReplyForm);
1125
- try {
1126
- textarea.setAttribute("aria-invalid", "false");
1127
- textarea.value = "";
1128
- replyDrafts.delete(form.dataset.planrReplyForm);
1129
- expandedReplies.delete(form.dataset.planrReplyForm);
1130
- controller.dispatch({
1131
- type: "add-reply",
1132
- pinId: form.dataset.planrReplyForm,
1133
- comment
1134
- });
1135
- emitDraftChange();
1136
- announce2("Reply added");
1137
- focusThread(form.dataset.planrReplyForm);
1138
- clearError();
1139
- } catch (error) {
1140
- textarea.value = comment;
1141
- replyDrafts.set(form.dataset.planrReplyForm, comment);
1142
- if (wasExpanded) expandedReplies.add(form.dataset.planrReplyForm);
1143
- textarea?.setAttribute("aria-invalid", "true");
1144
- showError(error);
1145
- textarea?.focus();
1146
- }
1147
- };
1148
- const onOverallChange = () => {
1149
- try {
1150
- const value = overall.value;
1151
- controller.dispatch({ type: "set-overall", overall: value });
1152
- overallDirty = false;
1153
- announce2("Overall note updated");
1154
- clearError();
1155
- } catch (error) {
1156
- showError(error);
1157
- }
1158
- };
1159
- const onDecision = (event) => {
1160
- try {
1161
- const selected = event.currentTarget.dataset.planrDecision;
1162
- const decision = controller.getReview()?.decision === selected ? "pending" : selected;
1163
- controller.dispatch({ type: "set-decision", decision });
1164
- announce2(decisionCopy(decision));
1165
- clearError();
1166
- } catch (error) {
1167
- showError(error);
1168
- }
1169
- };
1170
- const onSelect = (event) => {
1171
- if (event.detail?.source === "thread" || typeof event.detail?.pinId !== "string") return;
1172
- try {
1173
- controller.selectPin(event.detail.pinId);
1174
- focusThread(event.detail.pinId);
1175
- } catch (error) {
1176
- showError(error);
1177
- }
1178
- };
1179
- let lastOpened = null;
1180
- const onThreadOpen = (event) => {
1181
- const thread = event.target.closest?.(".planr-thread[data-planr-pin-id]");
1182
- if (!thread || !slot.contains(thread)) return;
1183
- const pin = controller.getReview()?.pins.find((entry) => entry.id === thread.dataset.planrPinId);
1184
- const key = pin && `${pin.id}:${pin.replies.map((reply) => reply.id).join(",")}`;
1185
- if (!key || lastOpened === key) return;
1186
- lastOpened = key;
1187
- presentation.onThreadOpen?.(pin.id, controller.getState());
1188
- root.dispatchEvent(
1189
- new window.CustomEvent("planr:artifact-thread-open", {
1190
- bubbles: true,
1191
- detail: Object.freeze({ pinId: pin.id })
1192
- })
1193
- );
1194
- };
1195
- const onDraftInput = (event) => {
1196
- const form = event.target.closest?.("[data-planr-reply-form]");
1197
- if (form)
1198
- form.querySelector(".planr-reply-send").disabled = !form.elements.namedItem("reply").value.trim();
1199
- emitDraftChange();
1200
- };
1201
- const onOverallInput = () => {
1202
- overallDirty = true;
1203
- emitDraftChange();
1204
- };
1205
- const onCompositionStart = () => {
1206
- composing = true;
1207
- };
1208
- const onCompositionEnd = () => {
1209
- composing = false;
1210
- if (renderQueued) {
1211
- renderQueued = false;
1212
- render();
1213
- }
1214
- };
1215
- slot.addEventListener("input", onDraftInput);
1216
- slot.addEventListener("focusin", onThreadOpen);
1217
- slot.addEventListener("click", onThreadOpen);
1218
- slot.addEventListener("compositionstart", onCompositionStart);
1219
- slot.addEventListener("compositionend", onCompositionEnd);
1220
- overall.addEventListener("input", onOverallInput);
1221
- identityInput.addEventListener("input", onInput);
1222
- slot.addEventListener("click", onClick);
1223
- slot.addEventListener("submit", onSubmit);
1224
- slot.addEventListener("keydown", onKeyDown);
1225
- overall.addEventListener("change", onOverallChange);
1226
- for (const button of decisionButtons) button.addEventListener("click", onDecision);
1227
- root.addEventListener(ARTIFACT_REVIEW_SELECT_EVENT, onSelect);
1228
- render();
1229
- return Object.freeze({
1230
- controller,
1231
- getReview: () => controller.getReview(),
1232
- getState: () => controller.getState(),
1233
- getIdentity: () => controller.getIdentity(),
1234
- setIdentity: (value) => controller.setIdentity(value),
1235
- dispatch: (action) => controller.dispatch(action),
1236
- replaceReview: (review) => controller.replaceReview(review),
1237
- selectPin: (pinId) => controller.selectPin(pinId),
1238
- render,
1239
- setPresentation(options = {}) {
1240
- if (options.filterKey !== presentation.filterKey || options.pageSize !== void 0 && options.pageSize !== presentation.pageSize)
1241
- visibleLimit = options.pageSize || presentation.pageSize || Infinity;
1242
- presentation = { ...presentation, ...options };
1243
- render();
1244
- },
1245
- snapshotDrafts,
1246
- getReviewOf: () => controller.getReview()?.reviewOf ?? reviewOf,
1247
- restoreDrafts(snapshot) {
1248
- const digest = controller.getReview()?.reviewOf ?? reviewOf;
1249
- if (!snapshot || snapshot.reviewOf !== digest) return false;
1250
- if (typeof snapshot.identity === "string") {
1251
- const name = snapshot.identity.slice(0, ARTIFACT_REVIEW_LIMITS.authorName);
1252
- controller.setIdentity(name.trim() ? { ...controller.getIdentity(), name } : null);
1253
- }
1254
- for (const [id, value] of Object.entries(snapshot.replies ?? {}).slice(
1255
- 0,
1256
- ARTIFACT_REVIEW_LIMITS.pins
1257
- )) {
1258
- if (id.length <= ARTIFACT_REVIEW_LIMITS.id && typeof value === "string")
1259
- replyDrafts.set(id, value.slice(0, ARTIFACT_REVIEW_LIMITS.text));
1260
- }
1261
- expandedReplies.clear();
1262
- const expanded = Array.isArray(snapshot.expandedReplies) ? snapshot.expandedReplies : [...replyDrafts.keys()];
1263
- for (const id of expanded.slice(0, ARTIFACT_REVIEW_LIMITS.pins))
1264
- if (typeof id === "string" && id.length <= ARTIFACT_REVIEW_LIMITS.id)
1265
- expandedReplies.add(id);
1266
- for (const [key, value] of Object.entries(snapshot.fields ?? {}).slice(
1267
- 0,
1268
- ARTIFACT_REVIEW_LIMITS.pins
1269
- )) {
1270
- if (key.length <= 512 && typeof value === "string")
1271
- extraDrafts.set(key, value.slice(0, ARTIFACT_REVIEW_LIMITS.text));
1272
- }
1273
- overallDirty = snapshot.overall?.dirty === true;
1274
- if (overallDirty && typeof snapshot.overall?.value === "string")
1275
- overall.value = snapshot.overall.value.slice(0, ARTIFACT_REVIEW_LIMITS.text);
1276
- render({ capture: false });
1277
- return true;
1278
- },
1279
- focusThread,
1280
- destroy() {
1281
- if (destroyed) return;
1282
- destroyed = true;
1283
- unsubscribe();
1284
- slot.removeEventListener("input", onDraftInput);
1285
- slot.removeEventListener("focusin", onThreadOpen);
1286
- slot.removeEventListener("click", onThreadOpen);
1287
- slot.removeEventListener("compositionstart", onCompositionStart);
1288
- slot.removeEventListener("compositionend", onCompositionEnd);
1289
- overall.removeEventListener("input", onOverallInput);
1290
- identityInput.removeEventListener("input", onInput);
1291
- slot.removeEventListener("click", onClick);
1292
- slot.removeEventListener("submit", onSubmit);
1293
- slot.removeEventListener("keydown", onKeyDown);
1294
- overall.removeEventListener("change", onOverallChange);
1295
- for (const button of decisionButtons) button.removeEventListener("click", onDecision);
1296
- root.removeEventListener(ARTIFACT_REVIEW_SELECT_EVENT, onSelect);
1297
- if (ownsController) controller.destroy();
1298
- }
1299
- });
1300
- }
1301
-
1302
- // local-stage-stub:./hosted-viewer.mjs
1303
- function mountHostedArtifactViewer() {
1304
- return null;
1305
- }
1306
-
1307
- // local-stage-stub:./share-dialog.mjs
1308
- function mountArtifactShareDialog({ root } = {}) {
1309
- const trigger = root?.querySelector?.('[data-planr-action="share"]');
1310
- if (trigger && !trigger.hasAttribute("data-planr-share-adapter")) trigger.hidden = true;
1311
- return null;
1312
- }
1313
-
1314
- // lib/artifact/ui/stage-mount.mjs
1315
- var ARTIFACT_STAGE_MOUNT_EVENT = "planr:artifact-stage-mount";
1316
- function publishArtifactStage(window, stage) {
1317
- window.__openPlanrArtifactStage = stage;
1318
- window.dispatchEvent(new window.CustomEvent(ARTIFACT_STAGE_MOUNT_EVENT, { detail: stage }));
1319
- }
1320
-
1321
- // lib/artifact/ui/stage-payload.mjs
1322
- var PRESENTATIONS = Object.freeze(["document", "canvas"]);
1323
- function positiveInteger(value, fallback) {
1324
- return Number.isInteger(value) && value > 0 ? value : fallback;
1325
- }
1326
- function freezeArtifactMetadata(artifact, index) {
1327
- if (!artifact || typeof artifact !== "object") {
1328
- throw new TypeError(`Artifact ${index + 1} must be an object.`);
1329
- }
1330
- if (typeof artifact.id !== "string" || artifact.id.length === 0) {
1331
- throw new TypeError(`Artifact ${index + 1} requires an id.`);
1332
- }
1333
- return Object.freeze({
1334
- id: artifact.id,
1335
- title: typeof artifact.title === "string" && artifact.title.length > 0 ? artifact.title : `Artifact ${index + 1}`,
1336
- sha256: typeof artifact.sha256 === "string" ? artifact.sha256 : "",
1337
- viewport: Object.freeze({
1338
- width: positiveInteger(artifact.viewport?.width, 1440),
1339
- height: positiveInteger(artifact.viewport?.height, 900)
1340
- }),
1341
- colorScheme: ["light", "dark"].includes(artifact.colorScheme) ? artifact.colorScheme : "light"
1342
- });
1343
- }
1344
- function requestedArtifactId(value) {
1345
- if (typeof value === "string") return value;
1346
- return typeof value?.id === "string" ? value.id : "";
1347
- }
1348
- function availableId(artifacts, requested, fallback = "") {
1349
- return artifacts.some(({ id }) => id === requested) ? requested : fallback;
1350
- }
1351
- function normalizeViewMode(value, artifactCount) {
1352
- if (artifactCount < 2) return "single";
1353
- return ["single", "variants", "split"].includes(value) ? value : "variants";
1354
- }
1355
- function createArtifactStagePayload(envelope = {}, { viewer } = {}) {
1356
- const artifacts = Object.freeze(
1357
- (Array.isArray(envelope?.artifacts) ? envelope.artifacts : []).map(freezeArtifactMetadata)
1358
- );
1359
- const sourceViewer = viewer && typeof viewer === "object" ? viewer : envelope?.viewer && typeof envelope.viewer === "object" ? envelope.viewer : {};
1360
- const firstId = artifacts[0]?.id ?? "";
1361
- const activeArtifactId = availableId(
1362
- artifacts,
1363
- requestedArtifactId(sourceViewer.activeArtifactId),
1364
- firstId
1365
- );
1366
- const mode = normalizeViewMode(sourceViewer.mode, artifacts.length);
1367
- return Object.freeze({
1368
- schemaVersion: "1.0.0",
1369
- artifacts,
1370
- viewer: Object.freeze({
1371
- mode,
1372
- activeArtifactId,
1373
- ...PRESENTATIONS.includes(sourceViewer.presentation) ? { presentation: sourceViewer.presentation } : {}
1374
- })
1375
- });
1376
- }
1377
-
1378
- // lib/artifact/ui/stage.mjs
1379
- var ARTIFACT_STAGE_EVENTS = Object.freeze({
1380
- change: "planr:stage-change",
1381
- point: "planr:artifact-point",
1382
- region: "planr:artifact-region",
1383
- layout: "planr:artifact-layout",
1384
- frameState: "planr:artifact-frame-state"
1385
- });
1386
- var ARTIFACT_STAGE_LIMITS = Object.freeze({
1387
- defaultZoom: 72,
1388
- minZoom: 25,
1389
- maxZoom: 200,
1390
- zoomStep: 10,
1391
- maxDocumentWidth: 16384,
1392
- maxDocumentHeight: 262144,
1393
- defaultFrameBudget: 3,
1394
- frameLoadTimeoutMs: 15e3
1395
- });
1396
- var VIEW_MODES = Object.freeze(["single", "variants", "split"]);
1397
- var REVIEW_MODES = Object.freeze(["interact", "comment"]);
1398
- var THEMES = Object.freeze(["auto", "light", "dark"]);
1399
- var PRESENTATIONS2 = Object.freeze(["document", "canvas"]);
1400
- var STATUSES = Object.freeze([
1401
- "ready",
1402
- "empty",
1403
- "bundling",
1404
- "loading",
1405
- "invalid",
1406
- "expired",
1407
- "decryption-failed",
1408
- "unsupported-browser"
1409
- ]);
1410
- var STATUS_COPY = Object.freeze({
1411
- empty: Object.freeze({
1412
- title: "No artifact content",
1413
- detail: "Choose a bundled HTML artifact to begin this review."
1414
- }),
1415
- bundling: Object.freeze({
1416
- title: "Bundling artifact",
1417
- detail: "Packaging local scripts, styles, fonts, and images without network access."
1418
- }),
1419
- loading: Object.freeze({
1420
- title: "Loading private review",
1421
- detail: "Validating the envelope and frozen artifact viewport."
1422
- }),
1423
- invalid: Object.freeze({
1424
- title: "This review is invalid",
1425
- detail: "The artifact envelope could not be decoded or validated."
1426
- }),
1427
- expired: Object.freeze({
1428
- title: "This encrypted review expired",
1429
- detail: "Ask the sender for a new immutable review link."
1430
- }),
1431
- "decryption-failed": Object.freeze({
1432
- title: "This key cannot decrypt the review",
1433
- detail: "Use the complete link, including its private fragment key."
1434
- }),
1435
- "unsupported-browser": Object.freeze({
1436
- title: "Browser support is required",
1437
- detail: "Use a current browser with Blob URL support to review this artifact."
1438
- })
1439
- });
1440
- function member(value, allowed, fallback) {
1441
- return allowed.includes(value) ? value : fallback;
1442
- }
1443
- function finite2(value, fallback = 0) {
1444
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
1445
- }
1446
- function clamp2(value, min, max) {
1447
- return Math.min(max, Math.max(min, value));
1448
- }
1449
- function normalized2(value) {
1450
- return Math.round(clamp2(finite2(value), 0, 1) * 1e6) / 1e6;
1451
- }
1452
- function artifactMetadata(artifact) {
1453
- return Object.freeze({
1454
- id: artifact.id,
1455
- title: artifact.title,
1456
- sha256: artifact.sha256,
1457
- viewport: artifact.viewport,
1458
- colorScheme: artifact.colorScheme
1459
- });
1460
- }
1461
- function requestedArtifactId2(value) {
1462
- if (typeof value === "string") return value;
1463
- return typeof value?.id === "string" ? value.id : "";
1464
- }
1465
- function availableId2(artifacts, requested, fallback = "") {
1466
- return artifacts.some(({ id }) => id === requested) ? requested : fallback;
1467
- }
1468
- function comparisonIdFor(artifacts, activeArtifactId, requested = "") {
1469
- if (requested !== activeArtifactId && artifacts.some(({ id }) => id === requested))
1470
- return requested;
1471
- return artifacts.find(({ id }) => id !== activeArtifactId)?.id ?? "";
1472
- }
1473
- function normalizeViewMode2(value, artifactCount) {
1474
- if (artifactCount < 2) return "single";
1475
- return member(value, VIEW_MODES, "variants");
1476
- }
1477
- function createArtifactStageState(payload = {}, shellModel = {}) {
1478
- const artifacts = Object.freeze(
1479
- (Array.isArray(payload?.artifacts) ? payload.artifacts : []).map(artifactMetadata)
1480
- );
1481
- const firstId = artifacts[0]?.id ?? "";
1482
- const activeArtifactId = availableId2(
1483
- artifacts,
1484
- requestedArtifactId2(shellModel.activeArtifact ?? shellModel.activeArtifactId) || requestedArtifactId2(payload?.viewer?.activeArtifactId),
1485
- firstId
1486
- );
1487
- const comparisonArtifactId = comparisonIdFor(
1488
- artifacts,
1489
- activeArtifactId,
1490
- requestedArtifactId2(shellModel.comparisonArtifact ?? shellModel.comparisonArtifactId)
1491
- );
1492
- const statusFallback = artifacts.length === 0 ? "empty" : "ready";
1493
- let status = member(shellModel.status, STATUSES, statusFallback);
1494
- if (artifacts.length === 0 && status === "ready") status = "empty";
1495
- const viewMode = normalizeViewMode2(
1496
- shellModel.viewMode ?? payload?.viewer?.mode,
1497
- artifacts.length
1498
- );
1499
- const presentation = resolveArtifactPresentation(
1500
- shellModel.presentation ?? payload?.viewer?.presentation,
1501
- { viewMode, artifactCount: artifacts.length }
1502
- );
1503
- return Object.freeze({
1504
- schemaVersion: "1.0.0",
1505
- artifacts,
1506
- activeArtifactId,
1507
- comparisonArtifactId,
1508
- viewMode,
1509
- presentation,
1510
- reviewMode: member(shellModel.reviewMode, REVIEW_MODES, "interact"),
1511
- zoom: clamp2(
1512
- Number.isInteger(shellModel.zoom) ? shellModel.zoom : ARTIFACT_STAGE_LIMITS.defaultZoom,
1513
- ARTIFACT_STAGE_LIMITS.minZoom,
1514
- ARTIFACT_STAGE_LIMITS.maxZoom
1515
- ),
1516
- railOpen: shellModel.railOpen === void 0 ? presentation === "canvas" : Boolean(shellModel.railOpen),
1517
- theme: member(shellModel.theme, THEMES, "auto"),
1518
- status
1519
- });
1520
- }
1521
- function nextState(state, changes) {
1522
- return Object.freeze({ ...state, ...changes });
1523
- }
1524
- function reduceArtifactStageState(state, action = {}) {
1525
- switch (action.type) {
1526
- case "set-active": {
1527
- const id = availableId2(state.artifacts, action.artifactId);
1528
- if (!id || id === state.activeArtifactId) return state;
1529
- const comparisonArtifactId = id === state.comparisonArtifactId ? state.activeArtifactId : comparisonIdFor(state.artifacts, id, state.comparisonArtifactId);
1530
- return nextState(state, { activeArtifactId: id, comparisonArtifactId });
1531
- }
1532
- case "set-comparison": {
1533
- const id = comparisonIdFor(state.artifacts, state.activeArtifactId, action.artifactId);
1534
- return id === state.comparisonArtifactId ? state : nextState(state, { comparisonArtifactId: id });
1535
- }
1536
- case "set-view-mode": {
1537
- const viewMode = normalizeViewMode2(action.viewMode, state.artifacts.length);
1538
- return viewMode === state.viewMode ? state : nextState(state, { viewMode });
1539
- }
1540
- case "set-review-mode": {
1541
- const reviewMode = member(action.reviewMode, REVIEW_MODES, state.reviewMode);
1542
- return reviewMode === state.reviewMode ? state : nextState(state, { reviewMode });
1543
- }
1544
- case "set-zoom": {
1545
- const zoom = clamp2(
1546
- Math.round(finite2(action.zoom, state.zoom)),
1547
- ARTIFACT_STAGE_LIMITS.minZoom,
1548
- ARTIFACT_STAGE_LIMITS.maxZoom
1549
- );
1550
- return zoom === state.zoom ? state : nextState(state, { zoom });
1551
- }
1552
- case "zoom-by":
1553
- return reduceArtifactStageState(state, {
1554
- type: "set-zoom",
1555
- zoom: state.zoom + finite2(action.delta)
1556
- });
1557
- case "set-rail-open": {
1558
- const railOpen = Boolean(action.railOpen);
1559
- return railOpen === state.railOpen ? state : nextState(state, { railOpen });
1560
- }
1561
- case "toggle-rail":
1562
- return nextState(state, { railOpen: !state.railOpen });
1563
- case "set-theme": {
1564
- const theme = member(action.theme, THEMES, state.theme);
1565
- return theme === state.theme ? state : nextState(state, { theme });
1566
- }
1567
- case "cycle-theme": {
1568
- const index = THEMES.indexOf(state.theme);
1569
- return nextState(state, { theme: THEMES[(index + 1) % THEMES.length] });
1570
- }
1571
- case "set-status": {
1572
- const status = member(action.status, STATUSES, state.status);
1573
- return status === state.status ? state : nextState(state, { status });
1574
- }
1575
- default:
1576
- return state;
1577
- }
1578
- }
1579
- function visibleArtifactIds(state) {
1580
- if (!state.activeArtifactId) return Object.freeze([]);
1581
- if (state.viewMode === "split" && state.comparisonArtifactId) {
1582
- return Object.freeze([state.activeArtifactId, state.comparisonArtifactId]);
1583
- }
1584
- return Object.freeze([state.activeArtifactId]);
1585
- }
1586
- function assertRect2(rect) {
1587
- if (!rect || !Number.isFinite(rect.left) || !Number.isFinite(rect.top) || !Number.isFinite(rect.width) || !Number.isFinite(rect.height) || rect.width <= 0 || rect.height <= 0) {
1588
- throw new RangeError("Artifact bounds must have positive finite dimensions.");
1589
- }
1590
- }
1591
- function clientPointToNormalized(rect, point) {
1592
- assertRect2(rect);
1593
- return Object.freeze({
1594
- x: normalized2((finite2(point?.x ?? point?.clientX) - rect.left) / rect.width),
1595
- y: normalized2((finite2(point?.y ?? point?.clientY) - rect.top) / rect.height)
1596
- });
1597
- }
1598
- function normalizedPointToClient(rect, point) {
1599
- assertRect2(rect);
1600
- return Object.freeze({
1601
- x: rect.left + normalized2(point?.x) * rect.width,
1602
- y: rect.top + normalized2(point?.y) * rect.height
1603
- });
1604
- }
1605
- function parseDataScript(document2, id) {
1606
- const node = document2.getElementById(id);
1607
- if (!node) throw new Error(`Missing artifact shell data: ${id}`);
1608
- return JSON.parse(node.textContent ?? "null");
1609
- }
1610
- function isEditableTarget(target) {
1611
- const HTMLElement = target?.ownerDocument?.defaultView?.HTMLElement;
1612
- return Boolean(
1613
- HTMLElement && target instanceof HTMLElement && (target.isContentEditable || ["INPUT", "TEXTAREA", "SELECT"].includes(target.tagName))
1614
- );
1615
- }
1616
- function stageArtifactById(state, id) {
1617
- return state.artifacts.find((artifact) => artifact.id === id) ?? null;
1618
- }
1619
- function updateStatus(document2, state) {
1620
- const statusPanel = document2.querySelector(".planr-stage-status");
1621
- const surface = document2.querySelector(".planr-stage-surface");
1622
- if (!statusPanel || !surface) return;
1623
- const ready = state.status === "ready";
1624
- statusPanel.hidden = ready;
1625
- surface.toggleAttribute("inert", !ready);
1626
- surface.setAttribute("aria-hidden", String(!ready));
1627
- if (ready) surface.removeAttribute("aria-hidden");
1628
- const copy = STATUS_COPY[state.status];
1629
- if (copy) {
1630
- const title = statusPanel.querySelector("strong");
1631
- const detail = statusPanel.querySelector("p");
1632
- if (title) title.textContent = copy.title;
1633
- if (detail) detail.textContent = copy.detail;
1634
- }
1635
- }
1636
- function emit(root, window, type, detail) {
1637
- root.dispatchEvent(new window.CustomEvent(type, { detail, bubbles: true }));
1638
- }
1639
- async function htmlForSource(window, source) {
1640
- if (typeof source === "string" && source.trimStart().startsWith("<")) return source;
1641
- if (source && typeof source === "object" && typeof source.html === "string") return source.html;
1642
- if (source instanceof window.Blob) return source.text();
1643
- if (source instanceof window.ArrayBuffer) return new window.TextDecoder().decode(source);
1644
- if (window.ArrayBuffer.isView(source)) {
1645
- return new window.TextDecoder().decode(
1646
- new window.Uint8Array(source.buffer, source.byteOffset, source.byteLength)
1647
- );
1648
- }
1649
- return null;
1650
- }
1651
- function mountArtifactStage({
1652
- document: document2 = globalThis.document,
1653
- window = document2?.defaultView,
1654
- resolveArtifactSource,
1655
- sourceTransport = "blob",
1656
- frameBudget: requestedFrameBudget,
1657
- frameLoadTimeoutMs = ARTIFACT_STAGE_LIMITS.frameLoadTimeoutMs,
1658
- bridgeClient,
1659
- onState,
1660
- review: reviewOptions = {},
1661
- share: shareOptions = {},
1662
- hosted: hostedOptions = {}
1663
- } = {}) {
1664
- if (!document2 || !window) return null;
1665
- const root = document2.querySelector(".planr-shell");
1666
- if (!root) return null;
1667
- const initializationError = globalThis.__OPENPLANR_ARTIFACT_STAGE_INITIALIZATION_ERROR__;
1668
- if (initializationError) {
1669
- const status = root.querySelector('[data-planr-slot="status"]');
1670
- if (status) {
1671
- status.textContent = initializationError;
1672
- status.setAttribute("data-error", "");
1673
- status.setAttribute("role", "alert");
1674
- }
1675
- root.setAttribute("data-planr-initialization-error", "");
1676
- return null;
1677
- }
1678
- if (!["blob", "srcdoc"].includes(sourceTransport)) {
1679
- throw new TypeError("Artifact source transport must be blob or srcdoc.");
1680
- }
1681
- let payload;
1682
- let shellModel;
1683
- let reviewConfig;
1684
- try {
1685
- payload = parseDataScript(document2, "planr-artifact-stage-payload");
1686
- shellModel = parseDataScript(document2, "planr-artifact-shell-model");
1687
- reviewConfig = parseDataScript(document2, "planr-artifact-review-state");
1688
- } catch {
1689
- payload = { artifacts: [], viewer: { mode: "single", activeArtifactId: "" } };
1690
- shellModel = { status: "invalid" };
1691
- reviewConfig = { reviewOf: "0".repeat(64), review: null };
1692
- }
1693
- let state;
1694
- try {
1695
- state = createArtifactStageState(payload, shellModel);
1696
- } catch {
1697
- state = createArtifactStageState({}, { status: "invalid" });
1698
- }
1699
- const frames = new Map(
1700
- [...document2.querySelectorAll("[data-planr-artifact-frame]")].map((frame) => [
1701
- frame.dataset.planrArtifactFrame,
1702
- frame
1703
- ])
1704
- );
1705
- const panels = new Map(
1706
- [...document2.querySelectorAll(".planr-artifact-panel[data-artifact-id]")].map((panel) => [
1707
- panel.dataset.artifactId,
1708
- panel
1709
- ])
1710
- );
1711
- const documentLayouts = /* @__PURE__ */ new Map();
1712
- const cleanup = [];
1713
- const frameBudget = requestedFrameBudget === void 0 ? Number(root.dataset.planrFrameBudget || ARTIFACT_STAGE_LIMITS.defaultFrameBudget) : requestedFrameBudget;
1714
- if (frameBudget !== null && (!Number.isInteger(frameBudget) || frameBudget < 1 || frameBudget > 8)) {
1715
- throw new RangeError(
1716
- "Artifact frame budget must be between 1 and 8, or null for eager loading."
1717
- );
1718
- }
1719
- if (!Number.isInteger(frameLoadTimeoutMs) || frameLoadTimeoutMs < 1 || frameLoadTimeoutMs > 12e4) {
1720
- throw new RangeError("Artifact frame load timeout must be between 1 and 120000 milliseconds.");
1721
- }
1722
- const frameDiagnostics = /* @__PURE__ */ new Map();
1723
- const frameLoads = /* @__PURE__ */ new Map();
1724
- const frameHolds = /* @__PURE__ */ new Map();
1725
- const frameHoldWaiters = /* @__PURE__ */ new Set();
1726
- const frameSlots = /* @__PURE__ */ new Map();
1727
- if (frameBudget !== null) {
1728
- for (const [id, frame] of frames) {
1729
- const host = frame.parentNode;
1730
- if (!host || !root.contains(host) || frame.hasAttribute("src") || frame.hasAttribute("srcdoc"))
1731
- continue;
1732
- const placeholder = document2.createComment("Artifact preview");
1733
- frameSlots.set(id, { host, placeholder });
1734
- host.replaceChild(placeholder, frame);
1735
- }
1736
- }
1737
- let frameUse = 0;
1738
- let frameQueue = Promise.resolve();
1739
- let activationGeneration = 0;
1740
- let disposed = false;
1741
- for (const frame of frames.values()) frame.dataset.planrFrameState = "unloaded";
1742
- function listen(target, type, handler, options) {
1743
- target.addEventListener(type, handler, options);
1744
- cleanup.push(() => target.removeEventListener(type, handler, options));
1745
- }
1746
- function render({ announce: announce2 = false } = {}) {
1747
- const visible = new Set(visibleArtifactIds(state));
1748
- root.dataset.planrView = state.viewMode;
1749
- root.dataset.planrReviewMode = state.reviewMode;
1750
- root.dataset.planrState = state.status;
1751
- root.dataset.planrRailOpen = String(state.railOpen);
1752
- root.dataset.planrPresentation = state.presentation;
1753
- document2.documentElement.dataset.planrPresentation = state.presentation;
1754
- document2.documentElement.dataset.planrTheme = state.theme;
1755
- const grid = document2.querySelector(".planr-frame-grid");
1756
- const surface = document2.querySelector(".planr-stage-surface");
1757
- const tablist = document2.querySelector(".planr-variants");
1758
- const rail = document2.getElementById("planr-review-rail");
1759
- const feedbackButton = document2.querySelector('[data-planr-action="feedback"]');
1760
- const addCommentButton = document2.querySelector('[data-planr-action="add-comment"]');
1761
- const themeButton = document2.querySelector('[data-planr-action="theme"]');
1762
- const statusSlot = document2.querySelector('[data-planr-slot="status"]');
1763
- const metadata = document2.querySelector(
1764
- ".planr-toolbar:not([data-studio-react-chrome]) .planr-title-block > span"
1765
- );
1766
- const breadcrumb = document2.querySelector(".planr-stage-heading > span:first-child");
1767
- const activeArtifact = stageArtifactById(state, state.activeArtifactId);
1768
- if (grid) grid.dataset.planrLayout = state.viewMode;
1769
- const visualOrder = state.viewMode === "split" ? [...visible, ...state.artifacts.map(({ id }) => id).filter((id) => !visible.has(id))] : state.artifacts.map(({ id }) => id);
1770
- if (surface) surface.style.setProperty("--planr-shell-zoom", String(state.zoom / 100));
1771
- if (tablist) tablist.hidden = state.viewMode === "single" || state.artifacts.length < 2;
1772
- if (rail) {
1773
- rail.toggleAttribute("inert", !state.railOpen);
1774
- rail.setAttribute("aria-hidden", String(!state.railOpen));
1775
- if (state.railOpen) rail.removeAttribute("aria-hidden");
1776
- }
1777
- if (feedbackButton) feedbackButton.setAttribute("aria-expanded", String(state.railOpen));
1778
- if (addCommentButton) {
1779
- const paused = root.hasAttribute("data-planr-room-comments-paused");
1780
- addCommentButton.disabled = paused;
1781
- addCommentButton.setAttribute("aria-pressed", String(state.reviewMode === "comment"));
1782
- addCommentButton.setAttribute(
1783
- "aria-label",
1784
- paused ? "Add comment unavailable: comments are paused" : "Add comment"
1785
- );
1786
- addCommentButton.dataset.planrTooltip = paused ? "Comments are paused" : "Add comment (C)";
1787
- }
1788
- if (themeButton) {
1789
- themeButton.textContent = state.theme;
1790
- themeButton.setAttribute("aria-label", `Shell theme ${state.theme}`);
1791
- }
1792
- if (statusSlot)
1793
- statusSlot.textContent = state.reviewMode === "comment" ? "Comment mode" : "Interactions enabled";
1794
- if (metadata && activeArtifact) {
1795
- metadata.textContent = `HTML · ${activeArtifact.viewport.width}×${activeArtifact.viewport.height}`;
1796
- }
1797
- if (breadcrumb)
1798
- breadcrumb.textContent = `ARTIFACT / ${(activeArtifact?.title ?? "Artifact").toUpperCase()}`;
1799
- for (const button of document2.querySelectorAll("[data-planr-view]")) {
1800
- const mode = button.dataset.planrView;
1801
- button.setAttribute("aria-pressed", String(mode === state.viewMode));
1802
- button.disabled = state.artifacts.length < 2 && mode !== "single";
1803
- }
1804
- for (const button of document2.querySelectorAll("[data-planr-mode]")) {
1805
- button.setAttribute("aria-pressed", String(button.dataset.planrMode === state.reviewMode));
1806
- }
1807
- for (const button of document2.querySelectorAll('[data-planr-action="zoom-reset"]')) {
1808
- button.textContent = `${state.zoom}%`;
1809
- }
1810
- for (const tab of document2.querySelectorAll('[role="tab"][data-artifact-id]')) {
1811
- const selected = tab.dataset.artifactId === state.activeArtifactId;
1812
- tab.setAttribute("aria-selected", String(selected));
1813
- tab.tabIndex = selected ? 0 : -1;
1814
- }
1815
- for (const [id, panel] of panels) {
1816
- const isVisible = visible.has(id);
1817
- const isPrimary = id === state.activeArtifactId;
1818
- panel.hidden = !isVisible;
1819
- panel.style.order = String(visualOrder.indexOf(id));
1820
- const artifact = stageArtifactById(state, id);
1821
- panel.setAttribute(
1822
- "aria-label",
1823
- `${isPrimary ? "Primary" : "Comparison"} artifact: ${artifact?.title ?? id}`
1824
- );
1825
- const frame = frames.get(id);
1826
- const frameReady = frame?.dataset.planrFrameState === "ready";
1827
- const annotationLayer = panel.querySelector("[data-planr-annotation-layer]");
1828
- if (frame) {
1829
- frame.tabIndex = isVisible && frameReady && state.status === "ready" && state.reviewMode === "interact" ? 0 : -1;
1830
- if (state.presentation === "document") {
1831
- frame.setAttribute("scrolling", "no");
1832
- frame.style.overflow = "hidden";
1833
- } else {
1834
- frame.removeAttribute("scrolling");
1835
- frame.style.removeProperty("overflow");
1836
- }
1837
- }
1838
- if (annotationLayer) {
1839
- const enabled = isVisible && frameReady && state.status === "ready" && state.reviewMode === "comment";
1840
- const hasComposer = Boolean(
1841
- annotationLayer.querySelector("[data-planr-annotation-composer]")
1842
- );
1843
- annotationLayer.tabIndex = enabled ? 0 : -1;
1844
- annotationLayer.setAttribute("aria-disabled", String(!enabled && !hasComposer));
1845
- }
1846
- }
1847
- updateStatus(document2, state);
1848
- renderFrameLoadingStatus();
1849
- if (typeof onState === "function") {
1850
- try {
1851
- onState(state);
1852
- } catch {
1853
- }
1854
- }
1855
- if (announce2) emit(root, window, ARTIFACT_STAGE_EVENTS.change, state);
1856
- }
1857
- function renderFrameLoadingStatus() {
1858
- const activeDiagnostic = frameDiagnostics.get(state.activeArtifactId);
1859
- const statusPanel = document2.querySelector(".planr-stage-status");
1860
- if (!statusPanel || !activeDiagnostic) return;
1861
- const detail = statusPanel.querySelector("p");
1862
- if (detail && state.status === "loading") {
1863
- const copy = {
1864
- source: "Preparing the selected screen.",
1865
- document: "Loading the selected screen.",
1866
- bridge: "Connecting the selected screen to the review."
1867
- };
1868
- detail.textContent = copy[activeDiagnostic.phase] ?? "Loading the selected screen.";
1869
- }
1870
- if (detail && state.status === "invalid" && activeDiagnostic.status === "error") {
1871
- detail.textContent = `The selected screen did not finish its ${activeDiagnostic.failedPhase ?? "preview"} step. Retry to load it again; saved feedback remains available.`;
1872
- }
1873
- let retry = statusPanel.querySelector('[data-planr-action="retry-frame"]');
1874
- if (!retry) {
1875
- retry = document2.createElement("button");
1876
- retry.className = "planr-toolbar-action";
1877
- retry.dataset.planrAction = "retry-frame";
1878
- retry.textContent = "Retry screen";
1879
- statusPanel.querySelector("div")?.append(retry);
1880
- }
1881
- retry.hidden = state.status !== "invalid" || activeDiagnostic.status !== "error";
1882
- }
1883
- function dispatch(action, { announce: announce2 = true } = {}) {
1884
- const previous = state;
1885
- state = reduceArtifactStageState(state, action);
1886
- if (state !== previous) {
1887
- render({ announce: announce2 });
1888
- if (["set-active", "set-comparison", "set-view-mode"].includes(action.type) && typeof resolveArtifactSource === "function") {
1889
- readyPromise = activateVisibleFrames();
1890
- }
1891
- }
1892
- return state;
1893
- }
1894
- function setActiveFromTab(tab, { focus = false } = {}) {
1895
- dispatch({ type: "set-active", artifactId: tab.dataset.artifactId });
1896
- if (focus) tab.focus();
1897
- }
1898
- function onClick(event) {
1899
- const target = event.target.closest?.("button");
1900
- if (!target) return;
1901
- if (target.hasAttribute("data-planr-close-feedback")) {
1902
- dispatch({ type: "set-rail-open", railOpen: false });
1903
- document2.querySelector('[data-planr-action="feedback"]')?.focus();
1904
- return;
1905
- }
1906
- if (target.dataset.planrView) {
1907
- dispatch({ type: "set-view-mode", viewMode: target.dataset.planrView });
1908
- return;
1909
- }
1910
- if (target.dataset.artifactId && target.getAttribute("role") === "tab") {
1911
- setActiveFromTab(target);
1912
- return;
1913
- }
1914
- if (target.dataset.planrMode) {
1915
- dispatch({ type: "set-review-mode", reviewMode: target.dataset.planrMode });
1916
- return;
1917
- }
1918
- switch (target.dataset.planrAction) {
1919
- case "add-comment":
1920
- if (!root.hasAttribute("data-planr-room-comments-paused")) {
1921
- dispatch({ type: "set-review-mode", reviewMode: "comment" });
1922
- }
1923
- break;
1924
- case "zoom-out":
1925
- dispatch({ type: "zoom-by", delta: -ARTIFACT_STAGE_LIMITS.zoomStep });
1926
- break;
1927
- case "zoom-reset":
1928
- dispatch({ type: "set-zoom", zoom: ARTIFACT_STAGE_LIMITS.defaultZoom });
1929
- break;
1930
- case "zoom-in":
1931
- dispatch({ type: "zoom-by", delta: ARTIFACT_STAGE_LIMITS.zoomStep });
1932
- break;
1933
- case "feedback": {
1934
- const rail = document2.getElementById("planr-review-rail");
1935
- if (state.railOpen && rail?.contains(document2.activeElement)) target.focus();
1936
- dispatch({ type: "toggle-rail" });
1937
- break;
1938
- }
1939
- case "more": {
1940
- const menu = target.closest(".planr-more")?.querySelector(".planr-more-menu");
1941
- if (!menu) break;
1942
- const open = menu.hidden;
1943
- menu.hidden = !open;
1944
- target.setAttribute("aria-expanded", String(open));
1945
- if (open) menu.querySelector('[role="menuitem"]')?.focus();
1946
- break;
1947
- }
1948
- case "sharing-options":
1949
- target.closest(".planr-more-menu").hidden = true;
1950
- document2.querySelector('[data-planr-action="more"]')?.setAttribute("aria-expanded", "false");
1951
- shareController?.open?.();
1952
- break;
1953
- case "retry-frame":
1954
- void retryFrames([state.activeArtifactId]);
1955
- break;
1956
- case "theme":
1957
- dispatch({ type: "cycle-theme" });
1958
- break;
1959
- default:
1960
- break;
1961
- }
1962
- }
1963
- function onKeyDown(event) {
1964
- if (event.defaultPrevented) return;
1965
- if (!event.altKey && !event.ctrlKey && !event.metaKey && !isEditableTarget(event.target)) {
1966
- if (event.key.toLowerCase() === "i") {
1967
- dispatch({ type: "set-review-mode", reviewMode: "interact" });
1968
- return;
1969
- }
1970
- if (event.key.toLowerCase() === "c") {
1971
- if (!root.hasAttribute("data-planr-room-comments-paused")) {
1972
- dispatch({ type: "set-review-mode", reviewMode: "comment" });
1973
- document2.querySelector('[data-planr-action="add-comment"]')?.focus();
1974
- }
1975
- return;
1976
- }
1977
- if (event.key === "Escape") {
1978
- const more = document2.querySelector(".planr-more-menu:not([hidden])");
1979
- if (more) {
1980
- more.hidden = true;
1981
- const trigger = document2.querySelector('[data-planr-action="more"]');
1982
- trigger?.setAttribute("aria-expanded", "false");
1983
- trigger?.focus();
1984
- return;
1985
- }
1986
- if (state.presentation === "document" && state.reviewMode === "comment") {
1987
- dispatch({ type: "set-review-mode", reviewMode: "interact" });
1988
- document2.querySelector('[data-planr-action="add-comment"]')?.focus();
1989
- return;
1990
- }
1991
- if (state.railOpen) {
1992
- dispatch({ type: "set-rail-open", railOpen: false });
1993
- document2.querySelector('[data-planr-action="feedback"]')?.focus();
1994
- return;
1995
- }
1996
- }
1997
- }
1998
- const tab = event.target.closest?.('[role="tab"][data-artifact-id]');
1999
- if (!tab || !["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return;
2000
- const tabs = [...document2.querySelectorAll('[role="tab"][data-artifact-id]')];
2001
- const index = tabs.indexOf(tab);
2002
- if (index < 0) return;
2003
- event.preventDefault();
2004
- const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : (index + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length;
2005
- setActiveFromTab(tabs[nextIndex], { focus: true });
2006
- }
2007
- function emitSelection(layer, start, end = start) {
2008
- if (state.reviewMode !== "comment" || state.status !== "ready") return;
2009
- const artifactId = layer.dataset.planrAnnotationLayer;
2010
- if (!visibleArtifactIds(state).includes(artifactId)) return;
2011
- const artifact = stageArtifactById(state, artifactId);
2012
- if (!artifact) return;
2013
- const region = clientSelectionToNormalized(layer.getBoundingClientRect(), start, end);
2014
- const measured = state.presentation === "document" ? documentLayouts.get(artifactId) : null;
2015
- const detail = Object.freeze({
2016
- schemaVersion: "1.0.0",
2017
- artifactId,
2018
- region,
2019
- viewport: measured ?? artifact.viewport
2020
- });
2021
- emit(root, window, ARTIFACT_STAGE_EVENTS.region, detail);
2022
- emit(root, window, ARTIFACT_STAGE_EVENTS.point, detail);
2023
- if (state.presentation === "document") {
2024
- dispatch({ type: "set-review-mode", reviewMode: "interact" });
2025
- }
2026
- }
2027
- for (const layer of document2.querySelectorAll("[data-planr-annotation-layer]")) {
2028
- let selection = null;
2029
- let selectionPreview = null;
2030
- listen(layer, "pointerdown", (event) => {
2031
- if (event.button !== 0 || state.reviewMode !== "comment" || state.status !== "ready") return;
2032
- if (event.target !== layer) return;
2033
- event.preventDefault();
2034
- selection = { pointerId: event.pointerId, start: { x: event.clientX, y: event.clientY } };
2035
- layer.setPointerCapture?.(event.pointerId);
2036
- selectionPreview = document2.createElement("span");
2037
- selectionPreview.className = "planr-region-selection";
2038
- selectionPreview.setAttribute("aria-hidden", "true");
2039
- layer.append(selectionPreview);
2040
- });
2041
- listen(layer, "pointermove", (event) => {
2042
- if (!selection || selection.pointerId !== event.pointerId || !selectionPreview) return;
2043
- const region = clientSelectionToNormalized(layer.getBoundingClientRect(), selection.start, {
2044
- x: event.clientX,
2045
- y: event.clientY
2046
- });
2047
- selectionPreview.style.left = `${region.x * 100}%`;
2048
- selectionPreview.style.top = `${region.y * 100}%`;
2049
- selectionPreview.style.width = `${region.w * 100}%`;
2050
- selectionPreview.style.height = `${region.h * 100}%`;
2051
- });
2052
- listen(layer, "pointercancel", (event) => {
2053
- if (!selection || selection.pointerId !== event.pointerId) return;
2054
- layer.releasePointerCapture?.(event.pointerId);
2055
- selection = null;
2056
- selectionPreview?.remove();
2057
- selectionPreview = null;
2058
- });
2059
- listen(layer, "pointerup", (event) => {
2060
- if (!selection || selection.pointerId !== event.pointerId) return;
2061
- const start = selection.start;
2062
- layer.releasePointerCapture?.(event.pointerId);
2063
- selection = null;
2064
- selectionPreview?.remove();
2065
- selectionPreview = null;
2066
- emitSelection(layer, start, { x: event.clientX, y: event.clientY });
2067
- });
2068
- listen(layer, "keydown", (event) => {
2069
- if (!["Enter", " "].includes(event.key)) return;
2070
- if (event.target !== layer) return;
2071
- event.preventDefault();
2072
- const bounds = layer.getBoundingClientRect();
2073
- emitSelection(layer, {
2074
- x: bounds.left + bounds.width / 2,
2075
- y: bounds.top + bounds.height / 2
2076
- });
2077
- });
2078
- }
2079
- for (const [artifactId, frame] of frames) {
2080
- listen(frame, ARTIFACT_STAGE_EVENTS.layout, (event) => {
2081
- if (state.presentation !== "document") return;
2082
- const width = event.detail?.width;
2083
- const height = event.detail?.height;
2084
- if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || width > ARTIFACT_STAGE_LIMITS.maxDocumentWidth || height < 1 || height > ARTIFACT_STAGE_LIMITS.maxDocumentHeight)
2085
- return;
2086
- const layout = Object.freeze({ width, height });
2087
- documentLayouts.set(artifactId, layout);
2088
- const panel = panels.get(artifactId);
2089
- if (!panel) return;
2090
- panel.dataset.planrLayoutMeasured = "true";
2091
- panel.style.setProperty("--planr-document-width", `${width}px`);
2092
- panel.style.setProperty("--planr-document-height", `${height}px`);
2093
- });
2094
- }
2095
- listen(root, "click", onClick);
2096
- listen(document2, "keydown", onKeyDown);
2097
- listen(document2, "click", (event) => {
2098
- const more = document2.querySelector(".planr-more-menu:not([hidden])");
2099
- if (!more || event.target.closest?.(".planr-more")) return;
2100
- more.hidden = true;
2101
- document2.querySelector('[data-planr-action="more"]')?.setAttribute("aria-expanded", "false");
2102
- });
2103
- const roomStateObserver = typeof window.MutationObserver === "function" ? new window.MutationObserver(() => {
2104
- if (root.hasAttribute("data-planr-room-comments-paused") && state.reviewMode === "comment") {
2105
- dispatch({ type: "set-review-mode", reviewMode: "interact" });
2106
- } else {
2107
- render();
2108
- }
2109
- }) : null;
2110
- roomStateObserver?.observe(root, {
2111
- attributes: true,
2112
- attributeFilter: ["data-planr-room-comments-paused"]
2113
- });
2114
- if (roomStateObserver) cleanup.push(() => roomStateObserver.disconnect());
2115
- render();
2116
- let readyPromise = Promise.resolve(state);
2117
- let feedbackController = null;
2118
- let annotationController = null;
2119
- let shareController = null;
2120
- let hostedController = null;
2121
- function disposeStage() {
2122
- if (disposed) return true;
2123
- disposed = true;
2124
- wakeFrameDemand();
2125
- for (const id of [...frameLoads.keys()]) releaseFrame(id);
2126
- for (const remove of cleanup.splice(0)) remove();
2127
- return true;
2128
- }
2129
- const controller = Object.freeze({
2130
- frameBudget,
2131
- holdFrames,
2132
- ensureFrames,
2133
- retryFrames,
2134
- getFrameDiagnostics: () => [...frameDiagnostics.values()],
2135
- getLoadedArtifactIds: () => [...frameLoads].filter(([, record]) => record.status === "ready").map(([id]) => id),
2136
- getState: () => state,
2137
- getFrame: (artifactId) => frames.get(artifactId) ?? null,
2138
- getPanel: (artifactId) => panels.get(artifactId) ?? null,
2139
- get review() {
2140
- return feedbackController;
2141
- },
2142
- get annotations() {
2143
- return annotationController;
2144
- },
2145
- get share() {
2146
- return shareController;
2147
- },
2148
- get hosted() {
2149
- return hostedController;
2150
- },
2151
- get ready() {
2152
- return readyPromise;
2153
- },
2154
- dispatch,
2155
- destroy() {
2156
- if (["creating", "ambiguous"].includes(shareController?.getState?.().phase)) {
2157
- shareController.destroy?.();
2158
- return false;
2159
- }
2160
- return disposeStage();
2161
- }
2162
- });
2163
- publishArtifactStage(window, controller);
2164
- listen(window, "pagehide", (event) => {
2165
- if (!event.persisted) disposeStage();
2166
- });
2167
- feedbackController = mountArtifactFeedbackRail({
2168
- document: document2,
2169
- window,
2170
- root,
2171
- stageController: controller,
2172
- reviewOf: reviewConfig?.reviewOf,
2173
- initialReview: reviewConfig?.review ?? null,
2174
- artifacts: state.artifacts,
2175
- ...reviewOptions
2176
- });
2177
- annotationController = mountArtifactAnnotations({
2178
- document: document2,
2179
- window,
2180
- root,
2181
- stageController: controller,
2182
- reviewController: feedbackController
2183
- });
2184
- const durablePasteShare = shareOptions.createShare && shareOptions.resumePreparedShare ? createDurablePasteShare({
2185
- scope: () => ({
2186
- workspaceId: window.location.pathname,
2187
- revisionId: reviewConfig.reviewOf ?? "unknown-revision",
2188
- actorId: "local-paste-owner"
2189
- }),
2190
- create: (request) => shareOptions.createShare(request),
2191
- commit: shareOptions.resumePreparedShare,
2192
- indexedDB: window.indexedDB
2193
- }) : null;
2194
- if (durablePasteShare) cleanup.push(() => durablePasteShare.close());
2195
- shareController = mountArtifactShareDialog({
2196
- document: document2,
2197
- window,
2198
- root,
2199
- stageController: controller,
2200
- ...shareOptions,
2201
- ...durablePasteShare ? {
2202
- createShare: (request) => durablePasteShare.run(request)
2203
- } : {}
2204
- });
2205
- hostedController = mountHostedArtifactViewer({
2206
- document: document2,
2207
- window,
2208
- ...hostedOptions
2209
- });
2210
- if (feedbackController?.destroy) cleanup.push(() => feedbackController.destroy());
2211
- if (annotationController?.destroy) cleanup.push(() => annotationController.destroy());
2212
- if (shareController?.destroy) cleanup.push(() => shareController.destroy());
2213
- if (hostedController?.destroy) cleanup.push(() => hostedController.destroy());
2214
- function cancelledFrame() {
2215
- const error = new Error("Artifact frame loading was cancelled.");
2216
- error.name = "AbortError";
2217
- return error;
2218
- }
2219
- function frameStatus(artifactId, status, phase, failure) {
2220
- const frame = frames.get(artifactId);
2221
- if (frame) {
2222
- frame.dataset.planrFrameState = status;
2223
- frame.dataset.planrFramePhase = phase;
2224
- }
2225
- const previous = frameDiagnostics.get(artifactId);
2226
- const now = window.performance.now();
2227
- const starting = status === "loading" && phase === "source";
2228
- const startedAt = starting ? now : previous?.startedAt ?? now;
2229
- const diagnostic = Object.freeze({
2230
- artifactId,
2231
- status,
2232
- phase,
2233
- startedAt,
2234
- elapsedMs: Math.max(0, Math.round(now - startedAt)),
2235
- attempt: (previous?.attempt ?? 0) + (starting ? 1 : 0),
2236
- transport: sourceTransport,
2237
- ...failure
2238
- });
2239
- frameDiagnostics.set(artifactId, diagnostic);
2240
- if (!disposed) {
2241
- emit(root, window, ARTIFACT_STAGE_EVENTS.frameState, diagnostic);
2242
- if (artifactId === state.activeArtifactId) render();
2243
- }
2244
- }
2245
- function releaseFrame(artifactId, {
2246
- status = "unloaded",
2247
- error = cancelledFrame(),
2248
- failure
2249
- } = {}) {
2250
- const record = frameLoads.get(artifactId);
2251
- if (!record) return;
2252
- frameLoads.delete(artifactId);
2253
- wakeFrameDemand();
2254
- record.cancelled = true;
2255
- record.abort.abort();
2256
- record.unlisten?.();
2257
- record.detach?.();
2258
- record.reject(error);
2259
- const frame = frames.get(artifactId);
2260
- const slot = frameSlots.get(artifactId);
2261
- if (slot && frame.parentNode === slot.host && !slot.placeholder.parentNode) {
2262
- slot.host.replaceChild(slot.placeholder, frame);
2263
- }
2264
- frame.removeAttribute("srcdoc");
2265
- frame.removeAttribute("src");
2266
- delete frame.dataset.planrArtifactDigest;
2267
- delete frame.dataset.planrBridgeTrusted;
2268
- try {
2269
- delete frame.__openPlanrBridge;
2270
- } catch {
2271
- }
2272
- if (record.sourceUrl) window.URL.revokeObjectURL(record.sourceUrl);
2273
- documentLayouts.delete(artifactId);
2274
- const panel = panels.get(artifactId);
2275
- if (panel) {
2276
- delete panel.dataset.planrLayoutMeasured;
2277
- panel.style.removeProperty("--planr-document-width");
2278
- panel.style.removeProperty("--planr-document-height");
2279
- }
2280
- frameStatus(artifactId, status, status, failure);
2281
- }
2282
- function assignArtifactSource(artifact) {
2283
- if (disposed) return Promise.reject(cancelledFrame());
2284
- const frame = frames.get(artifact.id);
2285
- if (!frame) return Promise.reject(new Error(`Missing artifact frame: ${artifact.id}`));
2286
- const slot = frameSlots.get(artifact.id);
2287
- const existing = frameLoads.get(artifact.id);
2288
- if (slot && (!slot.host.isConnected || !root.contains(slot.host) || existing?.status === "ready" && frame.parentNode !== slot.host)) {
2289
- releaseFrame(artifact.id);
2290
- return Promise.reject(cancelledFrame());
2291
- }
2292
- if (existing) {
2293
- if (existing.status === "ready" && existing.requireTrust && frames.get(artifact.id)?.dataset.planrBridgeTrusted !== "true") {
2294
- releaseFrame(artifact.id);
2295
- } else {
2296
- existing.used = ++frameUse;
2297
- return existing.promise;
2298
- }
2299
- }
2300
- const record = {
2301
- status: "loading",
2302
- used: ++frameUse,
2303
- cancelled: false,
2304
- abort: typeof window.AbortController === "function" ? new window.AbortController() : { signal: void 0, abort() {
2305
- } }
2306
- };
2307
- record.promise = new Promise((resolve, reject) => {
2308
- record.resolve = resolve;
2309
- record.reject = reject;
2310
- });
2311
- frameLoads.set(artifact.id, record);
2312
- const current = () => !disposed && !record.cancelled && frameLoads.get(artifact.id) === record;
2313
- const fail = (error) => {
2314
- if (current())
2315
- releaseFrame(artifact.id, {
2316
- status: "error",
2317
- error,
2318
- failure: {
2319
- failedPhase: frameDiagnostics.get(artifact.id)?.phase ?? "source",
2320
- code: error?.code ?? "E_ARTIFACT_FRAME_LOAD"
2321
- }
2322
- });
2323
- };
2324
- const timer = window.setTimeout(() => {
2325
- const error = new Error(`Artifact frame did not finish loading: ${artifact.id}`);
2326
- error.code = "E_ARTIFACT_FRAME_TIMEOUT";
2327
- fail(error);
2328
- }, frameLoadTimeoutMs);
2329
- let loaded = false;
2330
- const ready = () => {
2331
- if (current() && slot && (!slot.host.isConnected || !root.contains(slot.host) || frame.parentNode !== slot.host)) {
2332
- fail(cancelledFrame());
2333
- return;
2334
- }
2335
- if (!current() || !loaded || record.requireTrust && frame.dataset.planrBridgeTrusted !== "true")
2336
- return;
2337
- record.status = "ready";
2338
- record.unlisten();
2339
- frameStatus(artifact.id, "ready", "ready");
2340
- record.resolve(artifact.id);
2341
- };
2342
- const onLoad = () => {
2343
- loaded = true;
2344
- if (record.requireTrust && frame.dataset.planrBridgeTrusted !== "true") {
2345
- frameStatus(artifact.id, "loading", "bridge");
2346
- }
2347
- ready();
2348
- };
2349
- const onError = () => fail(new Error(`Artifact frame failed: ${artifact.id}`));
2350
- record.unlisten = () => {
2351
- window.clearTimeout(timer);
2352
- frame.removeEventListener("load", onLoad);
2353
- frame.removeEventListener("error", onError);
2354
- frame.removeEventListener("planr:artifact-bridge-ready", ready);
2355
- };
2356
- frameStatus(artifact.id, "loading", "source");
2357
- if (!current()) return record.promise;
2358
- void (async () => {
2359
- const source = await resolveArtifactSource(artifact, {
2360
- frame,
2361
- getState: () => state,
2362
- signal: record.abort.signal
2363
- });
2364
- if (!current()) return;
2365
- if (typeof window.TextDecoder !== "function") {
2366
- const error = new Error("UTF-8 decoding support is required for artifact sources.");
2367
- error.code = "E_ARTIFACT_BROWSER_UNSUPPORTED";
2368
- throw error;
2369
- }
2370
- const html = await htmlForSource(window, source);
2371
- if (!current()) return;
2372
- if (!html) {
2373
- throw new TypeError(
2374
- `Artifact source resolver must return HTML bytes or a Blob for ${artifact.id}.`
2375
- );
2376
- }
2377
- if (slot) {
2378
- if (!slot.host.isConnected || !root.contains(slot.host) || slot.placeholder.parentNode !== slot.host || frame.parentNode) {
2379
- throw cancelledFrame();
2380
- }
2381
- slot.host.replaceChild(frame, slot.placeholder);
2382
- }
2383
- if (typeof bridgeClient?.attach === "function") {
2384
- const detach = bridgeClient.attach({
2385
- artifact,
2386
- frame,
2387
- getState: () => state
2388
- });
2389
- if (typeof detach === "function") record.detach = detach;
2390
- if (!current()) {
2391
- record.detach?.();
2392
- return;
2393
- }
2394
- record.requireTrust = true;
2395
- }
2396
- frame.addEventListener("load", onLoad);
2397
- frame.addEventListener("error", onError);
2398
- frame.addEventListener("planr:artifact-bridge-ready", ready);
2399
- frame.dataset.planrArtifactDigest = artifact.sha256;
2400
- frameStatus(artifact.id, "loading", "document");
2401
- if (!current()) return;
2402
- if (slot && (!slot.host.isConnected || !root.contains(slot.host) || frame.parentNode !== slot.host))
2403
- throw cancelledFrame();
2404
- if (sourceTransport === "srcdoc") {
2405
- frame.removeAttribute("src");
2406
- frame.srcdoc = html;
2407
- } else {
2408
- if (typeof window.URL?.createObjectURL !== "function" || typeof window.Blob !== "function") {
2409
- const error = new Error("Blob URL support is required for artifact sources.");
2410
- error.code = "E_ARTIFACT_BROWSER_UNSUPPORTED";
2411
- throw error;
2412
- }
2413
- const sourceUrl = window.URL.createObjectURL(
2414
- new window.Blob([html], {
2415
- type: "text/html;charset=utf-8"
2416
- })
2417
- );
2418
- record.sourceUrl = sourceUrl;
2419
- frame.removeAttribute("srcdoc");
2420
- frame.src = sourceUrl;
2421
- }
2422
- })().catch(fail);
2423
- return record.promise;
2424
- }
2425
- function wakeFrameDemand() {
2426
- for (const resolve of frameHoldWaiters) resolve();
2427
- frameHoldWaiters.clear();
2428
- }
2429
- function holdFrames(artifactIds) {
2430
- if (disposed) throw cancelledFrame();
2431
- if (!Array.isArray(artifactIds) || artifactIds.some((id) => !frames.has(id))) {
2432
- throw new TypeError("Requested artifact frames must be known artifact IDs.");
2433
- }
2434
- const ids = [...new Set(artifactIds)];
2435
- for (const id of ids) frameHolds.set(id, (frameHolds.get(id) ?? 0) + 1);
2436
- let released = false;
2437
- return () => {
2438
- if (released) return;
2439
- released = true;
2440
- for (const id of ids) {
2441
- const count = frameHolds.get(id) ?? 0;
2442
- if (count > 1) frameHolds.set(id, count - 1);
2443
- else frameHolds.delete(id);
2444
- }
2445
- wakeFrameDemand();
2446
- };
2447
- }
2448
- function ensureFrames(artifactIds, isCurrent = () => true) {
2449
- if (disposed || !isCurrent()) return Promise.reject(cancelledFrame());
2450
- if (!Array.isArray(artifactIds) || artifactIds.some((id) => typeof id !== "string" || !frames.has(id))) {
2451
- return Promise.reject(new TypeError("Requested artifact frames must be known artifact IDs."));
2452
- }
2453
- const requested = [...new Set(artifactIds)];
2454
- if (frameBudget !== null && requested.length > frameBudget) {
2455
- return Promise.reject(
2456
- new RangeError(`At most ${frameBudget} artifact frames may be requested together.`)
2457
- );
2458
- }
2459
- if (frameBudget === null || requested.every((id) => frameLoads.has(id)))
2460
- return Promise.all(requested.map((id) => assignArtifactSource(stageArtifactById(state, id))));
2461
- const run = async () => {
2462
- if (disposed || !isCurrent()) throw cancelledFrame();
2463
- for (const id of requested) {
2464
- if (disposed || !isCurrent()) throw cancelledFrame();
2465
- if (!frameLoads.has(id)) {
2466
- while (frameLoads.size >= frameBudget) {
2467
- const candidates = [...frameLoads].filter(([loadedId]) => !requested.includes(loadedId) && !frameHolds.has(loadedId)).sort((a, b) => a[1].used - b[1].used);
2468
- if (!candidates.length) {
2469
- if (!frameHolds.size) throw new Error("The artifact frame budget is exhausted.");
2470
- await new Promise((resolve) => frameHoldWaiters.add(resolve));
2471
- if (disposed || !isCurrent()) throw cancelledFrame();
2472
- continue;
2473
- }
2474
- releaseFrame(candidates[0][0]);
2475
- }
2476
- }
2477
- await assignArtifactSource(stageArtifactById(state, id));
2478
- }
2479
- return requested;
2480
- };
2481
- const result = frameQueue.then(run);
2482
- frameQueue = result.catch(() => {
2483
- });
2484
- return result;
2485
- }
2486
- function frameIsReady(id) {
2487
- const frame = frames.get(id);
2488
- return frame?.dataset.planrFrameState === "ready" && (!frame.__openPlanrBridge || frame.dataset.planrBridgeTrusted === "true");
2489
- }
2490
- function setSelectionStatus(status, selection, generation) {
2491
- if (disposed || generation !== activationGeneration || visibleArtifactIds(state).join("|") !== selection)
2492
- return;
2493
- state = reduceArtifactStageState(state, { type: "set-status", status });
2494
- render({ announce: true });
2495
- }
2496
- function cancelInactiveLoads(ids) {
2497
- for (const [id, record] of frameLoads) {
2498
- if (record.status === "loading" && !ids.includes(id) && !frameHolds.has(id)) releaseFrame(id);
2499
- }
2500
- }
2501
- async function activateVisibleFrames() {
2502
- const generation = ++activationGeneration;
2503
- const isCurrent = () => generation === activationGeneration;
2504
- const ids = visibleArtifactIds(state);
2505
- const selection = ids.join("|");
2506
- if (ids.length === 0) return state;
2507
- cancelInactiveLoads(ids);
2508
- if (ids.every(frameIsReady)) {
2509
- setSelectionStatus("ready", selection, generation);
2510
- return state;
2511
- }
2512
- setSelectionStatus("loading", selection, generation);
2513
- try {
2514
- await ensureFrames(ids, isCurrent);
2515
- setSelectionStatus("ready", selection, generation);
2516
- } catch (error) {
2517
- const status = error?.code === "E_ARTIFACT_BROWSER_UNSUPPORTED" ? "unsupported-browser" : "invalid";
2518
- setSelectionStatus(status, selection, generation);
2519
- }
2520
- return state;
2521
- }
2522
- function retryFrames(artifactIds = visibleArtifactIds(state)) {
2523
- if (!Array.isArray(artifactIds) || artifactIds.some((id) => !frames.has(id))) {
2524
- return Promise.reject(new TypeError("Requested artifact frames must be known artifact IDs."));
2525
- }
2526
- for (const id of artifactIds) releaseFrame(id);
2527
- if (artifactIds.some((id) => visibleArtifactIds(state).includes(id))) {
2528
- readyPromise = activateVisibleFrames();
2529
- return readyPromise;
2530
- }
2531
- return ensureFrames(artifactIds);
2532
- }
2533
- if (state.status === "ready" && state.artifacts.length > 0) {
2534
- if (typeof resolveArtifactSource !== "function") {
2535
- frameStatus(state.activeArtifactId, "loading", "source");
2536
- frameStatus(state.activeArtifactId, "error", "error", {
2537
- failedPhase: "source",
2538
- code: "E_ARTIFACT_SOURCE_UNAVAILABLE"
2539
- });
2540
- state = reduceArtifactStageState(state, { type: "set-status", status: "invalid" });
2541
- render();
2542
- readyPromise = Promise.resolve(state);
2543
- } else {
2544
- state = reduceArtifactStageState(state, { type: "set-status", status: "loading" });
2545
- render();
2546
- readyPromise = activateVisibleFrames();
2547
- if (frameBudget === null) {
2548
- void Promise.allSettled(
2549
- state.artifacts.filter(({ id }) => id !== state.activeArtifactId).map(assignArtifactSource)
2550
- );
2551
- }
2552
- }
2553
- }
2554
- return controller;
2555
- }
2556
- function bootstrapArtifactStage(document2 = globalThis.document, options = {}) {
2557
- return mountArtifactStage({ ...options, document: document2, window: document2?.defaultView });
2558
- }
2559
- if (typeof document !== "undefined") {
2560
- const options = globalThis.__OPENPLANR_ARTIFACT_STAGE_OPTIONS__ ?? {};
2561
- if (document.readyState === "loading") {
2562
- document.addEventListener("DOMContentLoaded", () => bootstrapArtifactStage(document, options), {
2563
- once: true
2564
- });
2565
- } else {
2566
- queueMicrotask(() => bootstrapArtifactStage(document, options));
2567
- }
2568
- }