@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/schema.js
CHANGED
|
@@ -29,7 +29,15 @@ export const schema = new model.Schema({
|
|
|
29
29
|
group: "block",
|
|
30
30
|
content: "inline*",
|
|
31
31
|
defining: true,
|
|
32
|
-
attrs: {
|
|
32
|
+
attrs: {
|
|
33
|
+
...block,
|
|
34
|
+
level: { default: 2 },
|
|
35
|
+
pageIcon: { default: null },
|
|
36
|
+
pageCoverSrc: { default: null },
|
|
37
|
+
pageCoverAlt: { default: "" },
|
|
38
|
+
pageCoverX: { default: 50 },
|
|
39
|
+
pageCoverY: { default: 50 },
|
|
40
|
+
},
|
|
33
41
|
toDOM: (n) => [`h${n.attrs.level}`, ref(n), 0],
|
|
34
42
|
},
|
|
35
43
|
list_item: {
|
|
@@ -50,6 +58,12 @@ export const schema = new model.Schema({
|
|
|
50
58
|
"data-checked": n.attrs.checked ? "true" : "false",
|
|
51
59
|
}), 0],
|
|
52
60
|
},
|
|
61
|
+
quote: {
|
|
62
|
+
group: "block",
|
|
63
|
+
content: "inline*",
|
|
64
|
+
attrs: block,
|
|
65
|
+
toDOM: (n) => ["blockquote", ref(n), 0],
|
|
66
|
+
},
|
|
53
67
|
callout: {
|
|
54
68
|
group: "block",
|
|
55
69
|
content: "inline*",
|
|
@@ -68,8 +82,8 @@ export const schema = new model.Schema({
|
|
|
68
82
|
toggle: {
|
|
69
83
|
group: "block",
|
|
70
84
|
content: "inline*",
|
|
71
|
-
attrs: { ...block, open: { default: false } },
|
|
72
|
-
toDOM: (n) => ["div", ref(n, { class: "page-toggle", "data-open": n.attrs.open ? "true" : "false" }), 0],
|
|
85
|
+
attrs: { ...block, open: { default: false }, level: { default: null } },
|
|
86
|
+
toDOM: (n) => ["div", ref(n, { class: "page-toggle", "data-open": n.attrs.open ? "true" : "false", ...(n.attrs.level ? { "data-level": String(n.attrs.level) } : {}) }), 0],
|
|
73
87
|
},
|
|
74
88
|
divider: {
|
|
75
89
|
group: "block",
|
|
@@ -77,6 +91,14 @@ export const schema = new model.Schema({
|
|
|
77
91
|
attrs: block,
|
|
78
92
|
toDOM: (n) => ["hr", ref(n)],
|
|
79
93
|
},
|
|
94
|
+
// A table of contents holds no headings: its entries are derived from the document
|
|
95
|
+
// whenever it is drawn, so a rename, a move or a deletion needs no repair here.
|
|
96
|
+
toc: {
|
|
97
|
+
group: "block",
|
|
98
|
+
atom: true,
|
|
99
|
+
attrs: { ...block, maxLevel: { default: null } },
|
|
100
|
+
toDOM: (n) => ["div", ref(n, { class: "page-toc", "data-type": "toc", ...(n.attrs.maxLevel ? { "data-max-level": String(n.attrs.maxLevel) } : {}) })],
|
|
101
|
+
},
|
|
80
102
|
image: {
|
|
81
103
|
group: "block",
|
|
82
104
|
atom: true,
|
|
@@ -174,5 +196,32 @@ export const schema = new model.Schema({
|
|
|
174
196
|
inclusive: false,
|
|
175
197
|
toDOM: (m) => ["a", { href: safeHref(m.attrs.href) }, 0],
|
|
176
198
|
},
|
|
199
|
+
// A person mention is ordinary text with this mark over it: the name is the label
|
|
200
|
+
// and `id` is the people registry's reference. It links nowhere and notifies nobody.
|
|
201
|
+
mention: {
|
|
202
|
+
attrs: { id: {} },
|
|
203
|
+
inclusive: false,
|
|
204
|
+
excludes: "mention date link",
|
|
205
|
+
toDOM: (m) => ["span", { "data-page-mention": m.attrs.id }, 0],
|
|
206
|
+
},
|
|
207
|
+
// A date mention is the calendar date as ordinary ISO text with this mark over it:
|
|
208
|
+
// `date` is YYYY-MM-DD, and no time, zone, reminder or relative word is stored.
|
|
209
|
+
date: {
|
|
210
|
+
attrs: { date: {} },
|
|
211
|
+
inclusive: false,
|
|
212
|
+
excludes: "mention date link",
|
|
213
|
+
toDOM: (m) => ["time", { datetime: m.attrs.date, "data-page-date": "" }, 0],
|
|
214
|
+
},
|
|
215
|
+
// The floor-1 formats (format-native.js): written as named descriptors,
|
|
216
|
+
// drawn as the elements the saved article keeps for them.
|
|
217
|
+
underline: { toDOM: () => ["u", 0] },
|
|
218
|
+
text_color: {
|
|
219
|
+
attrs: { color: {} },
|
|
220
|
+
toDOM: (m) => ["span", { "data-color": m.attrs.color }, 0],
|
|
221
|
+
},
|
|
222
|
+
highlight: {
|
|
223
|
+
attrs: { color: {} },
|
|
224
|
+
toDOM: (m) => ["mark", { "data-color": m.attrs.color }, 0],
|
|
225
|
+
},
|
|
177
226
|
},
|
|
178
227
|
});
|
package/src/page/table-paste.js
CHANGED
|
@@ -33,7 +33,9 @@ function textGrid(text) {
|
|
|
33
33
|
export function gridFromClipboard(html, text, doc = globalThis.document) {
|
|
34
34
|
let found = null;
|
|
35
35
|
try { found = htmlGrid(html, doc); } catch { found = null; }
|
|
36
|
-
|
|
36
|
+
// An HTML table with other blocks around it is a document, not a sheet: its tab-separated
|
|
37
|
+
// text is no grid either, and the editor's own paste keeps every block.
|
|
38
|
+
if (!found && !/<table[\s>]/i.test(html || "")) {
|
|
37
39
|
try { found = textGrid(text); } catch { found = null; }
|
|
38
40
|
}
|
|
39
41
|
if (!found || !found.grid.length) return null;
|
package/src/page/table-style.js
CHANGED
|
@@ -9,11 +9,11 @@ export const TABLE_CSS = `
|
|
|
9
9
|
.page-table table.is-sized th, .page-table table.is-sized td { min-width: 0; overflow-wrap: anywhere; }
|
|
10
10
|
.page-table-resize { position: absolute; top: 0; bottom: 0; inset-inline-end: -4px; width: 8px; z-index: 2; cursor: col-resize; touch-action: none; }
|
|
11
11
|
.page-table-resize:hover, .page-table-resize:active, .page-table-resize:focus-visible { background: var(--page-accent); opacity: 0.4; }
|
|
12
|
-
.page-table-add-row, .page-table-add-col { position: absolute; z-index: 1;
|
|
13
|
-
.page-table-add-row { left: 0; right: 0; top: 100%; height:
|
|
14
|
-
.page-table-add-col { top: 0; bottom: 0; left: 100%; width:
|
|
12
|
+
.page-table-add-row, .page-table-add-col { position: absolute; z-index: 1; padding: 0; background: var(--page-chrome-bg-moved); color: var(--page-ink-soft); display: flex; align-items: center; justify-content: center; font: inherit; font-size: 14px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity 120ms; }
|
|
13
|
+
.page-table-add-row { left: 0; right: 0; top: 100%; height: 16px; border-radius: 0 0 6px 6px; border: 1px solid var(--page-chrome-line-moved); border-top: 0; padding-bottom: 2px; box-sizing: border-box; }
|
|
14
|
+
.page-table-add-col { top: 0; bottom: 0; left: 100%; width: 16px; border-radius: 0 6px 6px 0; border: 1px solid var(--page-chrome-line-moved); border-left: 0; box-sizing: border-box; }
|
|
15
15
|
.page-table:hover > .page-table-add-row, .page-table:hover > .page-table-add-col, .page-table-add-row:focus-visible, .page-table-add-col:focus-visible { opacity: 1; }
|
|
16
|
-
.page-table-add-row:hover, .page-table-add-col:hover { background: var(--page-
|
|
16
|
+
.page-table-add-row:hover, .page-table-add-col:hover { background: var(--page-pop-hover); color: var(--page-ink); }
|
|
17
17
|
.page-table-date { position: absolute; inset-inline-start: 0; top: 100%; z-index: 3; font: inherit; color: var(--page-pop-ink); background: var(--page-pop-bg); border: 1px solid var(--page-pop-line); border-radius: 6px; padding: 4px 6px; box-shadow: var(--page-pop-shadow); }
|
|
18
18
|
@media (max-width: 560px) {
|
|
19
19
|
.page-table-date { min-height: 44px; }
|
package/src/page/table.js
CHANGED
|
@@ -213,6 +213,33 @@ export const moveColumn = (delta) => (st, dispatch) => changeTable(st, dispatch,
|
|
|
213
213
|
export const MIN_WIDTH = 48, MAX_WIDTH = 1200, DEFAULT_WIDTH = 160;
|
|
214
214
|
const widthOf = (value) => (Number.isFinite(value) && value > 0 ? Math.round(Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, value))) : null);
|
|
215
215
|
|
|
216
|
+
// Clears every stored width, so the table fills the page again.
|
|
217
|
+
export const clearColumnWidths = (colId) => (st, dispatch) => changeTable(st, dispatch, (table) => {
|
|
218
|
+
const ids = columns(table);
|
|
219
|
+
if (!ids.some((id) => table.attrs.cols[id].width)) return null;
|
|
220
|
+
const cols = {};
|
|
221
|
+
for (const id of ids) {
|
|
222
|
+
const { width: _old, ...rest } = table.attrs.cols[id];
|
|
223
|
+
cols[id] = rest;
|
|
224
|
+
}
|
|
225
|
+
return { table: copyTable(table, { cols }, table.content), cell: Math.max(0, ids.indexOf(colId)) };
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// Several widths in one step: the first width set on a table that fills the page
|
|
229
|
+
// also fixes every other column at the width it is drawn at, so none of them jumps.
|
|
230
|
+
export const setColumnWidths = (widths, colId) => (st, dispatch) => changeTable(st, dispatch, (table) => {
|
|
231
|
+
const cols = { ...table.attrs.cols };
|
|
232
|
+
let changed = false;
|
|
233
|
+
for (const [id, width] of Object.entries(widths)) {
|
|
234
|
+
const next = widthOf(width);
|
|
235
|
+
if (!cols[id] || next === null || cols[id].width === next) continue;
|
|
236
|
+
cols[id] = { ...cols[id], width: next };
|
|
237
|
+
changed = true;
|
|
238
|
+
}
|
|
239
|
+
if (!changed) return null;
|
|
240
|
+
return { table: copyTable(table, { cols }, table.content), cell: columns(table).indexOf(colId) };
|
|
241
|
+
});
|
|
242
|
+
|
|
216
243
|
export const setColumnWidth = (colId, width) => (st, dispatch) => changeTable(st, dispatch, (table) => {
|
|
217
244
|
const col = table.attrs.cols[colId];
|
|
218
245
|
if (!col) return null;
|
|
@@ -295,9 +322,9 @@ export function pasteTable(view, event) {
|
|
|
295
322
|
if (found.width * found.height < 2) return false;
|
|
296
323
|
return pasteGrid(found.grid)(st, view.dispatch);
|
|
297
324
|
}
|
|
298
|
-
if (found.width * found.height < 2) return false;
|
|
325
|
+
if (found.width * found.height < 2 || found.own) return false;
|
|
299
326
|
if (found.height < 2 && !/<table[\s>]/i.test(data.getData("text/html"))) return false;
|
|
300
|
-
const table = tableFromGrid(found.grid, { header:
|
|
327
|
+
const table = tableFromGrid(found.grid, { header: found.height > 1 });
|
|
301
328
|
if (!table) return false;
|
|
302
329
|
if (tableSize(table) > TABLE_IMPORT_LIMIT) {
|
|
303
330
|
toast("This paste is too large for one table.", "error");
|
|
@@ -317,10 +344,25 @@ export function pasteTable(view, event) {
|
|
|
317
344
|
|
|
318
345
|
export const tableKeys = keymap.keymap({ Tab: nextCell, "Shift-Tab": prevCell, Enter: cellBelow, "Mod-Enter": addRow(true) });
|
|
319
346
|
|
|
347
|
+
function caretInCell(view, event) {
|
|
348
|
+
const cell = cellViews.get(event.target);
|
|
349
|
+
const pos = cell?.getPos();
|
|
350
|
+
if (pos == null || event.button || event.shiftKey || !view.editable) return false;
|
|
351
|
+
event.preventDefault();
|
|
352
|
+
const start = event.clientX < cell.contentDOM.getBoundingClientRect().left;
|
|
353
|
+
view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, pos + 1 + (start ? 0 : cell.node.content.size))));
|
|
354
|
+
view.focus();
|
|
355
|
+
cell.openDate();
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
320
359
|
export function tablePlugin() {
|
|
321
360
|
return new Plugin({
|
|
322
361
|
props: {
|
|
323
362
|
handlePaste: pasteTable,
|
|
363
|
+
handleDOMEvents: {
|
|
364
|
+
mousedown: caretInCell,
|
|
365
|
+
},
|
|
324
366
|
handleClickOn(view, _pos, node, nodePos, event, direct) {
|
|
325
367
|
if (node.type !== N.table_cell || !direct || event.button) return false;
|
|
326
368
|
const desc = view.nodeDOM(nodePos);
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
article[data-page] {
|
|
3
3
|
--page-link: #164583;
|
|
4
4
|
--page-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
5
|
+
--page-color-gray: #5B5953; --page-color-brown: #8A5A1E; --page-color-orange: #B4531A; --page-color-yellow: #7A6210; --page-color-green: #3E6A28;
|
|
6
|
+
--page-color-blue: #1F5A8A; --page-color-purple: #5E4596; --page-color-pink: #A23E6E; --page-color-red: #9E3B2F;
|
|
7
|
+
--page-mark-bg-gray: rgba(35, 34, 31, 0.12); --page-mark-bg-brown: rgba(138, 90, 30, 0.20); --page-mark-bg-orange: rgba(180, 83, 26, 0.20);
|
|
8
|
+
--page-mark-bg-yellow: rgba(122, 98, 16, 0.22); --page-mark-bg-green: rgba(62, 106, 40, 0.20); --page-mark-bg-blue: rgba(31, 90, 138, 0.18);
|
|
9
|
+
--page-mark-bg-purple: rgba(94, 69, 150, 0.18); --page-mark-bg-pink: rgba(162, 62, 110, 0.18); --page-mark-bg-red: rgba(158, 59, 47, 0.18);
|
|
5
10
|
min-height: 100vh;
|
|
6
11
|
box-sizing: border-box;
|
|
7
12
|
padding: var(--page-pad-top) 16px 30vh;
|
|
@@ -42,5 +47,24 @@
|
|
|
42
47
|
article[data-page] th, article[data-page] td { min-width: 120px; padding: 6px 10px; border-inline-end: 1px solid var(--page-rule); border-block-end: 1px solid var(--page-rule); text-align: start; white-space: pre-wrap; }
|
|
43
48
|
article[data-page] th { background: var(--page-code-bg); color: var(--page-ink-soft); font-weight: 600; }
|
|
44
49
|
article[data-page] th[data-kind="number"], article[data-page] td[data-kind="number"] { text-align: end; }
|
|
45
|
-
article[data-page] blockquote { color: var(--page-ink-soft);
|
|
50
|
+
article[data-page] blockquote { color: var(--page-ink-soft); padding-inline-start: var(--page-indent); }
|
|
51
|
+
article[data-page] span[data-color="gray"] { color: var(--page-color-gray); }
|
|
52
|
+
article[data-page] span[data-color="brown"] { color: var(--page-color-brown); }
|
|
53
|
+
article[data-page] span[data-color="orange"] { color: var(--page-color-orange); }
|
|
54
|
+
article[data-page] span[data-color="yellow"] { color: var(--page-color-yellow); }
|
|
55
|
+
article[data-page] span[data-color="green"] { color: var(--page-color-green); }
|
|
56
|
+
article[data-page] span[data-color="blue"] { color: var(--page-color-blue); }
|
|
57
|
+
article[data-page] span[data-color="purple"] { color: var(--page-color-purple); }
|
|
58
|
+
article[data-page] span[data-color="pink"] { color: var(--page-color-pink); }
|
|
59
|
+
article[data-page] span[data-color="red"] { color: var(--page-color-red); }
|
|
60
|
+
article[data-page] mark[data-color="gray"] { background: var(--page-mark-bg-gray); }
|
|
61
|
+
article[data-page] mark[data-color="brown"] { background: var(--page-mark-bg-brown); }
|
|
62
|
+
article[data-page] mark[data-color="orange"] { background: var(--page-mark-bg-orange); }
|
|
63
|
+
article[data-page] mark[data-color="yellow"] { background: var(--page-mark-bg-yellow); }
|
|
64
|
+
article[data-page] mark[data-color="green"] { background: var(--page-mark-bg-green); }
|
|
65
|
+
article[data-page] mark[data-color="blue"] { background: var(--page-mark-bg-blue); }
|
|
66
|
+
article[data-page] mark[data-color="purple"] { background: var(--page-mark-bg-purple); }
|
|
67
|
+
article[data-page] mark[data-color="pink"] { background: var(--page-mark-bg-pink); }
|
|
68
|
+
article[data-page] mark[data-color="red"] { background: var(--page-mark-bg-red); }
|
|
69
|
+
article[data-page] mark { color: inherit; }
|
|
46
70
|
}
|
|
@@ -95,6 +95,11 @@
|
|
|
95
95
|
article[data-page] {
|
|
96
96
|
--page-link: #164583;
|
|
97
97
|
--page-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
98
|
+
--page-color-gray: #5B5953; --page-color-brown: #8A5A1E; --page-color-orange: #B4531A; --page-color-yellow: #7A6210; --page-color-green: #3E6A28;
|
|
99
|
+
--page-color-blue: #1F5A8A; --page-color-purple: #5E4596; --page-color-pink: #A23E6E; --page-color-red: #9E3B2F;
|
|
100
|
+
--page-mark-bg-gray: rgba(35, 34, 31, 0.12); --page-mark-bg-brown: rgba(138, 90, 30, 0.20); --page-mark-bg-orange: rgba(180, 83, 26, 0.20);
|
|
101
|
+
--page-mark-bg-yellow: rgba(122, 98, 16, 0.22); --page-mark-bg-green: rgba(62, 106, 40, 0.20); --page-mark-bg-blue: rgba(31, 90, 138, 0.18);
|
|
102
|
+
--page-mark-bg-purple: rgba(94, 69, 150, 0.18); --page-mark-bg-pink: rgba(162, 62, 110, 0.18); --page-mark-bg-red: rgba(158, 59, 47, 0.18);
|
|
98
103
|
min-height: 100vh;
|
|
99
104
|
box-sizing: border-box;
|
|
100
105
|
padding: var(--page-pad-top) 16px 30vh;
|
|
@@ -135,7 +140,26 @@
|
|
|
135
140
|
article[data-page] th, article[data-page] td { min-width: 120px; padding: 6px 10px; border-inline-end: 1px solid var(--page-rule); border-block-end: 1px solid var(--page-rule); text-align: start; white-space: pre-wrap; }
|
|
136
141
|
article[data-page] th { background: var(--page-code-bg); color: var(--page-ink-soft); font-weight: 600; }
|
|
137
142
|
article[data-page] th[data-kind="number"], article[data-page] td[data-kind="number"] { text-align: end; }
|
|
138
|
-
article[data-page] blockquote { color: var(--page-ink-soft);
|
|
143
|
+
article[data-page] blockquote { color: var(--page-ink-soft); padding-inline-start: var(--page-indent); }
|
|
144
|
+
article[data-page] span[data-color="gray"] { color: var(--page-color-gray); }
|
|
145
|
+
article[data-page] span[data-color="brown"] { color: var(--page-color-brown); }
|
|
146
|
+
article[data-page] span[data-color="orange"] { color: var(--page-color-orange); }
|
|
147
|
+
article[data-page] span[data-color="yellow"] { color: var(--page-color-yellow); }
|
|
148
|
+
article[data-page] span[data-color="green"] { color: var(--page-color-green); }
|
|
149
|
+
article[data-page] span[data-color="blue"] { color: var(--page-color-blue); }
|
|
150
|
+
article[data-page] span[data-color="purple"] { color: var(--page-color-purple); }
|
|
151
|
+
article[data-page] span[data-color="pink"] { color: var(--page-color-pink); }
|
|
152
|
+
article[data-page] span[data-color="red"] { color: var(--page-color-red); }
|
|
153
|
+
article[data-page] mark[data-color="gray"] { background: var(--page-mark-bg-gray); }
|
|
154
|
+
article[data-page] mark[data-color="brown"] { background: var(--page-mark-bg-brown); }
|
|
155
|
+
article[data-page] mark[data-color="orange"] { background: var(--page-mark-bg-orange); }
|
|
156
|
+
article[data-page] mark[data-color="yellow"] { background: var(--page-mark-bg-yellow); }
|
|
157
|
+
article[data-page] mark[data-color="green"] { background: var(--page-mark-bg-green); }
|
|
158
|
+
article[data-page] mark[data-color="blue"] { background: var(--page-mark-bg-blue); }
|
|
159
|
+
article[data-page] mark[data-color="purple"] { background: var(--page-mark-bg-purple); }
|
|
160
|
+
article[data-page] mark[data-color="pink"] { background: var(--page-mark-bg-pink); }
|
|
161
|
+
article[data-page] mark[data-color="red"] { background: var(--page-mark-bg-red); }
|
|
162
|
+
article[data-page] mark { color: inherit; }
|
|
139
163
|
}
|
|
140
164
|
|
|
141
165
|
|
|
@@ -191,12 +215,12 @@
|
|
|
191
215
|
--page-code-bg: rgba(35, 34, 31, 0.045);
|
|
192
216
|
--page-code-ink: #23221F;
|
|
193
217
|
|
|
194
|
-
--page-chrome-ink-rest: rgba(35, 34, 31, 0.
|
|
195
|
-
--page-chrome-ink-moved: rgba(35, 34, 31, 0.
|
|
196
|
-
--page-chrome-bg-rest: rgba(35, 34, 31, 0.
|
|
197
|
-
--page-chrome-bg-moved: rgba(35, 34, 31, 0.
|
|
198
|
-
--page-chrome-line-rest: rgba(35, 34, 31, 0.
|
|
199
|
-
--page-chrome-line-moved: rgba(35, 34, 31, 0.
|
|
218
|
+
--page-chrome-ink-rest: rgba(35, 34, 31, 0.42);
|
|
219
|
+
--page-chrome-ink-moved: rgba(35, 34, 31, 0.62);
|
|
220
|
+
--page-chrome-bg-rest: rgba(35, 34, 31, 0.035);
|
|
221
|
+
--page-chrome-bg-moved: rgba(35, 34, 31, 0.06);
|
|
222
|
+
--page-chrome-line-rest: rgba(35, 34, 31, 0.22);
|
|
223
|
+
--page-chrome-line-moved: rgba(35, 34, 31, 0.36);
|
|
200
224
|
--page-chrome-shadow-rest: none;
|
|
201
225
|
--page-chrome-shadow-moved: none;
|
|
202
226
|
--page-chrome-radius: 7px;
|
|
@@ -283,7 +307,8 @@ article[data-page] figure[data-type="chart"] > svg { display: block; width: 100%
|
|
|
283
307
|
height: 15px;
|
|
284
308
|
}
|
|
285
309
|
|
|
286
|
-
.page-shell .page-dock .page-tool-turn
|
|
310
|
+
.page-shell .page-dock .page-tool-turn,
|
|
311
|
+
.page-shell .page-bubble .page-tool-turn {
|
|
287
312
|
width: auto;
|
|
288
313
|
gap: 4px;
|
|
289
314
|
padding: 0 8px;
|
|
@@ -54,12 +54,12 @@
|
|
|
54
54
|
--page-code-bg: rgba(35, 34, 31, 0.045);
|
|
55
55
|
--page-code-ink: #23221F;
|
|
56
56
|
|
|
57
|
-
--page-chrome-ink-rest: rgba(35, 34, 31, 0.
|
|
58
|
-
--page-chrome-ink-moved: rgba(35, 34, 31, 0.
|
|
59
|
-
--page-chrome-bg-rest: rgba(35, 34, 31, 0.
|
|
60
|
-
--page-chrome-bg-moved: rgba(35, 34, 31, 0.
|
|
61
|
-
--page-chrome-line-rest: rgba(35, 34, 31, 0.
|
|
62
|
-
--page-chrome-line-moved: rgba(35, 34, 31, 0.
|
|
57
|
+
--page-chrome-ink-rest: rgba(35, 34, 31, 0.42);
|
|
58
|
+
--page-chrome-ink-moved: rgba(35, 34, 31, 0.62);
|
|
59
|
+
--page-chrome-bg-rest: rgba(35, 34, 31, 0.035);
|
|
60
|
+
--page-chrome-bg-moved: rgba(35, 34, 31, 0.06);
|
|
61
|
+
--page-chrome-line-rest: rgba(35, 34, 31, 0.22);
|
|
62
|
+
--page-chrome-line-moved: rgba(35, 34, 31, 0.36);
|
|
63
63
|
--page-chrome-shadow-rest: none;
|
|
64
64
|
--page-chrome-shadow-moved: none;
|
|
65
65
|
--page-chrome-radius: 7px;
|
|
@@ -146,7 +146,8 @@ article[data-page] figure[data-type="chart"] > svg { display: block; width: 100%
|
|
|
146
146
|
height: 15px;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
.page-shell .page-dock .page-tool-turn
|
|
149
|
+
.page-shell .page-dock .page-tool-turn,
|
|
150
|
+
.page-shell .page-bubble .page-tool-turn {
|
|
150
151
|
width: auto;
|
|
151
152
|
gap: 4px;
|
|
152
153
|
padding: 0 8px;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// The saved table of contents' links: decode the anchor a link names, and reveal
|
|
2
|
+
// the block it points at in a reader. Loaded the first time a reader follows one.
|
|
3
|
+
const TOC_ANCHOR = "page-block-";
|
|
4
|
+
|
|
5
|
+
const fromB64url = (text) => {
|
|
6
|
+
const bin = atob(String(text).replace(/-/g, "+").replace(/_/g, "/"));
|
|
7
|
+
return new TextDecoder().decode(Uint8Array.from(bin, (ch) => ch.charCodeAt(0)));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function idFromHref(href) {
|
|
11
|
+
const hash = String(href ?? "").replace(/^#/, "");
|
|
12
|
+
if (!hash) return null;
|
|
13
|
+
try {
|
|
14
|
+
return hash.startsWith(TOC_ANCHOR) ? fromB64url(hash.slice(TOC_ANCHOR.length)) : decodeURIComponent(hash);
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// A table of contents link in a saved page shows its target and opens every collapsed
|
|
21
|
+
// body above it: reading only, so nothing is saved, no history moves and no peer is
|
|
22
|
+
// told. A link whose block is gone, or to anywhere else, changes nothing.
|
|
23
|
+
export function followTocLink(article, href) {
|
|
24
|
+
const id = idFromHref(href);
|
|
25
|
+
if (id === null) return null;
|
|
26
|
+
const target = [...article.querySelectorAll("[data-id]")].find((el) => el.getAttribute("data-id") === id) ?? null;
|
|
27
|
+
if (!target) return null;
|
|
28
|
+
for (let el = target.parentElement; el && el !== article; el = el.parentElement) if (el.tagName === "DETAILS") el.open = true;
|
|
29
|
+
target.scrollIntoView?.({ block: "start" });
|
|
30
|
+
return target;
|
|
31
|
+
}
|
package/src/page/toc.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The table of contents: the headings a document has and the anchors that link to them.
|
|
2
|
+
// The saved renderer and the editor's node view ask one helper; a reader's reveal and
|
|
3
|
+
// the anchor decoder live in toc-link.js, which nothing but a followed link loads.
|
|
4
|
+
export { TOC_ANCHOR, TOC_LABEL, blockAnchor, tocHtml, tocEntries } from "./projection.js";
|
|
5
|
+
export { idFromHref } from "./toc-link.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { state } from "../vendor/prosemirror.vendor.js";
|
|
2
|
+
|
|
3
|
+
// The update hooks and decorations a loaded view part registers for its editor
|
|
4
|
+
// view. The plugin below calls the hooks on every document change and collects the
|
|
5
|
+
// decorations on every draw, so a part keeps its blocks fresh without the editor
|
|
6
|
+
// importing it: the part adds its hook when it installs and removes it on dispose.
|
|
7
|
+
export const viewHooks = new WeakMap();
|
|
8
|
+
|
|
9
|
+
// The feature loader of each mounted editor, so an insert path can ask for a
|
|
10
|
+
// block's part before it draws the block.
|
|
11
|
+
export const viewFeatures = new WeakMap();
|
|
12
|
+
|
|
13
|
+
export function addViewHook(view, hook) {
|
|
14
|
+
const hooks = viewHooks.get(view) ?? new Set();
|
|
15
|
+
viewHooks.set(view, hooks);
|
|
16
|
+
hooks.add(hook);
|
|
17
|
+
return () => hooks.delete(hook);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function viewHookPlugin() {
|
|
21
|
+
let editorView = null;
|
|
22
|
+
const mounted = new WeakMap();
|
|
23
|
+
return new state.Plugin({
|
|
24
|
+
view: (view) => {
|
|
25
|
+
editorView = view;
|
|
26
|
+
mounted.set(view, (mounted.get(view) ?? 0) + 1);
|
|
27
|
+
return {
|
|
28
|
+
update: (next) => { for (const hook of viewHooks.get(next) ?? []) hook.update?.(next); },
|
|
29
|
+
destroy: () => {
|
|
30
|
+
mounted.set(view, mounted.get(view) - 1);
|
|
31
|
+
if (editorView === view) editorView = null;
|
|
32
|
+
// A reconfigure destroys this plugin view and mounts a new one for the same editor in
|
|
33
|
+
// the same task; only an editor that stays without one is really going away.
|
|
34
|
+
queueMicrotask(() => {
|
|
35
|
+
if (mounted.get(view)) return;
|
|
36
|
+
const hooks = viewHooks.get(view);
|
|
37
|
+
if (!hooks) return;
|
|
38
|
+
for (const hook of hooks) hook.destroy?.(view);
|
|
39
|
+
hooks.clear();
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
props: {
|
|
45
|
+
decorations(st) {
|
|
46
|
+
const hooks = editorView ? viewHooks.get(editorView) : null;
|
|
47
|
+
if (!hooks) return null;
|
|
48
|
+
let found = null;
|
|
49
|
+
for (const hook of hooks) {
|
|
50
|
+
const set = hook.decorations?.(st);
|
|
51
|
+
if (!set) continue;
|
|
52
|
+
found = found ? found.add(st.doc, set.find()) : set;
|
|
53
|
+
}
|
|
54
|
+
return found;
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
package/src/page/view.js
CHANGED
|
@@ -16,6 +16,10 @@ const LINEAGE = ["live-at", "live-by"];
|
|
|
16
16
|
|
|
17
17
|
export { applyPreparedArticle, prepareSavedArticle };
|
|
18
18
|
|
|
19
|
+
// A table of contents link in a saved page reveals its target. The reveal code and
|
|
20
|
+
// the anchor decoder load the first time somebody follows one, never with the page.
|
|
21
|
+
export const followTocLink = (article, href) => import("./toc-link.js").then((m) => m.followTocLink(article, href));
|
|
22
|
+
|
|
19
23
|
// The stamp of the file this reader accepted, recorded where a save reads it. The
|
|
20
24
|
// stamp module is the write path's own lazy chunk: a reader paints without it, and
|
|
21
25
|
// a save loads the one copy both paths then share.
|
|
@@ -262,6 +266,15 @@ function createReader({ doc, article, represented = null, feed = null, documentU
|
|
|
262
266
|
doc.addEventListener("toggle", onToggle, true);
|
|
263
267
|
listeners.push(["toggle", onToggle, true]);
|
|
264
268
|
|
|
269
|
+
const onTocLink = (event) => {
|
|
270
|
+
const link = event.target.closest?.('a[href^="#page-block-"], a[href^="#"]');
|
|
271
|
+
if (!link || !article.contains(link) || !link.closest('nav[aria-label="Table of contents"]')) return;
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
followTocLink(article, link.getAttribute("href"));
|
|
274
|
+
};
|
|
275
|
+
doc.addEventListener("click", onTocLink);
|
|
276
|
+
listeners.push(["click", onTocLink]);
|
|
277
|
+
|
|
265
278
|
if (represented) {
|
|
266
279
|
const prepared = prepareSavedArticle(represented.bytes, doc);
|
|
267
280
|
if (!prepared.ok) throw new Error(`clayjs: the saved file is not a readable page (${prepared.reason})`);
|
|
@@ -2,17 +2,45 @@ import * as Y from "../../vendor/yjs.vendor.js";
|
|
|
2
2
|
import { NET, createPageDoc, walk, docOf } from "./core.js";
|
|
3
3
|
import { createBridge, keepViewPos } from "./bridge.js";
|
|
4
4
|
import { createYProvider, entriesOf, dec } from "./provider.js";
|
|
5
|
+
import { createPageFileDebt } from "./file-debt.js";
|
|
5
6
|
|
|
6
|
-
export function createYPageBinding({ store, ydoc = new Y.Doc(), onNotice = () => {}, ownedTokens = [], authorToken = null, setTimer = setTimeout, clearTimer = clearTimeout, compact, captureTimeout = 500, checkpointWithin = () => {}, requestCheckpoint = null, registerUnsavedState = null, onStopped = () => {} }) {
|
|
7
|
+
export function createYPageBinding({ store, ydoc = new Y.Doc(), onNotice = () => {}, ownedTokens = [], authorToken = null, setTimer = setTimeout, clearTimer = clearTimeout, compact, captureTimeout = 500, checkpointWithin = () => {}, requestCheckpoint = null, requestExitCheckpoint = null, registerUnsavedState = null, onStopped = () => {}, journal = null, exitClosed = () => false, stats = { groups: 0, mismatch: 0, renders: 0, first: null } }) {
|
|
7
8
|
for (const [, v] of entriesOf(store.value.y)) Y.applyUpdateV2(ydoc, dec(v), NET);
|
|
8
9
|
const pd = createPageDoc(ydoc);
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
// The journal is installed before the provider, because the provider is what sees an
|
|
11
|
+
// update: nothing can reach the document without passing it first. Its own code lives
|
|
12
|
+
// with the recovery channel and is only loaded when a page has one to journal into.
|
|
13
|
+
const pageJournal = journal || null;
|
|
14
|
+
// The input this tab owes the file, recorded before the provider because the provider is
|
|
15
|
+
// what sees an update. The notification it fires through is attached once the binding's
|
|
16
|
+
// own registration exists, so an update that arrives while this function is still
|
|
17
|
+
// building cannot reach an uninitialized name.
|
|
18
|
+
let notifyDebt = () => {};
|
|
19
|
+
const debt = createPageFileDebt({ changed: () => notifyDebt() });
|
|
20
|
+
const provider = createYProvider({
|
|
21
|
+
store,
|
|
22
|
+
ydoc,
|
|
23
|
+
setTimer,
|
|
24
|
+
clearTimer,
|
|
25
|
+
compact,
|
|
26
|
+
// A full or failed room stops offering at once, but the checkpoint of what it already
|
|
27
|
+
// admitted goes out before the page locks and holds every save.
|
|
28
|
+
onStopped: (reason) => {
|
|
29
|
+
if (reason !== "page-full" && reason !== "compaction-failed") { onStopped(reason); return; }
|
|
30
|
+
Promise.resolve(requestCheckpoint?.({ timeoutMs: 10_000 })).catch(() => {}).finally(() => onStopped(reason));
|
|
31
|
+
},
|
|
32
|
+
onLocalUpdate: debt.local,
|
|
33
|
+
journal: { local: (u) => pageJournal?.local(u), dependency: (u) => pageJournal?.dependency(u) },
|
|
34
|
+
});
|
|
11
35
|
const bridge = createBridge({ pd, onNotice, ownedTokens, authorToken, stats, captureTimeout });
|
|
12
36
|
const waiters = new Set();
|
|
13
37
|
let disposed = false;
|
|
14
38
|
let checksQueued = false;
|
|
15
|
-
const unsaved = registerUnsavedState?.({
|
|
39
|
+
const unsaved = registerUnsavedState?.({
|
|
40
|
+
id: "page:live",
|
|
41
|
+
isPending: () => (exitClosed() && debt.pending()) || store.pending.length > 0 || provider.pending || provider.reconcilePending || Boolean(pageJournal && pageJournal.pending() > (pageJournal.failure ? 1 : 0)),
|
|
42
|
+
flush,
|
|
43
|
+
});
|
|
16
44
|
const changed = () => {
|
|
17
45
|
unsaved?.changed();
|
|
18
46
|
if (checksQueued) return;
|
|
@@ -22,12 +50,87 @@ export function createYPageBinding({ store, ydoc = new Y.Doc(), onNotice = () =>
|
|
|
22
50
|
if (!disposed) for (const waiter of [...waiters]) waiter.check();
|
|
23
51
|
});
|
|
24
52
|
};
|
|
53
|
+
notifyDebt = changed;
|
|
25
54
|
const offLocal = store.onLocal(() => { checkpointWithin(1500); changed(); });
|
|
26
55
|
const offRebase = store.onRebase(changed);
|
|
27
|
-
const offConfirm = store.onConfirm(
|
|
56
|
+
const offConfirm = store.onConfirm(() => {
|
|
57
|
+
pageJournal?.acknowledged();
|
|
58
|
+
changed();
|
|
59
|
+
});
|
|
60
|
+
// Everything the journal reports while this session is the active one is unsaved state:
|
|
61
|
+
// a write in flight, a write the store refused, or a room that has not confirmed.
|
|
62
|
+
if (pageJournal) pageJournal.onChange = changed;
|
|
63
|
+
|
|
64
|
+
// Everything the file or the room still owes: this tab's own input, a store op no
|
|
65
|
+
// answer has covered, a reconcile, a journal write still in flight.
|
|
66
|
+
const exitPending = () =>
|
|
67
|
+
debt.pending() || store.pending.length > 0 || provider.pending || provider.reconcilePending ||
|
|
68
|
+
Boolean(pageJournal && pageJournal.pending() > (pageJournal.failure ? 1 : 0));
|
|
69
|
+
|
|
70
|
+
// The exit's own flush, under the permit the barrier minted.
|
|
71
|
+
//
|
|
72
|
+
// A fast store's capture holds the room's value, so this tab's ops travel through the
|
|
73
|
+
// room first; a file store's capture holds its pending ops as they stand, so the
|
|
74
|
+
// checkpoint goes while they are still pending and their acceptance is what confirms
|
|
75
|
+
// them. The checkpoint is asked for only when this tab owes the file input: a tab
|
|
76
|
+
// holding nothing of its own leaves without making the room's writer save for it.
|
|
77
|
+
function flushExit({ permit, deadline, keepalive, signal, timeoutMs }) {
|
|
78
|
+
const end = Math.min(deadline ?? Infinity, Date.now() + timeoutMs);
|
|
79
|
+
const over = () => signal?.aborted || Date.now() >= end;
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
let done = false, timer = null, wake = null;
|
|
82
|
+
const waiter = {
|
|
83
|
+
check() { wake?.(); },
|
|
84
|
+
finish(result) {
|
|
85
|
+
if (done) return;
|
|
86
|
+
done = true;
|
|
87
|
+
waiters.delete(waiter);
|
|
88
|
+
clearTimer(timer);
|
|
89
|
+
signal?.removeEventListener("abort", aborted);
|
|
90
|
+
resolve(result);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const aborted = () => waiter.finish({ ok: false, code: "timeout" });
|
|
94
|
+
const wait = (ms) => new Promise((woken) => {
|
|
95
|
+
const wakeNow = () => {
|
|
96
|
+
clearTimer(timer);
|
|
97
|
+
wake = null;
|
|
98
|
+
woken();
|
|
99
|
+
};
|
|
100
|
+
timer = setTimer(wakeNow, ms);
|
|
101
|
+
wake = wakeNow;
|
|
102
|
+
});
|
|
103
|
+
const run = async () => {
|
|
104
|
+
provider.settle();
|
|
105
|
+
if (debt.pending() && requestExitCheckpoint) {
|
|
106
|
+
const result = await requestExitCheckpoint({
|
|
107
|
+
permit,
|
|
108
|
+
deadline: end,
|
|
109
|
+
signal,
|
|
110
|
+
keepalive,
|
|
111
|
+
needsSave: () => debt.pending(),
|
|
112
|
+
});
|
|
113
|
+
if (done) return;
|
|
114
|
+
if (!result?.ok) {
|
|
115
|
+
waiter.finish(result?.ok === false ? result : { ok: false, code: "checkpoint-failed" });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
while (!done) {
|
|
120
|
+
if (!exitPending()) { waiter.finish({ ok: true }); return; }
|
|
121
|
+
if (over()) { waiter.finish({ ok: false, code: "timeout" }); return; }
|
|
122
|
+
await wait(50);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
waiters.add(waiter);
|
|
126
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
127
|
+
run().catch((error) => waiter.finish({ ok: false, code: "flush-failed", message: error?.message || String(error) }));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
28
130
|
|
|
29
|
-
function flush({ timeoutMs = 10_000 } = {}) {
|
|
131
|
+
function flush({ permit = null, deadline: exitDeadline = null, keepalive = false, signal = null, timeoutMs = 10_000 } = {}) {
|
|
30
132
|
if (disposed) return Promise.resolve({ ok: false, code: "disposed" });
|
|
133
|
+
if (permit) return flushExit({ permit, deadline: exitDeadline, keepalive, signal, timeoutMs });
|
|
31
134
|
return new Promise((resolve) => {
|
|
32
135
|
const deadline = Date.now() + timeoutMs;
|
|
33
136
|
let done = false, checking = false, checkpointing = false, timer = null;
|
|
@@ -73,6 +176,9 @@ export function createYPageBinding({ store, ydoc = new Y.Doc(), onNotice = () =>
|
|
|
73
176
|
|
|
74
177
|
return {
|
|
75
178
|
ydoc, pd, provider, bridge, stats, flush, awareness,
|
|
179
|
+
get journal() { return pageJournal; },
|
|
180
|
+
filePending: () => debt.pending(),
|
|
181
|
+
acceptFile: (state) => debt.accepted(state),
|
|
76
182
|
doc: () => keepViewPos(docOf(pd, walk(pd))),
|
|
77
183
|
attach: (view) => bridge.attach(view),
|
|
78
184
|
dispatch: (tr) => bridge.dispatch(tr),
|
|
@@ -92,6 +198,10 @@ export function createYPageBinding({ store, ydoc = new Y.Doc(), onNotice = () =>
|
|
|
92
198
|
offRebase();
|
|
93
199
|
offConfirm();
|
|
94
200
|
unsaved?.dispose();
|
|
201
|
+
// Best effort, never the primary path: every append already resolved on its own
|
|
202
|
+
// commit, and what is left is only a write the store refused.
|
|
203
|
+
pageJournal?.close();
|
|
204
|
+
Promise.resolve(pageJournal?.channel?.flush?.()).catch(() => {});
|
|
95
205
|
for (const waiter of [...waiters]) waiter.finish({ ok: false, code: "disposed" });
|
|
96
206
|
bridge.dispose();
|
|
97
207
|
provider.dispose();
|