@jxsuite/studio 2.0.0 → 2.1.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 (419) hide show
  1. package/canvas.html +21 -0
  2. package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
  3. package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
  4. package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
  5. package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
  6. package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
  7. package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
  8. package/dist/chunks/content-source-34gnkbnn.js +38 -0
  9. package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
  10. package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
  11. package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
  12. package/dist/chunks/extension-sections-6mqydz35.js +301 -0
  13. package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
  14. package/dist/chunks/files-8kn321h1.js +70 -0
  15. package/dist/chunks/files-8kn321h1.js.map +9 -0
  16. package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
  17. package/dist/chunks/git-panel-tjbr191d.js +51 -0
  18. package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
  19. package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
  20. package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
  21. package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
  22. package/dist/chunks/layers-panel-87pb25km.js +58 -0
  23. package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
  24. package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
  25. package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
  26. package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
  27. package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
  28. package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
  29. package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
  30. package/dist/chunks/right-panel-zpdxd98t.js +37 -0
  31. package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
  32. package/dist/chunks/section-registry-zmckaxf4.js +30 -0
  33. package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
  34. package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
  35. package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
  36. package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
  37. package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
  38. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
  39. package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
  40. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
  41. package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
  42. package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
  43. package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
  44. package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
  45. package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
  46. package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
  47. package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
  48. package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
  49. package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
  50. package/dist/chunks/studio-41exgzhq.js +6690 -0
  51. package/dist/chunks/studio-41exgzhq.js.map +38 -0
  52. package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
  53. package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
  54. package/dist/chunks/studio-50k77cca.js +1787 -0
  55. package/dist/chunks/studio-50k77cca.js.map +18 -0
  56. package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
  57. package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
  58. package/dist/chunks/studio-6f0am8f2.js +53 -0
  59. package/dist/chunks/studio-6f0am8f2.js.map +10 -0
  60. package/dist/chunks/studio-6nmjc5qk.js +7002 -0
  61. package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
  62. package/dist/chunks/studio-7afd21nw.js +67 -0
  63. package/dist/chunks/studio-7afd21nw.js.map +10 -0
  64. package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
  65. package/dist/chunks/studio-7bmdvzne.js.map +10 -0
  66. package/dist/chunks/studio-8jpzqk5s.js +128 -0
  67. package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
  68. package/dist/chunks/studio-94m77tgk.js +1482 -0
  69. package/dist/chunks/studio-94m77tgk.js.map +13 -0
  70. package/dist/chunks/studio-9je6w5sq.js +1000 -0
  71. package/dist/chunks/studio-9je6w5sq.js.map +16 -0
  72. package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
  73. package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
  74. package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
  75. package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
  76. package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
  77. package/dist/chunks/studio-bw5dz6km.js +608 -0
  78. package/dist/chunks/studio-bw5dz6km.js.map +21 -0
  79. package/dist/chunks/studio-c6s5getc.js +4232 -0
  80. package/dist/chunks/studio-c6s5getc.js.map +39 -0
  81. package/dist/chunks/studio-cjm7fdjf.js +451 -0
  82. package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
  83. package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
  84. package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
  85. package/dist/chunks/studio-dg7zyt5x.js +28 -0
  86. package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
  87. package/dist/chunks/studio-dzcyvvvz.js +166 -0
  88. package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
  89. package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
  90. package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
  91. package/dist/chunks/studio-f12h6177.js +411 -0
  92. package/dist/chunks/studio-f12h6177.js.map +13 -0
  93. package/dist/chunks/studio-hbp1s8m9.js +1817 -0
  94. package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
  95. package/dist/chunks/studio-k2sxjph5.js +15295 -0
  96. package/dist/chunks/studio-k2sxjph5.js.map +235 -0
  97. package/dist/chunks/studio-myje51t3.js +385 -0
  98. package/dist/chunks/studio-myje51t3.js.map +12 -0
  99. package/dist/chunks/studio-na6rwzhw.js +353 -0
  100. package/dist/chunks/studio-na6rwzhw.js.map +11 -0
  101. package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
  102. package/dist/chunks/studio-p471gg5n.js.map +14 -0
  103. package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
  104. package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
  105. package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
  106. package/dist/chunks/studio-sas7mt3k.js +75 -0
  107. package/dist/chunks/studio-sas7mt3k.js.map +10 -0
  108. package/dist/chunks/studio-t5wqwzdh.js +74 -0
  109. package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
  110. package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
  111. package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
  112. package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
  113. package/dist/chunks/studio-tr20m8g8.js.map +9 -0
  114. package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
  115. package/dist/chunks/studio-v96qfnww.js.map +54 -0
  116. package/dist/chunks/studio-vcetc91k.js +659 -0
  117. package/dist/chunks/studio-vcetc91k.js.map +10 -0
  118. package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
  119. package/dist/chunks/studio-vmymjefp.js.map +98 -0
  120. package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
  121. package/dist/chunks/studio-x9hmy5kr.js +390 -0
  122. package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
  123. package/dist/chunks/studio-xa0ce0n7.js +335 -0
  124. package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
  125. package/dist/chunks/studio-xm3dbxvn.js +378 -0
  126. package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
  127. package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
  128. package/dist/chunks/studio-zv37yrfa.js +1054 -0
  129. package/dist/chunks/studio-zv37yrfa.js.map +13 -0
  130. package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
  131. package/dist/chunks/ws-client-654ts66j.js.map +11 -0
  132. package/dist/iframe-entry.js +668 -230
  133. package/dist/studio.js +23711 -35185
  134. package/index.html +35 -4685
  135. package/package.json +17 -15
  136. package/src/about/about-modal.ts +42 -11
  137. package/src/browse/library-commands.ts +240 -0
  138. package/src/browse/library-layouts.ts +369 -0
  139. package/src/browse/library-model.ts +396 -0
  140. package/src/browse/library-pane.ts +1049 -0
  141. package/src/browse/library-preview.ts +252 -0
  142. package/src/browse/library-source.ts +150 -0
  143. package/src/canvas/canvas-helpers.ts +33 -9
  144. package/src/canvas/canvas-live-render.ts +45 -29
  145. package/src/canvas/canvas-patcher.ts +151 -31
  146. package/src/canvas/canvas-perf.ts +46 -7
  147. package/src/canvas/canvas-render.ts +1092 -220
  148. package/src/canvas/canvas-surface.ts +240 -0
  149. package/src/canvas/canvas-utils.ts +1166 -124
  150. package/src/canvas/iframe-drop.ts +2 -1
  151. package/src/canvas/iframe-entry.ts +241 -4
  152. package/src/canvas/iframe-host.ts +1226 -143
  153. package/src/canvas/iframe-inline-edit.ts +31 -0
  154. package/src/canvas/iframe-interaction.ts +76 -16
  155. package/src/canvas/iframe-keys.ts +107 -45
  156. package/src/canvas/iframe-overlay.ts +38 -1
  157. package/src/canvas/iframe-protocol.ts +137 -2
  158. package/src/canvas/iframe-render.ts +70 -2
  159. package/src/canvas/iframe-slash.ts +6 -0
  160. package/src/canvas/path-mapping.ts +56 -6
  161. package/src/canvas/surface-registry.ts +292 -0
  162. package/src/collab/collab-commands.ts +244 -0
  163. package/src/collab/collab-session.ts +198 -32
  164. package/src/collab/collab-state.ts +28 -2
  165. package/src/collab/presence-chips.ts +100 -9
  166. package/src/commands/active-registry.ts +42 -0
  167. package/src/commands/app-commands.ts +176 -0
  168. package/src/commands/budget.ts +158 -0
  169. package/src/commands/command-args.ts +253 -0
  170. package/src/commands/context.ts +387 -0
  171. package/src/commands/defaults.ts +580 -0
  172. package/src/commands/keymap.ts +571 -0
  173. package/src/commands/levels.ts +345 -0
  174. package/src/commands/live-context.ts +193 -0
  175. package/src/commands/reference.ts +227 -0
  176. package/src/commands/registry.ts +293 -0
  177. package/src/component-props.ts +3 -2
  178. package/src/content/draft-state.ts +106 -0
  179. package/src/content/entry-commands.ts +282 -0
  180. package/src/content/entry-editor.ts +327 -0
  181. package/src/content/entry-fields.ts +116 -0
  182. package/src/content/entry-model.ts +175 -0
  183. package/src/editor/canvas-context-menu.ts +17 -11
  184. package/src/editor/canvas-slash-bridge.ts +11 -4
  185. package/src/editor/context-menu.ts +804 -204
  186. package/src/editor/convert-to-component.ts +31 -10
  187. package/src/editor/convert-to-repeater.ts +3 -2
  188. package/src/editor/inline-edit-apply.ts +5 -5
  189. package/src/editor/inline-edit.ts +86 -50
  190. package/src/editor/insert-zone-action.ts +1 -1
  191. package/src/editor/shortcuts.ts +1043 -329
  192. package/src/editor/slash-menu.ts +1 -0
  193. package/src/files/elements.ts +166 -0
  194. package/src/files/file-ops.ts +178 -31
  195. package/src/files/files.ts +898 -268
  196. package/src/files/fs-events.ts +21 -0
  197. package/src/files/media-meta.ts +203 -0
  198. package/src/files/media-paths.ts +141 -0
  199. package/src/files/media-upload.ts +7 -3
  200. package/src/files/media-usage.ts +189 -0
  201. package/src/github/github-auth.ts +235 -33
  202. package/src/github/github-publish.ts +80 -21
  203. package/src/grid/grid-controller.ts +152 -12
  204. package/src/grid/grid-layout.ts +225 -20
  205. package/src/grid/grid-open.ts +120 -0
  206. package/src/grid/grid-panel.ts +554 -39
  207. package/src/grid/grid-source.ts +27 -1
  208. package/src/grid/redirects-grid.ts +599 -0
  209. package/src/grid/redirects.ts +398 -0
  210. package/src/grid/sources/content-source.ts +108 -2
  211. package/src/i18n/i18n-commands.ts +322 -0
  212. package/src/new-project/add-repo-modal.ts +8 -10
  213. package/src/new-project/import-tab.ts +34 -9
  214. package/src/new-project/location-fields.ts +9 -1
  215. package/src/new-project/new-project-modal.ts +270 -262
  216. package/src/packages/jxsuite-update.ts +97 -48
  217. package/src/packages/pull-package-sync.ts +18 -9
  218. package/src/page-params.ts +2 -1
  219. package/src/panels/activity-bar.ts +152 -91
  220. package/src/panels/activity-panel.ts +481 -0
  221. package/src/panels/ai-chat/chat-markdown.ts +8 -3
  222. package/src/panels/ai-chat/chat-view.ts +245 -18
  223. package/src/panels/ai-chat/composer.ts +31 -5
  224. package/src/panels/ai-chat/sessions-view.ts +17 -10
  225. package/src/panels/ai-panel.ts +279 -120
  226. package/src/panels/block-action-bar.ts +1115 -224
  227. package/src/panels/bottom-dock.ts +337 -0
  228. package/src/panels/canvas-dnd-bridge.ts +37 -8
  229. package/src/panels/chat-panel.ts +29 -12
  230. package/src/panels/data-explorer.ts +254 -112
  231. package/src/panels/data-grid.ts +10 -4
  232. package/src/panels/dnd.ts +28 -51
  233. package/src/panels/editors.ts +434 -107
  234. package/src/panels/elements-panel.ts +66 -2
  235. package/src/panels/empty-state.ts +112 -0
  236. package/src/panels/events-panel.ts +596 -82
  237. package/src/panels/formula-workspace.ts +408 -218
  238. package/src/panels/frontmatter-fields.ts +51 -10
  239. package/src/panels/frontmatter-panel.ts +357 -79
  240. package/src/panels/git-panel.ts +375 -164
  241. package/src/panels/head-panel.ts +791 -85
  242. package/src/panels/i18n-panel.ts +592 -0
  243. package/src/panels/imports-panel.ts +95 -85
  244. package/src/panels/jump-bar.ts +584 -0
  245. package/src/panels/layers-panel.ts +1106 -328
  246. package/src/panels/left-panel.ts +109 -204
  247. package/src/panels/navigator-panels.ts +131 -0
  248. package/src/panels/overlays.ts +7 -2
  249. package/src/panels/pane-context.ts +1477 -0
  250. package/src/panels/pane-grid.ts +406 -0
  251. package/src/panels/panel-registry.ts +340 -0
  252. package/src/panels/panel-scheduler.ts +75 -0
  253. package/src/panels/problems-panel.ts +234 -0
  254. package/src/panels/properties-panel.ts +946 -1139
  255. package/src/panels/provenance.ts +258 -0
  256. package/src/panels/quick-search.ts +791 -122
  257. package/src/panels/right-panel.ts +228 -102
  258. package/src/panels/seo-modal.ts +395 -0
  259. package/src/panels/settings-pane.ts +203 -0
  260. package/src/panels/signals-panel.ts +354 -137
  261. package/src/panels/statement-editor.ts +41 -25
  262. package/src/panels/statusbar.ts +342 -106
  263. package/src/panels/style-panel.ts +974 -463
  264. package/src/panels/style-utils.ts +3 -3
  265. package/src/panels/stylebook-doc.ts +4 -2
  266. package/src/panels/stylebook-layers-panel.ts +3 -2
  267. package/src/panels/stylebook-panel.ts +51 -26
  268. package/src/panels/tab-strip.ts +1058 -78
  269. package/src/panels/target-line.ts +274 -0
  270. package/src/panels/toolbar.ts +631 -484
  271. package/src/panels/welcome-screen.ts +301 -198
  272. package/src/platform-errors.ts +26 -3
  273. package/src/platform.ts +96 -2
  274. package/src/platforms/cloud.ts +71 -8
  275. package/src/platforms/devserver.ts +92 -29
  276. package/src/publish/deploy-checklist.ts +383 -0
  277. package/src/publish/pages-service.ts +10 -1
  278. package/src/publish/publish-commands.ts +218 -0
  279. package/src/publish/publish-panel.ts +110 -16
  280. package/src/recent-projects.ts +3 -2
  281. package/src/services/a11y-report.ts +505 -0
  282. package/src/services/ai-models.ts +75 -2
  283. package/src/services/ai-project-tools.ts +29 -10
  284. package/src/services/ai-system-prompt.ts +53 -14
  285. package/src/services/ai-tools.ts +39 -3
  286. package/src/services/ai-writes.ts +154 -0
  287. package/src/services/announce.ts +97 -0
  288. package/src/services/automation.ts +424 -175
  289. package/src/services/cem-export.ts +9 -4
  290. package/src/services/clock.ts +61 -0
  291. package/src/services/document-assistant.ts +36 -8
  292. package/src/services/idle.ts +179 -0
  293. package/src/services/import-client.ts +26 -0
  294. package/src/services/monaco-buffer.ts +627 -0
  295. package/src/services/monaco-lazy.ts +36 -0
  296. package/src/services/notify.ts +271 -0
  297. package/src/services/profile.ts +155 -0
  298. package/src/services/references.ts +0 -0
  299. package/src/services/token-lint.ts +3 -2
  300. package/src/services/tool-executor.ts +53 -7
  301. package/src/services/trusted-types.ts +123 -0
  302. package/src/settings/contexts-section.ts +427 -0
  303. package/src/settings/contributed-section.ts +149 -11
  304. package/src/settings/css-vars-editor.ts +306 -349
  305. package/src/settings/defs-editor.ts +93 -34
  306. package/src/settings/dependencies-editor.ts +7 -7
  307. package/src/settings/extension-sections.ts +37 -5
  308. package/src/settings/general-settings.ts +204 -121
  309. package/src/settings/locales-section.ts +347 -0
  310. package/src/settings/preferences-accounts.ts +140 -0
  311. package/src/settings/preferences-dialog.ts +620 -0
  312. package/src/settings/preferences-keymap.ts +270 -0
  313. package/src/settings/project-sections.ts +260 -0
  314. package/src/settings/section-registry.ts +169 -0
  315. package/src/settings/settings-document.ts +385 -0
  316. package/src/shell.ts +1549 -0
  317. package/src/site-context.ts +45 -20
  318. package/src/state.ts +14 -275
  319. package/src/store.ts +88 -43
  320. package/src/studio.ts +790 -346
  321. package/src/style/live-preview.ts +36 -0
  322. package/src/style/project-styles.ts +349 -0
  323. package/src/style/token-ref.ts +123 -0
  324. package/src/tabs/patch-ops.ts +16 -4
  325. package/src/tabs/project-config.ts +416 -0
  326. package/src/tabs/selection.ts +323 -0
  327. package/src/tabs/tab.ts +182 -47
  328. package/src/tabs/transact.ts +209 -28
  329. package/src/types.ts +68 -13
  330. package/src/ui/ai-credentials-form.ts +39 -35
  331. package/src/ui/ai-managed-connect.ts +113 -0
  332. package/src/ui/dynamic-slot.ts +224 -81
  333. package/src/ui/expression-editor.ts +71 -63
  334. package/src/ui/field-input.ts +4 -6
  335. package/src/ui/field-row.ts +215 -38
  336. package/src/ui/form-controls.ts +194 -7
  337. package/src/ui/layers.ts +492 -69
  338. package/src/ui/media-picker.ts +115 -25
  339. package/src/ui/panel-resize.ts +109 -91
  340. package/src/ui/progress-modal.ts +131 -50
  341. package/src/ui/regions.ts +462 -0
  342. package/src/ui/schema-form.ts +229 -45
  343. package/src/ui/spectrum.ts +24 -8
  344. package/src/ui/timing.ts +49 -0
  345. package/src/ui/value-selector.ts +20 -0
  346. package/src/ui/value-source.ts +348 -0
  347. package/src/ui/virtual-window.ts +400 -0
  348. package/src/utils/edit-display.ts +3 -2
  349. package/src/utils/inherited-style.ts +67 -37
  350. package/src/view.ts +64 -78
  351. package/src/workspace/pane-derive.ts +1258 -0
  352. package/src/workspace/session.ts +317 -0
  353. package/src/workspace/workspace.ts +1345 -23
  354. package/styles/canvas.css +653 -0
  355. package/styles/forced-colors.css +106 -0
  356. package/styles/inspector.css +1454 -0
  357. package/styles/overlays.css +2704 -0
  358. package/styles/panels.css +1701 -0
  359. package/styles/shell.css +1423 -0
  360. package/styles/tokens.css +151 -0
  361. package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
  362. package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
  363. package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
  364. package/dist/chunks/files-02zn5y24.js +0 -50
  365. package/dist/chunks/files-02zn5y24.js.map +0 -9
  366. package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
  367. package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
  368. package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
  369. package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
  370. package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
  371. package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
  372. package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
  373. package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
  374. package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
  375. package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
  376. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
  377. package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
  378. package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
  379. package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
  380. package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
  381. package/dist/chunks/studio-568nx4t5.js +0 -469
  382. package/dist/chunks/studio-568nx4t5.js.map +0 -14
  383. package/dist/chunks/studio-9smfzqhc.js +0 -19
  384. package/dist/chunks/studio-9smfzqhc.js.map +0 -10
  385. package/dist/chunks/studio-asvf0j9e.js +0 -429
  386. package/dist/chunks/studio-asvf0j9e.js.map +0 -13
  387. package/dist/chunks/studio-d77qm3pk.js +0 -3427
  388. package/dist/chunks/studio-d77qm3pk.js.map +0 -26
  389. package/dist/chunks/studio-j6gqyjf4.js +0 -1854
  390. package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
  391. package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
  392. package/dist/chunks/studio-qkvt37c0.js.map +0 -9
  393. package/dist/chunks/studio-rq0vxb19.js +0 -3744
  394. package/dist/chunks/studio-rq0vxb19.js.map +0 -22
  395. package/dist/chunks/studio-wd6ewdvd.js +0 -853
  396. package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
  397. package/dist/chunks/studio-xq0t7n5t.js +0 -140
  398. package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
  399. package/dist/chunks/studio-xze4zrs1.js.map +0 -10
  400. package/dist/chunks/studio-ygzav7z5.js.map +0 -46
  401. package/dist/chunks/studio-zmy7241e.js +0 -399
  402. package/dist/chunks/studio-zmy7241e.js.map +0 -12
  403. package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
  404. package/src/browse/browse-modal.ts +0 -69
  405. package/src/browse/browse.ts +0 -904
  406. package/src/canvas/canvas-diff.ts +0 -216
  407. package/src/canvas/nested-site-style.ts +0 -58
  408. package/src/new-project/design-fields.ts +0 -264
  409. package/src/new-project/templates.ts +0 -61
  410. package/src/panels/tab-bar.ts +0 -548
  411. package/src/settings/settings-modal.ts +0 -203
  412. /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
  413. /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
  414. /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
  415. /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
  416. /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
  417. /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
  418. /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
  419. /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
@@ -0,0 +1,1482 @@
1
+ import {
2
+ listWindow,
3
+ measuredRowHeight,
4
+ revealListRow,
5
+ watchListWindow
6
+ } from "./studio-dzcyvvvz.js";
7
+ import {
8
+ autoSyncProjectOnOpen,
9
+ cleanupGitPanel,
10
+ maybePromptJxsuiteUpdate,
11
+ shouldInstallAutomation
12
+ } from "./studio-50k77cca.js";
13
+ import {
14
+ openCsvGridTab,
15
+ openPagesGrid
16
+ } from "./studio-9je6w5sq.js";
17
+ import {
18
+ collectionDirs,
19
+ collectionInfo,
20
+ confirmFileDelete,
21
+ markLocalMutation,
22
+ parseSourceForPath,
23
+ renamePromptMessage
24
+ } from "./studio-k2sxjph5.js";
25
+ import {
26
+ UPLOAD_ACCEPT,
27
+ invalidateUsages,
28
+ isImage,
29
+ uploadAssets
30
+ } from "./studio-f12h6177.js";
31
+ import {
32
+ showProgressModal
33
+ } from "./studio-na6rwzhw.js";
34
+ import {
35
+ rectOf,
36
+ registerPanel
37
+ } from "./studio-v96qfnww.js";
38
+ import {
39
+ activateTab,
40
+ activeRegistry,
41
+ errorMessage,
42
+ findContentTypeSchema,
43
+ isCollabPath,
44
+ localeLabel,
45
+ localeOfPath,
46
+ markSessionRestored,
47
+ moveTabToPane,
48
+ notify,
49
+ now,
50
+ openTab,
51
+ paneOfTab,
52
+ persistedSession,
53
+ receivingPane,
54
+ renameTab,
55
+ renderPopover,
56
+ replaceAllTabs,
57
+ resetProjectShell,
58
+ resolveI18n,
59
+ restoreSession,
60
+ setActivityTab,
61
+ setWorkspaceProject,
62
+ showPromptDialog,
63
+ workspace
64
+ } from "./studio-41exgzhq.js";
65
+ import {
66
+ classMap
67
+ } from "./studio-ckwz4d3x.js";
68
+ import {
69
+ ref,
70
+ repeat
71
+ } from "./studio-p471gg5n.js";
72
+ import {
73
+ html,
74
+ nothing
75
+ } from "./studio-5x5774ak.js";
76
+ import {
77
+ combine,
78
+ draggable,
79
+ dropTargetForElements,
80
+ monitorForElements
81
+ } from "./studio-bsxxx3x2.js";
82
+ import {
83
+ loadComponentRegistry
84
+ } from "./studio-s519zc99.js";
85
+ import {
86
+ projectState,
87
+ requireProjectState,
88
+ setProjectState
89
+ } from "./studio-cjm7fdjf.js";
90
+ import {
91
+ documentExtensions,
92
+ formatForPath,
93
+ loadFormats,
94
+ noFormatError,
95
+ refreshExtensionUi,
96
+ refreshFormats
97
+ } from "./studio-adn0z1k3.js";
98
+ import {
99
+ getPlatform,
100
+ hasPlatform
101
+ } from "./studio-sas7mt3k.js";
102
+
103
+ // src/packages/ensure-deps.ts
104
+ async function ensureDependenciesInstalled() {
105
+ if (shouldInstallAutomation(location.search)) {
106
+ return;
107
+ }
108
+ const platform = getPlatform();
109
+ if (!platform.dependenciesNeedInstall || !platform.installDependencies) {
110
+ return;
111
+ }
112
+ let needs = false;
113
+ try {
114
+ needs = await platform.dependenciesNeedInstall();
115
+ } catch {
116
+ return;
117
+ }
118
+ if (!needs) {
119
+ return;
120
+ }
121
+ const progress = showProgressModal({
122
+ status: "Running bun install…",
123
+ title: "Installing dependencies"
124
+ });
125
+ try {
126
+ const result = await platform.installDependencies();
127
+ if (result.ok) {
128
+ progress.done();
129
+ } else {
130
+ progress.fail(result.log ?? "bun install failed");
131
+ }
132
+ } catch (error) {
133
+ progress.fail(error instanceof Error ? error.message : String(error));
134
+ }
135
+ }
136
+
137
+ // src/content/entry-model.ts
138
+ function collectionNames() {
139
+ return Object.keys(projectState?.projectConfig?.content ?? {});
140
+ }
141
+ function entryCollection(name) {
142
+ const info = collectionInfo(name);
143
+ if (!info) {
144
+ return null;
145
+ }
146
+ if (/\.[a-z\d]+$/i.test(info.dir)) {
147
+ return null;
148
+ }
149
+ return {
150
+ def: info.def,
151
+ dir: info.dir,
152
+ ext: info.ext,
153
+ name: info.name,
154
+ schema: info.def.schema ?? null
155
+ };
156
+ }
157
+ function entryCollections() {
158
+ return collectionNames().map((name) => entryCollection(name)).filter((collection) => collection !== null);
159
+ }
160
+ function collectionOfPath(path) {
161
+ const matched = findContentTypeSchema(path ?? null, projectState?.projectConfig);
162
+ return matched ? entryCollection(matched.name) : null;
163
+ }
164
+ function cloneDefault(value) {
165
+ if (value === null || typeof value !== "object") {
166
+ return value;
167
+ }
168
+ return JSON.parse(JSON.stringify(value));
169
+ }
170
+ function emptyForField(field) {
171
+ if (typeof field.$ref === "string") {
172
+ return "";
173
+ }
174
+ switch (field.type) {
175
+ case "boolean": {
176
+ return false;
177
+ }
178
+ case "integer":
179
+ case "number": {
180
+ return 0;
181
+ }
182
+ case "array": {
183
+ return [];
184
+ }
185
+ case "object": {
186
+ return seedEntry(field);
187
+ }
188
+ default: {
189
+ return "";
190
+ }
191
+ }
192
+ }
193
+ function seedEntry(schema) {
194
+ const properties = schema?.properties;
195
+ if (!properties) {
196
+ return {};
197
+ }
198
+ const required = new Set(Array.isArray(schema?.required) ? schema.required : []);
199
+ const seed = {};
200
+ for (const [name, field] of Object.entries(properties)) {
201
+ if (field.default !== undefined) {
202
+ seed[name] = cloneDefault(field.default);
203
+ } else if (required.has(name)) {
204
+ seed[name] = emptyForField(field);
205
+ }
206
+ }
207
+ return seed;
208
+ }
209
+ function missingRequired(schema, value) {
210
+ const required = Array.isArray(schema?.required) ? schema.required : [];
211
+ return required.filter((name) => value[name] === undefined);
212
+ }
213
+
214
+ // src/recent-projects.ts
215
+ var STORAGE_KEY = "jx-studio-recent-projects";
216
+ var FILES_STORAGE_KEY = "jx-studio-recent-files";
217
+ var MAX_RECENT = 8;
218
+ var MAX_RECENT_FILES = 10;
219
+ var cache = [];
220
+ function backend() {
221
+ if (!hasPlatform()) {
222
+ return null;
223
+ }
224
+ const platform = getPlatform();
225
+ return platform.getRecentProjects && platform.saveRecentProjects ? platform : null;
226
+ }
227
+ function loadFromLocalStorage() {
228
+ try {
229
+ const raw = localStorage.getItem(STORAGE_KEY);
230
+ if (!raw) {
231
+ return [];
232
+ }
233
+ const parsed = JSON.parse(raw);
234
+ return Array.isArray(parsed) ? parsed : [];
235
+ } catch {
236
+ return [];
237
+ }
238
+ }
239
+ function currentList() {
240
+ return backend() ? cache : loadFromLocalStorage();
241
+ }
242
+ function commit(list) {
243
+ cache = list;
244
+ const store = backend();
245
+ if (store) {
246
+ store.saveRecentProjects(list);
247
+ } else {
248
+ try {
249
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
250
+ } catch {}
251
+ }
252
+ }
253
+ async function hydrateRecentProjects() {
254
+ const store = backend();
255
+ if (!store) {
256
+ return;
257
+ }
258
+ try {
259
+ cache = await store.getRecentProjects();
260
+ } catch {
261
+ cache = [];
262
+ }
263
+ }
264
+ function getRecentProjects() {
265
+ return currentList().toSorted((a, b) => b.timestamp - a.timestamp);
266
+ }
267
+ function addRecentProject(name, root) {
268
+ const projects = currentList().filter((p) => p.root !== root);
269
+ projects.unshift({ name, root, timestamp: now() });
270
+ if (projects.length > MAX_RECENT) {
271
+ projects.length = MAX_RECENT;
272
+ }
273
+ commit(projects);
274
+ }
275
+ function removeRecentProject(root) {
276
+ commit(currentList().filter((p) => p.root !== root));
277
+ }
278
+ function clearRecentProjects() {
279
+ cache = [];
280
+ const store = backend();
281
+ if (store) {
282
+ store.saveRecentProjects([]);
283
+ } else {
284
+ localStorage.removeItem(STORAGE_KEY);
285
+ }
286
+ }
287
+ function loadRecentFiles() {
288
+ try {
289
+ const raw = localStorage.getItem(FILES_STORAGE_KEY);
290
+ if (!raw) {
291
+ return [];
292
+ }
293
+ const parsed = JSON.parse(raw);
294
+ return Array.isArray(parsed) ? parsed : [];
295
+ } catch {
296
+ return [];
297
+ }
298
+ }
299
+ function getRecentFiles(root) {
300
+ const all = loadRecentFiles().toSorted((a, b) => b.timestamp - a.timestamp);
301
+ return root == null ? all : all.filter((f) => f.root === root);
302
+ }
303
+ function trackRecentFile(file) {
304
+ const all = loadRecentFiles().filter((f) => !(f.root === file.root && f.path === file.path));
305
+ all.unshift({ name: file.name, path: file.path, root: file.root, timestamp: now() });
306
+ const perRoot = new Map;
307
+ const kept = [];
308
+ for (const f of all.toSorted((a, b) => b.timestamp - a.timestamp)) {
309
+ const n = (perRoot.get(f.root) ?? 0) + 1;
310
+ perRoot.set(f.root, n);
311
+ if (n <= MAX_RECENT_FILES) {
312
+ kept.push(f);
313
+ }
314
+ }
315
+ localStorage.setItem(FILES_STORAGE_KEY, JSON.stringify(kept));
316
+ }
317
+
318
+ // src/files/files.ts
319
+ var fileIconMap = {
320
+ "sp-icon-document": html`<sp-icon-document></sp-icon-document>`,
321
+ "sp-icon-file-code": html`<sp-icon-file-code></sp-icon-file-code>`,
322
+ "sp-icon-file-txt": html`<sp-icon-file-txt></sp-icon-file-txt>`,
323
+ "sp-icon-folder": html`<sp-icon-folder></sp-icon-folder>`,
324
+ "sp-icon-folder-open": html`<sp-icon-folder-open></sp-icon-folder-open>`,
325
+ "sp-icon-image": html`<sp-icon-image></sp-icon-image>`
326
+ };
327
+ async function loadDirectory(dirPath) {
328
+ if (!projectState) {
329
+ return;
330
+ }
331
+ try {
332
+ const platform = getPlatform();
333
+ const entries = await platform.listDirectory(dirPath);
334
+ projectState.dirs.set(dirPath, entries);
335
+ } catch {
336
+ projectState.dirs.set(dirPath, []);
337
+ }
338
+ }
339
+ async function loadProject() {
340
+ try {
341
+ const platform = getPlatform();
342
+ const result = await platform.probeRootProject();
343
+ if (!result) {
344
+ return;
345
+ }
346
+ const { meta, info } = result;
347
+ refreshFormats();
348
+ loadFormats();
349
+ refreshExtensionUi(platform);
350
+ setProjectState({
351
+ dirs: new Map,
352
+ expanded: new Set,
353
+ isSiteProject: info.isSiteProject,
354
+ name: info.isSiteProject ? info.projectConfig?.name || meta.name : meta.name,
355
+ projectConfig: (info.isSiteProject ? info.projectConfig : null) || null,
356
+ projectDirs: info.directories || [],
357
+ projectRoot: ".",
358
+ root: meta.root,
359
+ searchQuery: "",
360
+ selectedPath: null
361
+ });
362
+ if (info.isSiteProject) {
363
+ setWorkspaceProject(meta.root || ".", info.projectConfig || null);
364
+ }
365
+ if (info.isSiteProject) {
366
+ addRecentProject(requireProjectState().name, meta.root);
367
+ await autoSyncProjectOnOpen();
368
+ await ensureDependenciesInstalled();
369
+ await loadDirectory(".");
370
+ await loadComponentRegistry();
371
+ await openLastSessionOrHome();
372
+ maybePromptJxsuiteUpdate(meta.root);
373
+ }
374
+ } catch {}
375
+ }
376
+ async function openProject({
377
+ renderLeftPanel
378
+ }) {
379
+ try {
380
+ const platform = getPlatform();
381
+ const result = await platform.openProject();
382
+ if (!result) {
383
+ return false;
384
+ }
385
+ const { config, handle } = result;
386
+ replaceAllTabs({
387
+ document: { children: [], tagName: "div" },
388
+ id: "initial"
389
+ });
390
+ refreshFormats();
391
+ loadFormats();
392
+ refreshExtensionUi(platform);
393
+ setProjectState({
394
+ ...projectState,
395
+ dirs: new Map,
396
+ expanded: new Set,
397
+ isSiteProject: true,
398
+ name: config.name || handle.name,
399
+ projectConfig: config,
400
+ projectRoot: handle.root,
401
+ searchQuery: "",
402
+ selectedPath: null
403
+ });
404
+ setWorkspaceProject(handle.root, config);
405
+ await autoSyncProjectOnOpen();
406
+ await ensureDependenciesInstalled();
407
+ await loadDirectory(".");
408
+ await loadComponentRegistry();
409
+ const conventionalDirs = new Set([
410
+ "pages",
411
+ "layouts",
412
+ "components",
413
+ "content",
414
+ "data",
415
+ "public",
416
+ "styles"
417
+ ]);
418
+ const entries = requireProjectState().dirs.get(".") || [];
419
+ const foundDirs = [];
420
+ for (const e of entries) {
421
+ if (e.type === "directory" && conventionalDirs.has(e.name)) {
422
+ foundDirs.push(e.name);
423
+ requireProjectState().expanded.add(e.path || e.name);
424
+ await loadDirectory(e.path || e.name);
425
+ }
426
+ }
427
+ requireProjectState().projectDirs = foundDirs;
428
+ cleanupGitPanel();
429
+ resetProjectShell();
430
+ setActivityTab("files");
431
+ addRecentProject(requireProjectState().name, requireProjectState().projectRoot);
432
+ renderLeftPanel();
433
+ await openLastSessionOrHome();
434
+ maybePromptJxsuiteUpdate(requireProjectState().projectRoot);
435
+ return true;
436
+ } catch (error) {
437
+ notify.error("Could not open the project.", {
438
+ detail: errorMessage(error),
439
+ source: "Open Project"
440
+ });
441
+ return false;
442
+ }
443
+ }
444
+ async function initProjectRepo(root) {
445
+ const platform = getPlatform();
446
+ if (platform.createDestination !== "path") {
447
+ return false;
448
+ }
449
+ try {
450
+ await platform.activate(root);
451
+ const status = await platform.gitStatus();
452
+ if (status.isRepo) {
453
+ return false;
454
+ }
455
+ await platform.gitInit();
456
+ notify.success("Initialized a git repository for this project.");
457
+ return true;
458
+ } catch (error) {
459
+ notify.warn("Could not initialize a git repository — the project itself was written.", {
460
+ detail: errorMessage(error),
461
+ source: "Source Control"
462
+ });
463
+ return false;
464
+ }
465
+ }
466
+ async function findHomePage() {
467
+ await loadFormats();
468
+ const exts = [...documentExtensions("page"), ".json"];
469
+ let entries;
470
+ try {
471
+ entries = await getPlatform().listDirectory("pages");
472
+ } catch {
473
+ return null;
474
+ }
475
+ const files = new Set(entries.filter((e) => e.type === "file").map((e) => e.name));
476
+ for (const ext of exts) {
477
+ if (files.has(`index${ext}`)) {
478
+ return `pages/index${ext}`;
479
+ }
480
+ }
481
+ return null;
482
+ }
483
+ async function openHomePage() {
484
+ const home = await findHomePage();
485
+ if (home) {
486
+ await openFileInTab(home);
487
+ }
488
+ }
489
+ async function openLastSessionOrHome() {
490
+ const session = persistedSession(workspace.projectRoot);
491
+ markSessionRestored(workspace.projectRoot);
492
+ if (session) {
493
+ const opened = await restoreSession(session, {
494
+ ensureSecondPane: () => {
495
+ receivingPane();
496
+ },
497
+ openFile: (path, paneId) => openFileInTab(path, { focus: false, paneId })
498
+ });
499
+ if (opened > 0) {
500
+ return true;
501
+ }
502
+ }
503
+ await openHomePage();
504
+ return false;
505
+ }
506
+ function fileTypeIconTpl(name, type) {
507
+ let tag;
508
+ if (type === "directory") {
509
+ tag = projectState?.expanded?.has(name) ? "sp-icon-folder-open" : "sp-icon-folder";
510
+ } else {
511
+ const ext = name.split(".").pop()?.toLowerCase();
512
+ switch (ext) {
513
+ case "json": {
514
+ tag = "sp-icon-file-code";
515
+ break;
516
+ }
517
+ case "md": {
518
+ tag = "sp-icon-file-txt";
519
+ break;
520
+ }
521
+ case "js":
522
+ case "ts": {
523
+ tag = "sp-icon-file-code";
524
+ break;
525
+ }
526
+ case "css": {
527
+ tag = "sp-icon-file-code";
528
+ break;
529
+ }
530
+ default: {
531
+ tag = ext && isImage(ext) ? "sp-icon-image" : "sp-icon-document";
532
+ break;
533
+ }
534
+ }
535
+ }
536
+ return fileIconMap[tag] || fileIconMap["sp-icon-document"];
537
+ }
538
+ function renderFilesTemplate({
539
+ openProject: openProjectFn,
540
+ openFileFromTree: openFileFn,
541
+ renderLeftPanel
542
+ }) {
543
+ if (!projectState) {
544
+ return html`<div class="file-tree-empty">No project loaded</div>`;
545
+ }
546
+ if (!projectState.isSiteProject && projectState.projectRoot === ".") {
547
+ return html`<div class="file-tree-empty">
548
+ <p style="margin:0 0 12px">Open a project folder to get started.</p>
549
+ <sp-button variant="accent" size="s" @click=${openProjectFn}>Open Project</sp-button>
550
+ </div>`;
551
+ }
552
+ return html`
553
+ ${projectState.isSiteProject ? html`
554
+ <div class="project-header">
555
+ <span class="project-name"
556
+ >${projectState.projectConfig?.name || projectState.name}</span
557
+ >
558
+ </div>
559
+ ` : nothing}
560
+ <div class="files-toolbar">
561
+ <sp-action-group size="xs" compact quiet>
562
+ <sp-action-button
563
+ size="xs"
564
+ label="New File"
565
+ @click=${() => createNewFile(".", renderLeftPanel)}
566
+ >
567
+ <sp-icon-add slot="icon"></sp-icon-add>
568
+ </sp-action-button>
569
+ <sp-action-button
570
+ size="xs"
571
+ label="Refresh"
572
+ @click=${async () => {
573
+ requireProjectState().dirs.clear();
574
+ await loadDirectory(".");
575
+ for (const dir of requireProjectState().expanded) {
576
+ await loadDirectory(dir);
577
+ }
578
+ renderLeftPanel();
579
+ }}
580
+ >
581
+ <sp-icon-refresh slot="icon"></sp-icon-refresh>
582
+ </sp-action-button>
583
+ </sp-action-group>
584
+ <sp-search
585
+ size="s"
586
+ quiet
587
+ placeholder="Filter files…"
588
+ value=${requireProjectState().searchQuery}
589
+ @input=${(e) => {
590
+ requireProjectState().searchQuery = e.target.value;
591
+ renderLeftPanel();
592
+ }}
593
+ @submit=${(e) => e.preventDefault()}
594
+ ></sp-search>
595
+ </div>
596
+ <div
597
+ class="file-tree"
598
+ role="tree"
599
+ aria-label="Project files"
600
+ ${ref((el) => {
601
+ if (el) {
602
+ afterFileTreeRender(el);
603
+ }
604
+ })}
605
+ >
606
+ ${fileTreeBodyTemplate({ openFileFn, renderLeftPanel })}
607
+ </div>
608
+ `;
609
+ }
610
+ var FILE_ROW_HEIGHT = 24;
611
+ var _fileRows = [];
612
+ var _fileList = null;
613
+ var _fileWatch = null;
614
+ var _filesRerender = null;
615
+ var _pendingFocusPath = null;
616
+ function fileRowHeight() {
617
+ return measuredRowHeight(_fileList, ".file-tree-item", FILE_ROW_HEIGHT);
618
+ }
619
+ function collectFileRows(dirPath, depth, rows, ctx, i18n) {
620
+ const entries = requireProjectState().dirs.get(dirPath);
621
+ if (!entries) {
622
+ loadDirectory(dirPath).then(() => ctx.renderLeftPanel());
623
+ rows.push({
624
+ depth,
625
+ expanded: false,
626
+ key: `loading:${dirPath}`,
627
+ loading: true,
628
+ name: "Loading…",
629
+ path: dirPath,
630
+ posInSet: 1,
631
+ setSize: 1,
632
+ type: "file"
633
+ });
634
+ return;
635
+ }
636
+ const sorted = [...entries].toSorted((a, b) => {
637
+ if (a.type === "directory" && b.type !== "directory") {
638
+ return -1;
639
+ }
640
+ if (a.type !== "directory" && b.type === "directory") {
641
+ return 1;
642
+ }
643
+ return a.name.localeCompare(b.name);
644
+ });
645
+ const query = requireProjectState().searchQuery.toLowerCase();
646
+ const filtered = query ? sorted.filter((e) => e.type === "directory" || e.name.toLowerCase().includes(query)) : sorted;
647
+ for (const [index, entry] of filtered.entries()) {
648
+ const isDir = entry.type === "directory";
649
+ const isExpanded = isDir && requireProjectState().expanded.has(entry.path);
650
+ rows.push({
651
+ depth,
652
+ expanded: isExpanded,
653
+ key: entry.path,
654
+ loading: false,
655
+ name: entry.name,
656
+ path: entry.path,
657
+ locale: localeOfPath(entry.path, i18n) ?? undefined,
658
+ posInSet: index + 1,
659
+ setSize: filtered.length,
660
+ type: entry.type
661
+ });
662
+ if (isExpanded) {
663
+ collectFileRows(entry.path, depth + 1, rows, ctx, i18n);
664
+ }
665
+ }
666
+ }
667
+ function fileIndexOfPath(path) {
668
+ return path === undefined ? -1 : _fileRows.findIndex((row) => !row.loading && row.path === path);
669
+ }
670
+ function fileStep(index, step) {
671
+ for (let i = index + step;i >= 0 && i < _fileRows.length; i += step) {
672
+ if (!_fileRows[i].loading) {
673
+ return i;
674
+ }
675
+ }
676
+ return -1;
677
+ }
678
+ function fileWindowChanged() {
679
+ if (_fileList?.isConnected !== true || _fileList.querySelector(".file-tree-item.dragging")) {
680
+ return;
681
+ }
682
+ queueMicrotask(() => _filesRerender?.());
683
+ }
684
+ function adoptFileTree(tree) {
685
+ _fileList = tree;
686
+ _fileWatch = watchListWindow(_fileWatch, tree, {
687
+ count: () => _fileRows.length,
688
+ onChange: fileWindowChanged,
689
+ rowHeight: fileRowHeight
690
+ });
691
+ const wanted = _pendingFocusPath;
692
+ if (wanted !== null) {
693
+ _pendingFocusPath = null;
694
+ fileRowElement(tree, wanted)?.focus();
695
+ }
696
+ }
697
+ function afterFileTreeRender(tree) {
698
+ queueMicrotask(() => adoptFileTree(tree));
699
+ }
700
+ function fileRowElement(tree, path) {
701
+ return tree.querySelector(`.file-tree-item[data-path="${CSS.escape(path)}"]`);
702
+ }
703
+ function fileTreeBodyTemplate(ctx) {
704
+ _filesRerender = ctx.renderLeftPanel;
705
+ _fileRows = [];
706
+ const { i18n } = resolveI18n(projectState?.projectConfig ?? {});
707
+ collectFileRows(".", 0, _fileRows, ctx, i18n);
708
+ const range = listWindow(_fileList, { count: _fileRows.length, rowHeight: fileRowHeight() });
709
+ const wanted = Math.max(0, fileIndexOfPath(requireProjectState().selectedPath ?? undefined));
710
+ const tabStop = Math.min(Math.max(wanted, range.start), Math.max(range.start, range.end - 1));
711
+ const slice = _fileRows.slice(range.start, range.end).map((row, offset) => ({ row, tabStop: range.start + offset === tabStop }));
712
+ return html`
713
+ <div style="height:${range.padTop}px" aria-hidden="true"></div>
714
+ ${repeat(slice, (entry) => entry.row.key, (entry) => fileRowTemplate(entry.row, entry.tabStop, ctx))}
715
+ <div style="height:${range.padBottom}px" aria-hidden="true"></div>
716
+ `;
717
+ }
718
+ function fileRowTemplate(row, tabStop, ctx) {
719
+ if (row.loading) {
720
+ return html`<div
721
+ class="file-tree-item"
722
+ style="padding-left:${8 + row.depth * 16}px;color:var(--fg-dim);font-style:italic"
723
+ >
724
+ Loading…
725
+ </div>`;
726
+ }
727
+ const isDir = row.type === "directory";
728
+ return html`
729
+ <div
730
+ class=${classMap({
731
+ "file-tree-item": true,
732
+ selected: requireProjectState().selectedPath === row.path
733
+ })}
734
+ style="padding-left:${8 + row.depth * 16}px"
735
+ role="treeitem"
736
+ aria-level=${row.depth + 1}
737
+ aria-posinset=${row.posInSet}
738
+ aria-setsize=${row.setSize}
739
+ tabindex=${tabStop ? "0" : "-1"}
740
+ data-path=${row.path}
741
+ data-type=${row.type}
742
+ aria-expanded=${isDir ? String(row.expanded) : nothing}
743
+ @click=${async (e) => {
744
+ e.stopPropagation();
745
+ if (isDir) {
746
+ await toggleTreeDirectory(row.path);
747
+ ctx.renderLeftPanel();
748
+ } else {
749
+ ctx.openFileFn(row.path);
750
+ }
751
+ }}
752
+ @contextmenu=${(e) => {
753
+ e.preventDefault();
754
+ e.stopPropagation();
755
+ showFileContextMenu(e, { name: row.name, path: row.path, type: row.type }, ctx);
756
+ }}
757
+ >
758
+ ${isDir ? html`<span class="file-tree-toggle">${row.expanded ? "▼" : "▶"}</span>` : html`<span class="file-tree-toggle empty"> </span>`}
759
+ <span class="file-tree-icon">${fileTypeIconTpl(row.path, row.type)}</span>
760
+ <span class="file-tree-name">${row.name}</span>
761
+ ${row.locale === undefined ? nothing : html`<span class="file-tree-locale">${localeLabel(row.locale)}</span>`}
762
+ </div>
763
+ `;
764
+ }
765
+ async function toggleTreeDirectory(path) {
766
+ const state = requireProjectState();
767
+ if (state.expanded.has(path)) {
768
+ state.expanded.delete(path);
769
+ return;
770
+ }
771
+ state.expanded.add(path);
772
+ if (!state.dirs.has(path)) {
773
+ await loadDirectory(path);
774
+ }
775
+ }
776
+ var _keyboardTrees = new WeakSet;
777
+ function setupTreeKeyboard(tree) {
778
+ if (_keyboardTrees.has(tree)) {
779
+ return;
780
+ }
781
+ _keyboardTrees.add(tree);
782
+ tree.addEventListener("keydown", (e) => {
783
+ onFileTreeKeydown(e, tree);
784
+ });
785
+ }
786
+ function onFileTreeKeydown(e, tree) {
787
+ const focused = tree.querySelector(".file-tree-item:focus");
788
+ if (!focused) {
789
+ return;
790
+ }
791
+ let handled = true;
792
+ switch (e.key) {
793
+ case "ArrowDown":
794
+ case "ArrowUp": {
795
+ const from = fileIndexOfPath(focused.dataset.path);
796
+ focusFileRow(tree, fileStep(from, e.key === "ArrowDown" ? 1 : -1));
797
+ break;
798
+ }
799
+ case "ArrowRight": {
800
+ const path = collapsedDirectoryAt(focused);
801
+ if (path !== null) {
802
+ toggleTreeDirectory(path).then(() => _filesRerender?.());
803
+ }
804
+ break;
805
+ }
806
+ case "ArrowLeft": {
807
+ const path = expandedDirectoryAt(focused);
808
+ if (path !== null) {
809
+ requireProjectState().expanded.delete(path);
810
+ _filesRerender?.();
811
+ }
812
+ break;
813
+ }
814
+ case "Enter": {
815
+ focused.click();
816
+ break;
817
+ }
818
+ default: {
819
+ handled = false;
820
+ }
821
+ }
822
+ if (handled) {
823
+ e.preventDefault();
824
+ }
825
+ }
826
+ function collapsedDirectoryAt(row) {
827
+ const { path, type } = row.dataset;
828
+ return type === "directory" && path !== undefined && !requireProjectState().expanded.has(path) ? path : null;
829
+ }
830
+ function expandedDirectoryAt(row) {
831
+ const { path, type } = row.dataset;
832
+ return type === "directory" && path !== undefined && requireProjectState().expanded.has(path) ? path : null;
833
+ }
834
+ function focusFileRow(tree, index) {
835
+ const row = _fileRows[index];
836
+ if (!row) {
837
+ return;
838
+ }
839
+ const el = fileRowElement(tree, row.path);
840
+ if (el) {
841
+ el.focus();
842
+ return;
843
+ }
844
+ if (revealListRow(_fileList, index, fileRowHeight())) {
845
+ _pendingFocusPath = row.path;
846
+ _filesRerender?.();
847
+ }
848
+ }
849
+ var _fileTreeDndCleanups = [];
850
+ function isFileDrag(e) {
851
+ return [...e.dataTransfer?.types ?? []].includes("Files");
852
+ }
853
+ function registerFileDropTarget(element, dir, activeClass, renderLeftPanel) {
854
+ const onDragOver = (e) => {
855
+ if (!isFileDrag(e)) {
856
+ return;
857
+ }
858
+ e.preventDefault();
859
+ e.stopPropagation();
860
+ if (e.dataTransfer) {
861
+ e.dataTransfer.dropEffect = "copy";
862
+ }
863
+ element.classList.add(activeClass);
864
+ };
865
+ const onDragLeave = () => element.classList.remove(activeClass);
866
+ const onDrop = (e) => {
867
+ element.classList.remove(activeClass);
868
+ const files = e.dataTransfer?.files;
869
+ if (!files?.length) {
870
+ return;
871
+ }
872
+ e.preventDefault();
873
+ e.stopPropagation();
874
+ uploadFilesToDir(files, dir, renderLeftPanel);
875
+ };
876
+ element.addEventListener("dragover", onDragOver);
877
+ element.addEventListener("dragleave", onDragLeave);
878
+ element.addEventListener("drop", onDrop);
879
+ return () => {
880
+ element.removeEventListener("dragover", onDragOver);
881
+ element.removeEventListener("dragleave", onDragLeave);
882
+ element.removeEventListener("drop", onDrop);
883
+ };
884
+ }
885
+ async function uploadFilesToDir(files, dir, renderLeftPanel) {
886
+ const uploaded = await uploadAssets([...files], { dir });
887
+ if (uploaded.length === 0) {
888
+ return;
889
+ }
890
+ if (dir !== ".") {
891
+ requireProjectState().expanded.add(dir);
892
+ }
893
+ renderLeftPanel();
894
+ }
895
+ function pickAndUploadTo(dir, renderLeftPanel) {
896
+ const input = document.createElement("input");
897
+ input.type = "file";
898
+ input.multiple = true;
899
+ input.accept = UPLOAD_ACCEPT;
900
+ input.addEventListener("change", () => {
901
+ if (input.files?.length) {
902
+ uploadFilesToDir(input.files, dir, renderLeftPanel);
903
+ }
904
+ });
905
+ input.click();
906
+ }
907
+ function registerFileTreeDnD({ renderLeftPanel }) {
908
+ for (const fn of _fileTreeDndCleanups) {
909
+ fn();
910
+ }
911
+ _fileTreeDndCleanups = [];
912
+ requestAnimationFrame(() => {
913
+ const tree = document.querySelector(".file-tree");
914
+ if (!tree) {
915
+ return;
916
+ }
917
+ const items = tree.querySelectorAll(".file-tree-item");
918
+ for (const row of items) {
919
+ const { path } = row.dataset;
920
+ const { type } = row.dataset;
921
+ if (!path) {
922
+ continue;
923
+ }
924
+ const cleanups = [
925
+ draggable({
926
+ element: row,
927
+ getInitialData() {
928
+ return { entryType: type, path, type: "file-tree" };
929
+ },
930
+ onDragStart() {
931
+ row.classList.add("dragging");
932
+ },
933
+ onDrop() {
934
+ row.classList.remove("dragging");
935
+ }
936
+ }),
937
+ registerFileDropTarget(row, type === "directory" ? path : parentDir(path), "drag-over", renderLeftPanel)
938
+ ];
939
+ if (type === "directory") {
940
+ cleanups.push(dropTargetForElements({
941
+ canDrop({ source }) {
942
+ if (source.data.type !== "file-tree") {
943
+ return false;
944
+ }
945
+ const srcPath = source.data.path;
946
+ if (srcPath === path) {
947
+ return false;
948
+ }
949
+ if (srcPath.startsWith(`${path}/`)) {
950
+ return false;
951
+ }
952
+ const srcParent = parentDir(srcPath);
953
+ if (srcParent === path) {
954
+ return false;
955
+ }
956
+ return true;
957
+ },
958
+ element: row,
959
+ getData() {
960
+ return { targetDir: path, type: "file-tree-target" };
961
+ },
962
+ onDrag() {
963
+ if (!row.classList.contains("drag-over")) {
964
+ row.classList.add("drag-over");
965
+ }
966
+ },
967
+ onDragEnter() {
968
+ row.classList.add("drag-over");
969
+ },
970
+ onDragLeave() {
971
+ row.classList.remove("drag-over");
972
+ },
973
+ onDrop() {
974
+ row.classList.remove("drag-over");
975
+ }
976
+ }));
977
+ }
978
+ _fileTreeDndCleanups.push(combine(...cleanups));
979
+ }
980
+ const rootCleanup = dropTargetForElements({
981
+ canDrop({ source }) {
982
+ if (source.data.type !== "file-tree") {
983
+ return false;
984
+ }
985
+ const srcPath = source.data.path;
986
+ return parentDir(srcPath) !== ".";
987
+ },
988
+ element: tree,
989
+ getData() {
990
+ return { targetDir: ".", type: "file-tree-target" };
991
+ },
992
+ onDragEnter() {
993
+ tree.classList.add("drag-over-root");
994
+ },
995
+ onDragLeave() {
996
+ tree.classList.remove("drag-over-root");
997
+ },
998
+ onDrop() {
999
+ tree.classList.remove("drag-over-root");
1000
+ }
1001
+ });
1002
+ _fileTreeDndCleanups.push(rootCleanup, registerFileDropTarget(tree, ".", "drag-over-root", renderLeftPanel));
1003
+ const monitorCleanup = monitorForElements({
1004
+ onDrop({ source, location: location2 }) {
1005
+ const [target] = location2.current.dropTargets;
1006
+ if (!target) {
1007
+ return;
1008
+ }
1009
+ if (source.data.type !== "file-tree") {
1010
+ return;
1011
+ }
1012
+ if (target.data.type !== "file-tree-target") {
1013
+ return;
1014
+ }
1015
+ const srcPath = source.data.path;
1016
+ const targetDirPath = target.data.targetDir;
1017
+ const fileName = srcPath.split("/").pop();
1018
+ const newPath = targetDirPath === "." ? fileName : `${targetDirPath}/${fileName}`;
1019
+ if (newPath === srcPath) {
1020
+ return;
1021
+ }
1022
+ moveFileEntry(srcPath, newPath, renderLeftPanel);
1023
+ }
1024
+ });
1025
+ _fileTreeDndCleanups.push(monitorCleanup);
1026
+ });
1027
+ }
1028
+ async function moveFileEntry(oldPath, newPath, renderLeftPanel) {
1029
+ const platform = getPlatform();
1030
+ markLocalMutation(oldPath, newPath);
1031
+ try {
1032
+ const report = await platform.renameFile(oldPath, newPath);
1033
+ for (const [id] of workspace.tabs.entries()) {
1034
+ if (id === oldPath) {
1035
+ renameTab(oldPath, newPath, newPath);
1036
+ } else if (id.startsWith(`${oldPath}/`)) {
1037
+ const newTabPath = newPath + id.slice(oldPath.length);
1038
+ renameTab(id, newTabPath, newTabPath);
1039
+ }
1040
+ }
1041
+ const oldParent = parentDir(oldPath);
1042
+ const newParent = parentDir(newPath);
1043
+ await loadDirectory(oldParent);
1044
+ if (newParent !== oldParent) {
1045
+ await loadDirectory(newParent);
1046
+ }
1047
+ if (newParent !== ".") {
1048
+ requireProjectState().expanded.add(newParent);
1049
+ }
1050
+ reloadRewrittenTabs(report, newPath);
1051
+ renderLeftPanel();
1052
+ notify.success(`Moved to ${newPath}`);
1053
+ } catch (error) {
1054
+ notify.error(`Could not move ${oldPath}.`, {
1055
+ detail: errorMessage(error),
1056
+ path: oldPath,
1057
+ source: "Files"
1058
+ });
1059
+ }
1060
+ }
1061
+ function parentDir(path) {
1062
+ const normalized = path.replaceAll("\\", "/");
1063
+ const lastSlash = normalized.lastIndexOf("/");
1064
+ return lastSlash === -1 ? "." : normalized.slice(0, lastSlash);
1065
+ }
1066
+ var _fileCtxHandle = null;
1067
+ function dismissFileContextMenu() {
1068
+ if (_fileCtxHandle) {
1069
+ _fileCtxHandle.dismiss();
1070
+ _fileCtxHandle = null;
1071
+ }
1072
+ }
1073
+ function fileRowFacts(entry) {
1074
+ const facts = {};
1075
+ if (entry.type === "directory") {
1076
+ const collection = collectionDirs().find(({ dir }) => entry.path === dir || entry.path.endsWith(`/${dir}`));
1077
+ if (collection) {
1078
+ facts.name = collection.name;
1079
+ }
1080
+ } else if (collectionOfPath(entry.path)) {
1081
+ facts.path = entry.path;
1082
+ }
1083
+ return facts;
1084
+ }
1085
+ function placedFileItems(entry) {
1086
+ const registry = activeRegistry();
1087
+ if (!registry) {
1088
+ return [];
1089
+ }
1090
+ const facts = fileRowFacts(entry);
1091
+ const items = [];
1092
+ for (const command of registry.forPlacement("context/file")) {
1093
+ const schema = command.args;
1094
+ if (!(schema?.required ?? []).every((key) => (key in facts))) {
1095
+ continue;
1096
+ }
1097
+ const args = {};
1098
+ for (const key of Object.keys(schema?.properties ?? {})) {
1099
+ if (key in facts) {
1100
+ args[key] = facts[key];
1101
+ }
1102
+ }
1103
+ const reason = registry.disabledReason(command.id);
1104
+ items.push(reason === undefined ? {
1105
+ action: () => {
1106
+ registry.run(command.id, args);
1107
+ },
1108
+ label: command.title
1109
+ } : { disabled: true, label: command.title, reason });
1110
+ }
1111
+ return items;
1112
+ }
1113
+ function showFileContextMenu(e, entry, ctx) {
1114
+ e.preventDefault();
1115
+ dismissFileContextMenu();
1116
+ const isDir = entry.type === "directory";
1117
+ const items = [];
1118
+ if (!isDir) {
1119
+ items.push({ action: () => ctx.openFileFn(entry.path), label: "Open" });
1120
+ }
1121
+ if (isDir) {
1122
+ items.push({
1123
+ action: () => createNewFile(entry.path, ctx.renderLeftPanel),
1124
+ label: "New File…"
1125
+ }, {
1126
+ action: () => pickAndUploadTo(entry.path, ctx.renderLeftPanel),
1127
+ label: "Upload Files…"
1128
+ });
1129
+ if (entry.path === "pages" || entry.path.endsWith("/pages")) {
1130
+ items.push({
1131
+ action: () => {
1132
+ openPagesGrid();
1133
+ },
1134
+ label: "Edit Pages in Grid"
1135
+ });
1136
+ }
1137
+ }
1138
+ items.push(...placedFileItems(entry), { label: "—" }, {
1139
+ action: () => renameFile(entry, ctx.renderLeftPanel),
1140
+ label: "Rename…"
1141
+ }, {
1142
+ action: () => deleteFile(entry, ctx.renderLeftPanel),
1143
+ danger: true,
1144
+ label: "Delete"
1145
+ });
1146
+ let x = e.clientX;
1147
+ let y = e.clientY;
1148
+ _fileCtxHandle = renderPopover(html`<sp-popover
1149
+ open
1150
+ style="position:fixed;z-index:10000;left:${x}px;top:${y}px"
1151
+ ${ref((el) => {
1152
+ if (!el) {
1153
+ return;
1154
+ }
1155
+ requestAnimationFrame(() => {
1156
+ const popover = el;
1157
+ const menuRect = rectOf(popover);
1158
+ if (x + menuRect.width > window.innerWidth) {
1159
+ x = window.innerWidth - menuRect.width - 4;
1160
+ }
1161
+ if (y + menuRect.height > window.innerHeight) {
1162
+ y = window.innerHeight - menuRect.height - 4;
1163
+ }
1164
+ popover.style.left = `${x}px`;
1165
+ popover.style.top = `${y}px`;
1166
+ });
1167
+ })}
1168
+ >
1169
+ <sp-menu>
1170
+ ${items.map((item) => item.label === "—" ? html`<sp-menu-divider></sp-menu-divider>` : html`<sp-menu-item
1171
+ style=${item.danger ? "color: var(--danger)" : ""}
1172
+ ?disabled=${item.disabled === true}
1173
+ aria-disabled=${item.disabled === true ? "true" : "false"}
1174
+ @click=${() => {
1175
+ if (item.disabled === true) {
1176
+ return;
1177
+ }
1178
+ dismissFileContextMenu();
1179
+ item.action?.();
1180
+ }}
1181
+ >${item.label}${item.reason ? html`<span slot="description">Needs ${item.reason}</span>` : nothing}</sp-menu-item
1182
+ >`)}
1183
+ </sp-menu>
1184
+ </sp-popover>`, {
1185
+ dismissOnOutsideClick: true,
1186
+ onDismiss: () => {
1187
+ _fileCtxHandle = null;
1188
+ }
1189
+ });
1190
+ }
1191
+ var BLANK_DOCUMENT = JSON.stringify({ children: [{ children: [], tagName: "p" }], tagName: "div" }, null, 2);
1192
+ async function createFileIn(request) {
1193
+ const { dir, ext, source = "Files" } = request;
1194
+ await loadFormats();
1195
+ let taken = new Set;
1196
+ try {
1197
+ const listing = await getPlatform().listDirectory(dir);
1198
+ taken = new Set(listing.map((entry) => entry.name));
1199
+ } catch {}
1200
+ const fileNameFor = (input) => ext === undefined ? input.trim() : `${input.trim().toLowerCase().replaceAll(/\s+/g, "-").replaceAll(/[^a-z\d-]/g, "")}${ext}`;
1201
+ const entered = await showPromptDialog(request.title ?? "New File", {
1202
+ confirmLabel: "Create",
1203
+ message: dir === "." ? "Creating in the project root." : `Creating in ${dir}/`,
1204
+ ...request.suggestedName === undefined ? {} : { placeholder: request.suggestedName },
1205
+ select: "stem",
1206
+ validate: (value) => {
1207
+ if (!value.trim()) {
1208
+ return ext === undefined ? "Enter a file name." : "Enter a name.";
1209
+ }
1210
+ const candidate = fileNameFor(value);
1211
+ if (!candidate || candidate === ext) {
1212
+ return "Enter at least one letter or number.";
1213
+ }
1214
+ return taken.has(candidate) ? `${candidate} already exists in ${dir}/.` : "";
1215
+ },
1216
+ value: request.suggestedName ?? "untitled.json"
1217
+ });
1218
+ if (!entered) {
1219
+ return null;
1220
+ }
1221
+ const fileName = fileNameFor(entered);
1222
+ const path = dir === "." ? fileName : `${dir}/${fileName}`;
1223
+ markLocalMutation(path);
1224
+ const format = formatForPath(fileName);
1225
+ const content = request.content ?? format?.studio?.newFileTemplate ?? (format ? "" : BLANK_DOCUMENT);
1226
+ try {
1227
+ await getPlatform().writeFile(path, content);
1228
+ await loadDirectory(dir);
1229
+ notify.success(`Created ${path}`);
1230
+ return path;
1231
+ } catch (error) {
1232
+ notify.error(`Could not create ${path}.`, {
1233
+ detail: errorMessage(error),
1234
+ path,
1235
+ source
1236
+ });
1237
+ return null;
1238
+ }
1239
+ }
1240
+ async function createNewFile(dirPath, renderLeftPanel) {
1241
+ const created = await createFileIn({ dir: dirPath, suggestedName: "untitled.json" });
1242
+ if (created !== null) {
1243
+ renderLeftPanel();
1244
+ }
1245
+ }
1246
+ async function showRenameFileDialog(currentName, path) {
1247
+ const message = await renamePromptMessage(path);
1248
+ return showPromptDialog("Rename", {
1249
+ confirmLabel: "Rename",
1250
+ ...message === undefined ? {} : { message },
1251
+ select: "stem",
1252
+ validate: (v) => v.trim() ? "" : "Enter a file name.",
1253
+ value: currentName
1254
+ });
1255
+ }
1256
+ function renameStatus(newName, report) {
1257
+ const refs = report.references;
1258
+ const tagNote = report.tag ? `; tag → <${report.tag.to}> (${report.tag.refsUpdated})` : "";
1259
+ if (refs && refs.refsUpdated > 0) {
1260
+ return `Renamed to ${newName}; updated ${refs.refsUpdated} reference(s) in ${refs.filesChanged} file(s)${tagNote}`;
1261
+ }
1262
+ if (tagNote) {
1263
+ return `Renamed to ${newName}${tagNote}`;
1264
+ }
1265
+ return `Renamed to ${newName}`;
1266
+ }
1267
+ function reloadRewrittenTabs(report, skipPath) {
1268
+ for (const f of report.references?.files ?? []) {
1269
+ if (f.path !== skipPath && workspace.tabs.has(f.path)) {
1270
+ reloadFileInTab(f.path);
1271
+ }
1272
+ }
1273
+ }
1274
+ async function renameFile(entry, renderLeftPanel) {
1275
+ const newName = await showRenameFileDialog(entry.name, entry.path);
1276
+ if (!newName || newName === entry.name) {
1277
+ return;
1278
+ }
1279
+ const entryPath = entry.path.replaceAll("\\", "/");
1280
+ const parentDirPath = entryPath.includes("/") ? entryPath.slice(0, entryPath.lastIndexOf("/")) : ".";
1281
+ const newPath = parentDirPath === "." ? newName : `${parentDirPath}/${newName}`;
1282
+ markLocalMutation(entry.path, newPath);
1283
+ try {
1284
+ const platform = getPlatform();
1285
+ const report = await platform.renameFile(entry.path, newPath);
1286
+ invalidateUsages();
1287
+ await loadDirectory(parentDirPath);
1288
+ if (requireProjectState().selectedPath === entry.path) {
1289
+ requireProjectState().selectedPath = newPath;
1290
+ }
1291
+ if (workspace.tabs.has(entry.path)) {
1292
+ renameTab(entry.path, newPath, newPath);
1293
+ }
1294
+ reloadRewrittenTabs(report, newPath);
1295
+ renderLeftPanel();
1296
+ notify.success(renameStatus(newName, report));
1297
+ } catch (error) {
1298
+ notify.error(`Could not rename ${entry.name}.`, {
1299
+ detail: errorMessage(error),
1300
+ path: entry.path,
1301
+ source: "Files"
1302
+ });
1303
+ }
1304
+ }
1305
+ async function deleteFile(entry, renderLeftPanel) {
1306
+ const confirmed = await confirmFileDelete(entry);
1307
+ if (!confirmed) {
1308
+ return;
1309
+ }
1310
+ try {
1311
+ const platform = getPlatform();
1312
+ markLocalMutation(entry.path);
1313
+ await platform.deleteFile(entry.path);
1314
+ invalidateUsages();
1315
+ const delPath = entry.path.replaceAll("\\", "/");
1316
+ const parentDirPath = delPath.includes("/") ? delPath.slice(0, delPath.lastIndexOf("/")) : ".";
1317
+ await loadDirectory(parentDirPath);
1318
+ if (requireProjectState().selectedPath === entry.path) {
1319
+ requireProjectState().selectedPath = null;
1320
+ }
1321
+ renderLeftPanel();
1322
+ notify.success(`Deleted ${entry.name}`);
1323
+ } catch (error) {
1324
+ notify.error(`Could not delete ${entry.name}.`, {
1325
+ detail: errorMessage(error),
1326
+ path: entry.path,
1327
+ source: "Files"
1328
+ });
1329
+ }
1330
+ }
1331
+ function revealOpenTab(tabId, opts) {
1332
+ const wanted = opts.paneId;
1333
+ const holder = paneOfTab(tabId);
1334
+ if (wanted !== undefined && holder && holder.id !== wanted) {
1335
+ if (holder.activeTabId === tabId) {
1336
+ return;
1337
+ }
1338
+ moveTabToPane(tabId, wanted);
1339
+ }
1340
+ activateTab(tabId, { focus: opts.focus !== false });
1341
+ }
1342
+ async function openFileInTab(path, opts = {}) {
1343
+ const follows = opts.focus !== false;
1344
+ for (const [id, tab] of workspace.tabs.entries()) {
1345
+ if (tab.documentPath === path) {
1346
+ revealOpenTab(id, opts);
1347
+ if (follows) {
1348
+ requireProjectState().selectedPath = path;
1349
+ }
1350
+ return;
1351
+ }
1352
+ }
1353
+ if (path.toLowerCase().endsWith(".csv")) {
1354
+ try {
1355
+ await openCsvGridTab(path);
1356
+ requireProjectState().selectedPath = path;
1357
+ trackRecentFile({
1358
+ name: path.split("/").pop() || path,
1359
+ path,
1360
+ root: requireProjectState().projectRoot
1361
+ });
1362
+ } catch (error) {
1363
+ notify.error(`Could not open ${path}.`, {
1364
+ detail: errorMessage(error),
1365
+ path,
1366
+ source: "Open File"
1367
+ });
1368
+ }
1369
+ return;
1370
+ }
1371
+ const platform = getPlatform();
1372
+ try {
1373
+ const content = await platform.readFile(path);
1374
+ if (!content) {
1375
+ return;
1376
+ }
1377
+ await loadFormats();
1378
+ let document2;
1379
+ let frontmatter;
1380
+ const format = formatForPath(path);
1381
+ if (format) {
1382
+ const result = await parseSourceForPath(path, content);
1383
+ ({ document: document2 } = result);
1384
+ ({ frontmatter } = result);
1385
+ } else if (path.endsWith(".json")) {
1386
+ document2 = JSON.parse(content);
1387
+ } else {
1388
+ throw noFormatError(path);
1389
+ }
1390
+ const id = path;
1391
+ openTab({
1392
+ id,
1393
+ documentPath: path,
1394
+ document: document2,
1395
+ ...frontmatter != null && { frontmatter },
1396
+ sourceFormat: format?.name ?? null,
1397
+ ...opts.paneId !== undefined && { paneId: opts.paneId },
1398
+ ...opts.preview === true && { preview: true },
1399
+ ...opts.focus === false && { focus: false }
1400
+ });
1401
+ if (follows) {
1402
+ requireProjectState().selectedPath = path;
1403
+ }
1404
+ trackRecentFile({
1405
+ name: path.split("/").pop() || path,
1406
+ path,
1407
+ root: requireProjectState().projectRoot
1408
+ });
1409
+ } catch (error) {
1410
+ notify.error(`Could not open ${path}.`, {
1411
+ detail: errorMessage(error),
1412
+ path,
1413
+ source: "Open File"
1414
+ });
1415
+ }
1416
+ }
1417
+ async function openFileInPane(paneId, path) {
1418
+ await openFileInTab(path, { focus: false, paneId, preview: true });
1419
+ }
1420
+ function reloadCleanTab(path) {
1421
+ if (isCollabPath(path)) {
1422
+ return;
1423
+ }
1424
+ for (const [, tab] of workspace.tabs.entries()) {
1425
+ if (tab.documentPath === path && !tab.doc.dirty) {
1426
+ reloadFileInTab(path);
1427
+ return;
1428
+ }
1429
+ }
1430
+ }
1431
+ async function reloadFileInTab(path) {
1432
+ for (const [, tab] of workspace.tabs.entries()) {
1433
+ if (tab.documentPath === path) {
1434
+ const platform = getPlatform();
1435
+ try {
1436
+ const content = await platform.readFile(path);
1437
+ if (!content) {
1438
+ return;
1439
+ }
1440
+ await loadFormats();
1441
+ if (formatForPath(path)) {
1442
+ const { document: document2, frontmatter } = await parseSourceForPath(path, content);
1443
+ tab.doc.document = document2;
1444
+ tab.doc.content.frontmatter = frontmatter;
1445
+ } else if (path.endsWith(".json")) {
1446
+ tab.doc.document = JSON.parse(content);
1447
+ }
1448
+ tab.doc.dirty = false;
1449
+ } catch (error) {
1450
+ notify.error(`Could not reload ${path} after it changed on disk.`, {
1451
+ detail: errorMessage(error),
1452
+ key: `reload:${path}`,
1453
+ path,
1454
+ source: "Files"
1455
+ });
1456
+ }
1457
+ return;
1458
+ }
1459
+ }
1460
+ }
1461
+ function registerFilesPanel() {
1462
+ registerPanel({
1463
+ id: "files",
1464
+ title: "Files",
1465
+ level: "project",
1466
+ dock: "navigator",
1467
+ icon: "sp-icon-folder",
1468
+ render: (ctx) => ctx.deps.renderFilesTemplate(),
1469
+ afterRender: (ctx, host) => {
1470
+ const tree = host.querySelector(".file-tree");
1471
+ if (tree) {
1472
+ ctx.deps.setupTreeKeyboard(tree);
1473
+ }
1474
+ ctx.deps.registerFileTreeDnD({ renderLeftPanel: ctx.rerender });
1475
+ }
1476
+ });
1477
+ }
1478
+
1479
+ export { ensureDependenciesInstalled, entryCollection, entryCollections, collectionOfPath, seedEntry, missingRequired, hydrateRecentProjects, getRecentProjects, addRecentProject, removeRecentProject, clearRecentProjects, getRecentFiles, trackRecentFile, loadDirectory, loadProject, openProject, initProjectRepo, findHomePage, openHomePage, openLastSessionOrHome, renderFilesTemplate, FILE_ROW_HEIGHT, setupTreeKeyboard, isFileDrag, uploadFilesToDir, pickAndUploadTo, registerFileTreeDnD, createFileIn, openFileInTab, openFileInPane, reloadCleanTab, reloadFileInTab, registerFilesPanel };
1480
+
1481
+ //# debugId=FB716457707AF69D64756E2164756E21
1482
+ //# sourceMappingURL=studio-94m77tgk.js.map