@joewinke/jatui 0.37.0 → 0.38.1

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 (266) hide show
  1. package/dist/artifact-blocks/AttachmentChip.svelte +66 -0
  2. package/dist/artifact-blocks/AttachmentChip.svelte.d.ts +7 -0
  3. package/dist/artifact-blocks/BlockComments.svelte +309 -0
  4. package/dist/artifact-blocks/BlockComments.svelte.d.ts +33 -0
  5. package/dist/artifact-blocks/BlockRenderer.svelte +674 -0
  6. package/dist/artifact-blocks/BlockRenderer.svelte.d.ts +55 -0
  7. package/dist/artifact-blocks/BlockVerdict.svelte +151 -0
  8. package/dist/artifact-blocks/BlockVerdict.svelte.d.ts +13 -0
  9. package/dist/artifact-blocks/BlocksArtifactView.svelte +1616 -0
  10. package/dist/artifact-blocks/BlocksArtifactView.svelte.d.ts +113 -0
  11. package/dist/artifact-blocks/CONTRACT.md +245 -0
  12. package/dist/artifact-blocks/CommentPill.svelte +49 -0
  13. package/dist/artifact-blocks/CommentPill.svelte.d.ts +16 -0
  14. package/dist/artifact-blocks/FoldedRounds.svelte +95 -0
  15. package/dist/artifact-blocks/FoldedRounds.svelte.d.ts +9 -0
  16. package/dist/artifact-blocks/Glyph.svelte +54 -0
  17. package/dist/artifact-blocks/Glyph.svelte.d.ts +10 -0
  18. package/dist/artifact-blocks/HistoryScrubber.svelte +500 -0
  19. package/dist/artifact-blocks/HistoryScrubber.svelte.d.ts +15 -0
  20. package/dist/artifact-blocks/Markdown.svelte +186 -0
  21. package/dist/artifact-blocks/Markdown.svelte.d.ts +30 -0
  22. package/dist/artifact-blocks/NoteComposer.svelte +31 -0
  23. package/dist/artifact-blocks/NoteComposer.svelte.d.ts +8 -0
  24. package/dist/artifact-blocks/PlainComposer.svelte +200 -0
  25. package/dist/artifact-blocks/PlainComposer.svelte.d.ts +15 -0
  26. package/dist/artifact-blocks/SectionTools.svelte +102 -0
  27. package/dist/artifact-blocks/SectionTools.svelte.d.ts +19 -0
  28. package/dist/artifact-blocks/adapter.d.ts +13 -0
  29. package/dist/artifact-blocks/adapter.js +31 -0
  30. package/dist/artifact-blocks/autoSend.d.ts +49 -0
  31. package/dist/artifact-blocks/autoSend.js +103 -0
  32. package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte +557 -0
  33. package/dist/artifact-blocks/blocks/actions/ActionsBlock.svelte.d.ts +48 -0
  34. package/dist/artifact-blocks/blocks/actions/block.d.ts +8 -0
  35. package/dist/artifact-blocks/blocks/actions/block.js +74 -0
  36. package/dist/artifact-blocks/blocks/actions/requests.d.ts +62 -0
  37. package/dist/artifact-blocks/blocks/actions/requests.js +26 -0
  38. package/dist/artifact-blocks/blocks/actions/schema.d.ts +168 -0
  39. package/dist/artifact-blocks/blocks/actions/schema.js +253 -0
  40. package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte +779 -0
  41. package/dist/artifact-blocks/blocks/callout/CalloutBlock.svelte.d.ts +40 -0
  42. package/dist/artifact-blocks/blocks/callout/block.d.ts +7 -0
  43. package/dist/artifact-blocks/blocks/callout/block.js +39 -0
  44. package/dist/artifact-blocks/blocks/callout/schema.d.ts +72 -0
  45. package/dist/artifact-blocks/blocks/callout/schema.js +115 -0
  46. package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte +875 -0
  47. package/dist/artifact-blocks/blocks/chart/ChartBlock.svelte.d.ts +59 -0
  48. package/dist/artifact-blocks/blocks/chart/block.d.ts +9 -0
  49. package/dist/artifact-blocks/blocks/chart/block.js +70 -0
  50. package/dist/artifact-blocks/blocks/chart/schema.d.ts +171 -0
  51. package/dist/artifact-blocks/blocks/chart/schema.js +498 -0
  52. package/dist/artifact-blocks/blocks/chart/thumb.d.ts +2 -0
  53. package/dist/artifact-blocks/blocks/chart/thumb.js +201 -0
  54. package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte +1048 -0
  55. package/dist/artifact-blocks/blocks/checklist/ChecklistBlock.svelte.d.ts +16 -0
  56. package/dist/artifact-blocks/blocks/checklist/Icon.svelte +105 -0
  57. package/dist/artifact-blocks/blocks/checklist/Icon.svelte.d.ts +17 -0
  58. package/dist/artifact-blocks/blocks/checklist/block.d.ts +10 -0
  59. package/dist/artifact-blocks/blocks/checklist/block.js +63 -0
  60. package/dist/artifact-blocks/blocks/checklist/itemReview.d.ts +16 -0
  61. package/dist/artifact-blocks/blocks/checklist/itemReview.js +63 -0
  62. package/dist/artifact-blocks/blocks/checklist/labels.d.ts +62 -0
  63. package/dist/artifact-blocks/blocks/checklist/labels.js +96 -0
  64. package/dist/artifact-blocks/blocks/checklist/retest.d.ts +43 -0
  65. package/dist/artifact-blocks/blocks/checklist/retest.js +68 -0
  66. package/dist/artifact-blocks/blocks/checklist/schema.d.ts +84 -0
  67. package/dist/artifact-blocks/blocks/checklist/schema.js +91 -0
  68. package/dist/artifact-blocks/blocks/code/CodeBlock.svelte +128 -0
  69. package/dist/artifact-blocks/blocks/code/CodeBlock.svelte.d.ts +12 -0
  70. package/dist/artifact-blocks/blocks/code/block.d.ts +8 -0
  71. package/dist/artifact-blocks/blocks/code/block.js +31 -0
  72. package/dist/artifact-blocks/blocks/code/highlight.d.ts +34 -0
  73. package/dist/artifact-blocks/blocks/code/highlight.js +280 -0
  74. package/dist/artifact-blocks/blocks/code/schema.d.ts +32 -0
  75. package/dist/artifact-blocks/blocks/code/schema.js +39 -0
  76. package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte +290 -0
  77. package/dist/artifact-blocks/blocks/collage/CollageBlock.svelte.d.ts +29 -0
  78. package/dist/artifact-blocks/blocks/collage/block.d.ts +9 -0
  79. package/dist/artifact-blocks/blocks/collage/block.js +29 -0
  80. package/dist/artifact-blocks/blocks/collage/schema.d.ts +52 -0
  81. package/dist/artifact-blocks/blocks/collage/schema.js +86 -0
  82. package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte +484 -0
  83. package/dist/artifact-blocks/blocks/compare/CompareBlock.svelte.d.ts +25 -0
  84. package/dist/artifact-blocks/blocks/compare/block.d.ts +9 -0
  85. package/dist/artifact-blocks/blocks/compare/block.js +32 -0
  86. package/dist/artifact-blocks/blocks/compare/schema.d.ts +74 -0
  87. package/dist/artifact-blocks/blocks/compare/schema.js +148 -0
  88. package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte +392 -0
  89. package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +30 -0
  90. package/dist/artifact-blocks/blocks/diagram/block.d.ts +9 -0
  91. package/dist/artifact-blocks/blocks/diagram/block.js +50 -0
  92. package/dist/artifact-blocks/blocks/diagram/constants.d.ts +48 -0
  93. package/dist/artifact-blocks/blocks/diagram/constants.js +53 -0
  94. package/dist/artifact-blocks/blocks/diagram/schema.d.ts +161 -0
  95. package/dist/artifact-blocks/blocks/diagram/schema.js +621 -0
  96. package/dist/artifact-blocks/blocks/diagram/thumb.d.ts +2 -0
  97. package/dist/artifact-blocks/blocks/diagram/thumb.js +157 -0
  98. package/dist/artifact-blocks/blocks/diagram/types.d.ts +74 -0
  99. package/dist/artifact-blocks/blocks/diagram/types.js +1 -0
  100. package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte +81 -0
  101. package/dist/artifact-blocks/blocks/diff/DiffBlock.svelte.d.ts +10 -0
  102. package/dist/artifact-blocks/blocks/diff/block.d.ts +7 -0
  103. package/dist/artifact-blocks/blocks/diff/block.js +36 -0
  104. package/dist/artifact-blocks/blocks/diff/schema.d.ts +18 -0
  105. package/dist/artifact-blocks/blocks/diff/schema.js +35 -0
  106. package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte +1055 -0
  107. package/dist/artifact-blocks/blocks/diff-review/DiffReviewBlock.svelte.d.ts +5 -0
  108. package/dist/artifact-blocks/blocks/diff-review/block.d.ts +15 -0
  109. package/dist/artifact-blocks/blocks/diff-review/block.js +64 -0
  110. package/dist/artifact-blocks/blocks/diff-review/decisions.d.ts +57 -0
  111. package/dist/artifact-blocks/blocks/diff-review/decisions.js +80 -0
  112. package/dist/artifact-blocks/blocks/diff-review/schema.d.ts +13 -0
  113. package/dist/artifact-blocks/blocks/diff-review/schema.js +84 -0
  114. package/dist/artifact-blocks/blocks/diff-review/types.d.ts +35 -0
  115. package/dist/artifact-blocks/blocks/diff-review/types.js +1 -0
  116. package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte +145 -0
  117. package/dist/artifact-blocks/blocks/explainer/ExplainerBlock.svelte.d.ts +5 -0
  118. package/dist/artifact-blocks/blocks/explainer/block.d.ts +8 -0
  119. package/dist/artifact-blocks/blocks/explainer/block.js +21 -0
  120. package/dist/artifact-blocks/blocks/explainer/schema.d.ts +26 -0
  121. package/dist/artifact-blocks/blocks/explainer/schema.js +69 -0
  122. package/dist/artifact-blocks/blocks/group-content.d.ts +10 -0
  123. package/dist/artifact-blocks/blocks/group-content.js +16 -0
  124. package/dist/artifact-blocks/blocks/group-data.d.ts +10 -0
  125. package/dist/artifact-blocks/blocks/group-data.js +7 -0
  126. package/dist/artifact-blocks/blocks/group-review.d.ts +10 -0
  127. package/dist/artifact-blocks/blocks/group-review.js +12 -0
  128. package/dist/artifact-blocks/blocks/group-visual.d.ts +17 -0
  129. package/dist/artifact-blocks/blocks/group-visual.js +14 -0
  130. package/dist/artifact-blocks/blocks/map/MapBlock.svelte +413 -0
  131. package/dist/artifact-blocks/blocks/map/MapBlock.svelte.d.ts +53 -0
  132. package/dist/artifact-blocks/blocks/map/block.d.ts +9 -0
  133. package/dist/artifact-blocks/blocks/map/block.js +62 -0
  134. package/dist/artifact-blocks/blocks/map/schema.d.ts +123 -0
  135. package/dist/artifact-blocks/blocks/map/schema.js +113 -0
  136. package/dist/artifact-blocks/blocks/map/thumb.d.ts +2 -0
  137. package/dist/artifact-blocks/blocks/map/thumb.js +69 -0
  138. package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte +33 -0
  139. package/dist/artifact-blocks/blocks/markdown/MarkdownBlock.svelte.d.ts +15 -0
  140. package/dist/artifact-blocks/blocks/markdown/block.d.ts +7 -0
  141. package/dist/artifact-blocks/blocks/markdown/block.js +28 -0
  142. package/dist/artifact-blocks/blocks/markdown/render.d.ts +17 -0
  143. package/dist/artifact-blocks/blocks/markdown/render.js +115 -0
  144. package/dist/artifact-blocks/blocks/markdown/schema.d.ts +10 -0
  145. package/dist/artifact-blocks/blocks/markdown/schema.js +23 -0
  146. package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte +2112 -0
  147. package/dist/artifact-blocks/blocks/media-picker/MediaPickerBlock.svelte.d.ts +5 -0
  148. package/dist/artifact-blocks/blocks/media-picker/block.d.ts +24 -0
  149. package/dist/artifact-blocks/blocks/media-picker/block.js +137 -0
  150. package/dist/artifact-blocks/blocks/media-picker/choices.d.ts +28 -0
  151. package/dist/artifact-blocks/blocks/media-picker/choices.js +35 -0
  152. package/dist/artifact-blocks/blocks/media-picker/decisions.d.ts +51 -0
  153. package/dist/artifact-blocks/blocks/media-picker/decisions.js +75 -0
  154. package/dist/artifact-blocks/blocks/media-picker/schema.d.ts +7 -0
  155. package/dist/artifact-blocks/blocks/media-picker/schema.js +199 -0
  156. package/dist/artifact-blocks/blocks/media-picker/types.d.ts +92 -0
  157. package/dist/artifact-blocks/blocks/media-picker/types.js +1 -0
  158. package/dist/artifact-blocks/blocks/media-picker/versions.d.ts +51 -0
  159. package/dist/artifact-blocks/blocks/media-picker/versions.js +118 -0
  160. package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte +247 -0
  161. package/dist/artifact-blocks/blocks/mockup/MockupBlock.svelte.d.ts +5 -0
  162. package/dist/artifact-blocks/blocks/mockup/block.d.ts +27 -0
  163. package/dist/artifact-blocks/blocks/mockup/block.js +52 -0
  164. package/dist/artifact-blocks/blocks/mockup/doc.d.ts +39 -0
  165. package/dist/artifact-blocks/blocks/mockup/doc.js +65 -0
  166. package/dist/artifact-blocks/blocks/mockup/sandbox.d.ts +98 -0
  167. package/dist/artifact-blocks/blocks/mockup/sandbox.js +63 -0
  168. package/dist/artifact-blocks/blocks/mockup/schema.d.ts +7 -0
  169. package/dist/artifact-blocks/blocks/mockup/schema.js +90 -0
  170. package/dist/artifact-blocks/blocks/mockup/types.d.ts +36 -0
  171. package/dist/artifact-blocks/blocks/mockup/types.js +17 -0
  172. package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte +359 -0
  173. package/dist/artifact-blocks/blocks/program/ProgramBlock.svelte.d.ts +32 -0
  174. package/dist/artifact-blocks/blocks/program/block.d.ts +35 -0
  175. package/dist/artifact-blocks/blocks/program/block.js +68 -0
  176. package/dist/artifact-blocks/blocks/program/schema.d.ts +132 -0
  177. package/dist/artifact-blocks/blocks/program/schema.js +268 -0
  178. package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte +315 -0
  179. package/dist/artifact-blocks/blocks/stats/StatsBlock.svelte.d.ts +24 -0
  180. package/dist/artifact-blocks/blocks/stats/block.d.ts +7 -0
  181. package/dist/artifact-blocks/blocks/stats/block.js +49 -0
  182. package/dist/artifact-blocks/blocks/stats/schema.d.ts +82 -0
  183. package/dist/artifact-blocks/blocks/stats/schema.js +43 -0
  184. package/dist/artifact-blocks/blocks/stats/thumb.d.ts +2 -0
  185. package/dist/artifact-blocks/blocks/stats/thumb.js +102 -0
  186. package/dist/artifact-blocks/blocks/table/TableBlock.svelte +490 -0
  187. package/dist/artifact-blocks/blocks/table/TableBlock.svelte.d.ts +28 -0
  188. package/dist/artifact-blocks/blocks/table/block.d.ts +9 -0
  189. package/dist/artifact-blocks/blocks/table/block.js +100 -0
  190. package/dist/artifact-blocks/blocks/table/schema.d.ts +143 -0
  191. package/dist/artifact-blocks/blocks/table/schema.js +278 -0
  192. package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte +347 -0
  193. package/dist/artifact-blocks/blocks/timeline/TimelineBlock.svelte.d.ts +42 -0
  194. package/dist/artifact-blocks/blocks/timeline/block.d.ts +9 -0
  195. package/dist/artifact-blocks/blocks/timeline/block.js +96 -0
  196. package/dist/artifact-blocks/blocks/timeline/constants.d.ts +26 -0
  197. package/dist/artifact-blocks/blocks/timeline/constants.js +27 -0
  198. package/dist/artifact-blocks/blocks/timeline/schema.d.ts +102 -0
  199. package/dist/artifact-blocks/blocks/timeline/schema.js +405 -0
  200. package/dist/artifact-blocks/blocks/timeline/thumb.d.ts +2 -0
  201. package/dist/artifact-blocks/blocks/timeline/thumb.js +117 -0
  202. package/dist/artifact-blocks/blocks/timeline/types.d.ts +66 -0
  203. package/dist/artifact-blocks/blocks/timeline/types.js +1 -0
  204. package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte +169 -0
  205. package/dist/artifact-blocks/blocks/transcript/TranscriptBlock.svelte.d.ts +26 -0
  206. package/dist/artifact-blocks/blocks/transcript/block.d.ts +9 -0
  207. package/dist/artifact-blocks/blocks/transcript/block.js +86 -0
  208. package/dist/artifact-blocks/blocks/transcript/schema.d.ts +114 -0
  209. package/dist/artifact-blocks/blocks/transcript/schema.js +126 -0
  210. package/dist/artifact-blocks/builtins.d.ts +40 -0
  211. package/dist/artifact-blocks/builtins.js +22 -0
  212. package/dist/artifact-blocks/contractBlocks.d.ts +92 -0
  213. package/dist/artifact-blocks/contractBlocks.js +279 -0
  214. package/dist/artifact-blocks/history.d.ts +140 -0
  215. package/dist/artifact-blocks/history.js +363 -0
  216. package/dist/artifact-blocks/index.d.ts +40 -0
  217. package/dist/artifact-blocks/index.js +46 -0
  218. package/dist/artifact-blocks/listen/context.d.ts +30 -0
  219. package/dist/artifact-blocks/listen/context.js +30 -0
  220. package/dist/artifact-blocks/liveData.d.ts +78 -0
  221. package/dist/artifact-blocks/liveData.js +137 -0
  222. package/dist/artifact-blocks/liveDataPoller.d.ts +41 -0
  223. package/dist/artifact-blocks/liveDataPoller.js +140 -0
  224. package/dist/artifact-blocks/openNav.d.ts +46 -0
  225. package/dist/artifact-blocks/openNav.js +106 -0
  226. package/dist/artifact-blocks/pageReviewKeys.d.ts +17 -0
  227. package/dist/artifact-blocks/pageReviewKeys.js +115 -0
  228. package/dist/artifact-blocks/params.d.ts +98 -0
  229. package/dist/artifact-blocks/params.js +159 -0
  230. package/dist/artifact-blocks/registry.d.ts +27 -0
  231. package/dist/artifact-blocks/registry.js +83 -0
  232. package/dist/artifact-blocks/review/BlockIcon.svelte +99 -0
  233. package/dist/artifact-blocks/review/BlockIcon.svelte.d.ts +17 -0
  234. package/dist/artifact-blocks/review/BulkRejectForm.svelte +94 -0
  235. package/dist/artifact-blocks/review/BulkRejectForm.svelte.d.ts +11 -0
  236. package/dist/artifact-blocks/review/BulkUndoBar.svelte +40 -0
  237. package/dist/artifact-blocks/review/BulkUndoBar.svelte.d.ts +9 -0
  238. package/dist/artifact-blocks/review/ItemNote.svelte +193 -0
  239. package/dist/artifact-blocks/review/ItemNote.svelte.d.ts +33 -0
  240. package/dist/artifact-blocks/review/NoteToggle.svelte +52 -0
  241. package/dist/artifact-blocks/review/NoteToggle.svelte.d.ts +12 -0
  242. package/dist/artifact-blocks/review/TeamChip.svelte +71 -0
  243. package/dist/artifact-blocks/review/TeamChip.svelte.d.ts +8 -0
  244. package/dist/artifact-blocks/review/attachments.d.ts +10 -0
  245. package/dist/artifact-blocks/review/attachments.js +37 -0
  246. package/dist/artifact-blocks/review/bulk.d.ts +52 -0
  247. package/dist/artifact-blocks/review/bulk.js +61 -0
  248. package/dist/artifact-blocks/review/itemReview.d.ts +33 -0
  249. package/dist/artifact-blocks/review/itemReview.js +112 -0
  250. package/dist/artifact-blocks/review/roundLines.d.ts +14 -0
  251. package/dist/artifact-blocks/review/roundLines.js +20 -0
  252. package/dist/artifact-blocks/review/rounds.d.ts +86 -0
  253. package/dist/artifact-blocks/review/rounds.js +302 -0
  254. package/dist/artifact-blocks/review/saver.d.ts +23 -0
  255. package/dist/artifact-blocks/review/saver.js +48 -0
  256. package/dist/artifact-blocks/review/team.d.ts +31 -0
  257. package/dist/artifact-blocks/review/team.js +42 -0
  258. package/dist/artifact-blocks/review/visualOrder.d.ts +29 -0
  259. package/dist/artifact-blocks/review/visualOrder.js +66 -0
  260. package/dist/artifact-blocks/review.d.ts +180 -0
  261. package/dist/artifact-blocks/review.js +716 -0
  262. package/dist/artifact-blocks/thumb-kit.d.ts +80 -0
  263. package/dist/artifact-blocks/thumb-kit.js +149 -0
  264. package/dist/artifact-blocks/types.d.ts +522 -0
  265. package/dist/artifact-blocks/types.js +1 -0
  266. package/package.json +11 -2
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Versions vs views for the media-picker (mos-0wvol).
3
+ *
4
+ * A review round's variants mix two different things: VERSIONS of the asset
5
+ * (v6 under review, v5 as last reviewed) and VIEWS of one version (the loop,
6
+ * an old-vs-new strip, a water-over-time map). Shown as one flat filmstrip
7
+ * the caption read "1 of 4 · v6 · v6" — meaningless. These helpers split an
8
+ * item into versions (newest first, the one under review marked) and, inside
9
+ * a version, labelled views.
10
+ *
11
+ * Pure: no DOM, no Svelte — the component and the tests share them.
12
+ * Backward compatible: an item whose variants carry fewer than two distinct
13
+ * versions has no version row (`versionsOf` returns []), exactly as before.
14
+ */
15
+ import type { MediaItem, MediaVariant } from "./types.js";
16
+ /** The version under review: explicit, else the first versioned variant's. */
17
+ export declare function currentVersionOf(item: MediaItem): string | null;
18
+ /** Which version a variant belongs to. Unversioned variants sit with the
19
+ * version under review (they are views of what is being judged). */
20
+ export declare function versionOfVariant(item: MediaItem, variant: MediaVariant): string | null;
21
+ /**
22
+ * Versions in display order: the one under review first, then the others
23
+ * in the order the publisher listed them (review-kit puts the newest first).
24
+ * Fewer than two distinct versions → [] (no switcher; render as before).
25
+ */
26
+ export declare function versionsOf(item: MediaItem): string[];
27
+ /** The views (variants) of one version; with no versions, every variant. */
28
+ export declare function viewsOf(item: MediaItem, version: string | null): MediaVariant[];
29
+ /**
30
+ * A view's human label with the redundant version prefix stripped:
31
+ * "v6" on a v6 video → "Loop"; "v6 · strip" → "strip"; "v5 (as reviewed)"
32
+ * → "Loop (as reviewed)". A label that says something else is kept as is.
33
+ */
34
+ export declare function cleanViewLabel(label: string | undefined | null, version: string | undefined | null, kind: MediaVariant["kind"]): string;
35
+ /** The label a view thumbnail and the caption show. */
36
+ export declare function viewLabel(item: MediaItem, variant: MediaVariant): string;
37
+ /**
38
+ * The card caption under the media:
39
+ * versioned → "v6 (reviewing) · Loop · 1 of 3" / "v5 (previous) · Loop · 1 of 1"
40
+ * flat → "1 of 4 · warm" (as before, minus a label that only repeats the version)
41
+ */
42
+ export declare function captionFor(item: MediaItem, variant: MediaVariant, version: string | null): string;
43
+ /**
44
+ * "Your verdict then: needs work. Note: “water not natural”" (or a no-record
45
+ * line). Takes the locked round line for the version (viewer or publisher
46
+ * history, merged by ../../review/rounds.ts).
47
+ */
48
+ export declare function historyLine(h: {
49
+ verdict?: string | null;
50
+ note?: string | null;
51
+ } | null | undefined): string;
@@ -0,0 +1,118 @@
1
+ /** The version under review: explicit, else the first versioned variant's. */
2
+ export function currentVersionOf(item) {
3
+ if (item.currentVersion)
4
+ return item.currentVersion;
5
+ return item.variants.find((v) => v.version)?.version ?? null;
6
+ }
7
+ /** Which version a variant belongs to. Unversioned variants sit with the
8
+ * version under review (they are views of what is being judged). */
9
+ export function versionOfVariant(item, variant) {
10
+ return variant.version ?? currentVersionOf(item);
11
+ }
12
+ /**
13
+ * Versions in display order: the one under review first, then the others
14
+ * in the order the publisher listed them (review-kit puts the newest first).
15
+ * Fewer than two distinct versions → [] (no switcher; render as before).
16
+ */
17
+ export function versionsOf(item) {
18
+ const cur = currentVersionOf(item);
19
+ if (!cur)
20
+ return [];
21
+ const seen = [cur];
22
+ for (const v of item.variants) {
23
+ const ver = v.version ?? cur;
24
+ if (!seen.includes(ver))
25
+ seen.push(ver);
26
+ }
27
+ return seen.length > 1 ? seen : [];
28
+ }
29
+ /** The views (variants) of one version; with no versions, every variant. */
30
+ export function viewsOf(item, version) {
31
+ if (versionsOf(item).length === 0 || version == null)
32
+ return item.variants;
33
+ return item.variants.filter((v) => versionOfVariant(item, v) === version);
34
+ }
35
+ const KIND_VIEW = {
36
+ video: "Loop",
37
+ image: "Still",
38
+ audio: "Audio",
39
+ };
40
+ /**
41
+ * A view's human label with the redundant version prefix stripped:
42
+ * "v6" on a v6 video → "Loop"; "v6 · strip" → "strip"; "v5 (as reviewed)"
43
+ * → "Loop (as reviewed)". A label that says something else is kept as is.
44
+ */
45
+ export function cleanViewLabel(label, version, kind) {
46
+ const fallback = KIND_VIEW[kind];
47
+ let s = (label ?? "").trim();
48
+ if (version) {
49
+ const v = version.trim();
50
+ // repeat: an older spec may carry "v6 · v6"
51
+ for (let guard = 0; guard < 3 && v; guard++) {
52
+ if (s.toLowerCase() === v.toLowerCase()) {
53
+ s = "";
54
+ break;
55
+ }
56
+ if (s.toLowerCase().startsWith(v.toLowerCase())) {
57
+ const rest = s.slice(v.length);
58
+ if (/^[\s·:\-—–|/]/.test(rest) || rest.startsWith("(")) {
59
+ s = rest.replace(/^[\s·:\-—–|/]+/, "").trim();
60
+ continue;
61
+ }
62
+ }
63
+ break;
64
+ }
65
+ }
66
+ if (!s)
67
+ return fallback;
68
+ if (/^\(.*\)$/.test(s))
69
+ return `${fallback} ${s}`;
70
+ return s;
71
+ }
72
+ /** The label a view thumbnail and the caption show. */
73
+ export function viewLabel(item, variant) {
74
+ const versioned = versionsOf(item).length > 0;
75
+ const version = versioned ? versionOfVariant(item, variant) : variant.version;
76
+ return cleanViewLabel(variant.label ?? variant.id, version, variant.kind);
77
+ }
78
+ /**
79
+ * The card caption under the media:
80
+ * versioned → "v6 (reviewing) · Loop · 1 of 3" / "v5 (previous) · Loop · 1 of 1"
81
+ * flat → "1 of 4 · warm" (as before, minus a label that only repeats the version)
82
+ */
83
+ export function captionFor(item, variant, version) {
84
+ const versions = versionsOf(item);
85
+ if (versions.length > 0 && version) {
86
+ const views = viewsOf(item, version);
87
+ const idx = Math.max(0, views.findIndex((v) => v.id === variant.id));
88
+ const role = version === currentVersionOf(item) ? "reviewing" : "previous";
89
+ return `${version} (${role}) · ${viewLabel(item, variant)} · ${idx + 1} of ${views.length}`;
90
+ }
91
+ const idx = Math.max(0, item.variants.findIndex((v) => v.id === variant.id));
92
+ const head = `${idx + 1} of ${item.variants.length}`;
93
+ const raw = variant.label?.trim();
94
+ if (!raw)
95
+ return head;
96
+ const label = variant.version && raw.toLowerCase() === variant.version.toLowerCase()
97
+ ? raw
98
+ : cleanViewLabel(raw, variant.version, variant.kind);
99
+ return `${head} · ${label}`;
100
+ }
101
+ const VERDICT_WORD = {
102
+ approve: "approved",
103
+ redo: "needs work",
104
+ reject: "rejected",
105
+ };
106
+ /**
107
+ * "Your verdict then: needs work. Note: “water not natural”" (or a no-record
108
+ * line). Takes the locked round line for the version (viewer or publisher
109
+ * history, merged by ../../review/rounds.ts).
110
+ */
111
+ export function historyLine(h) {
112
+ if (!h || (!h.verdict && !h.note))
113
+ return "No verdict was recorded for this version.";
114
+ const word = h.verdict ? (VERDICT_WORD[h.verdict] ?? h.verdict) : "a note";
115
+ return h.note
116
+ ? `Your verdict then: ${word}. Note: “${h.note}”`
117
+ : `Your verdict then: ${word}`;
118
+ }
@@ -0,0 +1,247 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MockupBlock — agent-authored HTML in device frames (mos-vzame).
4
+ *
5
+ * Each variant renders inside its own iframe. The iframe's sandbox
6
+ * attribute comes from mockupSandboxAttr() in ./sandbox.ts — the artifact
7
+ * sandbox's token list (a host may override it via services.sandboxAttr),
8
+ * which NEVER includes allow-same-origin (see that module for the threat
9
+ * model: opaque origin, no session, no cookies, no parent DOM). Scripts run
10
+ * only when the author passed allowScripts, and even then inside the opaque
11
+ * origin, with `sandbox="allow-scripts"` only and a no-beacon CSP in the
12
+ * document (jat-4yffn) — plus a "runs scripts" notice drawn HERE, in the
13
+ * parent, where the script cannot reach or restyle it. Scripts also need
14
+ * the HOST's word that its page enforces `frame-src 'self'`
15
+ * (`services.framePolicy.frameSrcSelfEnforced`, jatui-2qhbr): without it a
16
+ * script could navigate its own frame to an attacker URL, so the mockup
17
+ * renders script-free under a "scripts disabled" notice instead.
18
+ * referrerpolicy="no-referrer" mirrors ArtifactShell.
19
+ *
20
+ * The frame is a scaled viewport: the iframe is laid out at the DEVICE's
21
+ * pixel width and transform-scaled to the column, so a "desktop" mockup is
22
+ * laid out at 1440px even on a phone screen — not reflowed to column width.
23
+ * bind:clientWidth measures the wrapper; the scale correction lands on
24
+ * hydrate, so server HTML ships a sensible default.
25
+ *
26
+ * The theme toggle passes data-theme=light|dark into every fragment (see
27
+ * ./doc.ts) — the snippet's own CSS decides what the attribute means.
28
+ */
29
+ import { mockupSandboxAttr, mockupScriptsAllowed } from "./sandbox.js"
30
+ import type { BlockProps } from "../../types.js"
31
+ import { mockupDocument } from "./doc.js"
32
+ import {
33
+ MOCKUP_DIMS,
34
+ type MockupDevice,
35
+ type MockupDims,
36
+ type MockupParams,
37
+ } from "./types.js"
38
+ import SectionTools from "../../SectionTools.svelte"
39
+
40
+ let { params, ctx }: BlockProps<MockupParams> = $props()
41
+
42
+ let theme = $state<"light" | "dark">("light")
43
+ /** Measured on-screen width of each frame's viewport, by variant id. */
44
+ let frameWidths = $state<Record<string, number>>({})
45
+
46
+ const requested = $derived(params.allowScripts === true)
47
+ const scripted = $derived(
48
+ mockupScriptsAllowed(requested, ctx?.services?.framePolicy),
49
+ )
50
+ /** Asked for scripts, but the host has not asserted frame-src 'self'. */
51
+ const scriptsBlocked = $derived(requested && !scripted)
52
+ const sandbox = $derived(
53
+ mockupSandboxAttr(scripted, ctx?.services?.sandboxAttr),
54
+ )
55
+
56
+ /**
57
+ * Normalize any device value to a real MockupDevice (mos-659rz): the
58
+ * schema now guarantees a variant carries one, but a stale or malformed
59
+ * params object (history restored, a value the schema once accepted) must
60
+ * render a frame — anything unknown falls back to desktop, never to
61
+ * MOCKUP_DIMS[undefined].maxFrameW.
62
+ */
63
+ function deviceOf(device: unknown): MockupDevice {
64
+ return typeof device === "string" && device in MOCKUP_DIMS
65
+ ? (device as MockupDevice)
66
+ : "desktop"
67
+ }
68
+
69
+ /** Device dims — never undefined (mos-659rz): an unknown value falls back
70
+ * to desktop dims instead of crashing on `.maxFrameW`. */
71
+ function dims(device: unknown): MockupDims {
72
+ return MOCKUP_DIMS[deviceOf(device)] ?? MOCKUP_DIMS.desktop
73
+ }
74
+
75
+ /** device px → screen px for this frame (falls back to the frame cap). */
76
+ function scale(id: string, device: unknown): number {
77
+ const d = dims(device)
78
+ const measured = frameWidths[id]
79
+ const w = measured && measured > 0 ? measured : d.maxFrameW
80
+ return w / d.w
81
+ }
82
+
83
+ function frameStyle(id: string, device: unknown): string {
84
+ const d = dims(device)
85
+ return `max-width:${d.maxFrameW}px;aspect-ratio:${d.w}/${d.h};`
86
+ }
87
+ </script>
88
+
89
+ <section
90
+ class="border-base-content/10 bg-base-100 rounded-box border"
91
+ aria-label={params.title ?? "Mockup"}
92
+ >
93
+ <header
94
+ class="flex flex-wrap items-center gap-x-3 gap-y-2 px-4 pt-4 pb-1 sm:px-5"
95
+ >
96
+ <div class="min-w-0 flex-1">
97
+ {#if params.title}
98
+ <h2 class="text-lg font-semibold tracking-tight">{params.title}</h2>
99
+ {/if}
100
+ {#if params.intro}
101
+ <p class="text-base-content/60 mt-0.5 text-sm whitespace-pre-line">
102
+ {params.intro}
103
+ </p>
104
+ {/if}
105
+ </div>
106
+ <!-- Light / dark toggle: re-wraps every fragment with data-theme -->
107
+ <div
108
+ class="flex items-center gap-1"
109
+ role="group"
110
+ aria-label="Preview theme"
111
+ >
112
+ <button
113
+ type="button"
114
+ class="btn btn-xs rounded-field {theme === 'light'
115
+ ? 'btn-primary'
116
+ : 'border-base-content/10 bg-transparent text-base-content/70'}"
117
+ aria-pressed={theme === "light"}
118
+ onclick={() => (theme = "light")}
119
+ >
120
+ Light
121
+ </button>
122
+ <button
123
+ type="button"
124
+ class="btn btn-xs rounded-field {theme === 'dark'
125
+ ? 'btn-primary'
126
+ : 'border-base-content/10 bg-transparent text-base-content/70'}"
127
+ aria-pressed={theme === "dark"}
128
+ onclick={() => (theme = "dark")}
129
+ >
130
+ Dark
131
+ </button>
132
+ </div>
133
+ <SectionTools {ctx} inline />
134
+ </header>
135
+
136
+ {#if scripted}
137
+ <!-- Parent-drawn label (jat-4yffn): the frame's script cannot touch it. -->
138
+ <p
139
+ class="border-warning/40 bg-warning/10 text-base-content/80 rounded-field mx-4 mt-2 border px-3 py-2 text-xs sm:mx-5"
140
+ data-mockup-scripted
141
+ >
142
+ <strong class="font-semibold">This mockup runs scripts.</strong>
143
+ It is a sketch, not a real form — never type a password or a code into it.
144
+ </p>
145
+ {:else if scriptsBlocked}
146
+ <!-- jatui-2qhbr: degraded to script-free; say so, never fail silently. -->
147
+ <p
148
+ class="border-base-content/15 bg-base-200 text-base-content/70 rounded-field mx-4 mt-2 border px-3 py-2 text-xs sm:mx-5"
149
+ data-mockup-scripts-disabled
150
+ >
151
+ <strong class="font-semibold">Scripts disabled:</strong>
152
+ this host page does not restrict frame navigation, so the mockup is shown
153
+ without its interactive parts.
154
+ </p>
155
+ {/if}
156
+
157
+ <div class="flex flex-wrap items-start gap-5 px-4 pt-2 pb-5 sm:px-5">
158
+ {#each params.variants as v (v.id)}
159
+ {@const dev = deviceOf(v.device)}
160
+ {@const d = dims(dev)}
161
+ {@const s = scale(v.id, dev)}
162
+ <figure class="m-0">
163
+ <!-- Frame chrome per device; the viewport inside is the scaled iframe. -->
164
+ <div
165
+ class="relative mx-auto"
166
+ style={frameStyle(v.id, dev)}
167
+ bind:clientWidth={frameWidths[v.id]}
168
+ >
169
+ {#if dev === "desktop"}
170
+ <!-- Browser chrome -->
171
+ <div
172
+ class="border-base-content/15 bg-base-200 rounded-box overflow-hidden border"
173
+ style="height:100%"
174
+ >
175
+ <div
176
+ class="border-base-content/10 flex items-center gap-2 border-b px-3 py-1.5"
177
+ >
178
+ <span class="flex gap-1" aria-hidden="true">
179
+ <span class="bg-error size-2 rounded-full"></span>
180
+ <span class="bg-warning size-2 rounded-full"></span>
181
+ <span class="bg-success size-2 rounded-full"></span>
182
+ </span>
183
+ <span
184
+ class="bg-base-100 border-base-content/10 text-base-content/50 mx-auto rounded-field max-w-[60%] flex-1 truncate px-2 py-0.5 text-center text-2xs"
185
+ aria-hidden="true"
186
+ >
187
+ {v.label ?? v.id}
188
+ </span>
189
+ </div>
190
+ <iframe
191
+ data-mock-id={v.id}
192
+ srcdoc={mockupDocument(v.html, theme, scripted)}
193
+ {sandbox}
194
+ title="{v.label ?? v.id} — desktop preview"
195
+ referrerpolicy="no-referrer"
196
+ loading="lazy"
197
+ style="width:{d.w}px;height:{d.h -
198
+ 30 / s}px;transform:scale({s});transform-origin:0 0;border:0;"
199
+ ></iframe>
200
+ </div>
201
+ {:else}
202
+ <!-- Phone / tablet chrome: bezel + notch or camera dot -->
203
+ <div
204
+ class="bg-base-300 border-base-content/15 relative h-full overflow-hidden {dev ===
205
+ 'phone'
206
+ ? 'rounded-[2.25rem] border-[6px]'
207
+ : 'rounded-[1.5rem] border-[8px]'}"
208
+ >
209
+ <span
210
+ class="bg-base-content/20 absolute top-2 left-1/2 h-1.5 w-16 -translate-x-1/2 rounded-full {dev ===
211
+ 'tablet'
212
+ ? 'hidden'
213
+ : ''}"
214
+ aria-hidden="true"
215
+ ></span>
216
+ <span
217
+ class="bg-base-content/20 absolute top-2.5 left-3 size-1.5 rounded-full {dev ===
218
+ 'phone'
219
+ ? 'hidden'
220
+ : ''}"
221
+ aria-hidden="true"
222
+ ></span>
223
+ <iframe
224
+ data-mock-id={v.id}
225
+ srcdoc={mockupDocument(v.html, theme, scripted)}
226
+ {sandbox}
227
+ title="{v.label ?? v.id} — {dev} preview"
228
+ referrerpolicy="no-referrer"
229
+ loading="lazy"
230
+ style="width:{d.w}px;height:{d.h}px;transform:scale({s});transform-origin:0 0;border:0;"
231
+ ></iframe>
232
+ </div>
233
+ {/if}
234
+ </div>
235
+ <figcaption
236
+ class="text-base-content/60 mt-1.5 text-center text-xs tabular-nums"
237
+ >
238
+ {v.label ?? v.id}
239
+ <span class="text-base-content/40">
240
+ &middot; {dev}
241
+ {d.w}&times;{d.h}
242
+ </span>
243
+ </figcaption>
244
+ </figure>
245
+ {/each}
246
+ </div>
247
+ </section>
@@ -0,0 +1,5 @@
1
+ import type { BlockProps } from "../../types.js";
2
+ import { type MockupParams } from "./types.js";
3
+ declare const MockupBlock: import("svelte").Component<BlockProps<MockupParams>, {}, "">;
4
+ type MockupBlock = ReturnType<typeof MockupBlock>;
5
+ export default MockupBlock;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * mockup — agent-authored HTML shown in device frames (mos-vzame, epic
3
+ * mos-m1qrx).
4
+ *
5
+ * Params are DATA: one or more variants, each an HTML snippet rendered
6
+ * verbatim inside a sandboxed iframe shaped like a phone, tablet, or desktop
7
+ * frame. The iframe's sandbox attribute comes from `mockupSandboxAttr()` in
8
+ * ./sandbox.ts and allow-same-origin is NEVER emitted: the frame is an
9
+ * opaque origin with no session, no cookies, no parent DOM (see that module
10
+ * for the threat model). Scripts stay off unless `allowScripts` is true, and
11
+ * then the frame is `allow-scripts` only — no popups, no remote images or
12
+ * media, no network (jat-4yffn) — under a parent-drawn "runs scripts" notice,
13
+ * and only on a host that asserts an enforced `frame-src 'self'`
14
+ * (`services.framePolicy`, jatui-2qhbr); elsewhere it degrades to script-free.
15
+ *
16
+ * The light/dark toggle re-wraps fragments with `data-theme` on <html> (see
17
+ * ./doc.ts); the snippet's own CSS decides what that attribute means.
18
+ *
19
+ * Use it when the artifact IS a design artifact: a homepage concept, a
20
+ * component state exploration, an email template. One-line tip: publish each
21
+ * design option as its own variant (side by side) rather than a separate
22
+ * block per option — reviewers compare them at a glance.
23
+ */
24
+ import type { ArtifactBlockDef } from "../../types.js";
25
+ import type { MockupParams } from "./types.js";
26
+ declare const mockup: ArtifactBlockDef<MockupParams>;
27
+ export default mockup;
@@ -0,0 +1,52 @@
1
+ import { mockupSchema } from "./schema.js";
2
+ import MockupBlock from "./MockupBlock.svelte";
3
+ const example = {
4
+ title: "Checkout — concept A/B",
5
+ intro: "Same flow, two treatments. Toggle the theme to check both.",
6
+ variants: [
7
+ {
8
+ id: "a-phone",
9
+ label: "Concept A",
10
+ device: "phone",
11
+ html: `<main style="font:16px/1.5 system-ui;padding:24px">
12
+ <h1 style="font-size:20px;margin:0 0 8px">Checkout</h1>
13
+ <p style="margin:0 0 16px;color:#666">Concept A — single column</p>
14
+ <div style="height:44px;border-radius:12px;background:#eee"></div>
15
+ </main>`,
16
+ },
17
+ {
18
+ id: "b-phone",
19
+ label: "Concept B",
20
+ device: "phone",
21
+ html: `<main style="font:16px/1.5 system-ui;padding:24px">
22
+ <h1 style="font-size:20px;margin:0 0 8px">Checkout</h1>
23
+ <p style="margin:0 0 16px;color:#666">Concept B — summary card</p>
24
+ <div style="height:44px;border-radius:12px;background:#ddd"></div>
25
+ </main>`,
26
+ },
27
+ {
28
+ id: "a-desktop",
29
+ label: "Concept A · desktop",
30
+ device: "desktop",
31
+ html: `<main style="font:16px/1.5 system-ui;padding:48px;max-width:960px">
32
+ <h1 style="font-size:28px;margin:0 0 12px">Checkout — desktop</h1>
33
+ <div style="display:flex;gap:24px">
34
+ <div style="flex:1;height:180px;border-radius:12px;background:#eee"></div>
35
+ <div style="width:280px;height:180px;border-radius:12px;background:#ddd"></div>
36
+ </div>
37
+ </main>`,
38
+ },
39
+ ],
40
+ };
41
+ const mockup = {
42
+ type: "mockup",
43
+ title: "Mockup (sandboxed HTML in device frames)",
44
+ description: "Renders agent-authored HTML snippets inside phone, tablet, or desktop device frames — the way to put a UI concept into an artifact WITHOUT giving that HTML the page's origin: each variant is an iframe whose sandbox attribute comes from the artifact sandbox module (opaque origin, allow-same-origin never emitted, scripts only when allowScripts is true), so the mockup can never touch the session. allowScripts is for interaction only and is locked down: the frame gets no popups (links that open a new tab do nothing), no network, and images/media/fonts only as data: URIs (Google Fonts excepted), and the page shows a \"runs scripts\" notice above it; scripts also need the host to assert an enforced frame-src 'self', and on a host that does not, the mockup renders script-free under a \"scripts disabled\" notice — leave it off unless the concept needs JS. Pass a FRAGMENT per variant; the block wraps it and drives data-theme=light|dark on <html> so the block-level theme toggle re-skins designs that honor the attribute (a full document passes through unwrapped and owns its own theming). Variants render side by side for A/B comparison. A top-level `device` param is shorthand: it becomes the device of every variant that does not name its own (a variant's own device wins); a variant without any device defaults to phone. Budget: 48KB of HTML per variant, 256KB for the whole block. One-line tip: name variant ids after the option they show (a-phone, b-phone) — reviewers reference them in notes.",
45
+ schema: mockupSchema,
46
+ component: MockupBlock,
47
+ // mos-8fvt9: the section's Listen + comment sit on the heading row.
48
+ hostsTools: () => true,
49
+ example,
50
+ reviewableByDefault: true,
51
+ };
52
+ export default mockup;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The sandboxed document a mockup iframe renders (mos-vzame).
3
+ *
4
+ * The iframe gets its sandbox from mockupSandboxAttr() in ./sandbox.ts.
5
+ * allow-same-origin is absent in every mode that helper can emit; the frame
6
+ * is an opaque origin and can never reach the app session, cookies, or DOM.
7
+ * That module is the single source of truth for the boundary and its threat
8
+ * model — this file decides WHAT the opaque-origin document contains, and
9
+ * for a SCRIPTED mockup that includes the policy head (jat-4yffn):
10
+ * MOCKUP_SCRIPTED_CSP as a `<meta http-equiv>` plus DNS prefetch off. A
11
+ * srcdoc document has no response headers, so meta is the only carrier. It
12
+ * is the first element in <head> (before <meta charset>, which only has to
13
+ * sit in the first 1024 bytes — the policy head is well under that), so it
14
+ * governs everything the author wrote, and a script removing the element
15
+ * later does not lift an applied policy.
16
+ *
17
+ * Whether a mockup is rendered scripted at all is decided by the caller
18
+ * (mockupScriptsAllowed in ./sandbox.ts, jatui-2qhbr): this function trusts
19
+ * its `scripted` argument and emits the policy head for it.
20
+ *
21
+ * The light/dark toggle works by re-wrapping the snippet with `data-theme` on
22
+ * the <html> element — the author's CSS decides what that attribute means
23
+ * (DaisyUI themes, Tailwind `dark:` with a data-theme selector, hand CSS).
24
+ * A full document (one that already has <html>) passes through untouched:
25
+ * rewriting untrusted markup by regex is exactly the fragility this block
26
+ * refuses; authors who bring their own document own their own theming.
27
+ */
28
+ /**
29
+ * Wrap a mockup snippet into a full document carrying `data-theme`. A
30
+ * fragment gets the wrapper (body margin 0 — a mockup is full-bleed); a
31
+ * document that already brings `<html>` passes through verbatim — unless it
32
+ * is scripted, when the policy head is PREPENDED rather than spliced in
33
+ * (no regex surgery on untrusted markup). The parser puts those metas in an
34
+ * implied <head>; the author's own doctype/<html>/<head> tags that follow
35
+ * are then parse errors that merge or drop harmlessly (their <html>
36
+ * attributes still apply). The prefix carries a doctype so the document
37
+ * stays in standards mode.
38
+ */
39
+ export declare function mockupDocument(html: string, theme: "light" | "dark", scripted?: boolean): string;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The sandboxed document a mockup iframe renders (mos-vzame).
3
+ *
4
+ * The iframe gets its sandbox from mockupSandboxAttr() in ./sandbox.ts.
5
+ * allow-same-origin is absent in every mode that helper can emit; the frame
6
+ * is an opaque origin and can never reach the app session, cookies, or DOM.
7
+ * That module is the single source of truth for the boundary and its threat
8
+ * model — this file decides WHAT the opaque-origin document contains, and
9
+ * for a SCRIPTED mockup that includes the policy head (jat-4yffn):
10
+ * MOCKUP_SCRIPTED_CSP as a `<meta http-equiv>` plus DNS prefetch off. A
11
+ * srcdoc document has no response headers, so meta is the only carrier. It
12
+ * is the first element in <head> (before <meta charset>, which only has to
13
+ * sit in the first 1024 bytes — the policy head is well under that), so it
14
+ * governs everything the author wrote, and a script removing the element
15
+ * later does not lift an applied policy.
16
+ *
17
+ * Whether a mockup is rendered scripted at all is decided by the caller
18
+ * (mockupScriptsAllowed in ./sandbox.ts, jatui-2qhbr): this function trusts
19
+ * its `scripted` argument and emits the policy head for it.
20
+ *
21
+ * The light/dark toggle works by re-wrapping the snippet with `data-theme` on
22
+ * the <html> element — the author's CSS decides what that attribute means
23
+ * (DaisyUI themes, Tailwind `dark:` with a data-theme selector, hand CSS).
24
+ * A full document (one that already has <html>) passes through untouched:
25
+ * rewriting untrusted markup by regex is exactly the fragility this block
26
+ * refuses; authors who bring their own document own their own theming.
27
+ */
28
+ import { MOCKUP_SCRIPTED_CSP } from "./sandbox.js";
29
+ /** The scripted document's policy head (jat-4yffn). Attribute-safe: the CSP has no `"`. */
30
+ const SCRIPTED_POLICY = `<meta http-equiv="Content-Security-Policy" content="${MOCKUP_SCRIPTED_CSP}">
31
+ <meta http-equiv="x-dns-prefetch-control" content="off">
32
+ `;
33
+ const WRAPPER_HEAD = `<!doctype html>
34
+ <html lang="en" data-theme="THEME">
35
+ <head>
36
+ POLICY<meta charset="utf-8">
37
+ <meta name="viewport" content="width=device-width, initial-scale=1">
38
+ <style>
39
+ html[data-theme="light"] { color-scheme: light; }
40
+ html[data-theme="dark"] { color-scheme: dark; }
41
+ body { margin: 0; padding: 0; }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ `;
46
+ const WRAPPER_TAIL = `
47
+ </body>
48
+ </html>`;
49
+ /**
50
+ * Wrap a mockup snippet into a full document carrying `data-theme`. A
51
+ * fragment gets the wrapper (body margin 0 — a mockup is full-bleed); a
52
+ * document that already brings `<html>` passes through verbatim — unless it
53
+ * is scripted, when the policy head is PREPENDED rather than spliced in
54
+ * (no regex surgery on untrusted markup). The parser puts those metas in an
55
+ * implied <head>; the author's own doctype/<html>/<head> tags that follow
56
+ * are then parse errors that merge or drop harmlessly (their <html>
57
+ * attributes still apply). The prefix carries a doctype so the document
58
+ * stays in standards mode.
59
+ */
60
+ export function mockupDocument(html, theme, scripted = false) {
61
+ if (/<html[\s>]/i.test(html)) {
62
+ return scripted ? `<!doctype html>\n${SCRIPTED_POLICY}${html}` : html;
63
+ }
64
+ return `${WRAPPER_HEAD.replace("THEME", theme).replace("POLICY", scripted ? SCRIPTED_POLICY : "")}${html}${WRAPPER_TAIL}`;
65
+ }