@jxsuite/studio 10.5.0 → 10.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/locales/en/elements.json +434 -0
- package/data/webdata.json +116 -139
- package/dist/chunks/{2020-7sx2vexa.js → 2020-rqm7qrje.js} +3 -3
- package/dist/chunks/adapter-cyy8vbcx.js +4 -0
- package/dist/chunks/add-new-flow-8cgtejv2.js +4 -0
- package/dist/chunks/{cf-account-picker-ajh60k54.js → cf-account-picker-v3qd2dhw.js} +3 -3
- package/dist/chunks/cf-settings-1fnw874z.js +4 -0
- package/dist/chunks/{color-area-gbcftzh3.js → color-area-frd9y9q6.js} +2 -2
- package/dist/chunks/{color-field-85136598.js → color-field-rebpbe33.js} +3 -3
- package/dist/chunks/{color-slider-w1s3ndd8.js → color-slider-6bzmk3s7.js} +2 -2
- package/dist/chunks/color-t2bnnj8y.js +4 -0
- package/dist/chunks/{combobox-dk98wc5x.js → combobox-c49x7gf1.js} +2 -2
- package/dist/chunks/components-y8yhdd7d.js +4 -0
- package/dist/chunks/content-source-9e7qvmsf.js +4 -0
- package/dist/chunks/convert-to-repeater-pd2w4z21.js +4 -0
- package/dist/chunks/editor-mb9jzdrw.js +4 -0
- package/dist/chunks/entry-commands-at0wnx3f.js +4 -0
- package/dist/chunks/extension-sections-4z9qq0gh.js +4 -0
- package/dist/chunks/file-ops-dtdvjmza.js +4 -0
- package/dist/chunks/files-8pf70854.js +4 -0
- package/dist/chunks/format-host-fd3m2y2a.js +4 -0
- package/dist/chunks/fs-events-hrk29vcv.js +4 -0
- package/dist/chunks/grid-open-bh74v82z.js +4 -0
- package/dist/chunks/{grid-view-0v4hjspb.js → grid-view-a51mgzmq.js} +10 -10
- package/dist/chunks/{icons-j9eksgew.js → icons-qp8fn1k8.js} +2 -2
- package/dist/chunks/{iframe-entry-4zqtn3ry.js → iframe-entry-mpwe5pwk.js} +4 -4
- package/dist/chunks/{iframe-entry-2x9bn5ng.js → iframe-entry-msd43x9h.js} +4 -4
- package/dist/chunks/iframe-entry-rt8jz5t8.js +5 -0
- package/dist/chunks/{iframe-entry-54kwwhmg.js → iframe-entry-v40cr813.js} +2 -2
- package/dist/chunks/iframe-host-q0s0wwy3.js +4 -0
- package/dist/chunks/{image-bake-m8gzaan9.js → image-bake-q9ajdjsj.js} +3 -3
- package/dist/chunks/{image-editor-0afyws7c.js → image-editor-ke9std9b.js} +2 -2
- package/dist/chunks/index-57ang7ma.js +4 -0
- package/dist/chunks/index-eytr7av4.js +17 -0
- package/dist/chunks/{index-2q88g57p.js → index-x495reff.js} +3 -3
- package/dist/chunks/{javascript-5sxtr1g7.js → javascript-qe34bvaz.js} +3 -3
- package/dist/chunks/{jsonMode-m6sf47hq.js → jsonMode-m8rseh31.js} +8 -8
- package/dist/chunks/jx-validate-bbd1mary.js +4 -0
- package/dist/chunks/layers-panel-wcjtdqsa.js +4 -0
- package/dist/chunks/layers-yscpys6q.js +4 -0
- package/dist/chunks/{listbox-k5p41049.js → listbox-tktzq5xj.js} +2 -2
- package/dist/chunks/lit-html-qm5wbhqz.js +4 -0
- package/dist/chunks/media-library-2hfff88g.js +4 -0
- package/dist/chunks/media-open-4pb05cne.js +4 -0
- package/dist/chunks/media-picker-bzktmmwr.js +4 -0
- package/dist/chunks/{menu-26nez152.js → menu-xc3cbgsd.js} +2 -2
- package/dist/chunks/{monaco-binding-1s2b8snq.js → monaco-binding-7t38arh9.js} +3 -3
- package/dist/chunks/monaco-buffer-3vz94c9f.js +4 -0
- package/dist/chunks/monaco-setup-fstdew02.js +4 -0
- package/dist/chunks/notify-gbdcckrh.js +4 -0
- package/dist/chunks/parse-fpdzgk4c.js +4 -0
- package/dist/chunks/platform-9g72gdqr.js +4 -0
- package/dist/chunks/{popover-fhdnt4be.js → popover-cnmsav6b.js} +2 -2
- package/dist/chunks/publish-panel-91x1cb5d.js +4 -0
- package/dist/chunks/publish-quick-e2dmf7eg.js +4 -0
- package/dist/chunks/publish-workspace-1x8mnzrj.js +4 -0
- package/dist/chunks/quick-search-2m4y6p1x.js +4 -0
- package/dist/chunks/references-yg1g9rwx.js +4 -0
- package/dist/chunks/right-panel-7mfyhzrk.js +4 -0
- package/dist/chunks/source-control-rrc4jgp5.js +4 -0
- package/dist/chunks/{split-qh21j2b0.js → split-d15yencd.js} +2 -2
- package/dist/chunks/{studio-r5g7fx7q.js → studio-0jhhznxy.js} +6 -6
- package/dist/chunks/studio-0nb57pjg.js +7 -0
- package/dist/chunks/{studio-bpzwhn88.js → studio-0wa4wv4s.js} +4 -4
- package/dist/chunks/studio-0z8kp4xk.js +5 -0
- package/dist/chunks/{studio-jqvke4ac.js → studio-18gc5t3v.js} +4 -4
- package/dist/chunks/{studio-xh6rf05g.js → studio-1b79rpcb.js} +4 -4
- package/dist/chunks/studio-211q5j7m.js +4 -0
- package/dist/chunks/studio-213pyfxh.js +16 -0
- package/dist/chunks/studio-2b1smjvp.js +5 -0
- package/dist/chunks/{studio-dst8y8vh.js → studio-2v6tc9jp.js} +4 -4
- package/dist/chunks/studio-30j0wz99.js +5 -0
- package/dist/chunks/studio-3kmqhwtt.js +5 -0
- package/dist/chunks/studio-44drsmhw.js +5 -0
- package/dist/chunks/{studio-d5f7sskn.js → studio-495b6f2r.js} +4 -4
- package/dist/chunks/{studio-g65t6svn.js → studio-4gthb2fd.js} +4 -4
- package/dist/chunks/{studio-bmwgj8ez.js → studio-4xadhxj1.js} +4 -4
- package/dist/chunks/{studio-zav9hgb3.js → studio-52mzbxqm.js} +4 -4
- package/dist/chunks/{studio-thgexj3w.js → studio-56mmnsw8.js} +7 -7
- package/dist/chunks/studio-59hz8f4r.js +5 -0
- package/dist/chunks/{studio-bk0anq3b.js → studio-5k9mfafh.js} +3 -3
- package/dist/chunks/{studio-qarg1xf9.js → studio-5qez462c.js} +5 -5
- package/dist/chunks/studio-6tnpg0vx.js +5 -0
- package/dist/chunks/studio-6w8mt734.js +6 -0
- package/dist/chunks/studio-7hm04x5z.js +5 -0
- package/dist/chunks/studio-8atmfjjm.js +7 -0
- package/dist/chunks/{studio-69g6a0jn.js → studio-8n99z71s.js} +4 -4
- package/dist/chunks/{studio-ep1384t9.js → studio-8vmad6x2.js} +4 -4
- package/dist/chunks/{studio-6sxnqqw8.js → studio-93p5k4r4.js} +4 -4
- package/dist/chunks/{studio-hy936516.js → studio-9gb0m81w.js} +4 -4
- package/dist/chunks/{studio-ek4fb4eb.js → studio-a4ymg4vd.js} +4 -4
- package/dist/chunks/studio-anc8067x.js +5 -0
- package/dist/chunks/{studio-ketrgtph.js → studio-dbdt67kr.js} +5 -5
- package/dist/chunks/studio-dbex091t.js +5 -0
- package/dist/chunks/{studio-nngc45ct.js → studio-dj8ctxey.js} +4 -4
- package/dist/chunks/{studio-pgze9p34.js → studio-ed9bz68j.js} +5 -5
- package/dist/chunks/{studio-4ncxzw5m.js → studio-fe2d1fp5.js} +4 -4
- package/dist/chunks/{studio-m8hesdrw.js → studio-fgp4cyy3.js} +4 -4
- package/dist/chunks/{studio-151kgn69.js → studio-ft706q02.js} +4 -4
- package/dist/chunks/{studio-pv1ts08p.js → studio-g79s1dyg.js} +5 -5
- package/dist/chunks/{studio-zkkt13qd.js → studio-gahswxp4.js} +4 -4
- package/dist/chunks/{studio-x5ffyssw.js → studio-gbqc12bk.js} +4 -4
- package/dist/chunks/{studio-4jy967rj.js → studio-gxphbetb.js} +5 -5
- package/dist/chunks/studio-hdf01j7q.js +7 -0
- package/dist/chunks/studio-hf259xzd.js +48 -0
- package/dist/chunks/studio-jbva4wxw.js +5 -0
- package/dist/chunks/{studio-td3h1644.js → studio-jj9gf1qc.js} +4 -4
- package/dist/chunks/studio-jsjg3mjv.js +5 -0
- package/dist/chunks/studio-jtvb8hd7.js +5 -0
- package/dist/chunks/{studio-c72v1kwc.js → studio-kp4vzrct.js} +4 -4
- package/dist/chunks/{studio-s4k1v7dt.js → studio-kpx02gaa.js} +6 -6
- package/dist/chunks/{studio-3yr1zdp9.js → studio-kv1728ex.js} +8 -8
- package/dist/chunks/studio-m25ry0mf.js +5 -0
- package/dist/chunks/studio-mcbwmkyh.js +5 -0
- package/dist/chunks/studio-meajkr51.js +5 -0
- package/dist/chunks/studio-mkjrjpr2.js +6 -0
- package/dist/chunks/{studio-nv5eq0f7.js → studio-mr084886.js} +181 -181
- package/dist/chunks/{studio-43pynnhz.js → studio-mrb10hv3.js} +4 -4
- package/dist/chunks/{studio-7qqb7hb8.js → studio-nhpg405j.js} +4 -4
- package/dist/chunks/{studio-n271vdsg.js → studio-nsec0hxm.js} +41 -41
- package/dist/chunks/{studio-ztzf8mce.js → studio-nv0mh0p5.js} +4 -4
- package/dist/chunks/studio-pafmb06w.js +5 -0
- package/dist/chunks/{studio-4s5bka8t.js → studio-pgftd79b.js} +4 -4
- package/dist/chunks/{studio-wn2cmf9q.js → studio-pk961xgz.js} +4 -4
- package/dist/chunks/studio-pzbr7g05.js +5 -0
- package/dist/chunks/{studio-15mfah02.js → studio-qcc5hsw2.js} +6 -6
- package/dist/chunks/studio-qfq58c8n.js +5 -0
- package/dist/chunks/{studio-j3bq30yz.js → studio-qvb2jr83.js} +4 -4
- package/dist/chunks/studio-r85zw6ay.js +5 -0
- package/dist/chunks/{studio-f69wcajk.js → studio-s0kwce5w.js} +5 -5
- package/dist/chunks/studio-s6ymkf3q.js +14 -0
- package/dist/chunks/{studio-8ncm9zwx.js → studio-s7p5ccxa.js} +4 -4
- package/dist/chunks/{studio-zzgp5yka.js → studio-s9jjap3x.js} +4 -4
- package/dist/chunks/{studio-ber1dgte.js → studio-t5hde1ws.js} +4 -4
- package/dist/chunks/studio-vj9yv897.js +5 -0
- package/dist/chunks/{studio-ajfq4fmr.js → studio-wmak8xjw.js} +4 -4
- package/dist/chunks/{studio-k05g7efh.js → studio-wqsmvtqj.js} +4 -4
- package/dist/chunks/studio-x3z3rhej.js +5 -0
- package/dist/chunks/{studio-7qr8k0v2.js → studio-xb55kpmy.js} +4 -4
- package/dist/chunks/{studio-f3awr5nc.js → studio-xxtdajmv.js} +7 -7
- package/dist/chunks/{studio-1a7zshnv.js → studio-xxttcym1.js} +4 -4
- package/dist/chunks/{studio-czpmzs9r.js → studio-y2zsnzye.js} +7 -7
- package/dist/chunks/{studio-9f5vcb4w.js → studio-y3tjh2xb.js} +3 -3
- package/dist/chunks/studio-y4f1rdsj.js +5 -0
- package/dist/chunks/studio-ybkd5w8k.js +5 -0
- package/dist/chunks/{studio-cv6gnjqd.js → studio-yetr4gg1.js} +4 -4
- package/dist/chunks/{studio-ehj3csbm.js → studio-z295anhs.js} +5 -5
- package/dist/chunks/{studio-pte5w7rf.js → studio-z89e6wpg.js} +8 -8
- package/dist/chunks/{studio-4z0cdh16.js → studio-zbwr6prc.js} +3 -3
- package/dist/chunks/{studio-w3aafbpk.js → studio-zdbsv5q2.js} +3 -3
- package/dist/chunks/{studio-eq5vqn1q.js → studio-zvthj8hg.js} +4 -4
- package/dist/chunks/toast-2kn2vrn4.js +4 -0
- package/dist/chunks/{toast-host-g3r2s0kn.js → toast-host-3rxeyj3d.js} +3 -3
- package/dist/chunks/{tooltip-gtx40bjg.js → tooltip-ghkxkasp.js} +2 -2
- package/dist/chunks/{tree-a19s1c05.js → tree-vw4nc63s.js} +2 -2
- package/dist/chunks/{tsMode-3f116hth.js → tsMode-7gngmamr.js} +7 -7
- package/dist/chunks/workspace-gv0hxgsm.js +4 -0
- package/dist/chunks/workspace-home-j7e0mjy5.js +4 -0
- package/dist/chunks/{ws-client-cb49m87s.js → ws-client-vs11sxc5.js} +3 -3
- package/dist/iframe-entry.js +14 -14
- package/dist/manifest.json +84 -83
- package/dist/studio.js +80 -80
- package/package.json +12 -12
- package/src/browse/preview-style.ts +2 -1
- package/src/canvas/canvas-render.ts +6 -1
- package/src/canvas/iframe-drop.ts +84 -1
- package/src/canvas/iframe-entry.ts +9 -6
- package/src/canvas/iframe-render.ts +13 -0
- package/src/collab/collab-session.ts +2 -2
- package/src/commands/levels.ts +1 -1
- package/src/create/add-new-flow.ts +2 -1
- package/src/editor/convert-targets.ts +2 -23
- package/src/editor/slash-menu.ts +35 -17
- package/src/files/files.ts +18 -0
- package/src/format/format-choices.ts +5 -5
- package/src/format/format-host.ts +35 -3
- package/src/grid/redirects-grid.ts +4 -4
- package/src/locale/element-names.ts +131 -0
- package/src/panels/dnd.ts +3 -1
- package/src/panels/elements-panel.ts +93 -36
- package/src/panels/imports-panel.ts +3 -2
- package/src/panels/navigator-panels.ts +10 -10
- package/src/panels/panel-registry.ts +3 -3
- package/src/panels/site-panel.ts +2 -1
- package/src/panels/style-panel.ts +2 -1
- package/src/panels/styles-panel.ts +2 -2
- package/src/platforms/cloud.ts +5 -2
- package/src/services/ai-command-tools.ts +2 -1
- package/src/services/ai-project-tools.ts +18 -5
- package/src/services/jxn-language.ts +110 -0
- package/src/services/model-uri.ts +3 -0
- package/src/services/monaco-setup.ts +6 -2
- package/src/settings/contributed-section.ts +2 -1
- package/src/settings/extension-commands.ts +2 -1
- package/src/settings/general-settings.ts +2 -2
- package/src/settings/settings-document.ts +5 -5
- package/src/shell.ts +1 -1
- package/src/studio.ts +28 -11
- package/src/style/variables-view.ts +2 -2
- package/src/surfaces/panel-elements.json +466 -206
- package/src/surfaces/panel-elements.ts +61 -25
- package/src/tabs/project-config.ts +13 -13
- package/src/tabs/tab.ts +7 -2
- package/src/types.ts +11 -2
- package/src/view.ts +9 -2
- package/src/workspace/tab-workspace.ts +2 -2
- package/src/workspace/workspace-back.ts +2 -2
- package/dist/chunks/adapter-12dxr3z7.js +0 -4
- package/dist/chunks/add-new-flow-any4pw2q.js +0 -4
- package/dist/chunks/cf-settings-hbj16kh3.js +0 -4
- package/dist/chunks/color-thzpppyc.js +0 -4
- package/dist/chunks/components-a98hzqw9.js +0 -4
- package/dist/chunks/content-source-jq3fjs3w.js +0 -4
- package/dist/chunks/convert-to-repeater-g66r6qp1.js +0 -4
- package/dist/chunks/editor-rth1rzj8.js +0 -4
- package/dist/chunks/entry-commands-02j0s19w.js +0 -4
- package/dist/chunks/extension-sections-rqrs7dr0.js +0 -4
- package/dist/chunks/file-ops-6a49er4c.js +0 -4
- package/dist/chunks/files-b2p9y0yz.js +0 -4
- package/dist/chunks/format-host-7xe57k1s.js +0 -4
- package/dist/chunks/fs-events-0xsy168g.js +0 -4
- package/dist/chunks/grid-open-h3q4fjy5.js +0 -4
- package/dist/chunks/iframe-entry-m24fza30.js +0 -5
- package/dist/chunks/iframe-host-8jcmaktg.js +0 -4
- package/dist/chunks/index-7nj2375a.js +0 -4
- package/dist/chunks/jx-validate-808zgbgy.js +0 -4
- package/dist/chunks/layers-0ffdtqjn.js +0 -4
- package/dist/chunks/layers-panel-rw62wtvf.js +0 -4
- package/dist/chunks/lit-html-9bz9rcfv.js +0 -4
- package/dist/chunks/media-library-2a5hdbek.js +0 -4
- package/dist/chunks/media-open-069kzhj4.js +0 -4
- package/dist/chunks/media-picker-4p4pd28y.js +0 -4
- package/dist/chunks/monaco-buffer-m9fd2dka.js +0 -4
- package/dist/chunks/monaco-setup-e0crh3v6.js +0 -4
- package/dist/chunks/notify-re3js157.js +0 -4
- package/dist/chunks/parse-hee7ne9b.js +0 -4
- package/dist/chunks/platform-rywy3tkg.js +0 -4
- package/dist/chunks/publish-panel-czkpg8m4.js +0 -4
- package/dist/chunks/publish-quick-b9wbhqj6.js +0 -4
- package/dist/chunks/publish-workspace-yy6d23b7.js +0 -4
- package/dist/chunks/quick-search-tp7pjnmv.js +0 -4
- package/dist/chunks/references-wd2yj8s1.js +0 -4
- package/dist/chunks/right-panel-x971k0zc.js +0 -4
- package/dist/chunks/source-control-k4p00hpe.js +0 -4
- package/dist/chunks/studio-1d8m5s38.js +0 -16
- package/dist/chunks/studio-35pmfjtx.js +0 -5
- package/dist/chunks/studio-44xqe0b7.js +0 -4
- package/dist/chunks/studio-48yzqzqr.js +0 -5
- package/dist/chunks/studio-5rq3e4q5.js +0 -5
- package/dist/chunks/studio-6hcfm9q3.js +0 -5
- package/dist/chunks/studio-6mf31e16.js +0 -5
- package/dist/chunks/studio-8w5pa88p.js +0 -7
- package/dist/chunks/studio-9c6cbq2c.js +0 -4
- package/dist/chunks/studio-aaw7wxv8.js +0 -5
- package/dist/chunks/studio-afx9brsc.js +0 -5
- package/dist/chunks/studio-dmgh2rrj.js +0 -5
- package/dist/chunks/studio-erjccagy.js +0 -5
- package/dist/chunks/studio-ex1644g2.js +0 -5
- package/dist/chunks/studio-gw83z798.js +0 -7
- package/dist/chunks/studio-j52ks8q7.js +0 -5
- package/dist/chunks/studio-kefs4sz4.js +0 -5
- package/dist/chunks/studio-m3jrjfhy.js +0 -14
- package/dist/chunks/studio-n4pnf0sw.js +0 -5
- package/dist/chunks/studio-ncwx602y.js +0 -5
- package/dist/chunks/studio-nzp0sqjg.js +0 -5
- package/dist/chunks/studio-ph17an19.js +0 -5
- package/dist/chunks/studio-q509sqt8.js +0 -5
- package/dist/chunks/studio-s49y6m5b.js +0 -5
- package/dist/chunks/studio-t46rt6gx.js +0 -5
- package/dist/chunks/studio-veh91hjv.js +0 -5
- package/dist/chunks/studio-vswmz40d.js +0 -6
- package/dist/chunks/studio-w4mfaaam.js +0 -5
- package/dist/chunks/studio-x5py815g.js +0 -5
- package/dist/chunks/studio-z6s56dzz.js +0 -5
- package/dist/chunks/studio-ze1sm147.js +0 -7
- package/dist/chunks/studio-zn7ysyzb.js +0 -5
- package/dist/chunks/toast-w11qpkyz.js +0 -4
- package/dist/chunks/workspace-home-61n1jz3q.js +0 -4
- package/dist/chunks/workspace-sheek6zw.js +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/studio",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.6.0",
|
|
4
4
|
"description": "Jx Studio — visual builder for Jx documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "bun run scripts/build.ts",
|
|
49
49
|
"build:metafile": "bun build ./src/studio.ts --outdir dist-metafile --target browser --sourcemap=linked --metafile=metafile.json",
|
|
50
|
-
"gen:webdata": "bun run scripts/gen-webdata.
|
|
50
|
+
"gen:webdata": "bun run scripts/gen-webdata.ts",
|
|
51
51
|
"eval": "bun run evals/cli.ts",
|
|
52
52
|
"eval:headless": "bun run tests/harness/run-eval.ts",
|
|
53
53
|
"lint:styles": "bun run scripts/check-styles.ts",
|
|
@@ -60,16 +60,16 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@atlaskit/pragmatic-drag-and-drop": "^4.0.0",
|
|
62
62
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^3.0.0",
|
|
63
|
-
"@jxsuite/ai": "^0.43.
|
|
64
|
-
"@jxsuite/collab": "^0.11.
|
|
65
|
-
"@jxsuite/create": "^1.5.
|
|
66
|
-
"@jxsuite/formulas": "^0.0.
|
|
67
|
-
"@jxsuite/markup": "^1.0.
|
|
68
|
-
"@jxsuite/protocol": "^6.2.
|
|
69
|
-
"@jxsuite/runtime": "^6.
|
|
70
|
-
"@jxsuite/schema": "^5.
|
|
71
|
-
"@jxsuite/site": "^2.
|
|
72
|
-
"@jxsuite/ui": "^0.10.
|
|
63
|
+
"@jxsuite/ai": "^0.43.1",
|
|
64
|
+
"@jxsuite/collab": "^0.11.7",
|
|
65
|
+
"@jxsuite/create": "^1.5.5",
|
|
66
|
+
"@jxsuite/formulas": "^0.0.29",
|
|
67
|
+
"@jxsuite/markup": "^1.0.5",
|
|
68
|
+
"@jxsuite/protocol": "^6.2.3",
|
|
69
|
+
"@jxsuite/runtime": "^6.2.0",
|
|
70
|
+
"@jxsuite/schema": "^5.3.0",
|
|
71
|
+
"@jxsuite/site": "^2.4.0",
|
|
72
|
+
"@jxsuite/ui": "^0.10.2",
|
|
73
73
|
"@vue/reactivity": "3.5.44",
|
|
74
74
|
"ajv": "^8.20.0",
|
|
75
75
|
"ajv-formats": "^3.0.1",
|
|
@@ -27,6 +27,7 @@ import { isFunction, isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
|
27
27
|
import { previewAssetSrcFor } from "../canvas/asset-refs";
|
|
28
28
|
import { projectState } from "../store";
|
|
29
29
|
import type { JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
|
|
30
|
+
import { PROJECT_FILE_NAME } from "@jxsuite/schema/project-file";
|
|
30
31
|
|
|
31
32
|
/** The preview box, as a project sheet names the page's root and body inside a shadow root. */
|
|
32
33
|
const HOST_ROOTS = { body: ":host", root: ":host" };
|
|
@@ -93,7 +94,7 @@ export function headStylesheetHrefs(head: readonly JxHeadEntry[] | undefined): s
|
|
|
93
94
|
|
|
94
95
|
/** One linked stylesheet as a preview sheet, or null when it cannot be read. */
|
|
95
96
|
async function linkedSheet(href: string): Promise<CSSStyleSheet | null> {
|
|
96
|
-
const url = previewAssetSrcFor(href,
|
|
97
|
+
const url = previewAssetSrcFor(href, PROJECT_FILE_NAME);
|
|
97
98
|
try {
|
|
98
99
|
const response = await fetch(url);
|
|
99
100
|
if (!response.ok) {
|
|
@@ -175,8 +175,13 @@ export function initCanvasRender(ctx: CanvasRenderCtx) {
|
|
|
175
175
|
*/
|
|
176
176
|
function sourceLang(tab: Tab) {
|
|
177
177
|
const format = formatByName(tab.doc.sourceFormat);
|
|
178
|
+
if (!format) {
|
|
179
|
+
return "json";
|
|
180
|
+
}
|
|
178
181
|
// The essence: splitting the declared type would yield `markdown; variant=GFM` as a language id.
|
|
179
|
-
|
|
182
|
+
const fromType = mediaTypeEssence(format.mediaType)?.split("/").pop();
|
|
183
|
+
// `.jxn` has no media type to read a language from, and its path says everything it needs to.
|
|
184
|
+
return fromType ?? monacoLangForPath(tab.documentPath ?? "");
|
|
180
185
|
}
|
|
181
186
|
|
|
182
187
|
/**
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* preview is display-only and never the source of truth (patch-mid-drag safety).
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { parseJxPath } from "./path-mapping";
|
|
19
|
+
import { parseJxPath, serializeJxPath } from "./path-mapping";
|
|
20
20
|
import { displayTagName, isNonNullObject } from "@jxsuite/schema/guards";
|
|
21
21
|
import { rectOf, elementAtPoint } from "../utils/geometry";
|
|
22
22
|
import { getNodeAtPath, isAncestor, pathsEqual } from "../state";
|
|
@@ -161,6 +161,89 @@ export function resolveDropTarget(x: number, y: number, doc: Document): HTMLElem
|
|
|
161
161
|
return nearestPathEl(hit);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* The drop for a cursor over blank canvas: a point under no addressable node and outside a layout's
|
|
166
|
+
* chrome.
|
|
167
|
+
*
|
|
168
|
+
* **The canvas around the content is part of the page** (§5.3). A short page fills the top of a
|
|
169
|
+
* frame whose remainder is the body, or the unstamped layout node its blocks are drawn into, and
|
|
170
|
+
* that blank space is exactly where an author drags a new block. The drop goes to the nearest edge
|
|
171
|
+
* of the page's own top-level blocks, so below the content it lands after the last one. Layout
|
|
172
|
+
* chrome (`data-jx-layout-region`, the site header and footer a page is drawn inside) is not the
|
|
173
|
+
* page, so a drop over it resolves to nothing, as a click there edits nothing.
|
|
174
|
+
*
|
|
175
|
+
* Two shapes reach here. Without a layout the root is an element (`data-jx-path="[]"`) and the root
|
|
176
|
+
* branch of {@link computeDropInstruction} already answers. Inside a layout the page root has no
|
|
177
|
+
* element of its own (its children are drawn into the layout's slot), so its blocks are found by
|
|
178
|
+
* their stamped paths, and each keeps its own path as the target, which a text child of the root
|
|
179
|
+
* would otherwise shift.
|
|
180
|
+
*/
|
|
181
|
+
export function resolveBlankCanvasDrop(
|
|
182
|
+
x: number,
|
|
183
|
+
y: number,
|
|
184
|
+
doc: Document,
|
|
185
|
+
shadowDoc: JxMutableNode,
|
|
186
|
+
src: DragSrcKind,
|
|
187
|
+
): DropPreview | null {
|
|
188
|
+
const hit = elementAtPoint(x, y, doc);
|
|
189
|
+
if (!hit || nearestPathEl(hit) || hit.closest("[data-jx-layout-region]")) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
const root = doc.querySelector<HTMLElement>(`[data-jx-path="${serializeJxPath([])}"]`);
|
|
193
|
+
if (root) {
|
|
194
|
+
return computeDropInstruction(root, y, shadowDoc, src);
|
|
195
|
+
}
|
|
196
|
+
const blocks = [...doc.querySelectorAll<HTMLElement>("[data-jx-path]")].filter(
|
|
197
|
+
(el) => parseJxPath(el.dataset.jxPath ?? "").length === 2,
|
|
198
|
+
);
|
|
199
|
+
const [first, ...rest] = blocks;
|
|
200
|
+
if (!first) {
|
|
201
|
+
return canDrop(src, [])
|
|
202
|
+
? { edge: "inside", instruction: "make-child", referenceRect: rectFor(hit), targetPath: [] }
|
|
203
|
+
: null;
|
|
204
|
+
}
|
|
205
|
+
return nearestBlockEdge(first, rest, y, src);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** How far `cursorY` is from `block`, and whether its bottom edge is the nearer one. */
|
|
209
|
+
function blockEdge(block: HTMLElement, cursorY: number) {
|
|
210
|
+
const rect = rectFor(block);
|
|
211
|
+
const above = Math.abs(cursorY - rect.y);
|
|
212
|
+
const below = Math.abs(cursorY - (rect.y + rect.height));
|
|
213
|
+
return { below: below <= above, block, distance: Math.min(above, below) };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* {@link nearestChildEdge} over blocks that carry their own paths, for a root with no element. The
|
|
218
|
+
* first block is passed apart from the rest because an empty page is the caller's to answer.
|
|
219
|
+
*/
|
|
220
|
+
function nearestBlockEdge(
|
|
221
|
+
first: HTMLElement,
|
|
222
|
+
rest: readonly HTMLElement[],
|
|
223
|
+
cursorY: number,
|
|
224
|
+
src: DragSrcKind,
|
|
225
|
+
): DropPreview | null {
|
|
226
|
+
let best = blockEdge(first, cursorY);
|
|
227
|
+
for (const block of rest) {
|
|
228
|
+
const edge = blockEdge(block, cursorY);
|
|
229
|
+
if (edge.distance < best.distance) {
|
|
230
|
+
best = edge;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const targetPath = parseJxPath(best.block.dataset.jxPath ?? "");
|
|
234
|
+
if (!canDrop(src, targetPath)) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return best.below
|
|
238
|
+
? {
|
|
239
|
+
edge: "bottom",
|
|
240
|
+
instruction: "reorder-below",
|
|
241
|
+
referenceRect: rectFor(best.block),
|
|
242
|
+
targetPath,
|
|
243
|
+
}
|
|
244
|
+
: { edge: "top", instruction: "reorder-above", referenceRect: rectFor(best.block), targetPath };
|
|
245
|
+
}
|
|
246
|
+
|
|
164
247
|
/**
|
|
165
248
|
* Compute the structural drop placement for a cursor over `targetEl`, PURE against iframe-realm
|
|
166
249
|
* rects.
|
|
@@ -24,6 +24,7 @@ import { parseJxPath, serializeJxPath } from "./path-mapping";
|
|
|
24
24
|
import {
|
|
25
25
|
AUTO_SCROLL_STEP,
|
|
26
26
|
computeDropInstruction,
|
|
27
|
+
resolveBlankCanvasDrop,
|
|
27
28
|
rectFor,
|
|
28
29
|
resolveDropTarget,
|
|
29
30
|
scrollDirection,
|
|
@@ -107,8 +108,9 @@ setNpmModuleResolver((specifier) => npmModuleUrl(specifier, location.href));
|
|
|
107
108
|
|
|
108
109
|
/**
|
|
109
110
|
* Resolve the drop placement for a forwarded cursor: point hit-test → nearest `[data-jx-path]` →
|
|
110
|
-
* pure {@link computeDropInstruction}
|
|
111
|
-
*
|
|
111
|
+
* pure {@link computeDropInstruction}, or, over blank canvas, {@link resolveBlankCanvasDrop}. Returns
|
|
112
|
+
* null when the cursor resolves to no droppable target (a layout's chrome). Shared by the
|
|
113
|
+
* `dragMove` (display-only preview) and `drop` (fresh, authoritative) handlers.
|
|
112
114
|
*/
|
|
113
115
|
function previewAt(
|
|
114
116
|
cursor: { x: number; y: number },
|
|
@@ -118,7 +120,7 @@ function previewAt(
|
|
|
118
120
|
) {
|
|
119
121
|
const targetEl = resolveDropTarget(cursor.x, cursor.y, doc);
|
|
120
122
|
if (!targetEl) {
|
|
121
|
-
return
|
|
123
|
+
return resolveBlankCanvasDrop(cursor.x, cursor.y, doc, shadowDoc, src);
|
|
122
124
|
}
|
|
123
125
|
return computeDropInstruction(targetEl, cursor.y, shadowDoc, src);
|
|
124
126
|
}
|
|
@@ -703,10 +705,11 @@ export function startCanvasIframe(opts: {
|
|
|
703
705
|
const doc = container.ownerDocument;
|
|
704
706
|
const targetEl = resolveDropTarget(cursor.x, cursor.y, doc);
|
|
705
707
|
const hit = targetEl ? fileDropHitFor(targetEl, shadowDoc) : null;
|
|
706
|
-
const preview =
|
|
707
|
-
|
|
708
|
+
const preview = shadowDoc
|
|
709
|
+
? targetEl
|
|
708
710
|
? computeDropInstruction(targetEl, cursor.y, shadowDoc, FILE_DRAG_SRC)
|
|
709
|
-
:
|
|
711
|
+
: resolveBlankCanvasDrop(cursor.x, cursor.y, doc, shadowDoc, FILE_DRAG_SRC)
|
|
712
|
+
: null;
|
|
710
713
|
if (kind === "fileDragOver") {
|
|
711
714
|
if (e.dataTransfer) {
|
|
712
715
|
e.dataTransfer.dropEffect = "copy";
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
setCanvasDelinkCommands,
|
|
25
25
|
setCanvasDelinkPopovers,
|
|
26
26
|
setCanvasViewportTranspose,
|
|
27
|
+
setDocumentParser,
|
|
27
28
|
setRootMedia,
|
|
28
29
|
setSkipAutoRequests,
|
|
29
30
|
setSkipServerFunctions,
|
|
@@ -43,6 +44,17 @@ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
|
43
44
|
import type { JxDocument, JxElement, JxStyle } from "@jxsuite/schema/types";
|
|
44
45
|
import type { PathMapCtx } from "./path-mapping";
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Read a `.jxn` `$ref`, which the editing server answers as its text.
|
|
49
|
+
*
|
|
50
|
+
* Loaded when one turns up, because the parser is a third of this entry's size budget and most
|
|
51
|
+
* canvases never see a `.jxn` reference.
|
|
52
|
+
*/
|
|
53
|
+
async function parseDocumentText(text: string, source: string): Promise<JxDocument> {
|
|
54
|
+
const { parseJxn } = await import("@jxsuite/schema/jxn");
|
|
55
|
+
return parseJxn(text, source);
|
|
56
|
+
}
|
|
57
|
+
|
|
46
58
|
/**
|
|
47
59
|
* The retained render context a full render leaves behind so the surgical patcher can re-render an
|
|
48
60
|
* individual subtree (insert/replace/attr edits) with the SAME scope, doc base, path mapping, and
|
|
@@ -1070,6 +1082,7 @@ export async function renderResolvedDocument(opts: {
|
|
|
1070
1082
|
const assets = opts.assets ?? null;
|
|
1071
1083
|
setCanvasAssetResolver(assets ? (value) => resolveAssetRef(value, assets) : null);
|
|
1072
1084
|
setSkipServerFunctions(opts.mode !== "preview");
|
|
1085
|
+
setDocumentParser(parseDocumentText);
|
|
1073
1086
|
// Same gate for automatic `$prototype: "Request"` state entries. `buildScope` re-resolves every
|
|
1074
1087
|
// State entry on each full render, so without this an escalating authoring action (a signals-panel
|
|
1075
1088
|
// Edit, or Enter inside component-wrapped content) issued an HTTP request per render. Edit/design
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
setTransactObserver,
|
|
34
34
|
transactDoc,
|
|
35
35
|
} from "../tabs/transact";
|
|
36
|
-
import {
|
|
36
|
+
import { isProjectConfigPath } from "../tabs/tab";
|
|
37
37
|
import type { TransactOrigin } from "../tabs/transact";
|
|
38
38
|
import type { TransactionRecord } from "../tabs/patch-ops";
|
|
39
39
|
import type { Tab } from "../tabs/tab";
|
|
@@ -672,7 +672,7 @@ const liveTabs = new Set<Tab>();
|
|
|
672
672
|
*/
|
|
673
673
|
export function ensureCollab(tab: Tab): void {
|
|
674
674
|
const platform = maybePlatform();
|
|
675
|
-
if (!platform?.collab || !tab.documentPath || tab.documentPath
|
|
675
|
+
if (!platform?.collab || !tab.documentPath || isProjectConfigPath(tab.documentPath)) {
|
|
676
676
|
return;
|
|
677
677
|
}
|
|
678
678
|
const runtime = runtimeFor(tab);
|
package/src/commands/levels.ts
CHANGED
|
@@ -180,7 +180,7 @@ export const PANEL_PLACEMENT_MATRIX: Readonly<Record<PanelPlacement, PlacementRu
|
|
|
180
180
|
},
|
|
181
181
|
"rail/document": {
|
|
182
182
|
admits: ["document"],
|
|
183
|
-
note: "the rail's lower group — Outline, Data, and Packages with the developer panels on",
|
|
183
|
+
note: "the rail's lower group — Insert, Outline, Data, and Packages with the developer panels on",
|
|
184
184
|
},
|
|
185
185
|
navigator: {
|
|
186
186
|
admits: ["project", "document"],
|
|
@@ -101,6 +101,7 @@ import type {
|
|
|
101
101
|
AddNewView,
|
|
102
102
|
} from "../surfaces/add-new";
|
|
103
103
|
import type { JsonObject, JxMutableNode } from "@jxsuite/schema/types";
|
|
104
|
+
import { projectConfigPath } from "../tabs/tab";
|
|
104
105
|
|
|
105
106
|
/** The `source` every Problem this flow files carries. */
|
|
106
107
|
const SOURCE = "Add new";
|
|
@@ -385,7 +386,7 @@ async function performCollection(creation: Creation): Promise<AddNewResult | nul
|
|
|
385
386
|
if (!committed.ok) {
|
|
386
387
|
return null;
|
|
387
388
|
}
|
|
388
|
-
const wrote = [
|
|
389
|
+
const wrote = [projectConfigPath()];
|
|
389
390
|
const missed: string[] = [];
|
|
390
391
|
for (const write of [{ body: creation.content, path: creation.path }, ...creation.extraWrites]) {
|
|
391
392
|
try {
|
|
@@ -1,32 +1,11 @@
|
|
|
1
1
|
import elementsMeta from "../../data/elements-meta.json";
|
|
2
2
|
|
|
3
|
+
import { elementName } from "../locale/element-names";
|
|
3
4
|
import { ELEMENT_DEFS } from "./editable-tags";
|
|
4
5
|
import type { SlashCommand } from "./inline-edit";
|
|
5
6
|
|
|
6
7
|
export type { SlashCommand } from "./inline-edit";
|
|
7
8
|
|
|
8
|
-
const TAG_LABELS = new Map<string, string>([
|
|
9
|
-
["article", "Article"],
|
|
10
|
-
["aside", "Aside"],
|
|
11
|
-
["blockquote", "Blockquote"],
|
|
12
|
-
["div", "Div"],
|
|
13
|
-
["footer", "Footer"],
|
|
14
|
-
["h1", "Heading 1"],
|
|
15
|
-
["h2", "Heading 2"],
|
|
16
|
-
["h3", "Heading 3"],
|
|
17
|
-
["h4", "Heading 4"],
|
|
18
|
-
["h5", "Heading 5"],
|
|
19
|
-
["h6", "Heading 6"],
|
|
20
|
-
["header", "Header"],
|
|
21
|
-
["main", "Main"],
|
|
22
|
-
["nav", "Nav"],
|
|
23
|
-
["ol", "Numbered List"],
|
|
24
|
-
["p", "Paragraph"],
|
|
25
|
-
["search", "Search"],
|
|
26
|
-
["section", "Section"],
|
|
27
|
-
["ul", "Bulleted List"],
|
|
28
|
-
]);
|
|
29
|
-
|
|
30
9
|
const groups: Readonly<Record<string, string[]>> = elementsMeta.$convertGroups;
|
|
31
10
|
|
|
32
11
|
/**
|
|
@@ -56,7 +35,7 @@ export function getConvertTargets(currentTag: string, isEmpty: boolean): SlashCo
|
|
|
56
35
|
|
|
57
36
|
return [...tags].map((tag) => ({
|
|
58
37
|
description: "",
|
|
59
|
-
label:
|
|
38
|
+
label: elementName(tag),
|
|
60
39
|
tag,
|
|
61
40
|
}));
|
|
62
41
|
}
|
package/src/editor/slash-menu.ts
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import { openSlashMenuSurface } from "../surfaces/slash-menu";
|
|
30
30
|
import { componentSlashCommands } from "./insert-component";
|
|
31
|
+
import { elementName } from "../locale/element-names";
|
|
31
32
|
import { rectOf } from "../utils/geometry";
|
|
32
33
|
import type {
|
|
33
34
|
SlashMenuGroup,
|
|
@@ -39,24 +40,41 @@ import type { SlashCommand } from "./inline-edit";
|
|
|
39
40
|
|
|
40
41
|
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
43
|
+
/**
|
|
44
|
+
* The HTML elements the slash menu offers, in this order. Each row's label is the element's name in
|
|
45
|
+
* the locale catalog (`locale/element-names.ts`), so the menu, the Insert palette and the convert
|
|
46
|
+
* menu call an element the same thing in every language. A row carries no description: the
|
|
47
|
+
* catalog's are sentences, written for the palette's tooltips, and a menu row draws its description
|
|
48
|
+
* on the label's own line and cuts it off there, while a name like "Bulleted list" already says
|
|
49
|
+
* what the row inserts.
|
|
50
|
+
*/
|
|
51
|
+
const SLASH_TAGS = [
|
|
52
|
+
"h1",
|
|
53
|
+
"h2",
|
|
54
|
+
"h3",
|
|
55
|
+
"p",
|
|
56
|
+
"ul",
|
|
57
|
+
"ol",
|
|
58
|
+
"blockquote",
|
|
59
|
+
"img",
|
|
60
|
+
"hr",
|
|
61
|
+
"button",
|
|
62
|
+
"a",
|
|
63
|
+
"pre",
|
|
64
|
+
"table",
|
|
65
|
+
"div",
|
|
66
|
+
"section",
|
|
58
67
|
];
|
|
59
68
|
|
|
69
|
+
/** The element rows, named in the reader's language at the moment the menu opens. */
|
|
70
|
+
function elementCommands(): SlashCommand[] {
|
|
71
|
+
return SLASH_TAGS.map((tag) => ({
|
|
72
|
+
description: "",
|
|
73
|
+
label: elementName(tag),
|
|
74
|
+
tag,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
60
78
|
// ─── State ────────────────────────────────────────────────────────────────────
|
|
61
79
|
|
|
62
80
|
/** Callbacks a caller passes to {@link showSlashMenu}/{@link showSlashMenuAtRect}. */
|
|
@@ -127,7 +145,7 @@ export function showSlashMenuAtRect(
|
|
|
127
145
|
* on offer.
|
|
128
146
|
*/
|
|
129
147
|
function standardCommands(): SlashCommand[] {
|
|
130
|
-
return [...
|
|
148
|
+
return [...elementCommands(), ...componentSlashCommands()];
|
|
131
149
|
}
|
|
132
150
|
|
|
133
151
|
/** Everything on offer right now: what the caller named, or the standard set. */
|
package/src/files/files.ts
CHANGED
|
@@ -286,6 +286,24 @@ export async function loadProject() {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Show the documents the operating system asks of this window, now and from here on.
|
|
291
|
+
*
|
|
292
|
+
* Called once the window has finished loading whatever it holds, so a document opens on top of the
|
|
293
|
+
* restored session rather than being replaced by it. Called whether or not there is a project yet,
|
|
294
|
+
* because a welcome window can be given one later and then be asked for a document in it.
|
|
295
|
+
*/
|
|
296
|
+
export function followOpenDocuments(): void {
|
|
297
|
+
getPlatform().subscribeOpenDocument?.((path) => {
|
|
298
|
+
// A window whose project did not load has been told why, and has nowhere to put a tab.
|
|
299
|
+
if (!projectState) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
// It reports its own failure ("Could not open …"), as it does for every other caller.
|
|
303
|
+
void openFileInTab(path);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
289
307
|
// ─── Open Project (PAL-based) ─────────────────────────────────────────────
|
|
290
308
|
|
|
291
309
|
/**
|
|
@@ -69,11 +69,11 @@ function declaresDocumentKind(kinds: readonly string[]): boolean {
|
|
|
69
69
|
/**
|
|
70
70
|
* Whether this path is (or is used as) a layout.
|
|
71
71
|
*
|
|
72
|
-
*
|
|
73
|
-
* and throws, `packages/studio/src/site-context.ts` `JSON.parse`s and silently
|
|
74
|
-
* there is no `"layout"` document kind for a format to declare, so this clause
|
|
75
|
-
* cannot be derived. `layouts/` is excluded by {@link DOCUMENT_ROOTS} already;
|
|
76
|
-
* project whose `layout` points somewhere inside a document root.
|
|
72
|
+
* A layout is native at both readers — `packages/compiler/src/site/layout-resolver.ts` reads
|
|
73
|
+
* `.json` and `.jxn` and throws, `packages/studio/src/site-context.ts` `JSON.parse`s and silently
|
|
74
|
+
* returns null — and there is no `"layout"` document kind for a format to declare, so this clause
|
|
75
|
+
* is hand-written and cannot be derived. `layouts/` is excluded by {@link DOCUMENT_ROOTS} already;
|
|
76
|
+
* what remains is a project whose `layout` points somewhere inside a document root.
|
|
77
77
|
*/
|
|
78
78
|
function isLayoutPath(path: string): boolean {
|
|
79
79
|
const declared = projectState?.projectConfig?.layout;
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Format classes are auto-discovered server-side from the project imports map
|
|
5
5
|
* (specs/extensions.md); the studio introspects them via the PAL (`listFormats`) and invokes
|
|
6
6
|
* parse/serialize capabilities via `formatAction` (POST /__studio/format in the dev server, RPC on
|
|
7
|
-
* desktop). The studio
|
|
7
|
+
* desktop). The studio holds no other format knowledge: `.json` is native, and `.jxn` is read and
|
|
8
|
+
* written here in the same process with the same code as everywhere else (`spec.md` §22), so a host
|
|
9
|
+
* with no extension runtime has it too.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import { setProjectSchemasForMonaco } from "../services/monaco-lazy";
|
|
@@ -17,7 +19,8 @@ import type {
|
|
|
17
19
|
StudioPlatform,
|
|
18
20
|
} from "../types";
|
|
19
21
|
import { isString } from "@jxsuite/schema/guards";
|
|
20
|
-
import
|
|
22
|
+
import { parseJxn, serializeJxn } from "@jxsuite/schema/jxn";
|
|
23
|
+
import type { JxDocument, JxDocumentValue, JxMutableNode } from "@jxsuite/schema/types";
|
|
21
24
|
|
|
22
25
|
export interface StudioFormatHints {
|
|
23
26
|
icon?: string;
|
|
@@ -59,6 +62,24 @@ export interface StudioFormat {
|
|
|
59
62
|
capabilities: Record<string, { identifier: string; timing: string[] }>;
|
|
60
63
|
}
|
|
61
64
|
|
|
65
|
+
/**
|
|
66
|
+
* What opening a `.jxn` file is told about its format. It is not in {@link getFormats}, which is
|
|
67
|
+
* what a project chose and what a picker offers: a new document is still `.json`.
|
|
68
|
+
*/
|
|
69
|
+
const JXN_FORMAT: StudioFormat = {
|
|
70
|
+
capabilities: {
|
|
71
|
+
parse: { identifier: "parse", timing: ["compiler", "server"] },
|
|
72
|
+
serialize: { identifier: "serialize", timing: ["compiler", "server"] },
|
|
73
|
+
},
|
|
74
|
+
documentKinds: ["page", "component"],
|
|
75
|
+
exportTarget: false,
|
|
76
|
+
extensions: [".jxn"],
|
|
77
|
+
mediaType: null,
|
|
78
|
+
name: "jxn",
|
|
79
|
+
remote: false,
|
|
80
|
+
studio: { documentMode: { default: "component" } },
|
|
81
|
+
};
|
|
82
|
+
|
|
62
83
|
let _formats: StudioFormat[] = [];
|
|
63
84
|
let _loaded: Promise<StudioFormat[]> | null = null;
|
|
64
85
|
|
|
@@ -235,6 +256,9 @@ export function getFormats(): StudioFormat[] {
|
|
|
235
256
|
/** Look up the format claiming a file extension (".md" or "md"), optionally by capability. */
|
|
236
257
|
export function formatByExtension(ext: string, capability?: string): StudioFormat | undefined {
|
|
237
258
|
const norm = ext.startsWith(".") ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
|
|
259
|
+
if (norm === ".jxn") {
|
|
260
|
+
return JXN_FORMAT;
|
|
261
|
+
}
|
|
238
262
|
return _formats.find(
|
|
239
263
|
(f) => f.extensions.includes(norm) && (!capability || f.capabilities[capability]),
|
|
240
264
|
);
|
|
@@ -245,7 +269,7 @@ export function formatByName(name: string | null | undefined): StudioFormat | un
|
|
|
245
269
|
if (!name) {
|
|
246
270
|
return undefined;
|
|
247
271
|
}
|
|
248
|
-
return _formats.find((f) => f.name === name);
|
|
272
|
+
return name === JXN_FORMAT.name ? JXN_FORMAT : _formats.find((f) => f.name === name);
|
|
249
273
|
}
|
|
250
274
|
|
|
251
275
|
/** Error for opening a non-JSON file when no imported format class claims its extension. */
|
|
@@ -330,6 +354,9 @@ export async function formatParse(
|
|
|
330
354
|
source: string,
|
|
331
355
|
options?: FormatActionOptions,
|
|
332
356
|
): Promise<JxMutableNode> {
|
|
357
|
+
if (name === JXN_FORMAT.name) {
|
|
358
|
+
return parseJxn(source);
|
|
359
|
+
}
|
|
333
360
|
/* SAFETY: a format class's `parse` capability returns the Jx document it built from the source —
|
|
334
361
|
The contract every format class implements (specs/extensions.md) — and nothing here inspects
|
|
335
362
|
It before `splitFormatDocument` reads its keys. */
|
|
@@ -352,6 +379,11 @@ export async function formatSerialize(
|
|
|
352
379
|
doc: JxMutableNode,
|
|
353
380
|
options?: FormatActionOptions,
|
|
354
381
|
): Promise<string> {
|
|
382
|
+
if (name === JXN_FORMAT.name) {
|
|
383
|
+
// `source` is what keeps the comments: everything that did not change keeps its exact text.
|
|
384
|
+
// SAFETY: a document in its editing form, and every member of a JxDocument is optional.
|
|
385
|
+
return serializeJxn(doc as JxDocument, options?.source);
|
|
386
|
+
}
|
|
355
387
|
/* SAFETY: a format class's `serialize` capability returns the source text it wrote — the contract
|
|
356
388
|
Every format class implements (specs/extensions.md) — so the answer is a string. */
|
|
357
389
|
return (await formatAction({
|
|
@@ -29,7 +29,7 @@ import { projectState } from "../store";
|
|
|
29
29
|
import { clearProblems, notify, problemFindings } from "../services/notify";
|
|
30
30
|
import { showPromptDialog } from "../ui/layers";
|
|
31
31
|
import { activateTab, openTab, workspace } from "../workspace/workspace";
|
|
32
|
-
import {
|
|
32
|
+
import { projectConfigPath, commitProjectConfig } from "../tabs/project-config";
|
|
33
33
|
import { pageRoute } from "../panels/tab-strip";
|
|
34
34
|
import { createGridController, getGridController } from "./grid-controller";
|
|
35
35
|
import {
|
|
@@ -230,7 +230,7 @@ export function reportRedirectProblems(
|
|
|
230
230
|
action: "redirects.open",
|
|
231
231
|
detail: problem.detail,
|
|
232
232
|
key: `redirects.${problem.rule}:${problem.source}`,
|
|
233
|
-
path:
|
|
233
|
+
path: projectConfigPath(),
|
|
234
234
|
source: REDIRECTS_PROBLEM_SOURCE,
|
|
235
235
|
tier: "problem",
|
|
236
236
|
});
|
|
@@ -263,7 +263,7 @@ export function createRedirectsSource(): GridSource {
|
|
|
263
263
|
const load = () => rulesFromConfig(currentConfig());
|
|
264
264
|
|
|
265
265
|
return {
|
|
266
|
-
backingPaths: () => new Map([[
|
|
266
|
+
backingPaths: () => new Map([[projectConfigPath(), "*"]]),
|
|
267
267
|
capabilities: { delete: true, insert: true, remotePaging: false, remoteSort: false },
|
|
268
268
|
columns: () => Promise.resolve(redirectColumns()),
|
|
269
269
|
|
|
@@ -476,7 +476,7 @@ export function stageRedirectImport(
|
|
|
476
476
|
action: "redirects.import",
|
|
477
477
|
detail: notes.join("\n"),
|
|
478
478
|
key: "redirects.import",
|
|
479
|
-
path:
|
|
479
|
+
path: projectConfigPath(),
|
|
480
480
|
source: REDIRECTS_PROBLEM_SOURCE,
|
|
481
481
|
tier: "problem",
|
|
482
482
|
},
|