@jxsuite/studio 10.2.0 → 10.3.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 (271) hide show
  1. package/dist/chunks/{2020-bx702wqa.js → 2020-906vx2dq.js} +3 -3
  2. package/dist/chunks/adapter-hnvad61x.js +4 -0
  3. package/dist/chunks/add-new-flow-3tkn72ey.js +4 -0
  4. package/dist/chunks/cf-account-picker-xb8vvt2h.js +4 -0
  5. package/dist/chunks/cf-settings-mkbcz4z2.js +4 -0
  6. package/dist/chunks/components-w5zqwva1.js +4 -0
  7. package/dist/chunks/content-source-1vzxq1ba.js +4 -0
  8. package/dist/chunks/convert-to-repeater-gwzh9gar.js +4 -0
  9. package/dist/chunks/editor-wvf5e6j9.js +4 -0
  10. package/dist/chunks/entry-commands-mx2whrjx.js +4 -0
  11. package/dist/chunks/extension-sections-338ee35t.js +4 -0
  12. package/dist/chunks/file-ops-9bxvk59j.js +4 -0
  13. package/dist/chunks/files-t07yqqej.js +4 -0
  14. package/dist/chunks/format-host-5ja5gxyk.js +4 -0
  15. package/dist/chunks/fs-events-3424ezp3.js +4 -0
  16. package/dist/chunks/grid-open-1mpq9mzb.js +4 -0
  17. package/dist/chunks/{grid-view-tghcwrrt.js → grid-view-w9nfy203.js} +16 -16
  18. package/dist/chunks/icons-gb967qc8.js +4 -0
  19. package/dist/chunks/iframe-host-wsg0bn73.js +4 -0
  20. package/dist/chunks/image-bake-4afr4y6a.js +4 -0
  21. package/dist/chunks/image-editor-x7t8jbjn.js +4 -0
  22. package/dist/chunks/index-f9qr7kzh.js +4 -0
  23. package/dist/chunks/{index-vr006kbt.js → index-yjrfeks8.js} +3 -3
  24. package/dist/chunks/{javascript-z1g3s9fd.js → javascript-hy2mws8v.js} +3 -3
  25. package/dist/chunks/{jsonMode-7szwg8c4.js → jsonMode-vj30te2h.js} +6 -6
  26. package/dist/chunks/jx-validate-ttr5ewf0.js +4 -0
  27. package/dist/chunks/layers-m49nmwvx.js +4 -0
  28. package/dist/chunks/layers-panel-hvy2e549.js +4 -0
  29. package/dist/chunks/lit-html-k2mb9k0r.js +4 -0
  30. package/dist/chunks/media-library-ae67q4at.js +4 -0
  31. package/dist/chunks/media-open-yp9jm834.js +4 -0
  32. package/dist/chunks/media-picker-s41ngdfr.js +4 -0
  33. package/dist/chunks/{monaco-binding-v42w9c1v.js → monaco-binding-a51py33c.js} +3 -3
  34. package/dist/chunks/monaco-buffer-pa0g6ax7.js +4 -0
  35. package/dist/chunks/{monaco-setup-7bffxd6v.js → monaco-setup-g9kterhx.js} +3 -3
  36. package/dist/chunks/notify-bwehrp7q.js +4 -0
  37. package/dist/chunks/parse-hee7ne9b.js +4 -0
  38. package/dist/chunks/platform-kv0k65cy.js +4 -0
  39. package/dist/chunks/publish-panel-gzfxcm0t.js +4 -0
  40. package/dist/chunks/publish-quick-w3nxd7q4.js +4 -0
  41. package/dist/chunks/publish-workspace-dppd82tc.js +4 -0
  42. package/dist/chunks/quick-search-tdxaam55.js +4 -0
  43. package/dist/chunks/references-vqyr5s9x.js +4 -0
  44. package/dist/chunks/right-panel-0yrw4cgz.js +4 -0
  45. package/dist/chunks/source-control-6fk72me2.js +4 -0
  46. package/dist/chunks/{studio-j3d2fjqz.js → studio-0939t1q1.js} +4 -4
  47. package/dist/chunks/studio-0gvvqj3h.js +5 -0
  48. package/dist/chunks/studio-0khd8xtx.js +4 -0
  49. package/dist/chunks/studio-0w3541dg.js +7 -0
  50. package/dist/chunks/{studio-9b7177jq.js → studio-0x6f3nk9.js} +4 -4
  51. package/dist/chunks/studio-1410q5dw.js +5 -0
  52. package/dist/chunks/{studio-awxwjawq.js → studio-15hwqgd8.js} +4 -4
  53. package/dist/chunks/studio-1ks5d9y4.js +5 -0
  54. package/dist/chunks/{studio-881z1xrx.js → studio-2ggfzrrd.js} +4 -4
  55. package/dist/chunks/{studio-4jhw38b2.js → studio-321ym1tt.js} +4 -4
  56. package/dist/chunks/studio-3c10v2q1.js +5 -0
  57. package/dist/chunks/{studio-bvs9kpst.js → studio-3na1r48j.js} +8 -8
  58. package/dist/chunks/{studio-86r8hp7r.js → studio-3q7mhbgx.js} +5 -5
  59. package/dist/chunks/studio-3qr08sxq.js +4 -0
  60. package/dist/chunks/studio-3xk9zdgb.js +5 -0
  61. package/dist/chunks/{studio-qh8berdz.js → studio-423tfdte.js} +4 -4
  62. package/dist/chunks/{studio-75kcdbsk.js → studio-45mj4jqe.js} +4 -4
  63. package/dist/chunks/studio-4f7thq0p.js +5 -0
  64. package/dist/chunks/studio-4fnb5648.js +5 -0
  65. package/dist/chunks/studio-4zd6an2b.js +5 -0
  66. package/dist/chunks/studio-53wy9ek3.js +5 -0
  67. package/dist/chunks/studio-5n193zqd.js +5 -0
  68. package/dist/chunks/{studio-9rbe2xpw.js → studio-5tet77de.js} +3 -3
  69. package/dist/chunks/{studio-ybnwymxw.js → studio-7rrdbvex.js} +4 -4
  70. package/dist/chunks/studio-8kx6m0rg.js +5 -0
  71. package/dist/chunks/{studio-sxdb9h3q.js → studio-90zkgne5.js} +7 -7
  72. package/dist/chunks/studio-9c6cbq2c.js +4 -0
  73. package/dist/chunks/studio-9dq7rwc7.js +5 -0
  74. package/dist/chunks/{studio-cg1bfyhf.js → studio-9qx1ywbv.js} +8 -8
  75. package/dist/chunks/studio-a65kbs2e.js +9 -0
  76. package/dist/chunks/studio-aam8zt8z.js +5 -0
  77. package/dist/chunks/{studio-er5f383a.js → studio-abh9kfdh.js} +4 -4
  78. package/dist/chunks/studio-b4e0ftkt.js +5 -0
  79. package/dist/chunks/studio-b68k0z00.js +5 -0
  80. package/dist/chunks/{studio-399a25za.js → studio-bct64zmj.js} +4 -4
  81. package/dist/chunks/studio-bz0b5brj.js +6 -0
  82. package/dist/chunks/studio-cfnn70h1.js +5 -0
  83. package/dist/chunks/studio-daksw8kh.js +5 -0
  84. package/dist/chunks/{studio-r7pf0rkv.js → studio-e7vjyp04.js} +7 -7
  85. package/dist/chunks/studio-ea6r1zyr.js +5 -0
  86. package/dist/chunks/studio-egnjpyde.js +9 -0
  87. package/dist/chunks/studio-ejrypvmq.js +5 -0
  88. package/dist/chunks/studio-eqcddvc2.js +928 -0
  89. package/dist/chunks/studio-f8tyeyzt.js +5 -0
  90. package/dist/chunks/studio-fj6c905s.js +5 -0
  91. package/dist/chunks/{studio-646nn2ys.js → studio-fkrr7kgg.js} +5 -5
  92. package/dist/chunks/studio-fspe7m9t.js +5 -0
  93. package/dist/chunks/studio-g0hxtcga.js +5 -0
  94. package/dist/chunks/{studio-th7z3fg7.js → studio-g9brbewz.js} +4 -4
  95. package/dist/chunks/studio-ggx3j7nd.js +5 -0
  96. package/dist/chunks/{studio-43y04qck.js → studio-hfxttkyr.js} +4 -4
  97. package/dist/chunks/studio-hh394phe.js +5 -0
  98. package/dist/chunks/{studio-pjtntksy.js → studio-j0zf5x2x.js} +4 -4
  99. package/dist/chunks/studio-jeb7hth3.js +5 -0
  100. package/dist/chunks/studio-k0xzrse2.js +64 -0
  101. package/dist/chunks/studio-k1xqrgm5.js +5 -0
  102. package/dist/chunks/studio-k2evngrj.js +7 -0
  103. package/dist/chunks/studio-k3we3kqb.js +5 -0
  104. package/dist/chunks/{studio-sevbc3xv.js → studio-n9qba0gx.js} +4 -4
  105. package/dist/chunks/{studio-6hzdjfv0.js → studio-nf8esxxc.js} +4 -4
  106. package/dist/chunks/studio-nn4f9m9c.js +5 -0
  107. package/dist/chunks/studio-nskxe9q1.js +6 -0
  108. package/dist/chunks/{studio-ea74s8c1.js → studio-nvcn2ykc.js} +4 -4
  109. package/dist/chunks/studio-nwynxqtb.js +5 -0
  110. package/dist/chunks/studio-nzemfbkk.js +14 -0
  111. package/dist/chunks/studio-p2a03pgm.js +5 -0
  112. package/dist/chunks/{studio-4g8yb6jn.js → studio-pm17wdc4.js} +4 -4
  113. package/dist/chunks/{studio-71m92v9s.js → studio-q80fg1d6.js} +7 -7
  114. package/dist/chunks/studio-r050sez0.js +6 -0
  115. package/dist/chunks/studio-s7t4vp1d.js +6 -0
  116. package/dist/chunks/studio-s9fng7m8.js +7 -0
  117. package/dist/chunks/studio-sce30r7b.js +4 -0
  118. package/dist/chunks/studio-stahswgb.js +7 -0
  119. package/dist/chunks/studio-swedpd82.js +5 -0
  120. package/dist/chunks/studio-thz5t120.js +5 -0
  121. package/dist/chunks/studio-tpxcryf2.js +5 -0
  122. package/dist/chunks/{studio-wc6za3zs.js → studio-v5wktj6f.js} +4 -4
  123. package/dist/chunks/{studio-kg1abnz0.js → studio-vpevkfxw.js} +4 -4
  124. package/dist/chunks/studio-waekn290.js +5 -0
  125. package/dist/chunks/studio-wfct4h6n.js +5 -0
  126. package/dist/chunks/studio-wgc54sxf.js +5 -0
  127. package/dist/chunks/studio-wqfaqw4m.js +5 -0
  128. package/dist/chunks/studio-wse1rs8t.js +7 -0
  129. package/dist/chunks/studio-x0gsfr1d.js +16 -0
  130. package/dist/chunks/{studio-hhp4x8xf.js → studio-xpx91fby.js} +5 -5
  131. package/dist/chunks/studio-y1b86ast.js +5 -0
  132. package/dist/chunks/studio-yrcrncev.js +5 -0
  133. package/dist/chunks/{studio-rzqaz21g.js → studio-yzpskb0j.js} +4 -4
  134. package/dist/chunks/studio-zfdbmhp3.js +5 -0
  135. package/dist/chunks/{tsMode-f54j66ym.js → tsMode-jg1dtphj.js} +7 -7
  136. package/dist/chunks/workspace-9btkc8tk.js +4 -0
  137. package/dist/chunks/workspace-home-qcc4z5ag.js +4 -0
  138. package/dist/chunks/{ws-client-e8kj684q.js → ws-client-arz2qsy9.js} +3 -3
  139. package/dist/iframe-entry.js +13 -13
  140. package/dist/manifest.json +83 -78
  141. package/dist/studio.js +84 -84
  142. package/package.json +2 -2
  143. package/src/browse/library-pane.ts +44 -29
  144. package/src/browse/library-preview.ts +6 -2
  145. package/src/canvas/asset-refs.ts +13 -4
  146. package/src/canvas/asset-resolve.ts +54 -11
  147. package/src/canvas/canvas-render.ts +25 -0
  148. package/src/canvas/iframe-entry.ts +5 -1
  149. package/src/canvas/iframe-host.ts +16 -6
  150. package/src/canvas/iframe-protocol.ts +9 -1
  151. package/src/commands/context.ts +5 -0
  152. package/src/commands/live-context.ts +5 -2
  153. package/src/editor/shortcuts.ts +3 -0
  154. package/src/files/asset-versions.ts +108 -2
  155. package/src/files/file-ops.ts +11 -1
  156. package/src/files/files.ts +111 -48
  157. package/src/files/media-paths.ts +12 -4
  158. package/src/files/media-upload.ts +195 -21
  159. package/src/files/new-file-name.ts +39 -1
  160. package/src/media/image-bake.ts +164 -0
  161. package/src/media/image-editor.ts +846 -0
  162. package/src/media/image-format.ts +262 -0
  163. package/src/media/image-recipe.ts +651 -0
  164. package/src/media/media-commands.ts +65 -1
  165. package/src/media/media-library-model.ts +1 -1
  166. package/src/media/media-library.ts +175 -15
  167. package/src/media/media-open.ts +156 -3
  168. package/src/media/media-pane.ts +8 -4
  169. package/src/panels/pane-context.ts +6 -1
  170. package/src/panels/tab-strip.ts +12 -3
  171. package/src/studio.ts +13 -2
  172. package/src/surfaces/image-editor.json +1390 -0
  173. package/src/surfaces/image-editor.ts +272 -0
  174. package/src/surfaces/media-library.json +129 -1
  175. package/src/surfaces/media-library.ts +17 -2
  176. package/src/types.ts +2 -1
  177. package/src/workspace/tab-workspace.ts +2 -1
  178. package/src/workspace/workspace-switch.ts +3 -1
  179. package/src/workspace/workspace.ts +22 -1
  180. package/dist/chunks/adapter-pp011m5x.js +0 -4
  181. package/dist/chunks/add-new-flow-ecqmzz76.js +0 -4
  182. package/dist/chunks/cf-account-picker-c07znkqt.js +0 -4
  183. package/dist/chunks/cf-settings-0v2bmqy0.js +0 -4
  184. package/dist/chunks/components-yn0dn2pq.js +0 -4
  185. package/dist/chunks/content-source-g0j41yy6.js +0 -4
  186. package/dist/chunks/convert-to-repeater-sbjk7tgn.js +0 -4
  187. package/dist/chunks/editor-xz02jeh1.js +0 -4
  188. package/dist/chunks/entry-commands-kmt02crp.js +0 -4
  189. package/dist/chunks/extension-sections-0rprf12d.js +0 -4
  190. package/dist/chunks/file-ops-mexj5qed.js +0 -4
  191. package/dist/chunks/files-b2tcppdd.js +0 -4
  192. package/dist/chunks/format-host-btsevv0c.js +0 -4
  193. package/dist/chunks/fs-events-r0apejva.js +0 -4
  194. package/dist/chunks/grid-open-n2a8xkht.js +0 -4
  195. package/dist/chunks/icons-853n6qt3.js +0 -4
  196. package/dist/chunks/iframe-host-gx0ypyh3.js +0 -4
  197. package/dist/chunks/index-bs4k9tr6.js +0 -4
  198. package/dist/chunks/jx-validate-ymf10teh.js +0 -4
  199. package/dist/chunks/layers-p9nfya38.js +0 -4
  200. package/dist/chunks/layers-panel-tt1hzhhf.js +0 -4
  201. package/dist/chunks/lit-html-x18n2sfz.js +0 -4
  202. package/dist/chunks/media-library-0n45gv8r.js +0 -4
  203. package/dist/chunks/media-picker-s09at95z.js +0 -4
  204. package/dist/chunks/monaco-buffer-een2ea0v.js +0 -4
  205. package/dist/chunks/notify-qwb8radt.js +0 -4
  206. package/dist/chunks/parse-v77xtwv7.js +0 -4
  207. package/dist/chunks/platform-8jnez0wr.js +0 -4
  208. package/dist/chunks/publish-panel-aysnj611.js +0 -4
  209. package/dist/chunks/publish-quick-kpbqs9mz.js +0 -4
  210. package/dist/chunks/publish-workspace-nd0a8q99.js +0 -4
  211. package/dist/chunks/quick-search-vg0jkhk2.js +0 -4
  212. package/dist/chunks/references-a7v6a0e2.js +0 -4
  213. package/dist/chunks/right-panel-cvk1vxx5.js +0 -4
  214. package/dist/chunks/source-control-fsqzc6he.js +0 -4
  215. package/dist/chunks/studio-05jfwbmf.js +0 -5
  216. package/dist/chunks/studio-0q5jwddh.js +0 -9
  217. package/dist/chunks/studio-0w2dr0q8.js +0 -5
  218. package/dist/chunks/studio-1tzpds97.js +0 -9
  219. package/dist/chunks/studio-216b27sv.js +0 -4
  220. package/dist/chunks/studio-2ejhqns4.js +0 -5
  221. package/dist/chunks/studio-2r90cts3.js +0 -16
  222. package/dist/chunks/studio-399xknkb.js +0 -6
  223. package/dist/chunks/studio-4ncmwqw5.js +0 -5
  224. package/dist/chunks/studio-5atwjgkj.js +0 -7
  225. package/dist/chunks/studio-5frqk7c0.js +0 -5
  226. package/dist/chunks/studio-6cwj6xrx.js +0 -5
  227. package/dist/chunks/studio-78pywnp2.js +0 -6
  228. package/dist/chunks/studio-886t6ztg.js +0 -5
  229. package/dist/chunks/studio-8wnvtr4z.js +0 -5
  230. package/dist/chunks/studio-8z66jk4z.js +0 -5
  231. package/dist/chunks/studio-b2qpt0xy.js +0 -14
  232. package/dist/chunks/studio-c71806xz.js +0 -7
  233. package/dist/chunks/studio-cbwks69x.js +0 -928
  234. package/dist/chunks/studio-cg710sk9.js +0 -5
  235. package/dist/chunks/studio-cw0wb9qk.js +0 -7
  236. package/dist/chunks/studio-cyheys7k.js +0 -5
  237. package/dist/chunks/studio-ea5m9kg5.js +0 -5
  238. package/dist/chunks/studio-efdxjwyp.js +0 -64
  239. package/dist/chunks/studio-egmw2837.js +0 -5
  240. package/dist/chunks/studio-f746039f.js +0 -5
  241. package/dist/chunks/studio-fmdymd9v.js +0 -5
  242. package/dist/chunks/studio-gcm12z00.js +0 -6
  243. package/dist/chunks/studio-gj98m8gy.js +0 -7
  244. package/dist/chunks/studio-hasbgjah.js +0 -5
  245. package/dist/chunks/studio-hz5n7x27.js +0 -5
  246. package/dist/chunks/studio-jeraxdgv.js +0 -7
  247. package/dist/chunks/studio-jvgek9xt.js +0 -5
  248. package/dist/chunks/studio-jws1a87n.js +0 -5
  249. package/dist/chunks/studio-k2sz13zg.js +0 -5
  250. package/dist/chunks/studio-k3m4jkdk.js +0 -5
  251. package/dist/chunks/studio-kg5ya5r2.js +0 -5
  252. package/dist/chunks/studio-khwbgrtg.js +0 -5
  253. package/dist/chunks/studio-mx6wzh25.js +0 -5
  254. package/dist/chunks/studio-mxs3ar9t.js +0 -5
  255. package/dist/chunks/studio-nm4szey4.js +0 -5
  256. package/dist/chunks/studio-nqxqf8p2.js +0 -4
  257. package/dist/chunks/studio-qn7z4krm.js +0 -5
  258. package/dist/chunks/studio-qyed0ah6.js +0 -5
  259. package/dist/chunks/studio-r4bcdwhq.js +0 -5
  260. package/dist/chunks/studio-rtfnhrdg.js +0 -5
  261. package/dist/chunks/studio-s5tzw84t.js +0 -5
  262. package/dist/chunks/studio-t51y8v1d.js +0 -5
  263. package/dist/chunks/studio-t98vq4r6.js +0 -5
  264. package/dist/chunks/studio-tsxpqvh0.js +0 -6
  265. package/dist/chunks/studio-v1yascv2.js +0 -5
  266. package/dist/chunks/studio-wxq6v5sv.js +0 -5
  267. package/dist/chunks/studio-y60sts62.js +0 -4
  268. package/dist/chunks/studio-zndrqawb.js +0 -5
  269. package/dist/chunks/studio-zwaxn5s2.js +0 -5
  270. package/dist/chunks/workspace-home-x1787wpv.js +0 -4
  271. package/dist/chunks/workspace-zpe1np43.js +0 -4
@@ -0,0 +1,272 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * The Image editor surface: one image asset, edited where it is opened (studio.md §9.5).
4
+ *
5
+ * This is the adapter. `media/image-editor.ts` keeps the recipe, its undo stack, the decoded source
6
+ * and the one write door, and hands this module a flat projection: pre-formatted geometry strings,
7
+ * the values the fields show, and the words the `$switch`es read. The document renders that and
8
+ * decides nothing.
9
+ *
10
+ * **One island.** A drag converts screen pixels into the image's own frame, which needs the frame's
11
+ * drawn size, so the frame element is captured as it is created (`onNodeCreated`,
12
+ * studio-ui-guidelines.md §9.4) and the flow asks for its rect. The handles are captured the same
13
+ * way, so moving the roving tab stop to the next handle focuses an element this module was handed
14
+ * rather than one it searched for.
15
+ *
16
+ * **The container is CLEARED, because this is a pane and not a panel** — the reason
17
+ * `surfaces/media-pane.ts` gives: the canvas stage is handed to whichever mode owns it whole.
18
+ *
19
+ * @docs studio/projects/media
20
+ */
21
+
22
+ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
23
+ import { reactive } from "../reactivity";
24
+ import { mountSurface, registerSurface } from "../ui/surface";
25
+ import { rectOf } from "../utils/geometry";
26
+ import imageEditorDoc from "./image-editor.json";
27
+ import type { SurfaceHandle } from "../ui/surface";
28
+ import type { JxScope } from "@jxsuite/runtime/types";
29
+ import type { JxElement } from "@jxsuite/schema/types";
30
+
31
+ registerSurface("image-editor", imageEditorDoc);
32
+
33
+ /** One crop handle as the document draws it. */
34
+ export interface ImageHandleRow {
35
+ id: string;
36
+ /** Where it sits on screen, as a reader hears it. */
37
+ label: string;
38
+ /** `"0"` for the one handle in the tab order, `"-1"` for the other seven. */
39
+ tabindex: string;
40
+ }
41
+
42
+ /** Everything the document draws, as one value. */
43
+ export interface ImageEditorView {
44
+ region: string;
45
+ /** `loading` while the bytes are read, `refused` with the reason, `ready` with a picture. */
46
+ status: "loading" | "ready" | "refused";
47
+ /** What the loading or refused box says. */
48
+ message: string;
49
+ name: string;
50
+ alt: string;
51
+ /** A same-origin `blob:` URL for the bytes the editor holds. */
52
+ src: string;
53
+ /** The oriented aspect, width over height, as a number string. */
54
+ ratio: string;
55
+ /** The oriented width in CSS pixels, so the preview is never upscaled. */
56
+ maxWidth: string;
57
+ imageWidth: string;
58
+ imageHeight: string;
59
+ imageTransform: string;
60
+ /** The tone pass, the same string the bake sets on its context; `none` when neutral. */
61
+ filter: string;
62
+ cropX: string;
63
+ cropY: string;
64
+ cropW: string;
65
+ cropH: string;
66
+ handles: ImageHandleRow[];
67
+ handleHelpId: string;
68
+ /** The ratio the crop is held to, and the picker's rows. */
69
+ aspect: string;
70
+ aspectOptions: { label: string; value: string }[];
71
+ /** The crop area's cursor: `move`, or `default` when there is nowhere for it to go. */
72
+ moveCursor: string;
73
+ facts: string;
74
+ width: string;
75
+ maxOutputWidth: string;
76
+ fitLabel: string;
77
+ fitHint: string;
78
+ quality: "shown" | "hidden";
79
+ qualityValue: string;
80
+ brightness: string;
81
+ contrast: string;
82
+ saturation: string;
83
+ warning: string;
84
+ /** Disabled flags, each named for the state that disables. */
85
+ busy: boolean;
86
+ undecoded: boolean;
87
+ cannotUndo: boolean;
88
+ cannotRedo: boolean;
89
+ cannotFit: boolean;
90
+ clean: boolean;
91
+ cannotSave: boolean;
92
+ saving: boolean;
93
+ }
94
+
95
+ /** What a control can ask the editor to do. Every one is a decision the flow owns. */
96
+ export interface ImageEditorActions {
97
+ imageLoaded: (width: number, height: number) => void;
98
+ imageFailed: (src: string) => void;
99
+ rotate: (direction: number) => void;
100
+ flip: (axis: string) => void;
101
+ undo: () => void;
102
+ redo: () => void;
103
+ reset: () => void;
104
+ save: () => void;
105
+ fieldEdited: () => void;
106
+ fitForWeb: () => void;
107
+ setWidth: (value: string) => void;
108
+ setAspect: (value: string) => void;
109
+ setValue: (key: string, value: string) => void;
110
+ handleDown: (
111
+ handle: string,
112
+ pointerId: number,
113
+ clientX: number,
114
+ clientY: number,
115
+ target: Element,
116
+ ) => void;
117
+ handleMove: (pointerId: number, clientX: number, clientY: number) => void;
118
+ handleUp: (pointerId: number) => void;
119
+ handleFocused: (handle: string) => void;
120
+ nudge: (handle: string, dx: number, dy: number, large: boolean) => void;
121
+ cycleHandle: (handle: string, step: number) => void;
122
+ }
123
+
124
+ export interface ImageEditorSurfaceHandle {
125
+ ready: Promise<HTMLElement>;
126
+ /** Whether what was mounted is still inside the host; true while the mount is in flight. */
127
+ attached: () => boolean;
128
+ update: (view: ImageEditorView) => void;
129
+ /** The drawn frame's rect, or null before it exists. */
130
+ frameRect: () => DOMRect | null;
131
+ /** Move focus to one handle, when it has been drawn. */
132
+ focusHandle: (handle: string) => void;
133
+ /** Commit a number still being typed, so a save writes what is on screen. Keeps the focus. */
134
+ flushFields: () => void;
135
+ /** Put the view's values back in the number fields, whatever was typed into them. */
136
+ syncFields: (view: ImageEditorView) => void;
137
+ dispose: () => void;
138
+ }
139
+
140
+ type ImageEditorScope = ImageEditorView & ImageEditorActions;
141
+
142
+ /** Each number field's `part`, which is also the name of the view value it shows. */
143
+ const FIELD_VALUES = {
144
+ brightness: "brightness",
145
+ contrast: "contrast",
146
+ quality: "qualityValue",
147
+ saturation: "saturation",
148
+ width: "width",
149
+ } as const;
150
+
151
+ function isFieldPart(part: string): part is keyof typeof FIELD_VALUES {
152
+ return Object.hasOwn(FIELD_VALUES, part);
153
+ }
154
+
155
+ /**
156
+ * The `part` a definition declares, or `""`. Read off the DEFINITION, because a node is announced
157
+ * as it is created, before its attributes are applied.
158
+ */
159
+ function partOf(def: JxElement | string): string {
160
+ const part = isString(def) ? undefined : def.attributes?.["part"];
161
+ return isString(part) ? part : "";
162
+ }
163
+
164
+ /** The handle a mapped row stands for, read off its `$map` item. */
165
+ function mappedHandle(state: JxScope | undefined): string | null {
166
+ const map = state?.["$map"];
167
+ const item = isNonNullObject(map) && "item" in map ? map.item : undefined;
168
+ return isNonNullObject(item) && "id" in item && isString(item.id) ? item.id : null;
169
+ }
170
+
171
+ /**
172
+ * Write a projection into the scope. Each key is assigned through the reactive proxy, which
173
+ * notifies only when a value moved, so a drag that changes four crop strings redraws those four.
174
+ */
175
+ function project(scope: ImageEditorScope, view: ImageEditorView): void {
176
+ Object.assign(scope, view);
177
+ }
178
+
179
+ /**
180
+ * Mount the Image editor document into `host`, wired to `actions`.
181
+ *
182
+ * @param {HTMLElement} host - The pane's canvas stage
183
+ * @param {ImageEditorView} view - What to draw right now
184
+ * @param {ImageEditorActions} actions - What each control does
185
+ * @returns {ImageEditorSurfaceHandle}
186
+ */
187
+ export function mountImageEditorSurface(
188
+ host: HTMLElement,
189
+ view: ImageEditorView,
190
+ actions: ImageEditorActions,
191
+ ): ImageEditorSurfaceHandle {
192
+ host.replaceChildren();
193
+ const scope = reactive<ImageEditorScope>({ ...view, ...actions });
194
+
195
+ let frame: Element | null = null;
196
+ const handles = new Map<string, HTMLElement>();
197
+ const fields = new Map<keyof typeof FIELD_VALUES, HTMLElement>();
198
+ let mounted: SurfaceHandle | null = null;
199
+ let disposed = false;
200
+ const ready = mountSurface("image-editor", scope, host, {
201
+ onNodeCreated: (element, _path, def, state) => {
202
+ // `disposed` first: nodes are still created while a mount that was taken down runs out.
203
+ if (disposed || !(element instanceof HTMLElement)) {
204
+ return;
205
+ }
206
+ const part = partOf(def);
207
+ if (part === "frame") {
208
+ frame = element;
209
+ } else if (part === "handle") {
210
+ const id = mappedHandle(state);
211
+ if (id !== null) {
212
+ handles.set(id, element);
213
+ }
214
+ } else if (isFieldPart(part)) {
215
+ fields.set(part, element);
216
+ }
217
+ },
218
+ }).then((surface) => {
219
+ if (disposed) {
220
+ surface.dispose();
221
+ } else {
222
+ mounted = surface;
223
+ }
224
+ /* SAFETY: `image-editor.json`'s root definition is a `div` element, so the runtime mounts an
225
+ HTMLElement; a `Text` root only comes from a document whose root is a bare string. */
226
+ return surface.root as HTMLElement;
227
+ });
228
+
229
+ return {
230
+ attached: () => {
231
+ if (disposed) {
232
+ return false;
233
+ }
234
+ const root = mounted?.root;
235
+ return root === undefined ? true : host.contains(root);
236
+ },
237
+ dispose() {
238
+ disposed = true;
239
+ mounted?.dispose();
240
+ mounted = null;
241
+ frame = null;
242
+ handles.clear();
243
+ fields.clear();
244
+ },
245
+ focusHandle: (handle) => {
246
+ const element = handles.get(handle);
247
+ if (element?.isConnected) {
248
+ element.focus();
249
+ }
250
+ },
251
+ frameRect: () => (frame?.isConnected ? rectOf(frame) : null),
252
+ flushFields: () => {
253
+ /* A number input commits on blur, which fires its change event synchronously, and the
254
+ document's handler then takes the value. Focused straight back, so the author's place is
255
+ not lost to a keystroke that only meant "save". */
256
+ const active = document.activeElement;
257
+ if (active instanceof HTMLInputElement && host.contains(active)) {
258
+ active.blur();
259
+ active.focus();
260
+ }
261
+ },
262
+ ready,
263
+ syncFields: (next) => {
264
+ for (const [part, element] of fields) {
265
+ if (element.isConnected && "value" in element) {
266
+ element.value = next[FIELD_VALUES[part]];
267
+ }
268
+ }
269
+ },
270
+ update: (next) => project(scope, next),
271
+ };
272
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://jxsuite.com/schema/v1",
3
3
  "$id": "StudioMediaLibrary",
4
- "$description": "The Media workspace's library (`specs/studio.md` §22.6): the project's media as filters, a grid and the selected file's detail. ONE document in two modes. In `manage` it is the Media home tab, with Replace, Open in viewer and Delete under the detail; in `pick` it is the full-size picker inside a `jx-dialog` (`media-pick-dialog.json`), and the dialog's own footer answers Cancel or Use <file>. `media/media-library.ts` keeps every decision (which files a filter keeps, the sort, what a count may claim, where an arrow lands, what Replace writes) and hands this document strings and discriminants. **Unused is drawn with no number until the usage sweep has settled**, and Missing alt text and tags are not drawn at all: each needs a store that does not exist, and a filter that cannot be answered is not shipped as one that always answers nothing.",
4
+ "$description": "The Media workspace's library (`specs/studio.md` §22.6): the project's media as filters, a grid and the selected file's detail. ONE document in two modes. In `manage` it is the Media home tab, with Replace, Open in viewer, Rename, Duplicate and Delete under the detail; in `pick` it is the full-size picker inside a `jx-dialog` (`media-pick-dialog.json`), and the dialog's own footer answers Cancel or Use <file>. `media/media-library.ts` keeps every decision (which files a filter keeps, the sort, what a count may claim, where an arrow lands, what Replace writes) and hands this document strings and discriminants. **Unused is drawn with no number until the usage sweep has settled**, and Missing alt text and tags are not drawn at all: each needs a store that does not exist, and a filter that cannot be answered is not shipped as one that always answers nothing.",
5
5
  "tagName": "div",
6
6
  "attributes": {
7
7
  "part": "media-library",
@@ -1578,6 +1578,134 @@
1578
1578
  }
1579
1579
  ]
1580
1580
  },
1581
+ {
1582
+ "tagName": "div",
1583
+ "attributes": {
1584
+ "part": "edit-slot",
1585
+ "role": "none"
1586
+ },
1587
+ "style": {
1588
+ "display": "contents"
1589
+ },
1590
+ "$switch": {
1591
+ "$ref": "#/state/editable"
1592
+ },
1593
+ "cases": {
1594
+ "yes": {
1595
+ "tagName": "jx-button",
1596
+ "attributes": {
1597
+ "part": "edit-image"
1598
+ },
1599
+ "$props": {
1600
+ "size": "sm",
1601
+ "variant": "secondary"
1602
+ },
1603
+ "onclick": {
1604
+ "$prototype": "Function",
1605
+ "body": [
1606
+ {
1607
+ "operator": "call",
1608
+ "target": {
1609
+ "$ref": "#/state/editImage"
1610
+ },
1611
+ "value": []
1612
+ }
1613
+ ],
1614
+ "description": "Withheld, not disabled, for a file the editor cannot write: a refused format is not offered rather than offered and then refused (studio.md §9.5)."
1615
+ },
1616
+ "children": [
1617
+ {
1618
+ "tagName": "span",
1619
+ "textContent": "Edit image"
1620
+ }
1621
+ ]
1622
+ },
1623
+ "no": {
1624
+ "tagName": "span",
1625
+ "attributes": {
1626
+ "hidden": true
1627
+ }
1628
+ }
1629
+ }
1630
+ },
1631
+ {
1632
+ "tagName": "jx-button",
1633
+ "attributes": {
1634
+ "part": "rename"
1635
+ },
1636
+ "$props": {
1637
+ "size": "sm",
1638
+ "variant": "secondary"
1639
+ },
1640
+ "onclick": {
1641
+ "$prototype": "Function",
1642
+ "body": [
1643
+ {
1644
+ "operator": "call",
1645
+ "target": {
1646
+ "$ref": "#/state/rename"
1647
+ },
1648
+ "value": []
1649
+ }
1650
+ ]
1651
+ },
1652
+ "children": [
1653
+ {
1654
+ "tagName": "span",
1655
+ "textContent": "Rename"
1656
+ }
1657
+ ]
1658
+ },
1659
+ {
1660
+ "tagName": "div",
1661
+ "attributes": {
1662
+ "part": "duplicate-slot",
1663
+ "role": "none"
1664
+ },
1665
+ "style": {
1666
+ "display": "contents"
1667
+ },
1668
+ "$switch": {
1669
+ "$ref": "#/state/duplicable"
1670
+ },
1671
+ "cases": {
1672
+ "yes": {
1673
+ "tagName": "jx-button",
1674
+ "attributes": {
1675
+ "part": "duplicate"
1676
+ },
1677
+ "$props": {
1678
+ "size": "sm",
1679
+ "variant": "secondary"
1680
+ },
1681
+ "onclick": {
1682
+ "$prototype": "Function",
1683
+ "body": [
1684
+ {
1685
+ "operator": "call",
1686
+ "target": {
1687
+ "$ref": "#/state/duplicate"
1688
+ },
1689
+ "value": []
1690
+ }
1691
+ ],
1692
+ "description": "Withheld, not disabled, on a backend that cannot read a file's raw bytes: a copy read as text would be a broken file (studio.md §9.3)."
1693
+ },
1694
+ "children": [
1695
+ {
1696
+ "tagName": "span",
1697
+ "textContent": "Duplicate"
1698
+ }
1699
+ ]
1700
+ },
1701
+ "no": {
1702
+ "tagName": "span",
1703
+ "attributes": {
1704
+ "hidden": true
1705
+ }
1706
+ }
1707
+ }
1708
+ },
1581
1709
  {
1582
1710
  "tagName": "jx-button",
1583
1711
  "attributes": {
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * This is the adapter. `media/media-library.ts` is the flow: which files a filter keeps, in what
6
6
  * order, what each count may claim, which tile is selected, where an arrow lands and what Replace,
7
- * Delete and an upload write. It hands this module one projection, and this module hands the
8
- * document a scope and reports the nodes the flow needs to hold.
7
+ * Rename, Duplicate, Delete and an upload write. It hands this module one projection, and this
8
+ * module hands the document a scope and reports the nodes the flow needs to hold.
9
9
  *
10
10
  * **Four islands, all through `onNodeCreated`.** The flow needs four nodes the document creates:
11
11
  * each tile (watched for viewport entry, so a thumbnail is requested only when it is looked at, and
@@ -97,6 +97,13 @@ export interface MediaLibraryView {
97
97
  previewSpecimen: string;
98
98
  /** The Replace input's `accept`: the selected file's own format only. */
99
99
  replaceAccept: string;
100
+ /**
101
+ * Whether the selected file opens in the Image editor (studio.md §9.5): withheld on exactly the
102
+ * conditions `media.edit` gates on, so the palette and this pane cannot give two answers.
103
+ */
104
+ editable: "yes" | "no";
105
+ /** Whether the host can copy the selected file: `media.duplicate` gates on the same capability. */
106
+ duplicable: "yes" | "no";
100
107
  usageState: "hidden" | "shown";
101
108
  usageHeadline: string;
102
109
  usageFiles: MediaUsageRow[];
@@ -126,7 +133,13 @@ export interface MediaLibraryActions {
126
133
  openDocument: (path: string) => void;
127
134
  replace: (input: HTMLInputElement) => void;
128
135
  remove: () => void;
136
+ /** Rename the selected file, through the Rename dialog. */
137
+ rename: () => void;
138
+ /** Copy the selected file to a new name beside it, and select the copy. */
139
+ duplicate: () => void;
129
140
  openViewer: () => void;
141
+ /** Open the selected image in its Image editor. */
142
+ editImage: () => void;
130
143
  reset: () => void;
131
144
  /** A tile node was created for `file`. */
132
145
  tileHost: (element: HTMLElement, file: string) => void;
@@ -174,6 +187,8 @@ export function emptyMediaLibraryView(): MediaLibraryView {
174
187
  query: "",
175
188
  region: "",
176
189
  replaceAccept: "",
190
+ editable: "no",
191
+ duplicable: "no",
177
192
  selectedFile: "",
178
193
  sort: "",
179
194
  sortOptions: [],
package/src/types.ts CHANGED
@@ -406,7 +406,8 @@ export interface StudioPlatform {
406
406
  * tainting stops being a thing that can happen rather than a thing that is worked around.
407
407
  *
408
408
  * Optional, and feature-detected like every optional member: a backend without it simply does not
409
- * offer the image editor, which is the honest degradation `specs/desktop.md` §3.1 asks for.
409
+ * offer the image editor or a file's Duplicate, which is the honest degradation
410
+ * `specs/desktop.md` §3.1 asks for.
410
411
  */
411
412
  readFileBytes?: (path: string) => Promise<ArrayBuffer>;
412
413
  writeFile: (path: string, content: string) => Promise<void>;
@@ -47,7 +47,8 @@ export function workspaceOfTab(tab: Tab): WorkspaceId {
47
47
  if (mode === "git-diff") {
48
48
  return "publish";
49
49
  }
50
- if (mode === "media") {
50
+ // The viewer and its editing sibling (`media/image-editor.ts`, §9.5): one file, one workspace.
51
+ if (mode === "media" || mode === "image") {
51
52
  return "media";
52
53
  }
53
54
  if (CONTENT_MODES.has(mode)) {
@@ -46,6 +46,7 @@ import {
46
46
  setCloseLanding,
47
47
  setEmptyPaneFallback,
48
48
  setTabScope,
49
+ tabHoldsEditBesideDocument,
49
50
  workspace,
50
51
  } from "./workspace";
51
52
  import { homeWorkspaceOfTabId, isHomeWorkspace, workspaceHomeTabId } from "./workspace-ids";
@@ -119,7 +120,8 @@ export function viewsReplacedBy(
119
120
  homeWorkspaceOfTabId(other) !== id &&
120
121
  workspaceOfTab(tab) === id &&
121
122
  !tab.pinned &&
122
- !tab.doc.dirty
123
+ !tab.doc.dirty &&
124
+ !tabHoldsEditBesideDocument(tab)
123
125
  );
124
126
  });
125
127
  }
@@ -1063,12 +1063,33 @@ export function promoteTab(tabId: string) {
1063
1063
  */
1064
1064
  export function promoteDirtyPreviewTabs() {
1065
1065
  for (const [id, tab] of workspace.tabs) {
1066
- if (tab.preview && tab.doc.dirty) {
1066
+ if (tab.preview && (tab.doc.dirty || tabHoldsEditBesideDocument(tab))) {
1067
1067
  promoteTab(id);
1068
1068
  }
1069
1069
  }
1070
1070
  }
1071
1071
 
1072
+ /** The answer to {@link tabHoldsEditBesideDocument}, injected by the module that holds the edit. */
1073
+ let editBesideDocument: ((tab: Tab) => boolean) | null = null;
1074
+
1075
+ /**
1076
+ * Register who answers {@link tabHoldsEditBesideDocument}. Injected rather than imported, because
1077
+ * the Image editor (`media/image-editor.ts`, studio.md §9.5) reaches the upload and collab layers,
1078
+ * every one of which imports this module.
1079
+ */
1080
+ export function setEditBesideDocument(answer: (tab: Tab) => boolean): void {
1081
+ editBesideDocument = answer;
1082
+ }
1083
+
1084
+ /**
1085
+ * Whether this tab holds unsaved work that is NOT in its document: a media tab's document is a
1086
+ * stub, and an image edit is a recipe held beside it, so `tab.doc.dirty` cannot say. Every gate
1087
+ * that discards a tab without asking has to ask this too, or a pending crop goes in silence.
1088
+ */
1089
+ export function tabHoldsEditBesideDocument(tab: Tab): boolean {
1090
+ return editBesideDocument?.(tab) ?? false;
1091
+ }
1092
+
1072
1093
  /**
1073
1094
  * Pin or unpin a tab, moving it to the boundary of the pinned prefix so the head of the strip is
1074
1095
  * always exactly the pinned set.
@@ -1,4 +0,0 @@
1
- import{Gs,Ks,Wr}from"./studio-sxdb9h3q.js";export{Wr as draggable,Gs as dropTargetForElements,Ks as monitorForElements};
2
-
3
- //# debugId=12250D3D5E1C3ED864756E2164756E21
4
- //# sourceMappingURL=adapter-pp011m5x.js.map
@@ -1,4 +0,0 @@
1
- import{Q}from"./studio-nqxqf8p2.js";import{p}from"./studio-jws1a87n.js";import{g,C}from"./studio-y60sts62.js";import"./studio-9rbe2xpw.js";import{Kt,zt,dh,ae}from"./studio-b2qpt0xy.js";import"./studio-ea74s8c1.js";import{Ve,wt,L,R,sn,nt,dt}from"./studio-0w2dr0q8.js";import{Ft,Ma,lr,Pa}from"./studio-hz5n7x27.js";import{Pn,Wt,ln,go}from"./studio-k2sz13zg.js";import"./studio-qn7z4krm.js";import{sS}from"./studio-r7pf0rkv.js";import"./studio-mx6wzh25.js";import"./studio-t98vq4r6.js";import"./studio-399a25za.js";import"./studio-hhp4x8xf.js";import"./studio-th7z3fg7.js";import{Co,Yl}from"./studio-2r90cts3.js";import"./studio-cw0wb9qk.js";import"./studio-0q5jwddh.js";import"./studio-86r8hp7r.js";import"./studio-8wnvtr4z.js";import{Vc,_a,Sa}from"./studio-f746039f.js";import{Yu,Rb}from"./studio-rtfnhrdg.js";import{Ne,Fb}from"./studio-sevbc3xv.js";import"./studio-sxdb9h3q.js";import"./studio-qyed0ah6.js";import"./studio-zwaxn5s2.js";import"./studio-05jfwbmf.js";import"./studio-wxq6v5sv.js";import"./studio-5atwjgkj.js";import{K_,FE,fw,X_,xi}from"./studio-c71806xz.js";import"./studio-r4bcdwhq.js";import"./studio-4jhw38b2.js";import"./studio-rzqaz21g.js";import"./studio-k1wvtp03.js";import{dm}from"./studio-qh8berdz.js";import"./studio-egmw2837.js";import"./studio-cg1bfyhf.js";import"./studio-43y04qck.js";import{oE,Ky}from"./studio-gj98m8gy.js";import{ob,nw,Bs,Rc,P_,lm,iw,F_,mE,nl,j_,Ou,H_,fD,gE,cm,rb,bE,vE,yE,mD,gD,B_}from"./studio-jvgek9xt.js";import{Zg}from"./studio-v1yascv2.js";import{Xg,kd,Ed}from"./studio-jeraxdgv.js";import{ew,hE}from"./studio-886t6ztg.js";import{jl}from"./studio-khwbgrtg.js";import"./studio-mxs3ar9t.js";import{JE,vw}from"./studio-2ejhqns4.js";import{as}from"./studio-kg5ya5r2.js";import"./studio-399xknkb.js";import"./studio-78pywnp2.js";import"./studio-5frqk7c0.js";import{S,xn}from"./studio-cg710sk9.js";import{n,v}from"./studio-8z66jk4z.js";var z={$schema:"https://jxsuite.com/schema/v1",$id:"StudioAddNew",$description:"Add new: the one dialog that creates a page, a collection, a template, a component, a collection entry or an upload (`specs/studio.md` §9.1.1). One `jx-dialog`, so the platform owns modality, Escape, the backdrop and focus restoration. The kinds run down the left as a radiogroup and the chosen kind's name and options fill the right; the line above the buttons is the derived result, the same `deriveCreation` the write performs, so what it says is what is written. Nothing here composes a path, judges a name or knows a format: `create/add-new-flow.ts` keeps every decision and hands this document rows that are already strings, flags that are already booleans and a refusal that is already a sentence.",tagName:"jx-dialog",$props:{headline:"Add new",confirmLabel:{$ref:"#/state/cta"},confirmDisabled:{$ref:"#/state/blocked"},cancelLabel:"Cancel",size:"lg"},attributes:{part:"add-new","data-kind":"${state.kind}"},onconfirm:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/confirm"},value:[]}]},oncancel:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/closed"},value:[]}]},onclose:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/closed"},value:[]}]},ondragover:{$prototype:"Function",body:[{preventDefault:!0}]},ondrop:{$prototype:"Function",body:[{preventDefault:!0},{operator:"call",target:{$ref:"#/state/drop"},value:[{$ref:"event#/dataTransfer/files"}]}]},style:{$description:"The board's two columns in the kit's scale. The kinds take a fixed gutter so the right side does not move under the reader as they switch kinds, and the options column scrolls on its own so a long template list never pushes the result line and the buttons off the dialog. No part here is named `field`, `label`, `body`, `error`, `text` or `actions`: the kit is light DOM, and `jx-textfield`, `jx-select` and `jx-dialog` each draw internal nodes under those names inside this same tree.",'& [part="sheet"]':{display:"flex",gap:"var(--jx-space-5)",minHeight:"360px"},'& [part="kinds-column"]':{flex:"0 0 208px",display:"flex",flexDirection:"column",gap:"var(--jx-space-3)",borderRight:"1px solid var(--border)",paddingRight:"var(--jx-space-3)"},'& [part="kinds"]':{display:"flex",flexDirection:"column",alignItems:"stretch",gap:"var(--jx-space-1)"},'& [part="kind"]':{display:"block"},'& [part="kind"] > [part="control"]':{$description:"A kind is a row with its own two lines, so the button's box stretches across the gutter and starts its content at the left edge rather than centring an icon.",width:"100%",height:"auto",justifyContent:"flex-start",alignItems:"flex-start",textAlign:"start",padding:"var(--jx-space-2) var(--jx-space-3)",gap:"var(--jx-space-3)"},'& [part="kind-text"]':{display:"flex",flexDirection:"column",gap:"2px"},'& [part="kind-name"]':{fontSize:"var(--jx-text-md)",color:"var(--fg)"},'& [part="kind-hint"], & [part="choice-note"]':{fontSize:"var(--jx-text-sm)",color:"var(--fg-dim)",whiteSpace:"normal"},'& [part="chord-note"], & [part="note"], & [part="kind-intro"], & [part="drop-summary"]':{margin:"0",fontSize:"var(--jx-text-sm)",color:"var(--fg-dim)"},'& [part="chord-note"]':{marginTop:"auto"},'& [part="detail"]':{flex:"1",minWidth:"0",display:"flex",flexDirection:"column",gap:"var(--jx-space-4)",maxHeight:"60vh",overflowY:"auto"},'& [part="options"]':{display:"flex",flexDirection:"column",gap:"var(--jx-space-3)"},'& [part="name-slot"], & [part="options-slot"], & [part="url-slot"], & [part="own-page-slot"], & [part="copy-slot"], & [part="locale-slot"], & [part="localized-slot"]':{$description:"Branch hosts are structure rather than layout: `display: contents` puts what they carry straight into the column, so one gap rule spaces every row whether it is drawn or not.",display:"contents"},'& [part="group-label"]':{margin:"0",fontSize:"var(--jx-text-sm)",color:"var(--fg-dim)"},'& [part="url-row"], & [part="pattern-row"]':{display:"flex",flexWrap:"wrap",alignItems:"center",gap:"var(--jx-space-2)"},'& [part="url-label"]':{fontSize:"var(--jx-text-sm)",color:"var(--fg-dim)"},'& [part="route"], & [part="pattern"]':{fontFamily:"var(--font-mono)",fontSize:"var(--jx-text-sm)",color:"var(--fg)"},'& [part="start-row"]':{display:"flex",flexDirection:"column",gap:"var(--jx-space-2)"},'& [part="start-from"], & [part="wrap-in"], & [part="component-start"]':{display:"flex",flexWrap:"wrap",gap:"var(--jx-space-2)"},'& [part="choice-card"] > [part="control"]':{$description:"A choice is a card with a name and a note, as the board draws it: its own two lines inside one radio, so the reader hears one name and sees both.",minWidth:"132px",height:"auto",flexDirection:"column",alignItems:"flex-start",textAlign:"start",padding:"var(--jx-space-2) var(--jx-space-3)",gap:"2px"},'& [part="choice-name"]':{display:"block",fontSize:"var(--jx-text-md)",color:"var(--fg)"},'& [part="choice-note"]':{display:"block"},'& [part="presets"]':{display:"flex",flexWrap:"wrap",gap:"var(--jx-space-2)"},'& [part="drop"]':{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",gap:"var(--jx-space-2)",minHeight:"200px",padding:"var(--jx-space-5)",border:"1px dashed var(--border)",borderRadius:"var(--radius)",textAlign:"center"},'& [part="drop"][data-drop="true"]':{borderColor:"var(--accent)",background:"var(--accent-10)"},'& [part="drop-title"]':{margin:"0",fontSize:"var(--jx-text-md)",color:"var(--fg)"},'& [part="browse"]':{display:"inline-flex",alignItems:"center",gap:"var(--jx-space-2)",padding:"var(--jx-space-1) var(--jx-space-3)",border:"1px solid var(--border)",borderRadius:"var(--radius)",color:"var(--fg)",cursor:"pointer"},'& [part="browse"]:focus-within':{outline:"var(--jx-focus-ring)",outlineOffset:"2px"},'& [part="browse-input"]':{$description:"Visually hidden, never `display: none`: the input is the control, and a hidden-from-layout one cannot take focus from the keyboard. The label around it is what is drawn and what is clicked.",position:"absolute",width:"1px",height:"1px",opacity:"0",overflow:"hidden"},'& [part="saved"]':{margin:"0",paddingTop:"var(--jx-space-3)",borderTop:"1px solid var(--border)",fontSize:"var(--jx-text-sm)",color:"var(--fg-dim)",overflowWrap:"anywhere"}},children:[{tagName:"div",attributes:{part:"sheet"},children:[{tagName:"div",attributes:{part:"kinds-column"},children:[{tagName:"jx-action-group",attributes:{part:"kinds"},$props:{selects:"single",label:"What to create",orientation:"vertical",compact:!1},children:[{$prototype:"Array",items:{$ref:"#/state/kinds"},key:{$ref:"$map/item/id"},map:{tagName:"jx-action-button",attributes:{part:"kind","data-kind":"${$map.item.id}"},$props:{label:{$ref:"$map/item/label"},icon:{$ref:"$map/item/icon"},checked:{$ref:"$map/item/checked"}},onclick:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickKind"},value:[{$ref:"$map/item/id"}]}]},children:[{tagName:"span",attributes:{part:"kind-text"},children:[{tagName:"span",attributes:{part:"kind-name"},textContent:"${$map.item.label}"},{tagName:"span",attributes:{part:"kind-hint"},textContent:"${$map.item.hint}"}]}]}}]},{tagName:"p",attributes:{part:"chord-note"},textContent:"${state.chordNote}"}]},{tagName:"section",attributes:{part:"detail","aria-label":"${state.kindLabel}"},children:[{tagName:"p",attributes:{part:"kind-intro"},textContent:"${state.kindHint}"},{tagName:"div",attributes:{part:"name-slot",role:"none"},$switch:{$ref:"#/state/named"},cases:{true:{tagName:"jx-field",attributes:{part:"name-row"},$props:{span:!0,label:{$ref:"#/state/nameLabel"},invalid:{$ref:"#/state/invalid"}},children:[{tagName:"jx-textfield",attributes:{part:"name"},$props:{value:{$ref:"#/state/name"},placeholder:{$ref:"#/state/placeholder"},invalid:{$ref:"#/state/invalid"},error:{$ref:"#/state/error"},required:!0,autocomplete:"off"},oninput:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/setName"},value:[{$ref:"event#/target/value"}]}]},onkeydown:{$prototype:"Function",body:[{$switch:{$ref:"event#/key"},cases:{Enter:[{preventDefault:!0},{operator:"call",target:{$ref:"#/state/confirm"},value:[]}]}}]}}]}}},{tagName:"div",attributes:{part:"options-slot",role:"none"},$switch:{$ref:"#/state/kind"},cases:{page:{tagName:"div",attributes:{part:"options",role:"none"},children:[{tagName:"div",attributes:{part:"locale-slot",role:"none"},$switch:{$ref:"#/state/localeShown"},cases:{true:{tagName:"jx-field",attributes:{part:"locale-row"},$props:{span:!0,label:"Language"},children:[{tagName:"jx-select",attributes:{part:"locale"},$props:{size:"sm",label:"Language",options:{$ref:"#/state/localeOptions"},value:{$ref:"#/state/locale"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickLocale"},value:[{$ref:"event#/target/value"}]}]}}]}}},{tagName:"div",attributes:{part:"url-row"},children:[{tagName:"span",attributes:{part:"url-label"},textContent:"URL"},{tagName:"code",attributes:{part:"route"},textContent:"${state.route}"},{tagName:"jx-action-button",attributes:{part:"edit-url"},$props:{label:"Edit URL",toggles:!0,selected:{$ref:"#/state/editingUrl"},size:"sm"},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/toggleUrl"},value:[]}]},children:[{tagName:"span",textContent:"Edit"}]}]},{tagName:"div",attributes:{part:"url-slot",role:"none"},$switch:{$ref:"#/state/editingUrl"},cases:{true:{tagName:"jx-field",attributes:{part:"url-row-field"},$props:{span:!0,label:"URL"},children:[{tagName:"jx-textfield",attributes:{part:"url"},$props:{size:"sm",mono:!0,value:{$ref:"#/state/url"},placeholder:"/pricing"},oninput:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/setUrl"},value:[{$ref:"event#/target/value"}]}]},onkeydown:{$prototype:"Function",body:[{$switch:{$ref:"event#/key"},cases:{Enter:[{preventDefault:!0},{operator:"call",target:{$ref:"#/state/confirm"},value:[]}]}}]}}]}}},{tagName:"div",attributes:{part:"start-row"},children:[{tagName:"p",attributes:{part:"group-label"},textContent:"Start from"},{tagName:"jx-action-group",attributes:{part:"start-from"},$props:{selects:"single",label:"Start from",compact:!1},children:[{$prototype:"Array",items:{$ref:"#/state/startChoices"},key:{$ref:"$map/item/value"},map:{tagName:"jx-action-button",attributes:{part:"choice-card","data-value":"${$map.item.value}"},$props:{label:{$ref:"$map/item/label"},checked:{$ref:"$map/item/checked"},disabled:{$ref:"$map/item/disabled"},hint:{$ref:"$map/item/reason"},quiet:!1},onclick:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickStart"},value:[{$ref:"$map/item/value"}]}]},children:[{tagName:"span",attributes:{part:"choice-name"},textContent:"${$map.item.label}"},{tagName:"span",attributes:{part:"choice-note"},textContent:"${$map.item.note}"}]}}]}]}]},collection:{tagName:"div",attributes:{part:"options",role:"none"},children:[{tagName:"p",attributes:{part:"group-label"},textContent:"Each ${state.itemNoun} has"},{tagName:"jx-action-group",attributes:{part:"presets"},$props:{selects:"multiple",label:"Fields",compact:!1},children:[{$prototype:"Array",items:{$ref:"#/state/presets"},key:{$ref:"$map/item/id"},map:{tagName:"jx-action-button",attributes:{part:"preset","data-preset":"${$map.item.id}"},$props:{label:{$ref:"$map/item/label"},toggles:!0,selected:{$ref:"$map/item/selected"},quiet:!1,size:"sm",icon:{$ref:"$map/item/icon"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/togglePreset"},value:[{$ref:"$map/item/id"}]}]},children:[{tagName:"span",textContent:"${$map.item.label}"}]}}]},{tagName:"p",attributes:{part:"note"},textContent:"You can add or change fields later."},{tagName:"div",attributes:{part:"localized-slot",role:"none"},$switch:{$ref:"#/state/localizable"},cases:{true:{tagName:"jx-checkbox",attributes:{part:"localized"},$props:{checked:{$ref:"#/state/localized"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/setLocalized"},value:[{$ref:"event#/target/checked"}]}]},children:[{tagName:"span",textContent:"One folder per language (${state.localeSummary})"}]}}},{tagName:"jx-checkbox",attributes:{part:"own-page"},$props:{checked:{$ref:"#/state/ownPage"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/setOwnPage"},value:[{$ref:"event#/target/checked"}]}]},children:[{tagName:"span",textContent:"Give each ${state.itemNoun} its own page"}]},{tagName:"div",attributes:{part:"own-page-slot",role:"none"},$switch:{$ref:"#/state/ownPage"},cases:{true:{tagName:"div",attributes:{part:"pattern-row"},children:[{tagName:"span",attributes:{part:"url-label"},textContent:"at"},{tagName:"code",attributes:{part:"pattern"},textContent:"${state.pattern}"},{tagName:"span",attributes:{part:"url-label"},textContent:"using"},{tagName:"jx-select",attributes:{part:"page-template"},$props:{size:"sm",label:"Template for each page",options:{$ref:"#/state/templateOptions"},value:{$ref:"#/state/pageTemplate"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickPageTemplate"},value:[{$ref:"event#/target/value"}]}]}}]}}}]},template:{tagName:"div",attributes:{part:"options",role:"none"},children:[{tagName:"p",attributes:{part:"group-label"},textContent:"Wrap it in"},{tagName:"jx-action-group",attributes:{part:"wrap-in"},$props:{selects:"single",label:"Wrap it in",compact:!1},children:[{$prototype:"Array",items:{$ref:"#/state/wrapChoices"},key:{$ref:"$map/item/value"},map:{tagName:"jx-action-button",attributes:{part:"choice-card","data-value":"${$map.item.value}"},$props:{label:{$ref:"$map/item/label"},checked:{$ref:"$map/item/checked"},disabled:{$ref:"$map/item/disabled"},hint:{$ref:"$map/item/reason"},quiet:!1},onclick:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickWrap"},value:[{$ref:"$map/item/value"}]}]},children:[{tagName:"span",attributes:{part:"choice-name"},textContent:"${$map.item.label}"},{tagName:"span",attributes:{part:"choice-note"},textContent:"${$map.item.note}"}]}}]},{tagName:"p",attributes:{part:"note"},textContent:"Pages placed in this template fill its Content area. You can add more named areas later."}]},component:{tagName:"div",attributes:{part:"options",role:"none"},children:[{tagName:"p",attributes:{part:"group-label"},textContent:"Start from"},{tagName:"jx-action-group",attributes:{part:"component-start"},$props:{selects:"single",label:"Start from",compact:!1},children:[{$prototype:"Array",items:{$ref:"#/state/componentChoices"},key:{$ref:"$map/item/value"},map:{tagName:"jx-action-button",attributes:{part:"choice-card","data-value":"${$map.item.value}"},$props:{label:{$ref:"$map/item/label"},checked:{$ref:"$map/item/checked"},disabled:{$ref:"$map/item/disabled"},hint:{$ref:"$map/item/reason"},quiet:!1},onclick:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickComponentStart"},value:[{$ref:"$map/item/value"}]}]},children:[{tagName:"span",attributes:{part:"choice-name"},textContent:"${$map.item.label}"},{tagName:"span",attributes:{part:"choice-note"},textContent:"${$map.item.note}"}]}}]},{tagName:"div",attributes:{part:"copy-slot",role:"none"},$switch:{$ref:"#/state/copying"},cases:{true:{tagName:"jx-field",attributes:{part:"copy-row"},$props:{span:!0,label:"Copy of"},children:[{tagName:"jx-select",attributes:{part:"copy-of"},$props:{size:"sm",label:"Component to copy",options:{$ref:"#/state/copyOptions"},value:{$ref:"#/state/copyOf"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickCopy"},value:[{$ref:"event#/target/value"}]}]}}]}}},{tagName:"p",attributes:{part:"note"},textContent:"It appears under Components in the Site panel, ready to drag onto any page."}]},entry:{tagName:"div",attributes:{part:"options",role:"none"},children:[{tagName:"jx-field",attributes:{part:"collection-row"},$props:{span:!0,label:"Collection"},children:[{tagName:"jx-select",attributes:{part:"collection"},$props:{size:"sm",label:"Collection",options:{$ref:"#/state/collectionOptions"},value:{$ref:"#/state/collection"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickCollection"},value:[{$ref:"event#/target/value"}]}]}}]},{tagName:"div",attributes:{part:"locale-slot",role:"none"},$switch:{$ref:"#/state/localeShown"},cases:{true:{tagName:"jx-field",attributes:{part:"locale-row"},$props:{span:!0,label:"Language"},children:[{tagName:"jx-select",attributes:{part:"locale"},$props:{size:"sm",label:"Language",options:{$ref:"#/state/localeOptions"},value:{$ref:"#/state/locale"}},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/pickLocale"},value:[{$ref:"event#/target/value"}]}]}}]}}}]},media:{tagName:"div",attributes:{part:"drop","data-drop":"${state.dropActive}"},ondragover:{$prototype:"Function",body:[{preventDefault:!0},{operator:"call",target:{$ref:"#/state/dragOver"},value:[]}]},ondragleave:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/dragOut"},value:[]}]},children:[{tagName:"jx-icon",attributes:{part:"drop-glyph"},$props:{name:"upload-simple"}},{tagName:"p",attributes:{part:"drop-title"},textContent:"Drop images, video or documents"},{tagName:"label",attributes:{part:"browse"},children:[{tagName:"input",attributes:{part:"browse-input",type:"file",multiple:!0,accept:"${state.uploadAccept}"},onchange:{$prototype:"Function",body:[{operator:"call",target:{$ref:"#/state/choose"},value:[{$ref:"event#/target"}]}]}},{tagName:"span",textContent:"Browse files"}]},{tagName:"p",attributes:{part:"drop-summary"},textContent:"${state.fileSummary}"}]}}}]}]},{tagName:"p",attributes:{part:"saved","aria-live":"polite"},textContent:"${state.saved}"}]};L("add-new",z);function D(e){return{...e,blocked:!e.canConfirm,invalid:e.error!=="",named:e.kind!=="media"}}function E(e){let t=document.createElement("div");t.style.pointerEvents="auto",t.setAttribute(Ve,wt("dialog","add-new")),e.layer.append(t);let r=!1,o=()=>{if(r)return;r=!0,e.actions.closed()},l=()=>r,{actions:a}=e,d=g({...D(e.view),choose:a.choose,closed:o,confirm:a.confirm,dragOut:a.dragOut,dragOver:a.dragOver,drop:a.drop,pickCollection:a.pickCollection,pickComponentStart:a.pickComponentStart,pickCopy:a.pickCopy,pickKind:a.pickKind,pickLocale:a.pickLocale,pickPageTemplate:a.pickPageTemplate,pickStart:a.pickStart,pickWrap:a.pickWrap,setName:(m)=>{d.name=m,a.setName(m)},setLocalized:a.setLocalized,setOwnPage:a.setOwnPage,setUrl:(m)=>{d.url=m,a.setUrl(m)},toggleUrl:a.toggleUrl,togglePreset:a.togglePreset}),f=null,u=()=>{setTimeout(()=>{let m=f?.root;if(r||!(m instanceof HTMLElement))return;let c=m.querySelector('jx-textfield[part="name"]');if(c)dh(c,"all")},0)},b=R("add-new",d,t).then(async(m)=>{f=m;let c=m.root;if(l())return m.dispose(),c;if(await sn(c),l())return c;if(Kt(c),d.named)u();return c});return{close(){let m=f?.root;if(m instanceof HTMLElement)zt(m);f?.dispose(),f=null,t.remove(),o()},host:t,ready:b,update(m){Object.assign(d,D(m))}}}var y="Add new";async function W(){return(await oE()).map((t)=>({name:t.name,ref:t.path})).toSorted((t,r)=>t.name.localeCompare(r.name))}function _(){for(let e of K_("page")){if(e.ext===".json")continue;let t=Ma(e.ext,"serialize");if(t)return{ext:e.ext,name:t.name}}return null}function B(){let e=lr(),t=e?.extensions[0];return e&&t?{ext:t,name:e.name}:null}function J(){return ln(S?.projectConfig??{}).i18n}async function V(e){let t={},{i18n:r}=ln(S?.projectConfig??{});for(let o of await sS(e)){let l=Pn(go(o,r));if(l!==null)t[l]=o}return t}async function P(){await Ft().catch(()=>{});let e=await Promise.all(vw().map(async(t)=>{let r={dir:t.dir,ext:t.ext,formatName:B_(t),name:t.name,schema:t.schema};if(rb(t))r.localeDirs=await V(t);return r}));return{i18n:J(),article:_(),collections:e,componentTags:Ne.map((t)=>t.tagName),copyableTags:Ne.filter((t)=>I(t)).map((t)=>t.tagName),contentFormat:B(),declaredContent:JE(),defaultLayout:S?.projectConfig?.defaults?.layout??null,hasSelection:ew()!==null,layouts:await W()}}function I(e){let{path:t}=e;return n(t)&&e.source!=="npm"&&t.endsWith(".json")}async function Y(){let e=new Map;for(let t of Ne){let{path:r}=t;if(!I(t)||!n(r))continue;try{let o=JSON.parse(await p().readFile(r));if(v(o))e.set(t.tagName,o)}catch{}}return e}function U(e,t){let r=e.trim();if(r===Bs||r==="none"||r==="")return Bs;if(r===Rc)return Rc;let o=nl(r);return t.find((a)=>nl(a.ref)===o||nl(a.ref)===`layouts/${o}`||nl(a.ref)===`layouts/${o}.json`||a.name.toLowerCase()===r.toLowerCase())?.ref??null}function G(e,t){let r={};if(e.collection!==void 0)r.collection=e.collection;if(e.locale!==void 0){if(cm(e.locale,t)===null){let o=t.i18n?.locales??[];throw RangeError(`command "file.new" argument "locale": "${e.locale}" is not a language this `+`project declares — declared: ${o.length===0?"none":o.join(", ")}`)}r.locale=e.locale}if(e.template!==void 0){let o=U(e.template,t.layouts);if(o===null){let l=t.layouts.map((a)=>nl(a.ref));throw RangeError(`command "file.new" argument "template": "${e.template}" names no template — `+`declared: ${[Bs,Rc,...l].join(", ")}`)}if(e.kind==="collection")r.pageTemplate=o===Rc?Bs:o;else if(e.kind==="template")r.startFrom=o===Bs||o===Rc?"":o;else r.startFrom=o}return r}async function X(e){if(e.format===null)return Co(e.doc,null);return Pa(e.format,e.doc,{frontmatter:e.frontmatter??!0,mode:"roundtrip"})}async function F(e,t,r=!1){for(let o of mD(yE(e)))await Promise.all(o.map(async(l)=>{if(!r&&t.has(l))return;t.set(l,gD(l,t)?new Set:await fw(l))}))}function k(e){if(Zg(e),Ed(),e.some((t)=>t.startsWith("pages/")))Rb(),Xg();FE(e).then((t)=>{if(t)Ed(),xn("leftPanel")})}async function T(e,t,r){let o;try{o=await X(t)}catch(l){throw C.error(`Could not prepare ${e}.`,{detail:Q(l),path:e,source:y}),Error(`could not prepare ${e}`,{cause:l})}if(await X_(e,o,{quiet:r,source:y})===null)throw Error(`could not write ${e}`)}async function Z(e){let t=e.config,r=S?.projectConfig?.content,o=v(r)?{...r}:{};if(o[t.name]=t.entry,!(await Yl({content:o})).ok)return null;let a=["project.json"],d=[];for(let u of[{body:e.content,path:e.path},...e.extraWrites])try{await T(u.path,u.body,!0),a.push(u.path)}catch{d.push(u.path)}if(d.length===0)C.success(`Created the ${t.name} collection`);else C.warn(`Created the ${t.name} collection, but ${d.join(" and ")} could not be written.`,{source:y});k(a),dm(t.name),nt("content.showCollection",{collection:t.name},y);let f={kind:"collection",path:`content/${t.name}`,wrote:a};if(e.route!==void 0)f.route=e.route;return f}async function M(e,t,r){if(e.kind==="media"){let l=await Sa(r,{dir:_a,only:"media"});if(l.length===0)return null;let a=l.map((d)=>d.path);return k(a),nt("media.browse",{select:l[0].path},y),{kind:"media",path:_a,wrote:a}}if(e.kind==="collection")return Z(e);let o={kind:e.kind,path:e.path,wrote:[e.path]};if(e.route!==void 0)o.route=e.route;if(e.tag!==void 0)o.tag=e.tag;if(e.kind==="component"&&t.startFrom===P_){let l=await hE(e.tag);if(l===null)return null;return k([l]),await xi(l),o}try{await T(e.path,e.content,!1)}catch{return null}switch(k([e.path]),e.kind){case"component":{Fb(e.path,e.content.doc),await xi(e.path);break}case"template":{Ky(),await xi(e.path);break}case"entry":{if(t.collection!==void 0)dm(t.collection);await jl(e.path);break}default:await xi(e.path)}return o}async function Je(e){let t=e.kind??"page";if(t==="media")throw RangeError('command "file.new": Media is uploaded, not named; run file.new { kind: "media" } with no name to open the upload drop zone.');let r=await P(),o=G({...e,kind:t},r);if(t==="entry"&&o.collection===void 0&&r.collections.length===1)o.collection=r.collections[0].name;let l=bE(t,e.name,o,r),a=new Map;await F(l,a,!0);let d=vE(l,e.name,o,r,(u)=>a.get(u));if(d!=="")throw RangeError(`command "file.new": ${d}`);let f=await M(l,o,[]);if(f===null)throw Error(`file.new could not create ${l.path}; the reason is in Problems.`);return f}async function Ye(e){let t=await P(),r=t.collections.find((u)=>u.name===e);if(!r)return C.error(`"${e}" is not a collection this project declares.`,{source:y}),null;let o=`pages/${as(e)||e}`,l=`${o}/[slug].json`;if((await fw(o)).has("[slug].json"))return C.error(`${l} already exists.`,{detail:"Open it in Design and point its $paths at this collection, or rename it first.",path:l,source:y}),null;let d=fD(r.schema,r.formatName!==null),f=gE(e,d,j_(Ou(t),t),t);try{await T(l,{doc:f,format:null},!1)}catch{return null}return k([l]),l}function j(e){switch(e.kind){case"page":{let t={startFrom:e.pageStart};if(e.editingUrl)t.url=e.url;if(e.locale!=="")t.locale=e.locale;return t}case"template":return{startFrom:e.wrap};case"component":{let t={startFrom:e.componentStart};if(e.componentStart===lm&&e.copyOf!=="")t.copyOf=e.copyOf;return t}case"collection":return{fields:e.fields,localized:e.localized,ownPage:e.ownPage,pageTemplate:e.pageTemplate};case"entry":{let t={};if(e.collection!=="")t.collection=e.collection;if(e.locale!=="")t.locale=e.locale;return t}default:return{files:e.files.length}}}function w(e,t,r,o,l=""){return{checked:e===o?"true":"false",disabled:l!=="",label:t,note:r,reason:l,value:e}}function q(e,t){let r=t.layouts.map((o)=>w(o.ref,o.name,t.defaultLayout!==null&&nl(o.ref)===nl(t.defaultLayout)?"the site's default":"a template",e.pageStart));if(t.article!==null)r.push(w(Rc,"Article","mostly writing",e.pageStart));return r.push(w(Bs,"Blank","no template",e.pageStart)),r}function ee(){let e=ew();if(!e)return"nothing selected";let r=1+(Array.isArray(e.node.children)?e.node.children.length:0);return r===1?"1 element":`${r} elements`}function te(){let e=ae()?.keymap,t=e?.bindingsFor("file.new")??[];if(!e||t.length===0)return"";let[r,...o]=t.map((l)=>e.format(l));return o.length===0?`${r} opens this anywhere.`:`${r} opens this in the desktop app, ${o.join(" or ")} anywhere.`}function O(e,t,r,o){let l=nw[e.kind],a=ob.filter((s)=>s!=="entry"||t.collections.length>0).map((s)=>({checked:s===e.kind?"true":"false",hint:nw[s].hint,icon:nw[s].icon,id:s,label:nw[s].label})),d=r.config?.name??"",f=Yu(d===""?"items":d),u=t.i18n?.locales??[],b=t.collections.find((s)=>s.name===e.collection),m=u.length>0&&(e.kind==="page"||e.kind==="entry"&&b!==void 0&&rb(b)),c=[...t.layouts.map((s)=>({label:s.name,value:s.ref})),{label:"No template",value:Bs}];return{canConfirm:o===""&&!e.busy,chordNote:te(),collection:e.collection,collectionOptions:t.collections.map((s)=>({label:kd(s.name),value:s.name})),componentChoices:[w("blank","Blank","an empty block",e.componentStart),w(P_,"Current selection",ee(),e.componentStart,t.hasSelection===!0?"":"Select an element on the canvas first."),w(lm,"Copy of…","an existing one",e.componentStart,(t.copyableTags??[]).length>0?"":"This project has no components of its own to copy yet.")],copyOf:e.copyOf,copyOptions:[...t.componentDocs?.keys()??[]].map((s)=>({label:`<${s}>`,value:s})),copying:e.componentStart===lm,cta:l.cta,dropActive:e.dropActive?"true":"false",editingUrl:e.editingUrl,error:e.touched||e.kind==="media"?e.kind==="media"?"":o:"",fileSummary:e.files.length===0?"":e.files.map((s)=>s.name).join(", "),itemNoun:f,kind:e.kind,kindHint:l.hint,kindLabel:l.label,kinds:a,locale:e.locale,localeOptions:u.map((s)=>({label:Wt(s),value:s})),localeShown:m,localeSummary:u.join(", "),localizable:u.length>0,localized:e.localized,name:e.name,nameLabel:l.nameLabel,ownPage:e.ownPage,pageTemplate:e.pageTemplate,pattern:r.route??`/${d===""?"items":d}/:slug`,placeholder:l.placeholder,presets:iw.filter((s)=>s!=="richText"||t.contentFormat!==null).map((s)=>{let x=e.fields.includes(s);return{icon:x?"check":"plus",id:s,label:F_[s].label,selected:x}}),route:r.route===void 0||r.route===""?"/":r.route,saved:r.saved,startChoices:q(e,t),templateOptions:c,uploadAccept:Vc(),url:e.url,wrapChoices:[...t.layouts.map((s)=>w(s.ref,s.name,"keeps its frame around this one",e.wrap)),w("","Nothing","a fresh frame",e.wrap)]}}function oe(e,t){let r=e.kind??"page";return r==="entry"&&t.collections.length===0?"page":r}var A=null;function Ge(e={}){if(A)return A.done;let t,r=new Promise((l)=>{t=l}),o={done:r,handle:null};return A=o,ne(e,t,o),r}async function ne(e,t,r){let o=await P(),l=new Map,a={busy:!1,collection:e.collection??o.collections[0]?.name??"",locale:cm(e.locale,o)??o.i18n?.defaultLocale??"",localized:o.i18n!==null&&o.i18n!==void 0,componentStart:"blank",copyOf:"",dropActive:!1,editingUrl:!1,fields:[...mE],files:[],kind:oe(e,o),name:e.name??"",ownPage:!0,pageStart:H_(o,cm(e.locale,o)??o.i18n?.defaultLocale??""),pageTemplate:Ou(o),templateChosen:e.template!==void 0,touched:(e.name??"")!=="",url:"",wrap:Ou(o)===Bs?"":Ou(o)};if(e.template!==void 0){let i=U(e.template,o.layouts);if(i!==null)a.pageStart=i,a.pageTemplate=i===Rc?Bs:i,a.wrap=i===Bs||i===Rc?"":i}if(o.contentFormat===null)a.fields=a.fields.filter((i)=>i!=="richText");let d=!1,f=null,u=()=>bE(a.kind,a.name,j(a),o),b=(i)=>vE(i,a.name,j(a),o,(h)=>l.get(h)),m=()=>{let i=u();if(r.handle?.update(O(a,o,i,b(i))),yE(i).some((h)=>!l.has(h)))F(i,l).then(()=>{if(!d){let h=u();r.handle?.update(O(a,o,h,b(h)))}})},c=(i)=>{i(),m()},s=async()=>{if(a.busy)return;a.touched=!0;let i=u();if(await F(i,l,!0),b(i)!==""){m();return}a.busy=!0,m();try{f=await M(i,j(a),a.files)}catch(N){C.error("Could not create it.",{detail:Q(N),source:y}),f=null}if(a.busy=!1,f===null){m();return}r.handle?.close()},x=u(),K=E({actions:{choose:(i)=>{let h=[...i.files??[]];i.value="",c(()=>{a.files=[...a.files,...h]})},closed:()=>{if(d)return;d=!0,A=null,r.handle?.close(),t(f)},confirm:()=>{s()},dragOut:()=>{c(()=>{a.dropActive=!1})},dragOver:()=>{if(!a.dropActive)c(()=>{a.dropActive=!0})},drop:(i)=>{c(()=>{a.dropActive=!1;let h=i?[...i]:[];if(h.length>0)a.kind="media";a.files=[...a.files,...h]})},pickCollection:(i)=>{c(()=>{a.collection=i})},pickComponentStart:(i)=>{if(c(()=>{a.componentStart=i}),i===lm&&o.componentDocs===void 0)Y().then((h)=>{if(o.componentDocs=h,a.copyOf==="")a.copyOf=h.keys().next().value??"";if(!d)m()})},pickLocale:(i)=>{c(()=>{if(a.locale=i,!a.templateChosen)a.pageStart=H_(o,i)})},setLocalized:(i)=>{c(()=>{a.localized=i})},pickCopy:(i)=>{c(()=>{a.copyOf=i})},pickKind:(i)=>{c(()=>{a.kind=i})},pickPageTemplate:(i)=>{c(()=>{a.pageTemplate=i})},pickStart:(i)=>{c(()=>{a.pageStart=i,a.templateChosen=!0})},pickWrap:(i)=>{c(()=>{a.wrap=i})},setName:(i)=>{c(()=>{a.name=i,a.touched=!0})},setOwnPage:(i)=>{c(()=>{a.ownPage=i})},setUrl:(i)=>{c(()=>{a.url=i,a.touched=!0})},toggleUrl:()=>{c(()=>{if(!a.editingUrl)a.url=u().route??"/";a.editingUrl=!a.editingUrl})},togglePreset:(i)=>{c(()=>{let h=iw.find((N)=>N===i);if(h===void 0)return;a.fields=a.fields.includes(h)?a.fields.filter((N)=>N!==h):[...a.fields,h]})}},layer:dt("dialog"),view:O(a,o,x,b(x))});r.handle=K,m()}export{Ye as addCollectionPage,Je as createDirect,Ge as openAddNew};
2
-
3
- //# debugId=B6D44C107ECB0F6664756E2164756E21
4
- //# sourceMappingURL=add-new-flow-ecqmzz76.js.map