@panphora/clayjs 1.16.0 → 1.18.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/dist/all.js +1 -1
- package/dist/clay-dom.js +1 -1
- package/dist/clay-events.js +1 -1
- package/dist/clay-internals.js +1 -1
- package/dist/clay-options.js +1 -1
- package/dist/clay-router.js +1 -1
- package/dist/clay-ui.js +1 -1
- package/dist/clay-utils.js +1 -1
- package/dist/clay.js +1 -1
- package/dist/clay.standalone.js +72519 -59839
- package/dist/m/attrs/refetch-on-save-ZUE7PSCI.js +1 -0
- package/dist/m/attrs/save-freeze-LWGF6XUN.js +1 -0
- package/dist/m/c-2GRUM77Q.js +30 -0
- package/dist/m/c-2KIEGNC4.js +1 -0
- package/dist/m/{c-5R4PTRG2.js → c-2KL3BD52.js} +1 -1
- package/dist/m/c-2YYDMLNZ.js +1 -0
- package/dist/m/c-2ZZBC45O.js +1 -0
- package/dist/m/c-372N6H3B.js +1 -0
- package/dist/m/{c-4WKFKEKF.js → c-3G2DGZUP.js} +1 -1
- package/dist/m/c-3LDGIBMN.js +1 -0
- package/dist/m/c-3O7OBDCM.js +1 -0
- package/dist/m/c-4ASQPRQL.js +1 -0
- package/dist/m/c-4TXNLX4K.js +6 -0
- package/dist/m/c-4YU3BR4D.js +6 -0
- package/dist/m/c-5734MF2P.js +1 -0
- package/dist/m/c-57XDY6HC.js +1 -0
- package/dist/m/{c-VFX45IZQ.js → c-5B4YBZXC.js} +1 -1
- package/dist/m/c-5ONTYIUI.js +1 -0
- package/dist/m/c-5OOVFCJM.js +1 -0
- package/dist/m/c-5T6NZHNM.js +1 -0
- package/dist/m/{c-YDH7Q3NY.js → c-5ZQV7KC4.js} +1 -1
- package/dist/m/{c-Z36J63PP.js → c-6DB2OLRJ.js} +1 -1
- package/dist/m/c-6DNRCPNM.js +1 -0
- package/dist/m/c-6UGWFMQC.js +2 -0
- package/dist/m/c-7K4SCQB7.js +1 -0
- package/dist/m/c-7MG5RU63.js +1 -0
- package/dist/m/c-7PRJCA3E.js +1 -0
- package/dist/m/c-A6L3FMUS.js +1 -0
- package/dist/m/c-AMU3BPPR.js +4 -0
- package/dist/m/c-AMYEFA2E.js +1 -0
- package/dist/m/c-AQYCG7QW.js +1 -0
- package/dist/m/c-ATKAOH23.js +1 -0
- package/dist/m/c-B3JM5ZXC.js +1 -0
- package/dist/m/c-B5CS55WR.js +1 -0
- package/dist/m/c-B5UJQT7W.js +1 -0
- package/dist/m/c-B7J4K2AE.js +3 -0
- package/dist/m/c-BHZYSHW4.js +1 -0
- package/dist/m/c-BIJALSRD.js +1 -0
- package/dist/m/c-BJ7YQ5QZ.js +1 -0
- package/dist/m/c-BPF2NSPV.js +1 -0
- package/dist/m/{c-KIPQJX4B.js → c-BRLCTNMC.js} +1 -1
- package/dist/m/c-BUAYFEV5.js +1 -0
- package/dist/m/c-C6IPRG34.js +1 -0
- package/dist/m/{c-2JL6E75N.js → c-CAS3NND4.js} +1 -1
- package/dist/m/{c-4DRMS4QG.js → c-CCQ25GFY.js} +1 -1
- package/dist/m/c-CJFDLD43.js +1 -0
- package/dist/m/c-COPTL5WB.js +1 -0
- package/dist/m/c-D3E4WJ4M.js +166 -0
- package/dist/m/c-D5IKEKXW.js +7 -0
- package/dist/m/c-DHBRA6DL.js +1 -0
- package/dist/m/c-DKLS74XB.js +72 -0
- package/dist/m/c-DVYGFTSK.js +3 -0
- package/dist/m/c-E3ZXARWA.js +1 -0
- package/dist/m/c-EK63G3CG.js +1 -0
- package/dist/m/c-ERQSNR6K.js +13 -0
- package/dist/m/c-EVYAISP3.js +1 -0
- package/dist/m/c-EXKHPOVC.js +3 -0
- package/dist/m/{c-7VGZ7RU5.js → c-FWKHM4E7.js} +1 -1
- package/dist/m/{c-4Q2XTQ2N.js → c-GBKB5WDL.js} +1 -1
- package/dist/m/c-GNDL3TKB.js +1 -0
- package/dist/m/c-GRWMD6O5.js +1 -0
- package/dist/m/c-GRXHHFPO.js +1 -0
- package/dist/m/{c-OYTFP5QN.js → c-GY422N3Q.js} +1 -1
- package/dist/m/c-HAWFNBYG.js +1 -0
- package/dist/m/c-HG2C77BM.js +4 -0
- package/dist/m/c-HHGJBU4D.js +1 -0
- package/dist/m/c-HMV6P4RX.js +1 -0
- package/dist/m/c-HMYWIHFG.js +1 -0
- package/dist/m/c-HT2DKBV2.js +1 -0
- package/dist/m/c-I2SCCUEW.js +1 -0
- package/dist/m/{c-HRDCD3WN.js → c-ICJYHABF.js} +2 -2
- package/dist/m/c-IMZCWYHR.js +7 -0
- package/dist/m/c-IVMD3FEM.js +1 -0
- package/dist/m/c-IVTRVENZ.js +1 -0
- package/dist/m/c-IW6GGOU2.js +1 -0
- package/dist/m/c-JKZJIUIP.js +1 -0
- package/dist/m/{c-NZMNXVF6.js → c-JNLX3RJB.js} +1 -1
- package/dist/m/c-JRKY44DK.js +1 -0
- package/dist/m/c-K3ADYNLU.js +1 -0
- package/dist/m/c-KBS3UV6B.js +1 -0
- package/dist/m/c-KGHVFRR3.js +1 -0
- package/dist/m/c-KJJPV77R.js +1 -0
- package/dist/m/{c-CZUSOB7V.js → c-KNPDOMDY.js} +1 -1
- package/dist/m/{c-5Y6TNX5W.js → c-KP64LT2A.js} +2 -2
- package/dist/m/c-KQ7E3PYY.js +1 -0
- package/dist/m/c-L3F5ZLIV.js +1 -0
- package/dist/m/{c-LR4CGEPT.js → c-L42OLXWM.js} +3 -2
- package/dist/m/c-L5VFATT6.js +1 -0
- package/dist/m/c-L7JL5TL3.js +3 -0
- package/dist/m/c-LKSL4AUJ.js +1 -0
- package/dist/m/c-M6FNQCTQ.js +1 -0
- package/dist/m/c-MCOOHCSG.js +1 -0
- package/dist/m/c-MHI3GYKF.js +1 -0
- package/dist/m/c-MWXFLW4J.js +1 -0
- package/dist/m/c-NEL7HQW3.js +1 -0
- package/dist/m/c-NQJRVJ56.js +1 -0
- package/dist/m/c-NWX3MFT4.js +1 -0
- package/dist/m/{c-HPURYVZS.js → c-OJYM642Q.js} +1 -1
- package/dist/m/c-OKXI52N7.js +1 -0
- package/dist/m/{c-MMUIUUZG.js → c-OPLUZR3A.js} +1 -1
- package/dist/m/{c-J7CA54IP.js → c-OTMKN2OO.js} +1 -1
- package/dist/m/{c-LTGMXXKO.js → c-OYRZ7XG2.js} +1 -1
- package/dist/m/c-P3OFYUMA.js +1 -0
- package/dist/m/c-PLN76GF3.js +6 -0
- package/dist/m/c-PLP4JKFC.js +1 -0
- package/dist/m/c-Q26MME6N.js +1 -0
- package/dist/m/{c-S7H2DRCB.js → c-Q3XOWZEV.js} +1 -1
- package/dist/m/{c-SWRJFCNN.js → c-QIKT6SKM.js} +1 -1
- package/dist/m/c-QIXT3Q7C.js +28 -0
- package/dist/m/c-QO4HNGCQ.js +1 -0
- package/dist/m/c-R27DWQKX.js +1 -0
- package/dist/m/{c-HVYXIS72.js → c-R2XUODVY.js} +1 -1
- package/dist/m/{c-QNKREOL6.js → c-R7LBFMF5.js} +1 -1
- package/dist/m/c-RND3G2S2.js +1 -0
- package/dist/m/c-S2U6NJU4.js +1 -0
- package/dist/m/c-SPZPGD3R.js +1 -0
- package/dist/m/c-SW4YEG2D.js +8 -0
- package/dist/m/c-SZGYW5HT.js +1 -0
- package/dist/m/c-T637T7SN.js +1 -0
- package/dist/m/c-TJAXGIRV.js +1 -0
- package/dist/m/{c-KKWLDOPM.js → c-TNEM3RNM.js} +1 -1
- package/dist/m/c-TTB5EK4S.js +32 -0
- package/dist/m/c-U7SHNWDH.js +1 -0
- package/dist/m/c-UDHIVLYH.js +20 -0
- package/dist/m/{c-BBVGTQG7.js → c-UGNV5MFB.js} +1 -1
- package/dist/m/{c-TQZ2JYHD.js → c-UORXLSD3.js} +1 -1
- package/dist/m/c-UPD33ZPE.js +1 -0
- package/dist/m/c-URSMSFCB.js +1 -0
- package/dist/m/c-UY4QX42B.js +1 -0
- package/dist/m/c-V44KWI2L.js +4 -0
- package/dist/m/c-VG5XIN4T.js +1 -0
- package/dist/m/c-VILENZTD.js +1 -0
- package/dist/m/{c-33HUYEDI.js → c-VJYEVRYK.js} +1 -1
- package/dist/m/c-VR2BJ4AN.js +1 -0
- package/dist/m/c-W4JCVLAB.js +1 -0
- package/dist/m/c-W6DQSHXI.js +135 -0
- package/dist/m/c-WBGWGOM7.js +1 -0
- package/dist/m/{c-4L3BTFRK.js → c-WCXLG3MV.js} +1 -1
- package/dist/m/c-WGW764WX.js +1 -0
- package/dist/m/c-WMRJKQ2V.js +5 -0
- package/dist/m/c-WNTYKCDG.js +1 -0
- package/dist/m/c-WQWIRWO3.js +72 -0
- package/dist/m/c-X5LNHET5.js +1 -0
- package/dist/m/c-X5TSXZYJ.js +2 -0
- package/dist/m/c-XGH4GS2T.js +1 -0
- package/dist/m/c-Y6F2FORL.js +1 -0
- package/dist/m/c-YBKHUAK7.js +10 -0
- package/dist/m/c-YBSS55KU.js +20 -0
- package/dist/m/c-YCLU4UNT.js +2 -0
- package/dist/m/c-YVZ2L7CE.js +1 -0
- package/dist/m/c-ZK4GIZH2.js +1 -0
- package/dist/m/c-ZRXJD4G6.js +1 -0
- package/dist/m/c-ZWBI4HYK.js +1 -0
- package/dist/m/{c-HT33EQRN.js → c-ZWIOJGMT.js} +1 -1
- package/dist/m/core/admin-attrs-YY7T5FGU.js +1 -0
- package/dist/m/core/autosave-S6OMNDCK.js +1 -0
- package/dist/m/core/conflict-notice-IWHMHH43.js +1 -0
- package/dist/m/core/host-permission-6EVJGNPL.js +1 -0
- package/dist/m/core/is-edit-mode-GCX6767E.js +1 -0
- package/dist/m/core/{persist-HKHA66IY.js → persist-UKXV5KLA.js} +1 -1
- package/dist/m/core/recovery-DRFZRJED.js +1 -0
- package/dist/m/core/save-LYY7K6U7.js +1 -0
- package/dist/m/core/save-core-MKRPSEEO.js +1 -0
- package/dist/m/core/snapshot-NU3YM6GR.js +1 -0
- package/dist/m/core/unsaved-warning-7QTKEN7Z.js +1 -0
- package/dist/m/events/index-RN2JNGBV.js +1 -0
- package/dist/m/internals/index-TGLQINN3.js +1 -0
- package/dist/m/lib/cache-bust-MUMFXZK6.js +1 -0
- package/dist/m/loader-3BBNZZGM.js +1 -0
- package/dist/m/manifest-F449FF98.js +1 -0
- package/dist/m/manifest-edit-4396A009.js +1 -0
- package/dist/m/manifest.json +2509 -1474
- package/dist/m/options/options-EJMS3K24.js +1 -0
- package/dist/m/page/editor-6LRB3GE4.js +304 -0
- package/dist/m/plugins/ai-edit-UTV7YO2I.js +6 -0
- package/dist/m/plugins/{cms-HP3LSTPN.js → cms-LRAOZHT7.js} +9 -9
- package/dist/m/plugins/demo-OYO5GCAO.js +1 -0
- package/dist/m/plugins/{derive-UAAA4V4S.js → derive-FRE5JJQE.js} +1 -1
- package/dist/m/plugins/indicator-4VDXWYTD.js +1 -0
- package/dist/m/plugins/live-AD4H6XSL.js +1 -0
- package/dist/m/plugins/menu-M5WW7DBF.js +1 -0
- package/dist/m/plugins/page-MD3T2ZX3.js +2 -0
- package/dist/m/plugins/page-view-PAWVZNFY.js +1 -0
- package/dist/m/plugins/people-JCHVPF7P.js +1 -0
- package/dist/m/plugins/quickcrop-BQ5YYFCH.js +26 -0
- package/dist/m/plugins/{richclay-RC3L2F6X.js → richclay-KLGETK77.js} +2 -2
- package/dist/m/plugins/sortable-4S6SQC3D.js +1 -0
- package/dist/m/plugins/source-TJXOIOYY.js +1 -0
- package/dist/m/plugins/undo-NLPGCJQY.js +1 -0
- package/dist/m/plugins/{unsent-HJFLQJ2O.js → unsent-OA4RGBUT.js} +1 -1
- package/dist/m/plugins/upgrade-HSUNDNJD.js +1 -0
- package/dist/m/plugins/upload-NHGJXZK2.js +1 -0
- package/dist/m/plugins/visitor-drafts-BW43DGY3.js +20 -0
- package/dist/m/plugins/visitor-drafts-owner-7ODQG7JX.js +1 -0
- package/dist/m/plugins/wire-VBWXEXU2.js +1 -0
- package/dist/m/reader-core-4W2KYEWQ.js +77 -0
- package/dist/m/route/route-62CHXL5B.js +1 -0
- package/dist/m/sync/live-sync-MO5DH33W.js +1 -0
- package/dist/m/ui/index-IVWN2QC4.js +1 -0
- package/dist/m/vendor/sap-core.vendor-RN4ZVY4X.js +6 -0
- package/dist/offline-manifest.json +1560 -889
- package/dist/page/themes/light-1.css +33 -8
- package/dist/server/core.cjs +1554 -311
- package/dist/server/offline-runtime.cjs +63 -63
- package/dist/server/offline-runtime.mjs +63 -63
- package/dist/server/page-compaction-worker.cjs +3710 -0
- package/dist/server/rooms.mjs +5 -0
- package/docs/guide.md +653 -101
- package/entries/sap.js +5 -5
- package/package.json +1 -1
- package/src/core/admin-contenteditable.js +5 -2
- package/src/core/admin-inputs.js +1 -0
- package/src/core/admin-onclick.js +2 -1
- package/src/core/boot-phase.js +2 -1
- package/src/core/conflict-download.js +3 -29
- package/src/core/conflict-notice-ui.js +4 -7
- package/src/core/download.js +32 -0
- package/src/core/host-meta.js +56 -0
- package/src/core/is-edit-mode.js +17 -1
- package/src/core/mode-key.js +125 -19
- package/src/core/mode-support.js +5 -0
- package/src/core/mode-transition.js +20 -6
- package/src/core/preserve-authored.js +4 -4
- package/src/core/recovery.js +121 -33
- package/src/core/save-accepted.js +33 -0
- package/src/core/save-core.js +111 -32
- package/src/core/save-job.js +108 -0
- package/src/core/save-prepare.js +53 -0
- package/src/core/save-size-notice.js +3 -1
- package/src/core/save-slices.js +15 -0
- package/src/core/save.js +557 -127
- package/src/core/serialize-steps.js +109 -0
- package/src/core/snapshot.js +209 -40
- package/src/core/source-map.js +83 -38
- package/src/core/stale-host-notice.js +1 -0
- package/src/core/upgrade-run.js +59 -8
- package/src/dom/active-element.js +5 -0
- package/src/lib/assets.js +1 -0
- package/src/lib/autosave-scheduler.js +3 -4
- package/src/lib/slice.js +65 -0
- package/src/lib/uimotion.js +774 -0
- package/src/lib/unsaved-state.js +2 -2
- package/src/live/fast-lane.js +10 -3
- package/src/live/local-lane.js +202 -11
- package/src/live/ops.js +16 -0
- package/src/live/page-compaction.js +162 -0
- package/src/live/server-lane.js +1 -0
- package/src/live/store.js +11 -3
- package/src/loader-logic.js +3 -1
- package/src/loader.js +14 -4
- package/src/page/adopt-boot.js +2 -1
- package/src/page/awareness.js +33 -2
- package/src/page/blocks.js +122 -27
- package/src/page/board-views.js +2 -16
- package/src/page/boot-pack.js +1 -1
- package/src/page/chart-tables.js +29 -0
- package/src/page/chart.js +411 -72
- package/src/page/checklist.js +10 -135
- package/src/page/checkpoint-proof.js +295 -0
- package/src/page/chrome.js +257 -40
- package/src/page/clipboard-shape.js +126 -0
- package/src/page/document-title-dom.js +12 -0
- package/src/page/document-title-runtime.js +15 -1
- package/src/page/document-title.js +4 -1
- package/src/page/drag.js +33 -3
- package/src/page/editor.js +207 -31
- package/src/page/emoji-data.js +162 -0
- package/src/page/export-assets.js +357 -0
- package/src/page/export-html.js +209 -0
- package/src/page/export-markdown.js +361 -0
- package/src/page/features/board.js +12 -3
- package/src/page/features/chart.js +11 -0
- package/src/page/features/checklist.js +428 -0
- package/src/page/features/dates.js +180 -0
- package/src/page/features/emoji.js +187 -0
- package/src/page/features/export-html.js +189 -0
- package/src/page/features/export-markdown.js +169 -0
- package/src/page/features/import-markdown.js +349 -0
- package/src/page/features/media.js +61 -5
- package/src/page/features/mentions.js +188 -0
- package/src/page/features/registry.js +24 -1
- package/src/page/features/table.js +375 -85
- package/src/page/features/title-cover.js +331 -0
- package/src/page/features/title-icon.js +213 -0
- package/src/page/features/toc.js +87 -0
- package/src/page/format-command.js +33 -0
- package/src/page/format-gate.js +68 -18
- package/src/page/format-host.js +71 -0
- package/src/page/format-native-commands.js +158 -0
- package/src/page/format-native.js +42 -0
- package/src/page/image-actions.js +64 -0
- package/src/page/import-markdown.js +262 -0
- package/src/page/inline.js +32 -8
- package/src/page/items.js +141 -11
- package/src/page/markdown.js +30 -2
- package/src/page/media.js +35 -119
- package/src/page/mention-picker.js +99 -0
- package/src/page/merge.js +1 -1
- package/src/page/placeholder.js +14 -1
- package/src/page/projection.js +156 -25
- package/src/page/recover.js +10 -12
- package/src/page/recovery-ui.js +464 -0
- package/src/page/rich/chart.js +63 -7
- package/src/page/save-projection.js +67 -0
- package/src/page/save-states.js +14 -4
- package/src/page/schema.js +52 -3
- package/src/page/table-paste.js +3 -1
- package/src/page/table-style.js +4 -4
- package/src/page/table.js +44 -2
- package/src/page/themes/content.css +25 -1
- package/src/page/themes/light-1.css +33 -8
- package/src/page/themes/light-1.source.css +8 -7
- package/src/page/toc-link.js +31 -0
- package/src/page/toc.js +5 -0
- package/src/page/view-hooks.js +58 -0
- package/src/page/view.js +13 -0
- package/src/page/ybind/binding.js +116 -6
- package/src/page/ybind/bridge.js +187 -20
- package/src/page/ybind/containment.js +162 -0
- package/src/page/ybind/doc.js +17 -9
- package/src/page/ybind/file-debt.js +63 -0
- package/src/page/ybind/layout.js +7 -2
- package/src/page/ybind/marks.js +2 -1
- package/src/page/ybind/moves.js +5 -4
- package/src/page/ybind/origins.js +106 -4
- package/src/page/ybind/parts.js +8 -1
- package/src/page/ybind/provider.js +125 -18
- package/src/page/ybind/recovery-proof.js +32 -9
- package/src/page/ybind/recovery-replay.js +905 -4
- package/src/page/ybind/recovery.js +174 -45
- package/src/page/ybind/table.js +67 -11
- package/src/page/ybind/walk-delta.js +258 -0
- package/src/page/ybind/walk.js +54 -29
- package/src/page/ybind/writer.js +1 -1
- package/src/plugins/ai-edit.js +13 -9
- package/src/plugins/indicator-chip.js +91 -0
- package/src/plugins/indicator.js +13 -80
- package/src/plugins/live.js +126 -15
- package/src/plugins/menu/actions.js +196 -0
- package/src/plugins/menu/people.js +279 -0
- package/src/plugins/menu/placement.js +121 -0
- package/src/plugins/menu/preferences.js +9 -0
- package/src/plugins/menu/seo.js +316 -397
- package/src/plugins/menu/style.js +171 -0
- package/src/plugins/menu/variants.js +296 -0
- package/src/plugins/menu.js +186 -183
- package/src/plugins/page-view.js +3 -2
- package/src/plugins/page.js +362 -43
- package/src/plugins/sortable.js +8 -1
- package/src/plugins/source.js +6 -0
- package/src/plugins/upgrade.js +51 -17
- package/src/plugins/upload-ui.js +12 -17
- package/src/plugins/visitor-drafts/bottom-ui.js +12 -4
- package/src/plugins/visitor-drafts/intro-style.js +7 -2
- package/src/plugins/visitor-drafts/intro.js +7 -4
- package/src/plugins/visitor-drafts/owner-chip.js +8 -12
- package/src/plugins/visitor-drafts/prompt.js +11 -9
- package/src/plugins/visitor-drafts/source.js +4 -3
- package/src/plugins/visitor-drafts/store.js +138 -9
- package/src/plugins/visitor-drafts/ui-memory.js +2 -2
- package/src/plugins/visitor-drafts-owner.js +4 -6
- package/src/plugins/visitor-drafts.js +5 -3
- package/src/recovery/store.js +1 -1
- package/src/route/route.js +4 -0
- package/src/server/checkpoint/index.js +2 -2
- package/src/server/rooms/index.js +5 -1
- package/src/server/rooms/live-data-rooms.js +302 -50
- package/src/server/rooms/page-compaction-worker.js +12 -0
- package/src/server/rooms/page-compactor.js +92 -0
- package/src/server/rooms/page-room-conformance.js +461 -0
- package/src/source/parse-steps.js +94 -0
- package/src/source/session.js +233 -57
- package/src/sync/editing-sender.js +12 -0
- package/src/sync/live-sync-edit.js +22 -2
- package/src/sync/live-sync.js +150 -23
- package/src/sync/page-relay.js +52 -0
- package/src/sync/presence-store.js +44 -4
- package/src/sync/presence.js +6 -3
- package/src/sync/saved-stream.js +28 -14
- package/src/ui/bevel-controls.js +1 -0
- package/src/ui/bevel-dialog.js +5 -2
- package/src/ui/editor-ui.js +77 -0
- package/src/ui/page-colour.js +281 -0
- package/src/ui/toast.js +16 -7
- package/src/vendor/hyper-html-api-upgrade.vendor.js +2 -2
- package/src/vendor/hyper-undo.vendor.js +1 -1
- package/src/vendor/hypercms.vendor.js +9 -9
- package/src/vendor/parse5.vendor.js +2 -2
- package/src/vendor/sap-core.vendor.js +5 -5
- package/dist/m/attrs/refetch-on-save-NWPPXOCZ.js +0 -1
- package/dist/m/attrs/save-freeze-FUP4Q4X3.js +0 -1
- package/dist/m/c-2M73252O.js +0 -1
- package/dist/m/c-2SPVQSHC.js +0 -1
- package/dist/m/c-35NBGMZB.js +0 -1
- package/dist/m/c-3IQRECYS.js +0 -1
- package/dist/m/c-4B6H4G5V.js +0 -1
- package/dist/m/c-4FFHZM32.js +0 -1
- package/dist/m/c-4YSCRICO.js +0 -1
- package/dist/m/c-54RCDCBU.js +0 -1
- package/dist/m/c-5M4YEKCX.js +0 -4
- package/dist/m/c-5V3MOVJT.js +0 -1
- package/dist/m/c-66O32MV3.js +0 -28
- package/dist/m/c-6HMB24EV.js +0 -1
- package/dist/m/c-7O6AFR65.js +0 -1
- package/dist/m/c-ANQTJ3B5.js +0 -2
- package/dist/m/c-AYVOREBE.js +0 -1
- package/dist/m/c-B2ZXPOS3.js +0 -3
- package/dist/m/c-BMIZKMPV.js +0 -1
- package/dist/m/c-BTDI4Y23.js +0 -1
- package/dist/m/c-CJZSLMVQ.js +0 -1
- package/dist/m/c-DBE6ORSI.js +0 -1
- package/dist/m/c-EDHUR4MX.js +0 -1
- package/dist/m/c-EY6UOM54.js +0 -1
- package/dist/m/c-F3CKJTJS.js +0 -1
- package/dist/m/c-F7Y6FLPY.js +0 -1
- package/dist/m/c-FA4DZVTC.js +0 -1
- package/dist/m/c-G2BUZITR.js +0 -1
- package/dist/m/c-GB3JTV6E.js +0 -1
- package/dist/m/c-GBGWXTX4.js +0 -1
- package/dist/m/c-GHBCCVG4.js +0 -1
- package/dist/m/c-GJAEGGXY.js +0 -1
- package/dist/m/c-GOWBDM6J.js +0 -1
- package/dist/m/c-IB52R237.js +0 -1
- package/dist/m/c-IKWNE4R4.js +0 -1
- package/dist/m/c-IU3IYEU6.js +0 -1
- package/dist/m/c-IZ2EBRAJ.js +0 -1
- package/dist/m/c-KR4UW4KW.js +0 -1
- package/dist/m/c-KY3VJGUA.js +0 -8
- package/dist/m/c-LFPHCXY3.js +0 -1
- package/dist/m/c-LRPUO2MX.js +0 -1
- package/dist/m/c-LXENEBV7.js +0 -1
- package/dist/m/c-NMJRXKPC.js +0 -8
- package/dist/m/c-OIBUTDXI.js +0 -1
- package/dist/m/c-PPOSCAQD.js +0 -1
- package/dist/m/c-QIX5BJYN.js +0 -1
- package/dist/m/c-QQHEX6HA.js +0 -1
- package/dist/m/c-R7RMANJI.js +0 -9
- package/dist/m/c-RI4NT75Y.js +0 -1
- package/dist/m/c-RM7J7BV2.js +0 -1
- package/dist/m/c-RUJKSUQW.js +0 -1
- package/dist/m/c-SGCCC4FW.js +0 -25
- package/dist/m/c-T4VO4OCB.js +0 -1
- package/dist/m/c-TQEMVJCS.js +0 -1
- package/dist/m/c-TR6RMDQ7.js +0 -3
- package/dist/m/c-UKHTLWUW.js +0 -1
- package/dist/m/c-UNN7LWCT.js +0 -1
- package/dist/m/c-UXBMRDZB.js +0 -235
- package/dist/m/c-V7PZZRG7.js +0 -1
- package/dist/m/c-VL52ADDR.js +0 -1
- package/dist/m/c-WNYBLIOZ.js +0 -1
- package/dist/m/c-WWDBYXLK.js +0 -1
- package/dist/m/c-XNNW2CWK.js +0 -1
- package/dist/m/c-Y6USGTS5.js +0 -5
- package/dist/m/c-YG2NJYS6.js +0 -135
- package/dist/m/c-Z4VRC7GU.js +0 -7
- package/dist/m/c-Z6K52OLP.js +0 -19
- package/dist/m/c-ZDY2MDAA.js +0 -21
- package/dist/m/c-ZUCOKOHT.js +0 -1
- package/dist/m/core/admin-attrs-VGNLAUDK.js +0 -1
- package/dist/m/core/autosave-E4PEFPZ6.js +0 -1
- package/dist/m/core/conflict-notice-VQ6Y64PH.js +0 -1
- package/dist/m/core/host-permission-XN3K2IW3.js +0 -1
- package/dist/m/core/is-edit-mode-JXHDVGUL.js +0 -1
- package/dist/m/core/recovery-TBBSICE2.js +0 -1
- package/dist/m/core/save-WUCHTQBE.js +0 -1
- package/dist/m/core/save-core-FJVHT5M3.js +0 -1
- package/dist/m/core/snapshot-F3SWDZMM.js +0 -1
- package/dist/m/core/unsaved-warning-STKBHE6G.js +0 -1
- package/dist/m/events/index-23DPTUT4.js +0 -1
- package/dist/m/internals/index-WOVAP3RU.js +0 -1
- package/dist/m/lib/cache-bust-CGWT6ZJI.js +0 -1
- package/dist/m/loader-HXX6YNH5.js +0 -1
- package/dist/m/manifest-F22DEDAC.js +0 -1
- package/dist/m/manifest-edit-C3C7376B.js +0 -1
- package/dist/m/options/options-2LH76JQ3.js +0 -1
- package/dist/m/page/editor-XUTB2HB2.js +0 -189
- package/dist/m/plugins/ai-edit-ZQ4CGWIP.js +0 -6
- package/dist/m/plugins/demo-ZIBFAA3R.js +0 -1
- package/dist/m/plugins/indicator-PV4YW2ID.js +0 -1
- package/dist/m/plugins/live-NUTTXSBS.js +0 -1
- package/dist/m/plugins/menu-HCKO3DSP.js +0 -138
- package/dist/m/plugins/page-KSUH7G6J.js +0 -2
- package/dist/m/plugins/page-view-W73OW7WI.js +0 -1
- package/dist/m/plugins/people-AJJ32YCG.js +0 -1
- package/dist/m/plugins/quickcrop-QNY3PKLD.js +0 -26
- package/dist/m/plugins/sortable-YRMQX3ZU.js +0 -1
- package/dist/m/plugins/source-KHK5UUSK.js +0 -1
- package/dist/m/plugins/undo-OF2L3CXU.js +0 -1
- package/dist/m/plugins/upgrade-ZNYODP5Y.js +0 -1
- package/dist/m/plugins/upload-XSGNILUY.js +0 -1
- package/dist/m/plugins/visitor-drafts-XK5GW55Q.js +0 -21
- package/dist/m/plugins/visitor-drafts-owner-KSSH75EV.js +0 -1
- package/dist/m/plugins/wire-ZWVWKF76.js +0 -1
- package/dist/m/reader-core-ILWLXVMP.js +0 -68
- package/dist/m/route/route-KOCZSEGN.js +0 -1
- package/dist/m/sync/live-sync-4AUVXVJU.js +0 -1
- package/dist/m/ui/index-EVHKMKBL.js +0 -1
- package/dist/m/vendor/sap-core.vendor-A7BMUYZT.js +0 -6
- /package/dist/m/{c-FILLTJA7.js → c-QTTJYJMA.js} +0 -0
- /package/dist/m/plugins/{quickcrop-QNY3PKLD.js.LEGAL.txt → quickcrop-BQ5YYFCH.js.LEGAL.txt} +0 -0
- /package/dist/m/plugins/{richclay-RC3L2F6X.js.LEGAL.txt → richclay-KLGETK77.js.LEGAL.txt} +0 -0
package/src/page/format-gate.js
CHANGED
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
// lands between `capabilities()` and `activate()` is refused rather than missed.
|
|
30
30
|
|
|
31
31
|
import { PAGE_FORMAT, PAGE_FORMAT_EXTENSION, PAGE_FORMAT_UPGRADE, formatCapability, notePageFormatFloor, pageFormatDecision, pageFormatFloor } from "./format-level.js";
|
|
32
|
+
import { registerUnsavedState } from "../lib/unsaved-state.js";
|
|
33
|
+
|
|
34
|
+
const PENDING_COMMAND = "page-format-command";
|
|
32
35
|
|
|
33
36
|
const UPGRADE_MESSAGE = "This document uses a newer page format; update the editor in the other tabs to keep writing";
|
|
34
37
|
|
|
@@ -46,6 +49,10 @@ export function createFormatGate({
|
|
|
46
49
|
let floor = 0;
|
|
47
50
|
let pending = null;
|
|
48
51
|
let lanePending = false;
|
|
52
|
+
let laneReceipt = null;
|
|
53
|
+
let laneAsked = false;
|
|
54
|
+
let laneJoin = Promise.resolve(false);
|
|
55
|
+
let laneJoining = null;
|
|
49
56
|
let disposed = false;
|
|
50
57
|
|
|
51
58
|
// The floor this gate knows the document at. A save or a room answer can tell
|
|
@@ -54,9 +61,22 @@ export function createFormatGate({
|
|
|
54
61
|
function note(level) {
|
|
55
62
|
floor = Math.max(floor, formatCapability(level));
|
|
56
63
|
notePageFormatFloor(floor);
|
|
64
|
+
if (floor >= PAGE_FORMAT) requestLane();
|
|
57
65
|
return floor;
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
// A raised floor is one lane switch per tab, however this tab learned of it:
|
|
69
|
+
// its own accepted save (activateNow joins with that receipt), another tab's
|
|
70
|
+
// write that came in with the document, or a relay's meta. A switch the lane
|
|
71
|
+
// has not accepted yet stays pending, and the retry path covers it.
|
|
72
|
+
function requestLane() {
|
|
73
|
+
if (laneAsked || disposed) return laneJoin;
|
|
74
|
+
laneAsked = true;
|
|
75
|
+
lanePending = true;
|
|
76
|
+
laneJoin = joinLane(null);
|
|
77
|
+
return laneJoin;
|
|
78
|
+
}
|
|
79
|
+
|
|
60
80
|
async function read() {
|
|
61
81
|
try {
|
|
62
82
|
const answer = await host.read();
|
|
@@ -88,7 +108,10 @@ export function createFormatGate({
|
|
|
88
108
|
if (disposed) return { status: "disposed", retry: false };
|
|
89
109
|
if (!(await supportedHere())) return { status: "unsupported-host", code: "page-format-unsupported", extension: PAGE_FORMAT_EXTENSION };
|
|
90
110
|
await read();
|
|
91
|
-
if (floor >= PAGE_FORMAT)
|
|
111
|
+
if (floor >= PAGE_FORMAT) {
|
|
112
|
+
await requestLane();
|
|
113
|
+
return { status: "active", floor };
|
|
114
|
+
}
|
|
92
115
|
const admitted = await admittance();
|
|
93
116
|
const early = pageFormatDecision({ floor, required: PAGE_FORMAT, capability, admitted });
|
|
94
117
|
if (!early.ok) return upgrade(early.minimum);
|
|
@@ -106,28 +129,45 @@ export function createFormatGate({
|
|
|
106
129
|
if (disposed) {
|
|
107
130
|
// The save was accepted, so the document is at floor 1 either way; this
|
|
108
131
|
// tab is gone and must not act on it.
|
|
132
|
+
laneAsked = true;
|
|
109
133
|
note(PAGE_FORMAT);
|
|
110
134
|
return { status: "disposed", retry: false };
|
|
111
135
|
}
|
|
136
|
+
laneAsked = true;
|
|
112
137
|
note(PAGE_FORMAT);
|
|
113
138
|
lanePending = true;
|
|
114
|
-
|
|
115
|
-
|
|
139
|
+
laneReceipt = answer.receipt ?? null;
|
|
140
|
+
await joinLane(laneReceipt);
|
|
141
|
+
return { status: "active", floor, receipt: laneReceipt };
|
|
116
142
|
}
|
|
117
143
|
|
|
118
144
|
// Joining the new lane scope happens after the receipt is accepted, and only
|
|
119
145
|
// then: an interrupted switch leaves this tab on floor 0's scope with every
|
|
120
|
-
// store intact and the switch still pending, never a lane it cannot cover.
|
|
146
|
+
// store intact and the switch still pending, never a lane it cannot cover. The
|
|
147
|
+
// receipt is kept, so the retry of a refused switch is covered by the same write.
|
|
121
148
|
async function joinLane(receipt) {
|
|
122
149
|
if (!lane || !lanePending) return false;
|
|
150
|
+
if (laneJoining) return laneJoining;
|
|
151
|
+
const covering = receipt ?? laneReceipt;
|
|
152
|
+
laneJoining = (async () => {
|
|
153
|
+
try {
|
|
154
|
+
// The receipt is what says the pending work went with an accepted write.
|
|
155
|
+
const result = await lane.switchScope(PAGE_FORMAT, { covered: Boolean(covering), receipt: covering });
|
|
156
|
+
// "already" is the lane answering that this tab is on that scope: the
|
|
157
|
+
// join held, and there is nothing left pending.
|
|
158
|
+
if (result && (result.status === "switched" || result.status === "already")) {
|
|
159
|
+
lanePending = false;
|
|
160
|
+
laneReceipt = null;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
} catch {}
|
|
164
|
+
return false;
|
|
165
|
+
})();
|
|
123
166
|
try {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
} catch {}
|
|
130
|
-
return false;
|
|
167
|
+
return await laneJoining;
|
|
168
|
+
} finally {
|
|
169
|
+
laneJoining = null;
|
|
170
|
+
}
|
|
131
171
|
}
|
|
132
172
|
|
|
133
173
|
function activate() {
|
|
@@ -150,16 +190,25 @@ export function createFormatGate({
|
|
|
150
190
|
// same attempt, and a retry after a failure starts a new one.
|
|
151
191
|
activate,
|
|
152
192
|
// A new-format command: capture what it applies to, ask the gate, and apply
|
|
153
|
-
// only when activation succeeded and nothing moved underneath it.
|
|
193
|
+
// only when activation succeeded and nothing moved underneath it. While it waits on
|
|
194
|
+
// activation the work is unsaved and belongs to the person who asked for it: the
|
|
195
|
+
// exit asks the unsaved-state registry, and this command answers to its own name
|
|
196
|
+
// until it applies or fails, so an exit is refused rather than committing and
|
|
197
|
+
// disposing the editor underneath it.
|
|
154
198
|
async run(command) {
|
|
155
199
|
const stamp = { revision: revision(), installed: installed() };
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
return { status: "
|
|
200
|
+
const unsaved = registerUnsavedState({ id: PENDING_COMMAND, isPending: () => true });
|
|
201
|
+
try {
|
|
202
|
+
const result = await activate();
|
|
203
|
+
if (result.status !== "active") return result;
|
|
204
|
+
if (disposed) return { status: "disposed", retry: false };
|
|
205
|
+
if (revision() !== stamp.revision || installed() !== stamp.installed) {
|
|
206
|
+
return { status: "retry", code: "format-gate-stale", retry: true };
|
|
207
|
+
}
|
|
208
|
+
return { status: "applied", floor, result: await command?.apply?.(stamp) };
|
|
209
|
+
} finally {
|
|
210
|
+
unsaved.dispose();
|
|
161
211
|
}
|
|
162
|
-
return { status: "applied", floor, result: await command?.apply?.(stamp) };
|
|
163
212
|
},
|
|
164
213
|
// Retry what is still pending: a lane switch whose receipt is in hand, or an
|
|
165
214
|
// activation that failed on the wire. Unsaved work is never part of this.
|
|
@@ -173,6 +222,7 @@ export function createFormatGate({
|
|
|
173
222
|
dispose() {
|
|
174
223
|
disposed = true;
|
|
175
224
|
lanePending = false;
|
|
225
|
+
laneReceipt = null;
|
|
176
226
|
return { status: "disposed" };
|
|
177
227
|
},
|
|
178
228
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The production host adapter behind the format gate: the floor the document is
|
|
2
|
+
// already at, the host's own admission answer, and the one explicit save that
|
|
3
|
+
// raises it.
|
|
4
|
+
//
|
|
5
|
+
// The host is the authority, so `capabilities()` answers nothing here: the editors
|
|
6
|
+
// admitted to the document are checked under its admission/write lock when the save
|
|
7
|
+
// carrying the marker arrives, which is the check no registration can race. A save
|
|
8
|
+
// refused there reports `page-format-upgrade-required` like any other refusal, and
|
|
9
|
+
// this tab stays at the floor it already knew.
|
|
10
|
+
//
|
|
11
|
+
// Nothing is noted before the host answers: `notePageFormatFloor` runs when the gate
|
|
12
|
+
// accepts the activation, so a refusal leaves every later save at floor 0.
|
|
13
|
+
|
|
14
|
+
import { PAGE_FORMAT, PAGE_FORMAT_META, PAGE_FORMAT_UPGRADE, knownPageFormatFloor, pageFormatFloor } from "./format-level.js";
|
|
15
|
+
import { hostPageFormat } from "../core/host-meta.js";
|
|
16
|
+
|
|
17
|
+
const FAILED = "page-format-activation-failed";
|
|
18
|
+
|
|
19
|
+
// The document's own marker, as it was served. A tab that joined a document already
|
|
20
|
+
// at floor 1 must not ask to activate it again.
|
|
21
|
+
function servedFloor() {
|
|
22
|
+
const tag = document.querySelector(`meta[name="${PAGE_FORMAT_META}"]`);
|
|
23
|
+
return tag ? pageFormatFloor(tag.outerHTML) : 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// The editor's own explicit save, fetched when a floor is really raised: a tab that
|
|
27
|
+
// never uses a newer format never downloads the lane to raise one.
|
|
28
|
+
const savePage = async (options) => (await import("../core/save.js")).savePageForce(() => {}, options);
|
|
29
|
+
|
|
30
|
+
// A forced save asked for while another is on the wire is skipped rather than queued.
|
|
31
|
+
// The activation is the person's own request, so it waits for the save ahead of it to
|
|
32
|
+
// settle and asks again, instead of answering "try again" for a save it never sent.
|
|
33
|
+
const SKIPPED_ATTEMPTS = 4;
|
|
34
|
+
const SKIPPED_WAIT_MS = 10000;
|
|
35
|
+
const skippedBehindSave = (result) => result?.ok === false && result?.msgType === "skipped" && /in progress/i.test(result?.msg ?? "");
|
|
36
|
+
const saveSettles = async () => {
|
|
37
|
+
const { isSaveInProgress } = await import("../core/save-core.js");
|
|
38
|
+
const until = Date.now() + SKIPPED_WAIT_MS;
|
|
39
|
+
while (isSaveInProgress() && Date.now() < until) await new Promise((r) => setTimeout(r, 50));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function createFormatHost({
|
|
43
|
+
save = savePage,
|
|
44
|
+
settles = saveSettles,
|
|
45
|
+
supported = () => hostPageFormat().then((floor) => floor === PAGE_FORMAT),
|
|
46
|
+
} = {}) {
|
|
47
|
+
return {
|
|
48
|
+
read() {
|
|
49
|
+
return { floor: Math.max(knownPageFormatFloor(), servedFloor()), revision: null };
|
|
50
|
+
},
|
|
51
|
+
capabilities() {
|
|
52
|
+
return [];
|
|
53
|
+
},
|
|
54
|
+
supported,
|
|
55
|
+
async activate({ floor = PAGE_FORMAT } = {}) {
|
|
56
|
+
let result;
|
|
57
|
+
try {
|
|
58
|
+
result = await save({ pageFormatFloor: floor });
|
|
59
|
+
for (let attempt = 1; attempt < SKIPPED_ATTEMPTS && skippedBehindSave(result); attempt += 1) {
|
|
60
|
+
await settles();
|
|
61
|
+
result = await save({ pageFormatFloor: floor });
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return { accepted: false, code: error?.code ?? FAILED };
|
|
65
|
+
}
|
|
66
|
+
if (result?.ok) return { accepted: true, revision: null, receipt: result.receipt ?? result.etag ?? null };
|
|
67
|
+
if (result?.code === PAGE_FORMAT_UPGRADE) return { accepted: false, code: PAGE_FORMAT_UPGRADE };
|
|
68
|
+
return { accepted: false, code: result?.code ?? FAILED };
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// The commands behind the four formats this release adds — underline, text color,
|
|
2
|
+
// highlight and the quote block — and the rows the More menu offers them by.
|
|
3
|
+
//
|
|
4
|
+
// Nothing here is on the ready path. Every caller loads this module the first
|
|
5
|
+
// time one of the commands runs, so reading and drawing an existing document
|
|
6
|
+
// never fetches a transaction, a palette or a refusal notice.
|
|
7
|
+
//
|
|
8
|
+
// Every command that writes one of them goes through runFormat, which is the
|
|
9
|
+
// editor's own format gate: nothing is applied and no descriptor is written
|
|
10
|
+
// unless the gate answered "applied".
|
|
11
|
+
import { history } from "../vendor/prosemirror.vendor.js";
|
|
12
|
+
import { schema } from "./schema.js";
|
|
13
|
+
import { PALETTE, markable } from "./format-native.js";
|
|
14
|
+
import { announce } from "./announce.js";
|
|
15
|
+
import toast from "../ui/toast-lazy.js";
|
|
16
|
+
import { openPageMenu } from "./chrome.js";
|
|
17
|
+
|
|
18
|
+
// blocks.js's STEP, read here rather than imported from it: this module is part
|
|
19
|
+
// of the projection's graph, and blocks.js reads the projection's constants as it
|
|
20
|
+
// loads. A command of ours is one undo item by itself, like every other command.
|
|
21
|
+
const STEP = "page-step";
|
|
22
|
+
const asStep = (tr) => history.closeHistory(tr).setMeta(STEP, true);
|
|
23
|
+
|
|
24
|
+
const holds = (mark, attrs) => !!mark && Object.keys(mark.type.attrs).every((name) => mark.attrs[name] === attrs[name]);
|
|
25
|
+
|
|
26
|
+
function toggle(view, type, attrs) {
|
|
27
|
+
const st = view.state;
|
|
28
|
+
const { from, to, empty, $from } = st.selection;
|
|
29
|
+
const tr = st.tr;
|
|
30
|
+
if (empty) {
|
|
31
|
+
if (holds(type.isInSet(st.storedMarks || $from.marks()), attrs)) tr.removeStoredMark(type);
|
|
32
|
+
else tr.addStoredMark(type.create(attrs));
|
|
33
|
+
} else {
|
|
34
|
+
let all = true;
|
|
35
|
+
st.doc.nodesBetween(from, to, (node) => {
|
|
36
|
+
if (all && node.isText && !holds(type.isInSet(node.marks), attrs)) all = false;
|
|
37
|
+
});
|
|
38
|
+
if (all) tr.removeMark(from, to, type);
|
|
39
|
+
else tr.addMark(from, to, type.create(attrs));
|
|
40
|
+
}
|
|
41
|
+
view.dispatch(asStep(tr));
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function remove(view, type) {
|
|
46
|
+
const st = view.state;
|
|
47
|
+
const tr = st.tr;
|
|
48
|
+
if (st.selection.empty) tr.removeStoredMark(type);
|
|
49
|
+
else tr.removeMark(st.selection.from, st.selection.to, type);
|
|
50
|
+
view.dispatch(asStep(tr));
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The gate, in one place: an editor mounted without one cannot write a floor-1
|
|
55
|
+
// format, and a refusal leaves the content exactly as it was.
|
|
56
|
+
export function runFormat(view, apply) {
|
|
57
|
+
const run = view.runFormatCommand;
|
|
58
|
+
if (typeof run !== "function") return Promise.resolve({ status: "unsupported-host", code: "page-format-unsupported", retry: false });
|
|
59
|
+
return run(apply);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// A mark command: toggle it on the selection, or with reset take it off. A
|
|
63
|
+
// selection the mark cannot reach is refused before the gate is asked, so a code
|
|
64
|
+
// block never raises the document's floor.
|
|
65
|
+
export function formatMark(view, name, attrs = {}, { reset = false } = {}) {
|
|
66
|
+
const type = schema.marks[name];
|
|
67
|
+
if (!type) return Promise.resolve({ status: "refused", code: "unsupported-format" });
|
|
68
|
+
if (!view.editable) return Promise.resolve({ status: "refused", code: "not-editable" });
|
|
69
|
+
if (!markable(view.state, type)) return Promise.resolve({ status: "refused", code: "mark-not-allowed" });
|
|
70
|
+
return runFormat(view, () => (reset ? remove(view, type) : toggle(view, type, attrs)));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The four formats pasted in from elsewhere are floor-1 content too: the slice is
|
|
74
|
+
// inserted only once the gate answered applied. A refusal keeps the text and
|
|
75
|
+
// writes no descriptor; a selection that moved while the answer came back takes
|
|
76
|
+
// neither, and neither does a document that changed underneath the paste: the
|
|
77
|
+
// plain fallback would write at a place a peer edit has moved, so the text stays
|
|
78
|
+
// in the clipboard instead.
|
|
79
|
+
export function pasteFormats(view, slice, from, to) {
|
|
80
|
+
const before = view.state.doc;
|
|
81
|
+
const place = () => {
|
|
82
|
+
if (view.isDestroyed) return false;
|
|
83
|
+
const st = view.state;
|
|
84
|
+
if (st.selection.from !== from || st.selection.to !== to) return false;
|
|
85
|
+
view.dispatch(st.tr.replaceSelection(slice).scrollIntoView());
|
|
86
|
+
return true;
|
|
87
|
+
};
|
|
88
|
+
const plain = () => {
|
|
89
|
+
const text = slice.content.textBetween(0, slice.content.size, "\n", " ");
|
|
90
|
+
if (!view.isDestroyed && text && view.state.doc === before) view.dispatch(view.state.tr.insertText(text, from, to));
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
93
|
+
runFormat(view, place).then((result) => {
|
|
94
|
+
if (result?.status === "applied") return;
|
|
95
|
+
refuse(view, result?.message ?? "That format isn\u2019t available here.");
|
|
96
|
+
plain();
|
|
97
|
+
}, plain);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// The typed `" ` shortcut: the paragraph the reader is typing in became a quote,
|
|
101
|
+
// and the space that typed it goes with it. A refusal puts the space back as
|
|
102
|
+
// itself, so the typed character is never swallowed.
|
|
103
|
+
export function quoteFromSpace(plugin, view, { at, from, to }, plain) {
|
|
104
|
+
const convert = () => {
|
|
105
|
+
if (view.isDestroyed) return false;
|
|
106
|
+
const now = view.state;
|
|
107
|
+
const current = now.doc.nodeAt(at);
|
|
108
|
+
if (!current || current.type !== schema.nodes.paragraph || current.textContent !== '"') return plain();
|
|
109
|
+
const tr = now.tr.delete(at + 1, at + 2).setNodeMarkup(at, schema.nodes.quote, current.attrs);
|
|
110
|
+
view.dispatch(asStep(tr.setMeta(plugin, { transform: tr, from, to, text: " " })));
|
|
111
|
+
return true;
|
|
112
|
+
};
|
|
113
|
+
view.runFormatCommand(convert).then((result) => {
|
|
114
|
+
if (result?.status === "applied") return;
|
|
115
|
+
if (result) refuse(view, refusalText(result));
|
|
116
|
+
plain();
|
|
117
|
+
}, plain);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The More menu's rows: the underline command, the palette of a color mark and
|
|
121
|
+
// the notice a refusal raises. The palette is fixed: nine keys and the way back
|
|
122
|
+
// to none, and no arbitrary color picker is offered.
|
|
123
|
+
const CANNOT_MARK = "This text can\u2019t take that format.";
|
|
124
|
+
const refusalText = (result) => (result.status === "upgrade-required" ? result.message : result.code === "mark-not-allowed" ? CANNOT_MARK : "That format isn\u2019t available here.");
|
|
125
|
+
|
|
126
|
+
// A refusal is said to a screen reader and shown, since a sighted reader whose
|
|
127
|
+
// click did nothing otherwise has no way to know why.
|
|
128
|
+
export const refuse = (view, text) => {
|
|
129
|
+
if (view.isDestroyed) return;
|
|
130
|
+
announce(view, text);
|
|
131
|
+
toast(text, "error");
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const notice = (view, result) => {
|
|
135
|
+
if (result?.status !== "applied") refuse(view, refusalText(result ?? {}));
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const runNative = (view, name, attrs = {}, options = undefined) => {
|
|
139
|
+
formatMark(view, name, attrs, options).then((result) => notice(view, result));
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// A block command from a later format, like the turn into a quote: the write asks
|
|
143
|
+
// the gate first, and a refusal is a notice rather than a change.
|
|
144
|
+
export const gateTurn = (view, turn) => {
|
|
145
|
+
runFormat(view, turn).then((result) => notice(view, result));
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// The palette of one color mark, as its own short menu: the nine fixed keys and
|
|
149
|
+
// the way back to none.
|
|
150
|
+
export function nativeMenu(view, name, anchor) {
|
|
151
|
+
if (name === "underline") return runNative(view, name);
|
|
152
|
+
const colorLabel = name === "text_color" ? "Text" : "Highlight";
|
|
153
|
+
const noneLabel = name === "text_color" ? "Automatic" : "No highlight";
|
|
154
|
+
return openPageMenu(view, anchor, [
|
|
155
|
+
...PALETTE.map((color) => ({ key: `${name}_${color}`, label: `${colorLabel}: ${color[0].toUpperCase()}${color.slice(1)}`, icon: "\u25CF", run: (v) => runNative(v, name, { color }) })),
|
|
156
|
+
{ key: `${name}_none`, label: noneLabel, icon: "\u00d7", run: (v) => runNative(v, name, {}, { reset: true }) },
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The four formats this release adds: underline, text color, highlight and the
|
|
2
|
+
// quote block.
|
|
3
|
+
//
|
|
4
|
+
// Each is written as the named descriptor an older runtime already keeps as an
|
|
5
|
+
// opaque mark, `x["text_color",{"color":"red"}]`, so R reads back exactly what F
|
|
6
|
+
// wrote. The palette is fixed and its keys are the only colors a descriptor may
|
|
7
|
+
// name: one naming any other color stays opaque rather than being read as a
|
|
8
|
+
// style, and no command accepts arbitrary CSS.
|
|
9
|
+
//
|
|
10
|
+
// This half is the reading one: what the schema, inline.js and the projection need
|
|
11
|
+
// to read a descriptor back and draw it. The commands that write one are in
|
|
12
|
+
// format-native-commands.js, which a page loads the first time one runs.
|
|
13
|
+
export const NATIVE_NAMES = new Set(["underline", "text_color", "highlight"]);
|
|
14
|
+
|
|
15
|
+
export const PALETTE = ["gray", "brown", "orange", "yellow", "green", "blue", "purple", "pink", "red"];
|
|
16
|
+
const COLORS = new Set(PALETTE);
|
|
17
|
+
|
|
18
|
+
// The descriptor attrs of a native mark, or null when this name and these attrs
|
|
19
|
+
// are not one of the formats this runtime writes.
|
|
20
|
+
export function nativeAttrs(name, attrs) {
|
|
21
|
+
const plain = attrs !== null && typeof attrs === "object" && !Array.isArray(attrs);
|
|
22
|
+
if (name === "underline") return plain && Object.keys(attrs).length === 0 ? {} : null;
|
|
23
|
+
if (name === "text_color" || name === "highlight") {
|
|
24
|
+
const keys = plain ? Object.keys(attrs) : [];
|
|
25
|
+
return keys.length === 1 && keys[0] === "color" && COLORS.has(attrs.color) ? { color: attrs.color } : null;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Whether this selection holds text the mark may be added to: a code block takes
|
|
31
|
+
// no marks, and a block selection is not text at all. It stays here, beside the
|
|
32
|
+
// reading half, because the keyboard decides whether Mod-u is its own before any
|
|
33
|
+
// command is loaded.
|
|
34
|
+
export function markable(st, type) {
|
|
35
|
+
const { $from, $to, empty } = st.selection;
|
|
36
|
+
if (empty) return $from.parent.type.allowsMarkType(type);
|
|
37
|
+
let ok = false;
|
|
38
|
+
st.doc.nodesBetween($from.pos, $to.pos, (node, _at, parent) => {
|
|
39
|
+
if (!ok && node.isText && parent?.type.allowsMarkType(type)) ok = true;
|
|
40
|
+
});
|
|
41
|
+
return ok;
|
|
42
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// The image menu's own actions: the alt text field and the width presets, reached
|
|
2
|
+
// by the chrome's actions menu. Its own module, so a document that never sets
|
|
3
|
+
// either downloads neither.
|
|
4
|
+
import { asStep } from "./blocksel.js";
|
|
5
|
+
|
|
6
|
+
const indexOfId = (doc, id) => {
|
|
7
|
+
let found = -1;
|
|
8
|
+
doc.forEach((n, _, i) => { if (n.attrs.id === id) found = i; });
|
|
9
|
+
return found;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function setImageAttrs(view, id, patch) {
|
|
13
|
+
const st = view.state;
|
|
14
|
+
const index = indexOfId(st.doc, id);
|
|
15
|
+
if (index < 0) return false;
|
|
16
|
+
let at = 0;
|
|
17
|
+
for (let i = 0; i < index; i++) at += st.doc.child(i).nodeSize;
|
|
18
|
+
const node = st.doc.child(index);
|
|
19
|
+
view.dispatch(asStep(st.tr.setNodeMarkup(at, null, { ...node.attrs, ...patch })));
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function openAltEditor(view, id) {
|
|
24
|
+
const index = indexOfId(view.state.doc, id);
|
|
25
|
+
if (index < 0) return false;
|
|
26
|
+
const shell = view.dom.parentElement;
|
|
27
|
+
const doc = shell.ownerDocument;
|
|
28
|
+
let at = 0;
|
|
29
|
+
for (let i = 0; i < index; i++) at += view.state.doc.child(i).nodeSize;
|
|
30
|
+
const box = doc.createElement("div");
|
|
31
|
+
box.className = "page-link";
|
|
32
|
+
box.dataset.mode = "alt";
|
|
33
|
+
const input = doc.createElement("input");
|
|
34
|
+
input.className = "page-link-input";
|
|
35
|
+
input.type = "text";
|
|
36
|
+
input.value = view.state.doc.child(index).attrs.alt || "";
|
|
37
|
+
input.placeholder = "Describe the image";
|
|
38
|
+
input.setAttribute("aria-label", "Alt text");
|
|
39
|
+
box.append(input);
|
|
40
|
+
const rect = view.nodeDOM(at)?.getBoundingClientRect?.();
|
|
41
|
+
const shellBox = shell.getBoundingClientRect();
|
|
42
|
+
if (rect) {
|
|
43
|
+
box.style.setProperty("--page-pop-x", `${rect.left - shellBox.left}px`);
|
|
44
|
+
box.style.setProperty("--page-pop-y", `${rect.bottom - shellBox.top + 6}px`);
|
|
45
|
+
}
|
|
46
|
+
shell.append(box);
|
|
47
|
+
let done = false;
|
|
48
|
+
const close = (commit, refocus = true) => {
|
|
49
|
+
if (done) return;
|
|
50
|
+
done = true;
|
|
51
|
+
const value = input.value.trim();
|
|
52
|
+
box.remove();
|
|
53
|
+
const i = indexOfId(view.state.doc, id);
|
|
54
|
+
if (commit && i >= 0 && value !== view.state.doc.child(i).attrs.alt) setImageAttrs(view, id, { alt: value });
|
|
55
|
+
if (refocus) view.focus();
|
|
56
|
+
};
|
|
57
|
+
input.addEventListener("keydown", (e) => {
|
|
58
|
+
if (e.key === "Enter") { e.preventDefault(); close(true); }
|
|
59
|
+
else if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); close(false); }
|
|
60
|
+
});
|
|
61
|
+
input.addEventListener("blur", () => close(true, false));
|
|
62
|
+
input.focus();
|
|
63
|
+
return true;
|
|
64
|
+
}
|