@jxsuite/studio 6.1.0 → 6.1.2
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/dist/chunks/2020-f2gkpy8d.js +4 -0
- package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
- package/dist/chunks/adapter-4912g339.js +4 -0
- package/dist/chunks/cf-account-picker-wccaenh6.js +4 -0
- package/dist/chunks/cf-settings-br6105tr.js +4 -0
- package/dist/chunks/color-28ksk2qm.js +4 -0
- package/dist/chunks/color-area-0fj8a4ga.js +4 -0
- package/dist/chunks/color-field-xch8kx2g.js +4 -0
- package/dist/chunks/color-slider-jxd8v350.js +4 -0
- package/dist/chunks/combobox-p0efwjke.js +4 -0
- package/dist/chunks/components-db68vsn7.js +4 -0
- package/dist/chunks/content-source-1449qesq.js +4 -0
- package/dist/chunks/convert-to-repeater-5h457egj.js +4 -0
- package/dist/chunks/dialog-c4x05vyx.js +4 -0
- package/dist/chunks/editor-x9jn86a7.js +4 -0
- package/dist/chunks/entry-commands-may24cpt.js +4 -0
- package/dist/chunks/extension-sections-pvr14vxr.js +4 -0
- package/dist/chunks/file-ops-x9v54pcj.js +4 -0
- package/dist/chunks/files-j3knqf0s.js +4 -0
- package/dist/chunks/format-host-tyre2trb.js +4 -0
- package/dist/chunks/fs-events-64ma9htc.js +4 -0
- package/dist/chunks/git-panel-84ps5dex.js +4 -0
- package/dist/chunks/grid-view-d2wq9ezg.js +51 -0
- package/dist/chunks/icons-pnfz478q.js +4 -0
- package/dist/chunks/iframe-entry-fvbk89gm.js +5 -0
- package/dist/chunks/iframe-entry-jdcktez8.js +5 -0
- package/dist/chunks/iframe-entry-x0ygf2yr.js +5 -0
- package/dist/chunks/iframe-entry-zap785cw.js +5 -0
- package/dist/chunks/iframe-host-ck6vejt5.js +4 -0
- package/dist/chunks/index-b8xw3vy9.js +4 -0
- package/dist/chunks/{index-gybvv7a6.js → index-zpjmzqfk.js} +3 -3
- package/dist/chunks/{javascript-rnvdp3k4.js → javascript-m54mnwp6.js} +3 -3
- package/dist/chunks/jsonMode-w15cmrcm.js +13 -0
- package/dist/chunks/jx-validate-at3zn8wa.js +4 -0
- package/dist/chunks/layers-h4xp1pss.js +4 -0
- package/dist/chunks/layers-panel-m5hw1d7g.js +4 -0
- package/dist/chunks/listbox-e74qjyq6.js +4 -0
- package/dist/chunks/lit-html-8t8t3dnr.js +4 -0
- package/dist/chunks/media-picker-2cweexc5.js +4 -0
- package/dist/chunks/menu-n6yzswmy.js +4 -0
- package/dist/chunks/menu-ynw13y0s.js +4 -0
- package/dist/chunks/monaco-binding-7gx83x3r.js +4 -0
- package/dist/chunks/monaco-buffer-qzxn567r.js +4 -0
- package/dist/chunks/monaco-setup-gxf6656g.js +4 -0
- package/dist/chunks/notify-cj5zbrs2.js +4 -0
- package/dist/chunks/number-field-y62xq0ty.js +4 -0
- package/dist/chunks/parse-b7ddhr8m.js +4 -0
- package/dist/chunks/platform-3dfws9js.js +4 -0
- package/dist/chunks/popover-jt2gxmzs.js +4 -0
- package/dist/chunks/publish-panel-1gvxbj21.js +4 -0
- package/dist/chunks/quick-search-0dajrwqg.js +4 -0
- package/dist/chunks/references-49qgt404.js +4 -0
- package/dist/chunks/right-panel-jm1bwe53.js +4 -0
- package/dist/chunks/section-registry-6kwc4fky.js +4 -0
- package/dist/chunks/select-rzds841e.js +4 -0
- package/dist/chunks/split-n2gfpm5m.js +4 -0
- package/dist/chunks/{studio-5g0ag7c0.js → studio-0q988f9w.js} +3 -3
- package/dist/chunks/studio-0rt52vpq.js +5 -0
- package/dist/chunks/studio-18vgbhs5.js +5 -0
- package/dist/chunks/{studio-pxg4ap1e.js → studio-1fxkaaea.js} +4 -4
- package/dist/chunks/studio-24mccdrj.js +5 -0
- package/dist/chunks/studio-2eh507zk.js +9 -0
- package/dist/chunks/studio-2hcbccfv.js +5 -0
- package/dist/chunks/studio-2j1cb95x.js +5 -0
- package/dist/chunks/studio-2jbs3rxv.js +5 -0
- package/dist/chunks/{studio-qbj760tx.js → studio-485046te.js} +7 -7
- package/dist/chunks/studio-4h8k4dw9.js +5 -0
- package/dist/chunks/studio-5f6n18kx.js +5 -0
- package/dist/chunks/studio-5gthpky6.js +5 -0
- package/dist/chunks/studio-5ve6bbep.js +5 -0
- package/dist/chunks/studio-6ep3js6b.js +9 -0
- package/dist/chunks/studio-6wkmf3wz.js +9 -0
- package/dist/chunks/studio-841rf3cg.js +5 -0
- package/dist/chunks/studio-8gmwgheg.js +5 -0
- package/dist/chunks/studio-8qm0ep50.js +5 -0
- package/dist/chunks/{studio-anyrgzvb.js → studio-90pq9km1.js} +4 -4
- package/dist/chunks/studio-924p1tm1.js +6 -0
- package/dist/chunks/{studio-v2bvmbjj.js → studio-a3g98wjr.js} +4 -4
- package/dist/chunks/studio-a3shpz9y.js +5 -0
- package/dist/chunks/studio-amsbaaar.js +6 -0
- package/dist/chunks/studio-at5spjbc.js +7 -0
- package/dist/chunks/studio-b2xv945e.js +5 -0
- package/dist/chunks/studio-bn5nkafw.js +5 -0
- package/dist/chunks/studio-bw6q5xjf.js +9 -0
- package/dist/chunks/studio-c7q0t9x1.js +5 -0
- package/dist/chunks/studio-camnhc96.js +6 -0
- package/dist/chunks/studio-cffqvnzd.js +5 -0
- package/dist/chunks/studio-d81xfh1g.js +5 -0
- package/dist/chunks/studio-dgz8d0c1.js +5 -0
- package/dist/chunks/studio-efvfrsf8.js +5 -0
- package/dist/chunks/studio-er30bpcx.js +5 -0
- package/dist/chunks/studio-ezscgr64.js +5 -0
- package/dist/chunks/studio-ffrrncvz.js +5 -0
- package/dist/chunks/studio-fpphgn8y.js +10 -0
- package/dist/chunks/studio-j1kvhdjk.js +4 -0
- package/dist/chunks/{studio-txjjzrsm.js → studio-j430n16g.js} +5 -5
- package/dist/chunks/studio-jjkg84nj.js +5 -0
- package/dist/chunks/studio-m0q32ars.js +4 -0
- package/dist/chunks/studio-m6bdst11.js +5 -0
- package/dist/chunks/studio-nv2cptd4.js +64 -0
- package/dist/chunks/studio-nwfrc98n.js +5 -0
- package/dist/chunks/studio-p1wgvp7r.js +5 -0
- package/dist/chunks/studio-pag2wncm.js +5 -0
- package/dist/chunks/studio-qvanvzfd.js +5 -0
- package/dist/chunks/studio-qwa64kt7.js +5 -0
- package/dist/chunks/studio-rk1zc4x3.js +928 -0
- package/dist/chunks/studio-se2r3ptc.js +5 -0
- package/dist/chunks/studio-sg2a0dh0.js +5 -0
- package/dist/chunks/studio-tjzjhe68.js +5 -0
- package/dist/chunks/{studio-0x3jyb8r.js → studio-tq0rtfwj.js} +4 -4
- package/dist/chunks/{studio-cpxkgjrj.js → studio-v0jzqdjm.js} +4 -4
- package/dist/chunks/studio-v2w7q8r1.js +7 -0
- package/dist/chunks/studio-v5vjq1h0.js +4 -0
- package/dist/chunks/studio-v8aga3vp.js +14 -0
- package/dist/chunks/studio-vfcdrfmv.js +5 -0
- package/dist/chunks/studio-vmfkgdjf.js +5 -0
- package/dist/chunks/studio-wt1816jj.js +5 -0
- package/dist/chunks/studio-wz73q5kq.js +4 -0
- package/dist/chunks/studio-y04qtxvp.js +7 -0
- package/dist/chunks/studio-y21g8zzg.js +5 -0
- package/dist/chunks/studio-y64hrt8p.js +12 -0
- package/dist/chunks/studio-ynn90f58.js +16 -0
- package/dist/chunks/studio-yxz682sx.js +5 -0
- package/dist/chunks/studio-zp7snvkq.js +5 -0
- package/dist/chunks/studio-zq4z6tm6.js +5 -0
- package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
- package/dist/chunks/tabs-f5cw3tgp.js +4 -0
- package/dist/chunks/textfield-wewc8rq2.js +4 -0
- package/dist/chunks/toast-c9fn075p.js +4 -0
- package/dist/chunks/toast-host-ke5z4ng7.js +4 -0
- package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
- package/dist/chunks/tooltip-5v8ngwj2.js +4 -0
- package/dist/chunks/tree-mdq3v5cw.js +4 -0
- package/dist/chunks/tsMode-sf49ee9x.js +14 -0
- package/dist/chunks/workspace-jtfahpee.js +4 -0
- package/dist/chunks/ws-client-nze0k6mv.js +4 -0
- package/dist/iframe-entry.js +16 -16
- package/dist/manifest.json +63 -63
- package/dist/studio.js +98 -98
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +11 -11
- package/src/about/about-modal.ts +4 -3
- package/src/account/action-flow.ts +45 -38
- package/src/account-status.ts +7 -2
- package/src/browse/library-layouts.ts +8 -5
- package/src/browse/library-model.ts +18 -6
- package/src/browse/library-pane.ts +37 -21
- package/src/browse/library-preview.ts +14 -9
- package/src/browse/library-source.ts +1 -1
- package/src/canvas/asset-refs.ts +24 -9
- package/src/canvas/asset-resolve.ts +19 -23
- package/src/canvas/canvas-helpers.ts +7 -6
- package/src/canvas/canvas-live-render.ts +44 -53
- package/src/canvas/canvas-patcher.ts +30 -30
- package/src/canvas/canvas-perf.ts +11 -5
- package/src/canvas/canvas-render.ts +66 -42
- package/src/canvas/canvas-surface.ts +5 -3
- package/src/canvas/canvas-utils.ts +163 -64
- package/src/canvas/dialog-path.ts +9 -4
- package/src/canvas/dialog-state.ts +1 -2
- package/src/canvas/diff-marks.ts +9 -16
- package/src/canvas/diff-toolbar.ts +8 -7
- package/src/canvas/edit-width-drag.ts +4 -1
- package/src/canvas/edit-width.ts +1 -3
- package/src/canvas/iframe-channel.ts +27 -13
- package/src/canvas/iframe-drop.ts +13 -12
- package/src/canvas/iframe-editable-root.ts +29 -16
- package/src/canvas/iframe-entry.ts +80 -63
- package/src/canvas/iframe-eval.ts +73 -49
- package/src/canvas/iframe-host.ts +203 -118
- package/src/canvas/iframe-inline-edit.ts +69 -32
- package/src/canvas/iframe-insert.ts +5 -4
- package/src/canvas/iframe-interaction.ts +37 -26
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-patch.ts +18 -15
- package/src/canvas/iframe-position.ts +12 -6
- package/src/canvas/iframe-protocol.ts +15 -16
- package/src/canvas/iframe-render.ts +41 -36
- package/src/canvas/iframe-slash.ts +24 -11
- package/src/canvas/iframe-subtree.ts +6 -2
- package/src/canvas/path-mapping.ts +19 -12
- package/src/canvas/popover-path.ts +7 -3
- package/src/canvas/popover-state.ts +1 -2
- package/src/canvas/serialize-scope.ts +13 -5
- package/src/canvas/stage-scrollbar.ts +2 -1
- package/src/canvas/surface-registry.ts +21 -3
- package/src/collab/collab-commands.ts +7 -2
- package/src/collab/collab-session.ts +84 -45
- package/src/collab/collab-state.ts +4 -3
- package/src/collab/monaco-binding.ts +16 -13
- package/src/collab/monaco-cursors.ts +5 -2
- package/src/commands/budget.ts +3 -3
- package/src/commands/command-args.ts +103 -71
- package/src/commands/context.ts +22 -20
- package/src/commands/defaults.ts +4 -1
- package/src/commands/keymap.ts +47 -43
- package/src/commands/levels.ts +3 -3
- package/src/commands/live-context.ts +10 -8
- package/src/commands/reference.ts +2 -1
- package/src/commands/registry.ts +12 -10
- package/src/commands/run-reported.ts +15 -10
- package/src/component-props.ts +46 -30
- package/src/content/collection-match.ts +5 -4
- package/src/content/draft-state.ts +4 -3
- package/src/content/entry-commands.ts +3 -2
- package/src/content/entry-editor.ts +29 -17
- package/src/content/entry-fields.ts +12 -7
- package/src/content/entry-model.ts +21 -12
- package/src/editor/canvas-context-menu.ts +1 -2
- package/src/editor/canvas-slash-bridge.ts +3 -6
- package/src/editor/content-slice.ts +16 -14
- package/src/editor/context-menu.ts +35 -22
- package/src/editor/convert-targets.ts +30 -35
- package/src/editor/convert-to-component.ts +11 -5
- package/src/editor/convert-to-repeater.ts +34 -22
- package/src/editor/editable-tags.ts +17 -2
- package/src/editor/file-drop-action.ts +21 -22
- package/src/editor/inline-edit-apply.ts +35 -17
- package/src/editor/inline-edit.ts +66 -50
- package/src/editor/inline-format.ts +59 -56
- package/src/editor/inline-link.ts +40 -15
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/merge-tags.ts +58 -19
- package/src/editor/repeater-scope.ts +39 -28
- package/src/editor/shortcuts.ts +87 -47
- package/src/editor/slash-menu.ts +1 -6
- package/src/files/asset-versions.ts +4 -4
- package/src/files/components.ts +15 -10
- package/src/files/elements.ts +5 -6
- package/src/files/file-ops.ts +77 -36
- package/src/files/files.ts +90 -95
- package/src/files/media-meta.ts +2 -1
- package/src/files/media-paths.ts +6 -1
- package/src/files/serialize-document.ts +2 -2
- package/src/format/constraints.ts +1 -1
- package/src/format/convert-file.ts +27 -13
- package/src/format/format-choices.ts +2 -1
- package/src/format/format-host.ts +76 -50
- package/src/github/github-auth-status.ts +27 -3
- package/src/github/github-auth.ts +19 -9
- package/src/github/github-publish.ts +24 -20
- package/src/grid/cell-editors.ts +46 -31
- package/src/grid/cell-popovers.ts +5 -3
- package/src/grid/edit-buffer.ts +10 -9
- package/src/grid/grid-controller.ts +20 -22
- package/src/grid/grid-layout.ts +21 -5
- package/src/grid/grid-open.ts +3 -6
- package/src/grid/grid-panel.ts +11 -4
- package/src/grid/grid-source.ts +8 -8
- package/src/grid/grid-view.ts +14 -17
- package/src/grid/redirects-grid.ts +14 -5
- package/src/grid/redirects.ts +16 -14
- package/src/grid/schema-columns.ts +25 -19
- package/src/grid/sources/connector-source.ts +47 -20
- package/src/grid/sources/content-source.ts +25 -24
- package/src/grid/sources/csv-file-source.ts +2 -2
- package/src/grid/tabulator-tables.d.ts +55 -34
- package/src/hosting/stage.ts +7 -7
- package/src/i18n/i18n-commands.ts +1 -2
- package/src/media/media-pane.ts +4 -1
- package/src/new-project/add-repo-modal.ts +20 -19
- package/src/new-project/import-tab.ts +10 -14
- package/src/new-project/location-fields.ts +2 -1
- package/src/new-project/new-project-modal.ts +12 -9
- package/src/packages/jxsuite-update.ts +4 -3
- package/src/packages/pull-package-sync.ts +26 -25
- package/src/page-params.ts +97 -68
- package/src/panels/activity-panel.ts +17 -9
- package/src/panels/ai-chat/attached-context.ts +13 -7
- package/src/panels/ai-chat/chat-view.ts +80 -73
- package/src/panels/ai-chat/composer.ts +30 -33
- package/src/panels/ai-chat/sessions-view.ts +1 -2
- package/src/panels/ai-panel.ts +23 -20
- package/src/panels/block-action-bar.ts +46 -35
- package/src/panels/bottom-dock.ts +5 -3
- package/src/panels/canvas-dnd-bridge.ts +31 -24
- package/src/panels/data-explorer.ts +62 -25
- package/src/panels/data-grid.ts +14 -11
- package/src/panels/dnd.ts +108 -118
- package/src/panels/editors.ts +55 -22
- package/src/panels/elements-panel.ts +10 -3
- package/src/panels/events-panel.ts +84 -69
- package/src/panels/formula-workspace.ts +76 -51
- package/src/panels/frontmatter-fields.ts +24 -19
- package/src/panels/frontmatter-panel.ts +44 -34
- package/src/panels/git-diff-open.ts +4 -4
- package/src/panels/git-panel.ts +49 -61
- package/src/panels/head-panel.ts +131 -111
- package/src/panels/i18n-panel.ts +10 -3
- package/src/panels/imports-panel.ts +23 -47
- package/src/panels/jump-bar.ts +48 -29
- package/src/panels/layers-panel.ts +97 -74
- package/src/panels/left-panel.ts +3 -3
- package/src/panels/pane-context.ts +31 -22
- package/src/panels/pane-grid.ts +49 -20
- package/src/panels/panel-registry.ts +22 -37
- package/src/panels/properties-panel.ts +175 -121
- package/src/panels/quick-search.ts +53 -31
- package/src/panels/right-panel.ts +3 -3
- package/src/panels/seo-modal.ts +13 -17
- package/src/panels/settings-menu.ts +15 -15
- package/src/panels/signals-panel.ts +153 -131
- package/src/panels/statement-editor.ts +230 -140
- package/src/panels/style-panel.ts +254 -216
- package/src/panels/style-utils.ts +114 -51
- package/src/panels/stylebook-doc.ts +57 -54
- package/src/panels/stylebook-layers-panel.ts +9 -9
- package/src/panels/stylebook-panel.ts +13 -17
- package/src/panels/tab-drop.ts +27 -11
- package/src/panels/tab-strip.ts +36 -26
- package/src/platform-errors.ts +57 -43
- package/src/platform.ts +58 -30
- package/src/platforms/cloud.ts +345 -175
- package/src/platforms/default-platform.ts +10 -4
- package/src/platforms/devserver.ts +174 -127
- package/src/platforms/read-batcher.ts +16 -8
- package/src/project-list.ts +2 -1
- package/src/publish/deploy-checklist.ts +8 -2
- package/src/publish/pages-service.ts +28 -13
- package/src/publish/publish-commands.ts +5 -4
- package/src/publish/publish-panel.ts +10 -6
- package/src/recent-projects.ts +15 -8
- package/src/resize-edges.ts +26 -36
- package/src/services/a11y-report.ts +26 -19
- package/src/services/agent-seed.ts +23 -11
- package/src/services/ai-ask.ts +9 -12
- package/src/services/ai-command-tools.ts +68 -62
- package/src/services/ai-import-tools.ts +80 -67
- package/src/services/ai-models.ts +17 -13
- package/src/services/ai-project-tools.ts +68 -32
- package/src/services/ai-session-store.ts +19 -10
- package/src/services/ai-system-prompt.ts +46 -20
- package/src/services/ai-tools.ts +71 -27
- package/src/services/ai-write-report.ts +20 -12
- package/src/services/ai-writes.ts +4 -4
- package/src/services/automation.ts +58 -31
- package/src/services/cem-export.ts +149 -58
- package/src/services/clock.ts +6 -1
- package/src/services/code-services.ts +37 -31
- package/src/services/context-manager.ts +14 -10
- package/src/services/context-resolver.ts +20 -8
- package/src/services/data-service.ts +19 -15
- package/src/services/directory-picker.ts +18 -12
- package/src/services/document-assistant.ts +39 -19
- package/src/services/gated-registry.ts +10 -6
- package/src/services/harness/chat-reducer.ts +23 -1
- package/src/services/import-client.ts +91 -38
- package/src/services/import-seed.ts +8 -2
- package/src/services/jx-validate.ts +28 -31
- package/src/services/live-preview.ts +0 -0
- package/src/services/live-surfaces.ts +6 -3
- package/src/services/model-uri.ts +10 -2
- package/src/services/module-preloads.ts +12 -8
- package/src/services/monaco-buffer.ts +43 -24
- package/src/services/monaco-lazy.ts +7 -8
- package/src/services/monaco-setup.ts +23 -16
- package/src/services/notify.ts +44 -17
- package/src/services/popover-report.ts +25 -10
- package/src/services/preview-eval.ts +20 -8
- package/src/services/profile.ts +13 -6
- package/src/services/project-path.ts +13 -10
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +8 -7
- package/src/services/settings/kernel.ts +13 -4
- package/src/services/settings/write-queue.ts +9 -9
- package/src/services/token-lint.ts +12 -10
- package/src/services/tool-executor.ts +17 -5
- package/src/services/tool-outcomes.ts +44 -24
- package/src/services/trusted-types.ts +24 -7
- package/src/settings/contexts-section.ts +11 -8
- package/src/settings/contributed-section.ts +79 -60
- package/src/settings/css-vars-editor.ts +37 -16
- package/src/settings/defs-editor.ts +38 -35
- package/src/settings/extension-rows.ts +16 -8
- package/src/settings/extension-sections.ts +23 -13
- package/src/settings/extensions-section.ts +4 -1
- package/src/settings/general-settings.ts +11 -8
- package/src/settings/locales-section.ts +20 -12
- package/src/settings/preferences-dialog.ts +9 -6
- package/src/settings/preferences-keymap.ts +7 -5
- package/src/settings/preferences-sections.ts +11 -5
- package/src/settings/project-sections.ts +1 -1
- package/src/settings/schema-field-ui.ts +9 -0
- package/src/settings/settings-document.ts +5 -5
- package/src/shell/tree.ts +6 -5
- package/src/shell.ts +104 -81
- package/src/site-context.ts +37 -22
- package/src/state.ts +41 -49
- package/src/store.ts +54 -34
- package/src/studio.ts +39 -77
- package/src/style/live-preview.ts +1 -1
- package/src/style/project-styles.ts +6 -7
- package/src/style/token-ref.ts +5 -4
- package/src/surfaces/about.ts +18 -19
- package/src/surfaces/add-repo.ts +17 -19
- package/src/surfaces/ai-chat.ts +31 -36
- package/src/surfaces/ai-credentials-form.ts +6 -16
- package/src/surfaces/ai-managed-connect.ts +3 -4
- package/src/surfaces/ai-model-picker.ts +3 -5
- package/src/surfaces/block-action-bar.ts +22 -11
- package/src/surfaces/boot-failure.ts +4 -5
- package/src/surfaces/bottom-dock.ts +9 -18
- package/src/surfaces/canvas-stage.ts +12 -11
- package/src/surfaces/cf-account-picker.ts +17 -18
- package/src/surfaces/commandbar.ts +14 -9
- package/src/surfaces/convert-repeater.ts +17 -19
- package/src/surfaces/data-actions.ts +9 -7
- package/src/surfaces/dialog.ts +26 -28
- package/src/surfaces/diff-toolbar.ts +9 -7
- package/src/surfaces/doc-header.ts +10 -19
- package/src/surfaces/empty-state.ts +7 -10
- package/src/surfaces/entry-editor.ts +14 -11
- package/src/surfaces/expression-editor.ts +11 -25
- package/src/surfaces/files-panel.ts +27 -14
- package/src/surfaces/formula-palette.ts +4 -5
- package/src/surfaces/git-panel.ts +7 -15
- package/src/surfaces/github-auth.ts +16 -20
- package/src/surfaces/github-publish.ts +17 -22
- package/src/surfaces/grid-cell.ts +30 -31
- package/src/surfaces/grid-open.ts +10 -14
- package/src/surfaces/grid-panel.ts +13 -9
- package/src/surfaces/grid-replace.ts +13 -13
- package/src/surfaces/grid-views.ts +15 -18
- package/src/surfaces/inspector-dock.ts +13 -12
- package/src/surfaces/jump-bar.ts +5 -6
- package/src/surfaces/jxsuite-update.ts +16 -22
- package/src/surfaces/library-pane.ts +25 -20
- package/src/surfaces/logic-panel.ts +17 -30
- package/src/surfaces/logic-workspace.ts +13 -15
- package/src/surfaces/media-browser.ts +11 -10
- package/src/surfaces/media-field.ts +4 -5
- package/src/surfaces/media-pane.ts +8 -7
- package/src/surfaces/menu.ts +29 -23
- package/src/surfaces/navigator-dock.ts +11 -12
- package/src/surfaces/new-project.ts +27 -26
- package/src/surfaces/pane-context.ts +20 -25
- package/src/surfaces/pane-grid.ts +15 -10
- package/src/surfaces/panel-activity.ts +5 -6
- package/src/surfaces/panel-data.ts +3 -4
- package/src/surfaces/panel-deploy-checklist.ts +5 -6
- package/src/surfaces/panel-elements.ts +5 -7
- package/src/surfaces/panel-i18n.ts +5 -7
- package/src/surfaces/panel-imports.ts +6 -13
- package/src/surfaces/panel-outline.ts +25 -19
- package/src/surfaces/panel-page.ts +11 -18
- package/src/surfaces/panel-problems.ts +5 -7
- package/src/surfaces/panel-signals.ts +20 -34
- package/src/surfaces/panel-stylebook-layers.ts +5 -8
- package/src/surfaces/preferences.ts +21 -11
- package/src/surfaces/progress-modal.ts +15 -9
- package/src/surfaces/properties-panel.json +1 -1
- package/src/surfaces/properties-panel.ts +20 -42
- package/src/surfaces/publish.ts +18 -18
- package/src/surfaces/push-plan.ts +7 -8
- package/src/surfaces/rail.json +126 -73
- package/src/surfaces/rail.ts +38 -12
- package/src/surfaces/reference-field.ts +11 -15
- package/src/surfaces/schema-builder.ts +16 -22
- package/src/surfaces/schema-form.ts +15 -25
- package/src/surfaces/secret-field.ts +9 -6
- package/src/surfaces/seo.ts +21 -15
- package/src/surfaces/settings-contexts.ts +9 -7
- package/src/surfaces/settings-contributed.ts +14 -10
- package/src/surfaces/settings-css-vars.ts +5 -12
- package/src/surfaces/settings-defs.json +2 -2
- package/src/surfaces/settings-defs.ts +17 -21
- package/src/surfaces/settings-deploy.ts +8 -15
- package/src/surfaces/settings-extensions.ts +5 -6
- package/src/surfaces/settings-head.ts +33 -39
- package/src/surfaces/settings-locales.ts +8 -20
- package/src/surfaces/settings-overview.ts +5 -6
- package/src/surfaces/settings-packages.ts +8 -7
- package/src/surfaces/settings-pane.ts +9 -18
- package/src/surfaces/settings-rawjson.ts +5 -6
- package/src/surfaces/shell.ts +1 -2
- package/src/surfaces/slash-menu.ts +19 -9
- package/src/surfaces/statements.ts +13 -18
- package/src/surfaces/statusbar.ts +17 -14
- package/src/surfaces/style-panel.ts +18 -31
- package/src/surfaces/stylebook-chrome.ts +3 -5
- package/src/surfaces/tab-strip.ts +14 -13
- package/src/surfaces/target-line.ts +6 -7
- package/src/surfaces/welcome.ts +5 -7
- package/src/tabs/patch-ops.ts +4 -3
- package/src/tabs/project-config.ts +32 -21
- package/src/tabs/selection.ts +36 -8
- package/src/tabs/tab.ts +52 -41
- package/src/tabs/transact.ts +98 -53
- package/src/tabs/typing.ts +31 -0
- package/src/types.ts +32 -17
- package/src/ui/ai-credentials-form.ts +10 -13
- package/src/ui/ai-model-picker.ts +3 -3
- package/src/ui/cf-account-picker.ts +7 -9
- package/src/ui/color-selector.ts +9 -5
- package/src/ui/dynamic-slot.ts +7 -8
- package/src/ui/expression-editor.ts +112 -105
- package/src/ui/form-controls.ts +57 -48
- package/src/ui/formula-catalog.ts +239 -230
- package/src/ui/formula-chips.ts +33 -18
- package/src/ui/layers.ts +44 -31
- package/src/ui/media-picker.ts +18 -9
- package/src/ui/popover-surface.ts +20 -12
- package/src/ui/progress-modal.ts +13 -8
- package/src/ui/regions.ts +7 -6
- package/src/ui/schema-form.ts +113 -70
- package/src/ui/surface.ts +71 -8
- package/src/ui/timing.ts +9 -0
- package/src/ui/value-source.ts +44 -31
- package/src/ui/virtual-window.ts +3 -2
- package/src/utils/base64.ts +3 -1
- package/src/utils/edit-display.ts +103 -74
- package/src/utils/element-selectors.ts +3 -2
- package/src/utils/google-fonts.ts +15 -14
- package/src/utils/inherited-style.ts +18 -20
- package/src/utils/link-target.ts +1 -1
- package/src/utils/preview-format.ts +25 -3
- package/src/utils/strip-events.ts +49 -22
- package/src/utils/studio-utils.ts +29 -14
- package/src/view.ts +2 -3
- package/src/workspace/pane-derive.ts +10 -12
- package/src/workspace/session.ts +53 -43
- package/src/workspace/workspace.ts +47 -36
- package/dist/chunks/2020-nkxms0ay.js +0 -4
- package/dist/chunks/adapter-m948ab5f.js +0 -4
- package/dist/chunks/cf-account-picker-n8hr38v7.js +0 -4
- package/dist/chunks/cf-settings-356xdtne.js +0 -4
- package/dist/chunks/color-area-jqhna8zy.js +0 -4
- package/dist/chunks/color-field-embq5ctg.js +0 -4
- package/dist/chunks/color-fpezvnkt.js +0 -4
- package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
- package/dist/chunks/combobox-773hdzyj.js +0 -4
- package/dist/chunks/components-yk3t844e.js +0 -4
- package/dist/chunks/content-source-bawmyc09.js +0 -4
- package/dist/chunks/convert-to-repeater-7wns8kf1.js +0 -4
- package/dist/chunks/dialog-mtm9fq5t.js +0 -4
- package/dist/chunks/editor-6sc8nacq.js +0 -4
- package/dist/chunks/entry-commands-8ay9fh81.js +0 -4
- package/dist/chunks/extension-sections-na7rj3nr.js +0 -4
- package/dist/chunks/file-ops-gye0qw56.js +0 -4
- package/dist/chunks/files-dmc2xvcq.js +0 -4
- package/dist/chunks/format-host-t76b53f8.js +0 -4
- package/dist/chunks/fs-events-cg4tpz06.js +0 -4
- package/dist/chunks/git-panel-yef7bshy.js +0 -4
- package/dist/chunks/grid-view-7tt4bxw1.js +0 -51
- package/dist/chunks/icons-qbh8akb1.js +0 -4
- package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
- package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
- package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
- package/dist/chunks/iframe-host-tmz30gfp.js +0 -4
- package/dist/chunks/index-nag78a9h.js +0 -4
- package/dist/chunks/jsonMode-zhwsprqk.js +0 -13
- package/dist/chunks/jx-validate-3gbp5nkn.js +0 -4
- package/dist/chunks/layers-fvz99859.js +0 -4
- package/dist/chunks/layers-panel-gchmbf9p.js +0 -4
- package/dist/chunks/listbox-s8fkmnq8.js +0 -4
- package/dist/chunks/lit-html-9agdspkt.js +0 -4
- package/dist/chunks/media-picker-rhjvyy00.js +0 -4
- package/dist/chunks/menu-2e4y9j7b.js +0 -4
- package/dist/chunks/menu-37d07z9m.js +0 -4
- package/dist/chunks/monaco-binding-2jnhjjvr.js +0 -4
- package/dist/chunks/monaco-buffer-rp21359g.js +0 -4
- package/dist/chunks/monaco-setup-vhh7gayr.js +0 -4
- package/dist/chunks/notify-h3www560.js +0 -4
- package/dist/chunks/number-field-gw7sq8rn.js +0 -4
- package/dist/chunks/parse-897fvqah.js +0 -4
- package/dist/chunks/platform-hz34wcd7.js +0 -4
- package/dist/chunks/popover-8htra4er.js +0 -4
- package/dist/chunks/publish-panel-exsmjzyj.js +0 -4
- package/dist/chunks/quick-search-pgaqeh0m.js +0 -4
- package/dist/chunks/references-2cgdxnb8.js +0 -4
- package/dist/chunks/right-panel-jf856w5w.js +0 -4
- package/dist/chunks/section-registry-jnf8r5gy.js +0 -4
- package/dist/chunks/select-n3v7gwas.js +0 -4
- package/dist/chunks/split-pff8f3ze.js +0 -4
- package/dist/chunks/studio-0f55h9ye.js +0 -5
- package/dist/chunks/studio-0sqdhnew.js +0 -12
- package/dist/chunks/studio-19x1c75w.js +0 -5
- package/dist/chunks/studio-2sqcndak.js +0 -5
- package/dist/chunks/studio-3aey7dd3.js +0 -5
- package/dist/chunks/studio-3bpdkq2s.js +0 -5
- package/dist/chunks/studio-47y6fbpf.js +0 -5
- package/dist/chunks/studio-4m3826v2.js +0 -5
- package/dist/chunks/studio-4nx2dj1g.js +0 -4
- package/dist/chunks/studio-4xv5s47r.js +0 -9
- package/dist/chunks/studio-5g4t8v6w.js +0 -5
- package/dist/chunks/studio-5py436sf.js +0 -5
- package/dist/chunks/studio-77kt594t.js +0 -5
- package/dist/chunks/studio-8f87n763.js +0 -10
- package/dist/chunks/studio-8mb00b84.js +0 -5
- package/dist/chunks/studio-8p51gq2h.js +0 -6
- package/dist/chunks/studio-954ds834.js +0 -5
- package/dist/chunks/studio-9ft45c83.js +0 -9
- package/dist/chunks/studio-a2138x5k.js +0 -7
- package/dist/chunks/studio-a9yzstqj.js +0 -5
- package/dist/chunks/studio-acdksxx6.js +0 -5
- package/dist/chunks/studio-ar8jcf2y.js +0 -5
- package/dist/chunks/studio-bmn4mn9j.js +0 -7
- package/dist/chunks/studio-e7xxa0sj.js +0 -5
- package/dist/chunks/studio-ecaz25vk.js +0 -5
- package/dist/chunks/studio-ery42sqr.js +0 -4
- package/dist/chunks/studio-fzdywr6p.js +0 -9
- package/dist/chunks/studio-g5fmf0c1.js +0 -5
- package/dist/chunks/studio-g7nse7zb.js +0 -5
- package/dist/chunks/studio-geq1ea6k.js +0 -14
- package/dist/chunks/studio-ghkb32r8.js +0 -5
- package/dist/chunks/studio-ghrhy2y7.js +0 -5
- package/dist/chunks/studio-grawhey1.js +0 -5
- package/dist/chunks/studio-h0xgswew.js +0 -5
- package/dist/chunks/studio-h9c4p2tx.js +0 -5
- package/dist/chunks/studio-jyzj5cxs.js +0 -5
- package/dist/chunks/studio-mgx8yhnz.js +0 -64
- package/dist/chunks/studio-mpb04y1d.js +0 -6
- package/dist/chunks/studio-n17mty64.js +0 -5
- package/dist/chunks/studio-nwh2eqsk.js +0 -6
- package/dist/chunks/studio-q1h883bt.js +0 -16
- package/dist/chunks/studio-q4tv82h7.js +0 -9
- package/dist/chunks/studio-qpqa7vk3.js +0 -5
- package/dist/chunks/studio-qqz8e5gk.js +0 -5
- package/dist/chunks/studio-qvasen3f.js +0 -5
- package/dist/chunks/studio-rggdb3hm.js +0 -5
- package/dist/chunks/studio-sghy936h.js +0 -4
- package/dist/chunks/studio-skprc6ef.js +0 -5
- package/dist/chunks/studio-tt2nactf.js +0 -5
- package/dist/chunks/studio-tyasmc39.js +0 -5
- package/dist/chunks/studio-vk9ezads.js +0 -5
- package/dist/chunks/studio-vzacdfsc.js +0 -4
- package/dist/chunks/studio-w4c0xmrw.js +0 -5
- package/dist/chunks/studio-wfjqhhbk.js +0 -5
- package/dist/chunks/studio-wgh5kxpj.js +0 -5
- package/dist/chunks/studio-xasj0796.js +0 -5
- package/dist/chunks/studio-y78s8r2n.js +0 -5
- package/dist/chunks/studio-z8w183sh.js +0 -5
- package/dist/chunks/studio-zscd9ty4.js +0 -928
- package/dist/chunks/studio-zxp13sss.js +0 -7
- package/dist/chunks/studio-zygnmm01.js +0 -5
- package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
- package/dist/chunks/tabs-jwthg40w.js +0 -4
- package/dist/chunks/textfield-spy3hc77.js +0 -4
- package/dist/chunks/toast-4d35zd64.js +0 -4
- package/dist/chunks/toast-host-8sjmfed3.js +0 -4
- package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
- package/dist/chunks/tree-h19v1jfz.js +0 -4
- package/dist/chunks/tsMode-gr0m4dzr.js +0 -14
- package/dist/chunks/workspace-m15qtcqp.js +0 -4
- package/dist/chunks/ws-client-nf61frr1.js +0 -4
|
@@ -51,11 +51,21 @@ import type {
|
|
|
51
51
|
SerializableRect,
|
|
52
52
|
} from "./iframe-protocol";
|
|
53
53
|
import type { DocPos } from "./iframe-position";
|
|
54
|
+
import { isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
54
55
|
import type { JxPath } from "../state";
|
|
55
56
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
56
57
|
|
|
58
|
+
/** The commit a block session posts when its text reaches the document. */
|
|
59
|
+
type EditCommitMsg = Extract<IframeToParent, { kind: "editCommit" }>;
|
|
60
|
+
|
|
61
|
+
/** The commit a prop-bound session posts for its instance's `$props` value. */
|
|
62
|
+
type EditCommitPropMsg = Extract<IframeToParent, { kind: "editCommitProp" }>;
|
|
63
|
+
|
|
64
|
+
/** The toolbar commands that toggle an inline tag (every intent but `link` and `insertData`). */
|
|
65
|
+
type InlineFormatCommand = Exclude<ApplyFormatIntent["command"], "link" | "insertData">;
|
|
66
|
+
|
|
57
67
|
/** Toolbar command → inline tag (mirrors the parent's old `cmdToTag`, now iframe-side). */
|
|
58
|
-
const CMD_TO_TAG
|
|
68
|
+
const CMD_TO_TAG = {
|
|
59
69
|
bold: "strong",
|
|
60
70
|
code: "code",
|
|
61
71
|
italic: "em",
|
|
@@ -63,7 +73,19 @@ const CMD_TO_TAG: Record<string, string> = {
|
|
|
63
73
|
subscript: "sub",
|
|
64
74
|
superscript: "sup",
|
|
65
75
|
underline: "u",
|
|
66
|
-
}
|
|
76
|
+
} satisfies Record<InlineFormatCommand, string>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The CSS Custom Highlight API as this realm may or may not provide it. lib.dom declares both
|
|
80
|
+
* members unconditionally, but happy-dom and non-Chromium engines ship neither.
|
|
81
|
+
*/
|
|
82
|
+
interface HighlightRealm {
|
|
83
|
+
Highlight?: typeof Highlight;
|
|
84
|
+
CSS?: { highlights?: HighlightRegistry };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The global object, read through the optional shape above so the feature probes stay honest. */
|
|
88
|
+
const highlightRealm: HighlightRealm = globalThis;
|
|
67
89
|
|
|
68
90
|
/** Inline tags whose active state the snapshot reports (everything except `a`, handled via link). */
|
|
69
91
|
const ACTIVE_TAG_PROBES = ["strong", "em", "u", "del", "sub", "sup", "code"];
|
|
@@ -125,19 +147,19 @@ function deliveredOutsideProps(node: JxMutableNode | undefined, prop: string): b
|
|
|
125
147
|
if (!node) {
|
|
126
148
|
return false;
|
|
127
149
|
}
|
|
128
|
-
const attrs = node.attributes
|
|
150
|
+
const attrs = node.attributes;
|
|
129
151
|
if (attrs) {
|
|
130
152
|
if (attrs[`props.${prop}`] !== undefined) {
|
|
131
153
|
return true;
|
|
132
154
|
}
|
|
133
155
|
const reflected = new Set([prop.toLowerCase(), camelToKebab(prop).toLowerCase()]);
|
|
134
|
-
for (const
|
|
135
|
-
if (
|
|
156
|
+
for (const name of Object.keys(attrs)) {
|
|
157
|
+
if (attrs[name] !== undefined && reflected.has(name.toLowerCase())) {
|
|
136
158
|
return true;
|
|
137
159
|
}
|
|
138
160
|
}
|
|
139
161
|
const payload = attrs["data-jx-props"];
|
|
140
|
-
if (
|
|
162
|
+
if (isString(payload) && payloadNames(payload, prop)) {
|
|
141
163
|
return true;
|
|
142
164
|
}
|
|
143
165
|
}
|
|
@@ -145,14 +167,14 @@ function deliveredOutsideProps(node: JxMutableNode | undefined, prop: string): b
|
|
|
145
167
|
if (RESERVED_KEYS.has(prop) || prop.startsWith("$")) {
|
|
146
168
|
return false;
|
|
147
169
|
}
|
|
148
|
-
return
|
|
170
|
+
return node[prop] !== undefined;
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
/** Whether a `data-jx-props` JSON payload carries `prop`. Unparseable payloads deliver nothing. */
|
|
152
174
|
function payloadNames(payload: string, prop: string): boolean {
|
|
153
175
|
try {
|
|
154
176
|
const parsed: unknown = JSON.parse(payload);
|
|
155
|
-
return
|
|
177
|
+
return isNonNullObject(parsed) && prop in parsed;
|
|
156
178
|
} catch {
|
|
157
179
|
return false;
|
|
158
180
|
}
|
|
@@ -238,11 +260,8 @@ export function startIframeInlineEdit(
|
|
|
238
260
|
* `Highlight`/`CSS.highlights`, so this is a silent no-op there (and in non-Chromium engines).
|
|
239
261
|
*/
|
|
240
262
|
const updateHighlight = () => {
|
|
241
|
-
const HighlightCtor =
|
|
242
|
-
|
|
243
|
-
| undefined;
|
|
244
|
-
const highlights = (globalThis.CSS as unknown as { highlights?: Map<string, unknown> })
|
|
245
|
-
?.highlights;
|
|
263
|
+
const HighlightCtor = highlightRealm.Highlight;
|
|
264
|
+
const highlights = highlightRealm.CSS?.highlights;
|
|
246
265
|
if (!HighlightCtor || !highlights) {
|
|
247
266
|
return;
|
|
248
267
|
}
|
|
@@ -256,9 +275,7 @@ export function startIframeInlineEdit(
|
|
|
256
275
|
|
|
257
276
|
/** Tear down the Custom Highlight (on teardown / real editEnd). */
|
|
258
277
|
const clearHighlight = () => {
|
|
259
|
-
|
|
260
|
-
?.highlights;
|
|
261
|
-
highlights?.delete(HIGHLIGHT_NAME);
|
|
278
|
+
highlightRealm.CSS?.highlights?.delete(HIGHLIGHT_NAME);
|
|
262
279
|
};
|
|
263
280
|
|
|
264
281
|
/**
|
|
@@ -309,6 +326,9 @@ export function startIframeInlineEdit(
|
|
|
309
326
|
}
|
|
310
327
|
};
|
|
311
328
|
|
|
329
|
+
/** Whether this typing burst's first keystroke has been announced (`editInput`). */
|
|
330
|
+
let inputAnnounced = false;
|
|
331
|
+
|
|
312
332
|
const enterEditAt = (el: HTMLElement, path: JxPath) => {
|
|
313
333
|
// Show raw `${expr}` syntax for editing (the render displays it as ❪ expr ❫).
|
|
314
334
|
restoreTemplateExpressions(el);
|
|
@@ -316,14 +336,14 @@ export function startIframeInlineEdit(
|
|
|
316
336
|
startEditing(el, path, {
|
|
317
337
|
// `inPlace` rides along only when set: a release commit is the common case and the flag is
|
|
318
338
|
// Optional, so omitting it keeps the message shape unchanged for every non-tick commit.
|
|
319
|
-
onCommit: (p, children, textContent, inPlace) =>
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
339
|
+
onCommit: (p, children, textContent, inPlace) => {
|
|
340
|
+
inputAnnounced = false;
|
|
341
|
+
const commit: EditCommitMsg = { children, kind: "editCommit", path: p, textContent };
|
|
342
|
+
if (inPlace) {
|
|
343
|
+
commit.inPlace = true;
|
|
344
|
+
}
|
|
345
|
+
channel.post(commit);
|
|
346
|
+
},
|
|
327
347
|
onEnd: () => {
|
|
328
348
|
clearHighlight();
|
|
329
349
|
lastNonEmptyRange = null;
|
|
@@ -351,11 +371,11 @@ export function startIframeInlineEdit(
|
|
|
351
371
|
if (!shadowDoc) {
|
|
352
372
|
return true;
|
|
353
373
|
}
|
|
354
|
-
const node = getNodeAtPath(shadowDoc, hostPath)
|
|
374
|
+
const node = getNodeAtPath(shadowDoc, hostPath);
|
|
355
375
|
if (deliveredOutsideProps(node, prop)) {
|
|
356
376
|
return false;
|
|
357
377
|
}
|
|
358
|
-
const raw =
|
|
378
|
+
const raw = node?.$props?.[prop];
|
|
359
379
|
if (raw == null) {
|
|
360
380
|
return true; // Unset — editing ADDS the prop, overriding the definition default.
|
|
361
381
|
}
|
|
@@ -366,7 +386,7 @@ export function startIframeInlineEdit(
|
|
|
366
386
|
* Objects were already excluded; the scalar cases were not, and are the ones a component
|
|
367
387
|
* actually declares. They stay editable in the properties panel, which knows their type.
|
|
368
388
|
*/
|
|
369
|
-
if (
|
|
389
|
+
if (!isString(raw)) {
|
|
370
390
|
return false;
|
|
371
391
|
}
|
|
372
392
|
return !raw.includes("${");
|
|
@@ -383,14 +403,19 @@ export function startIframeInlineEdit(
|
|
|
383
403
|
el,
|
|
384
404
|
hostPath,
|
|
385
405
|
{
|
|
386
|
-
onCommit: (p, _children, textContent, inPlace) =>
|
|
387
|
-
|
|
406
|
+
onCommit: (p, _children, textContent, inPlace) => {
|
|
407
|
+
inputAnnounced = false;
|
|
408
|
+
const commit: EditCommitPropMsg = {
|
|
388
409
|
kind: "editCommitProp",
|
|
389
410
|
path: p,
|
|
390
411
|
prop,
|
|
391
412
|
value: textContent ?? "",
|
|
392
|
-
|
|
393
|
-
|
|
413
|
+
};
|
|
414
|
+
if (inPlace) {
|
|
415
|
+
commit.inPlace = true;
|
|
416
|
+
}
|
|
417
|
+
channel.post(commit);
|
|
418
|
+
},
|
|
394
419
|
onEnd: () => {
|
|
395
420
|
clearHighlight();
|
|
396
421
|
lastNonEmptyRange = null;
|
|
@@ -512,6 +537,7 @@ export function startIframeInlineEdit(
|
|
|
512
537
|
if (start === -1 || end === -1 || end <= start) {
|
|
513
538
|
return false;
|
|
514
539
|
}
|
|
540
|
+
// SAFETY: `blocksInOrder` selects `[data-jx-path]`, so every block it returns carries the key.
|
|
515
541
|
const between = blocks
|
|
516
542
|
.slice(start + 1, end)
|
|
517
543
|
.map((el) => parseJxPath(el.dataset.jxPath as string));
|
|
@@ -566,12 +592,22 @@ export function startIframeInlineEdit(
|
|
|
566
592
|
owns what a slash MEANS (start of a block or after a space; anywhere else it is punctuation).
|
|
567
593
|
The matching `input` half re-filters the open menu, for the same reason: a contenteditable's
|
|
568
594
|
`input` targets its host, not the block the caret happens to be in. */
|
|
569
|
-
const onSlashKey = (e:
|
|
595
|
+
const onSlashKey = (e: KeyboardEvent) => handleSlashTrigger(e);
|
|
570
596
|
const onSlashInput = () => {
|
|
571
597
|
if (isSlashActive()) {
|
|
572
598
|
refreshSlashMenu();
|
|
573
599
|
}
|
|
574
600
|
};
|
|
601
|
+
/* Typed text reaches the document on the idle tick, half a second later. The parent hears about
|
|
602
|
+
the burst on its first keystroke instead, so its Save is not disabled over words a save would
|
|
603
|
+
keep. Once per burst: the flag re-arms when the burst commits. */
|
|
604
|
+
const onEditInput = () => {
|
|
605
|
+
if (!inputAnnounced) {
|
|
606
|
+
inputAnnounced = true;
|
|
607
|
+
channel.post({ kind: "editInput" });
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
container.addEventListener("input", onEditInput);
|
|
575
611
|
container.addEventListener("keydown", onSlashKey);
|
|
576
612
|
container.addEventListener("input", onSlashInput);
|
|
577
613
|
doc.addEventListener("mouseup", onMouseUp, true);
|
|
@@ -639,6 +675,7 @@ export function startIframeInlineEdit(
|
|
|
639
675
|
return () => {
|
|
640
676
|
container.removeEventListener("keydown", onSlashKey);
|
|
641
677
|
container.removeEventListener("input", onSlashInput);
|
|
678
|
+
container.removeEventListener("input", onEditInput);
|
|
642
679
|
doc.removeEventListener("mouseup", onMouseUp, true);
|
|
643
680
|
doc.removeEventListener("keyup", onKeyUp, true);
|
|
644
681
|
doc.removeEventListener("blur", onBlurCapture, true);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* (happy-dom's `elementFromPoint` returns null) and lives in iframe-interaction.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { isNumber } from "@jxsuite/schema/guards";
|
|
20
21
|
import { parseJxPath } from "./path-mapping";
|
|
21
22
|
import { rectOf } from "../utils/geometry";
|
|
22
23
|
import { childIndex, parentElementPath } from "../state";
|
|
@@ -48,7 +49,7 @@ function isRowLayout(parent: HTMLElement): boolean {
|
|
|
48
49
|
const isGrid = display === "grid" || display === "inline-grid";
|
|
49
50
|
return (
|
|
50
51
|
(isFlex && style.flexDirection.startsWith("row")) ||
|
|
51
|
-
(isGrid &&
|
|
52
|
+
(isGrid && style.gridAutoFlow.startsWith("column"))
|
|
52
53
|
);
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -78,11 +79,11 @@ export function computeInsertZones(
|
|
|
78
79
|
shadowDoc: JxMutableNode,
|
|
79
80
|
): InsertZone[] | null {
|
|
80
81
|
void shadowDoc;
|
|
81
|
-
const serialized = targetEl.dataset
|
|
82
|
+
const serialized = targetEl.dataset.jxPath;
|
|
82
83
|
if (serialized == null) {
|
|
83
84
|
return null;
|
|
84
85
|
}
|
|
85
|
-
const targetPath = parseJxPath(serialized)
|
|
86
|
+
const targetPath = parseJxPath(serialized);
|
|
86
87
|
|
|
87
88
|
// Empty container: one centered "+" that inserts as the container's first child.
|
|
88
89
|
if (targetEl.classList.contains(EMPTY_CONTAINER_CLASS)) {
|
|
@@ -99,7 +100,7 @@ export function computeInsertZones(
|
|
|
99
100
|
return null;
|
|
100
101
|
}
|
|
101
102
|
const childIdx = childIndex(targetPath);
|
|
102
|
-
if (
|
|
103
|
+
if (!isNumber(childIdx)) {
|
|
103
104
|
return null;
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { jxPathSelector, parseJxPath, serializeJxPath } from "./path-mapping";
|
|
10
|
+
import { isFunction } from "@jxsuite/schema/guards";
|
|
10
11
|
import { rectOf } from "../utils/geometry";
|
|
11
12
|
import { computeInsertZones, insertZonesKey } from "./iframe-insert";
|
|
12
13
|
import { getActiveElement, isEditing } from "../editor/inline-edit";
|
|
@@ -32,9 +33,10 @@ function isCanvasLeave(e: Event, doc: Document): boolean {
|
|
|
32
33
|
|
|
33
34
|
/** Walk up from an element to the nearest ancestor carrying a `data-jx-path`; null if none. */
|
|
34
35
|
function nearestPathEl(start: Element | null): HTMLElement | null {
|
|
35
|
-
let el = start instanceof Element ?
|
|
36
|
+
let el = start instanceof Element ? start : null;
|
|
36
37
|
while (el) {
|
|
37
|
-
|
|
38
|
+
// Only HTML elements are stamped (`makeStamper` skips everything else), so only they can match.
|
|
39
|
+
if (el instanceof HTMLElement && el.dataset.jxPath) {
|
|
38
40
|
return el;
|
|
39
41
|
}
|
|
40
42
|
el = el.parentElement;
|
|
@@ -57,6 +59,7 @@ export function nearestHit(target: EventTarget | null): NodeHit | null {
|
|
|
57
59
|
if (!el) {
|
|
58
60
|
return null;
|
|
59
61
|
}
|
|
62
|
+
// SAFETY: nearestPathEl returns only an element whose `data-jx-path` is a non-empty string.
|
|
60
63
|
return measureHit(el, el.dataset.jxPath as string);
|
|
61
64
|
}
|
|
62
65
|
|
|
@@ -75,8 +78,9 @@ export function nearestHit(target: EventTarget | null): NodeHit | null {
|
|
|
75
78
|
* from a node that is on screen is a worse failure than drawing one for a node that is not.
|
|
76
79
|
*/
|
|
77
80
|
function isRendered(el: Element): boolean {
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
// Absent on engines that predate it, whatever lib.dom says.
|
|
82
|
+
const check: Element["checkVisibility"] | undefined = el.checkVisibility;
|
|
83
|
+
return isFunction(check) ? check.call(el, { checkVisibilityCSS: true }) : true;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
|
@@ -92,10 +96,7 @@ function isRendered(el: Element): boolean {
|
|
|
92
96
|
* The serialized path is the same string the renderer stamps, so a stored selection path
|
|
93
97
|
* round-trips back to its element.
|
|
94
98
|
*/
|
|
95
|
-
export function measureHits(
|
|
96
|
-
paths: (string | number)[][],
|
|
97
|
-
doc: Document = document,
|
|
98
|
-
): { hits: NodeHit[]; hidden: (string | number)[][] } {
|
|
99
|
+
export function measureHits(paths: (string | number)[][], doc: Document = document) {
|
|
99
100
|
const hits: NodeHit[] = [];
|
|
100
101
|
const hidden: (string | number)[][] = [];
|
|
101
102
|
for (const path of paths) {
|
|
@@ -119,8 +120,15 @@ export function measureHits(
|
|
|
119
120
|
* {@link file://./iframe-drop.ts}'s `startGrabDetector` receives its deps rather than reaching for
|
|
120
121
|
* module state.
|
|
121
122
|
*/
|
|
123
|
+
type InvokerAction = Extract<IframeToParent, { kind: "popoverTargetClick" }>["action"];
|
|
124
|
+
|
|
122
125
|
/** The three `popovertargetaction` keywords. Anything else falls back to the HTML default. */
|
|
123
|
-
const INVOKER_ACTIONS = new Set(["toggle", "show", "hide"]);
|
|
126
|
+
const INVOKER_ACTIONS = new Set<string>(["toggle", "show", "hide"] satisfies InvokerAction[]);
|
|
127
|
+
|
|
128
|
+
/** Whether an attribute value is one of the {@link INVOKER_ACTIONS} keywords. */
|
|
129
|
+
function isInvokerAction(value: string): value is InvokerAction {
|
|
130
|
+
return INVOKER_ACTIONS.has(value);
|
|
131
|
+
}
|
|
124
132
|
|
|
125
133
|
/**
|
|
126
134
|
* Report an invoker click, when the click landed on one and it names an addressable popover.
|
|
@@ -152,14 +160,14 @@ function postPopoverInvoke(
|
|
|
152
160
|
need escaping — a rule this file already keeps in exactly one place (`jxPathSelector`) and
|
|
153
161
|
should not gain a second spelling of. The set scanned is the addressable POPOVERS, which is
|
|
154
162
|
also the set the answer has to come from, so the scan is the test. */
|
|
155
|
-
const panels = invoker.ownerDocument.querySelectorAll("[data-jx-popover]");
|
|
156
|
-
const panel = [...panels].find((el) => el.id === id)
|
|
163
|
+
const panels = invoker.ownerDocument.querySelectorAll<HTMLElement>("[data-jx-popover]");
|
|
164
|
+
const panel = [...panels].find((el) => el.id === id);
|
|
157
165
|
const serialized = panel?.dataset.jxPath;
|
|
158
166
|
if (!serialized) {
|
|
159
167
|
return;
|
|
160
168
|
}
|
|
161
169
|
const raw = invoker.getAttribute("popovertargetaction") ?? "toggle";
|
|
162
|
-
const action =
|
|
170
|
+
const action = isInvokerAction(raw) ? raw : "toggle";
|
|
163
171
|
channel.post({ action, kind: "popoverTargetClick", targetPath: parseJxPath(serialized) });
|
|
164
172
|
}
|
|
165
173
|
|
|
@@ -190,10 +198,10 @@ function postCommandInvoke(
|
|
|
190
198
|
}
|
|
191
199
|
// Scanned, not selected, for the reason `postPopoverInvoke` gives; the addressable overlays are
|
|
192
200
|
// The de-popovered panels and the stamped dialogs.
|
|
193
|
-
const overlays = invoker.ownerDocument.querySelectorAll(
|
|
201
|
+
const overlays = invoker.ownerDocument.querySelectorAll<HTMLElement>(
|
|
194
202
|
"[data-jx-popover], dialog[data-jx-path]",
|
|
195
203
|
);
|
|
196
|
-
const overlay = [...overlays].find((el) => el.id === id)
|
|
204
|
+
const overlay = [...overlays].find((el) => el.id === id);
|
|
197
205
|
const serialized = overlay?.dataset.jxPath;
|
|
198
206
|
if (!serialized) {
|
|
199
207
|
return;
|
|
@@ -270,7 +278,7 @@ export function startInteraction(
|
|
|
270
278
|
channel.post({ kind: "paneFocus" });
|
|
271
279
|
};
|
|
272
280
|
|
|
273
|
-
const onClick = (e:
|
|
281
|
+
const onClick = (e: MouseEvent) => {
|
|
274
282
|
/*
|
|
275
283
|
* Preview keeps anchors live, so a click would navigate this iframe and destroy the render (and
|
|
276
284
|
* the editing session with it). Report the intent so the parent can open the real page in a real
|
|
@@ -278,7 +286,8 @@ export function startInteraction(
|
|
|
278
286
|
* rather than replacing the canvas with a half-loaded page.
|
|
279
287
|
*
|
|
280
288
|
* Design/edit never reach this: the runtime de-links anchors onto `data-jx-href` there, so there
|
|
281
|
-
* is no navigation to intercept and a click means "select this element".
|
|
289
|
+
* is no navigation to intercept and a click means "select this element". Project Styles keeps
|
|
290
|
+
* its specimens' links live and refuses the navigation just below.
|
|
282
291
|
*/
|
|
283
292
|
if (isPreview()) {
|
|
284
293
|
if (e.target instanceof Element) {
|
|
@@ -292,14 +301,18 @@ export function startInteraction(
|
|
|
292
301
|
// No hit post: a click in preview is a click on the page, never a selection.
|
|
293
302
|
return;
|
|
294
303
|
}
|
|
304
|
+
// Project Styles keeps its link specimen live (`iframe-render.ts`), so a click on it must
|
|
305
|
+
// Select it rather than follow it.
|
|
306
|
+
if (deps?.getMode?.() === "stylebook" && e.target instanceof Element && e.target.closest("a")) {
|
|
307
|
+
e.preventDefault();
|
|
308
|
+
}
|
|
295
309
|
const hit = nearestHit(e.target);
|
|
296
310
|
if (hit) {
|
|
297
311
|
// Ctrl/Cmd is the accumulate gesture (studio-ui-guidelines.md §8.1). The iframe reports the
|
|
298
312
|
// Modifier and nothing else — it holds no selection state, so what a modified click MEANS is
|
|
299
313
|
// The parent's to decide, exactly as the unmodified click already was.
|
|
300
|
-
const mouse = e as MouseEvent;
|
|
301
314
|
channel.post({
|
|
302
|
-
additive:
|
|
315
|
+
additive: e.ctrlKey === true || e.metaKey === true,
|
|
303
316
|
hit,
|
|
304
317
|
kind: "hit",
|
|
305
318
|
});
|
|
@@ -388,11 +401,10 @@ export function startInteraction(
|
|
|
388
401
|
reportInsertZones(target, { x: moveX, y: moveY });
|
|
389
402
|
};
|
|
390
403
|
|
|
391
|
-
const onMove = (e:
|
|
392
|
-
const pe = e as PointerEvent;
|
|
404
|
+
const onMove = (e: PointerEvent) => {
|
|
393
405
|
moveTarget = e.target;
|
|
394
|
-
moveX =
|
|
395
|
-
moveY =
|
|
406
|
+
moveX = e.clientX;
|
|
407
|
+
moveY = e.clientY;
|
|
396
408
|
if (movePending) {
|
|
397
409
|
return;
|
|
398
410
|
}
|
|
@@ -409,8 +421,7 @@ export function startInteraction(
|
|
|
409
421
|
}
|
|
410
422
|
};
|
|
411
423
|
|
|
412
|
-
const onContextMenu = (e:
|
|
413
|
-
const me = e as MouseEvent;
|
|
424
|
+
const onContextMenu = (e: MouseEvent) => {
|
|
414
425
|
// Preview keeps the NATIVE menu: the Jx element menu's verbs (duplicate, delete, paste, wrap)
|
|
415
426
|
// Are document edits, and preview does not edit. Copy Link Address does what it says instead.
|
|
416
427
|
if (isPreview()) {
|
|
@@ -441,8 +452,8 @@ export function startInteraction(
|
|
|
441
452
|
channel.post({
|
|
442
453
|
kind: "contextMenu",
|
|
443
454
|
path: hit.path,
|
|
444
|
-
x:
|
|
445
|
-
y:
|
|
455
|
+
x: e.clientX,
|
|
456
|
+
y: e.clientY,
|
|
446
457
|
});
|
|
447
458
|
};
|
|
448
459
|
|
|
@@ -47,7 +47,7 @@ export function parentCursorToIframe(
|
|
|
47
47
|
cursor: { x: number; y: number },
|
|
48
48
|
iframeRect: { left: number; top: number },
|
|
49
49
|
scale = 1,
|
|
50
|
-
)
|
|
50
|
+
) {
|
|
51
51
|
return {
|
|
52
52
|
x: (cursor.x - iframeRect.left) / scale,
|
|
53
53
|
y: (cursor.y - iframeRect.top) / scale,
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { reapplyStyle } from "@jxsuite/runtime";
|
|
18
|
-
import {
|
|
18
|
+
import { isNonNullObject, isNumber, isString } from "@jxsuite/schema/guards";
|
|
19
|
+
import { getNodeAtPath } from "@jxsuite/schema/doc-ops";
|
|
19
20
|
import { applyDocOpToDoc } from "../tabs/doc-op-apply";
|
|
20
21
|
import { parseJxPath, serializeJxPath } from "./path-mapping";
|
|
21
22
|
import { disposeSubtree, renderSubtreeIframe } from "./iframe-subtree";
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
templateToEditDisplay,
|
|
26
27
|
} from "../utils/edit-display";
|
|
27
28
|
import type { IframeRenderCtx } from "./iframe-render";
|
|
28
|
-
import type {
|
|
29
|
+
import type { JxDocOp } from "../tabs/patch-ops";
|
|
29
30
|
import type { JxPath } from "../state";
|
|
30
31
|
import type { JxMutableNode, JxStyle } from "@jxsuite/schema/types";
|
|
31
32
|
|
|
@@ -36,7 +37,7 @@ import type { JxMutableNode, JxStyle } from "@jxsuite/schema/types";
|
|
|
36
37
|
*/
|
|
37
38
|
export function applyIframePatch(
|
|
38
39
|
shadowDoc: JxMutableNode,
|
|
39
|
-
forwardOps:
|
|
40
|
+
forwardOps: JxDocOp[],
|
|
40
41
|
container: HTMLElement,
|
|
41
42
|
ctx?: IframeRenderCtx,
|
|
42
43
|
echoPaths?: JxPath[],
|
|
@@ -98,7 +99,7 @@ function requireCtx(ctx: IframeRenderCtx | undefined): IframeRenderCtx {
|
|
|
98
99
|
|
|
99
100
|
function applyOpToDom(
|
|
100
101
|
doc: JxMutableNode,
|
|
101
|
-
op:
|
|
102
|
+
op: JxDocOp,
|
|
102
103
|
container: HTMLElement,
|
|
103
104
|
ctx: IframeRenderCtx | undefined,
|
|
104
105
|
): void {
|
|
@@ -138,7 +139,7 @@ function applyOpToDom(
|
|
|
138
139
|
return;
|
|
139
140
|
}
|
|
140
141
|
default: {
|
|
141
|
-
// The
|
|
142
|
+
// The JxDocOp union is exhaustively handled above; this guards a future op kind.
|
|
142
143
|
throw new Error(`iframe-patch-unsupported-op:${JSON.stringify(op)}`);
|
|
143
144
|
}
|
|
144
145
|
}
|
|
@@ -292,7 +293,7 @@ function remapChildPaths(
|
|
|
292
293
|
continue;
|
|
293
294
|
}
|
|
294
295
|
const idx = p[depth + 1];
|
|
295
|
-
if (
|
|
296
|
+
if (!isNumber(idx) || idx < fromIndex) {
|
|
296
297
|
continue;
|
|
297
298
|
}
|
|
298
299
|
if (!parentPath.every((seg, i) => p[i] === seg)) {
|
|
@@ -338,7 +339,9 @@ function patchStyle(
|
|
|
338
339
|
): void {
|
|
339
340
|
const el = requireElement(container, path);
|
|
340
341
|
const node = getNodeAtPath(doc, path);
|
|
341
|
-
|
|
342
|
+
/* SAFETY: buildScope writes `$media` only from the document's own `$media` map or the runtime's
|
|
343
|
+
* root media map, both Record<string, string>, and the host scope may not supply the key. */
|
|
344
|
+
const mediaQueries = (ctx?.defs["$media"] as Record<string, string> | undefined) ?? {};
|
|
342
345
|
reapplyStyle(el, editModeStyle(node?.style), mediaQueries);
|
|
343
346
|
}
|
|
344
347
|
|
|
@@ -372,20 +375,20 @@ function findElement(container: HTMLElement, path: (string | number)[]): HTMLEle
|
|
|
372
375
|
* top-level template-string values are blanked so no reactive bindings are created for them.
|
|
373
376
|
*/
|
|
374
377
|
function editModeStyle(style: JxStyle | undefined): JxStyle | undefined {
|
|
375
|
-
if (!style ||
|
|
378
|
+
if (!style || !isNonNullObject(style)) {
|
|
376
379
|
return undefined;
|
|
377
380
|
}
|
|
378
|
-
const out:
|
|
381
|
+
const out: JxStyle = {};
|
|
379
382
|
for (const [k, v] of Object.entries(style)) {
|
|
380
383
|
/* OMITTED, not blanked. It used to be written as `""`, which was a silent no-op while a
|
|
381
384
|
declaration went to `el.style` and is a malformed declaration now that it goes into a rule.
|
|
382
385
|
Dropping the key is what blanking always meant: the property falls back to the cascade. */
|
|
383
|
-
if (
|
|
386
|
+
if (isString(v) && v.includes("${")) {
|
|
384
387
|
continue;
|
|
385
388
|
}
|
|
386
389
|
out[k] = v;
|
|
387
390
|
}
|
|
388
|
-
return out
|
|
391
|
+
return out;
|
|
389
392
|
}
|
|
390
393
|
|
|
391
394
|
/**
|
|
@@ -393,15 +396,15 @@ function editModeStyle(style: JxStyle | undefined): JxStyle | undefined {
|
|
|
393
396
|
* template-string and $ref display rules (and the legacy patcher's textDisplayValue).
|
|
394
397
|
*/
|
|
395
398
|
function textDisplayValue(node: JxMutableNode | undefined): string {
|
|
396
|
-
const v =
|
|
399
|
+
const v = node?.textContent;
|
|
397
400
|
if (v == null) {
|
|
398
401
|
return "";
|
|
399
402
|
}
|
|
400
|
-
if (
|
|
403
|
+
if (isString(v)) {
|
|
401
404
|
return v.includes("${") ? templateToEditDisplay(v) : v;
|
|
402
405
|
}
|
|
403
|
-
if (
|
|
404
|
-
const ref = String(
|
|
406
|
+
if (isNonNullObject(v) && v.$ref) {
|
|
407
|
+
const ref = String(v.$ref);
|
|
405
408
|
const label = ref.startsWith("#/state/") ? ref.slice(8) : ref;
|
|
406
409
|
return `{${label}}`;
|
|
407
410
|
}
|
|
@@ -33,6 +33,12 @@ export interface DocRange {
|
|
|
33
33
|
head: DocPos;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** A live DOM caret position: a node and an offset into it, as a Selection endpoint carries. */
|
|
37
|
+
export interface DomPosition {
|
|
38
|
+
node: Node;
|
|
39
|
+
offset: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
36
42
|
/** Whether an element is a block the caret may live inside. Injected so the editable set is policy. */
|
|
37
43
|
export type EditablePredicate = (el: HTMLElement) => boolean;
|
|
38
44
|
|
|
@@ -91,7 +97,7 @@ export function elementForPath(container: HTMLElement, path: JxPath): HTMLElemen
|
|
|
91
97
|
|
|
92
98
|
/** The block's rendered text length — the maximum valid offset within it. */
|
|
93
99
|
export function blockTextLength(block: HTMLElement): number {
|
|
94
|
-
return block.textContent
|
|
100
|
+
return block.textContent.length;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
/**
|
|
@@ -126,11 +132,12 @@ export function offsetOf(block: HTMLElement, node: Node, offset: number): number
|
|
|
126
132
|
* every offset-based editor carries at a line break, and it is invisible for the paths that use
|
|
127
133
|
* this (caret restore after a patch, and block-boundary edits, which clamp to 0 / length anyway).
|
|
128
134
|
*/
|
|
129
|
-
export function domPositionAt(block: HTMLElement, offset: number):
|
|
135
|
+
export function domPositionAt(block: HTMLElement, offset: number): DomPosition {
|
|
130
136
|
const target = Math.max(0, Math.min(offset, blockTextLength(block)));
|
|
131
137
|
const walker = block.ownerDocument.createTreeWalker(block, NodeFilter.SHOW_TEXT);
|
|
132
138
|
let seen = 0;
|
|
133
139
|
let last: Text | null = null;
|
|
140
|
+
// SAFETY: the walker was created with NodeFilter.SHOW_TEXT, so every node it yields is a Text.
|
|
134
141
|
let current = walker.nextNode() as Text | null;
|
|
135
142
|
while (current) {
|
|
136
143
|
const len = current.length;
|
|
@@ -139,6 +146,7 @@ export function domPositionAt(block: HTMLElement, offset: number): { node: Node;
|
|
|
139
146
|
}
|
|
140
147
|
seen += len;
|
|
141
148
|
last = current;
|
|
149
|
+
// SAFETY: the same SHOW_TEXT walker as above, so the next node is a Text or the walk's end.
|
|
142
150
|
current = walker.nextNode() as Text | null;
|
|
143
151
|
}
|
|
144
152
|
// No text node reaches the target: either the block has no text at all (caret goes to the element
|
|
@@ -169,10 +177,7 @@ export function toDocPos(
|
|
|
169
177
|
* block whose text shrank under a remote edit lands at the block's new end instead of being
|
|
170
178
|
* dropped.
|
|
171
179
|
*/
|
|
172
|
-
export function toDomPosition(
|
|
173
|
-
container: HTMLElement,
|
|
174
|
-
pos: DocPos,
|
|
175
|
-
): { node: Node; offset: number } | null {
|
|
180
|
+
export function toDomPosition(container: HTMLElement, pos: DocPos): DomPosition | null {
|
|
176
181
|
const el = elementForPath(container, pos.path);
|
|
177
182
|
return el ? domPositionAt(el, pos.offset) : null;
|
|
178
183
|
}
|
|
@@ -246,6 +251,7 @@ export function adjacentBlock(
|
|
|
246
251
|
const key = serializeJxPath(path);
|
|
247
252
|
const index = blocks.findIndex((el) => el.dataset.jxPath === key);
|
|
248
253
|
const neighbour = index === -1 ? undefined : blocks[index + direction];
|
|
254
|
+
// SAFETY: blocksInOrder collects only elements matching `[data-jx-path]`, so the attribute is set.
|
|
249
255
|
return neighbour
|
|
250
256
|
? { el: neighbour, path: parseJxPath(neighbour.dataset.jxPath as string) }
|
|
251
257
|
: null;
|