@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
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// The readable snapshot: one static HTML document rendered from the live page
|
|
2
|
+
// model through the same projection the saved page carries, plus the inventory
|
|
3
|
+
// of every image, attachment, chart and embed it references. Nothing here
|
|
4
|
+
// fetches, downloads or writes a file: inlining those resources and the export
|
|
5
|
+
// action belong to the feature that calls this.
|
|
6
|
+
//
|
|
7
|
+
// The document keeps the content and drops the machinery: no script, stylesheet,
|
|
8
|
+
// live tag, save hook or block envelope, every toggle open, and a table of
|
|
9
|
+
// contents as a plain list rather than links into a page that is not running.
|
|
10
|
+
import { fragmentCleaner, renderModel } from "./projection.js";
|
|
11
|
+
import { documentTitle } from "./document-title.js";
|
|
12
|
+
|
|
13
|
+
/** A snapshot that cannot be produced without lying about its contents. */
|
|
14
|
+
export class PageExportError extends Error {
|
|
15
|
+
constructor(code, message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "PageExportError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const fail = (code, message) => { throw new PageExportError(code, message); };
|
|
23
|
+
|
|
24
|
+
// A reference the document may carry: bytes already inside the file, an address
|
|
25
|
+
// on the origin the page was read from, or a plain http(s) URL. A script URL or a
|
|
26
|
+
// second document in a data URL is refused by name, never dropped in silence.
|
|
27
|
+
const DATA_IMAGE = /^data:image\/(?:png|jpe?g|gif|webp);/i;
|
|
28
|
+
const SCRIPT_URL = /^(?:javascript|vbscript|data):/i;
|
|
29
|
+
const REMOTE = /^https?:\/\//i;
|
|
30
|
+
const ROOTED = /^\/(?!\/)/;
|
|
31
|
+
const squash = (value) => String(value ?? "").trim().replace(/[\u0000-\u0020]/g, "");
|
|
32
|
+
const carriable = (value) => DATA_IMAGE.test(value) || !SCRIPT_URL.test(value);
|
|
33
|
+
|
|
34
|
+
function reference(raw) {
|
|
35
|
+
const s = squash(raw);
|
|
36
|
+
if (!s || s.startsWith("#")) return null;
|
|
37
|
+
if (DATA_IMAGE.test(s)) return { url: s, inline: true };
|
|
38
|
+
if (/^\/\//.test(s)) return { url: `https:${s}`, inline: false };
|
|
39
|
+
if (ROOTED.test(s) || REMOTE.test(s)) return { url: s, inline: false };
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const RESOURCE_TAGS = { img: "image", iframe: "embed", embed: "embed", object: "embed", video: "embed", audio: "embed", source: "embed", track: "embed" };
|
|
44
|
+
const SOURCE_ATTR = { object: "data" };
|
|
45
|
+
const STATIC_MEDIA = /<(?:img|svg|video|audio|canvas|iframe|table)\b/i;
|
|
46
|
+
const STATIC_TEXT = /<(?:meta|style|img|br|hr|input)\b[^>]*>/gi;
|
|
47
|
+
|
|
48
|
+
const blockOf = (el) => el.closest?.("[data-id]")?.getAttribute("data-id") || null;
|
|
49
|
+
const where = (kind, blockId) => (blockId ? `${kind} in block ${blockId}` : kind);
|
|
50
|
+
|
|
51
|
+
// Every reference the document carries, in the order it appears, each named by the
|
|
52
|
+
// block that holds it. Charts are listed too: their drawing is already SVG inside
|
|
53
|
+
// the file, so they name no address and need nothing fetched.
|
|
54
|
+
function resourcesOf(container) {
|
|
55
|
+
const out = [];
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
const add = (kind, raw, blockId) => {
|
|
58
|
+
const ref = reference(raw);
|
|
59
|
+
if (ref === undefined) {
|
|
60
|
+
fail("unsafe-url", `The ${where(kind, blockId)} points at ${String(raw).trim().slice(0, 120)}, which a reading copy cannot carry`);
|
|
61
|
+
}
|
|
62
|
+
if (!ref) return;
|
|
63
|
+
const key = `${kind}\u0000${ref.url}`;
|
|
64
|
+
if (seen.has(key)) return;
|
|
65
|
+
seen.add(key);
|
|
66
|
+
out.push({ kind, url: ref.url, blockId: blockId ?? null, inline: ref.inline });
|
|
67
|
+
};
|
|
68
|
+
for (const el of container.querySelectorAll("*")) {
|
|
69
|
+
const tag = el.tagName.toLowerCase();
|
|
70
|
+
const kind = RESOURCE_TAGS[tag];
|
|
71
|
+
if (kind) add(kind, el.getAttribute(SOURCE_ATTR[tag] ?? "src"), blockOf(el));
|
|
72
|
+
if (tag === "img" || tag === "source") {
|
|
73
|
+
for (const candidate of String(el.getAttribute("srcset") ?? "").split(",")) add("image", candidate.trim().split(/\s+/)[0], blockOf(el));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const el of container.querySelectorAll('[data-type="file"]')) {
|
|
77
|
+
add("attachment", el.getAttribute("data-src"), blockOf(el));
|
|
78
|
+
add("attachment", el.querySelector("a[href]")?.getAttribute("href"), blockOf(el));
|
|
79
|
+
}
|
|
80
|
+
for (const el of container.querySelectorAll('figure[data-type="chart"]')) {
|
|
81
|
+
const blockId = blockOf(el);
|
|
82
|
+
const key = `chart\u0000${blockId ?? ""}`;
|
|
83
|
+
if (seen.has(key)) continue;
|
|
84
|
+
seen.add(key);
|
|
85
|
+
out.push({ kind: "chart", url: null, blockId: blockId ?? null, inline: true });
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The article as a reader gets it: no script, stylesheet or runtime attribute, and
|
|
91
|
+
// no link to a script URL. A resource the document needs was already checked.
|
|
92
|
+
function scrub(container) {
|
|
93
|
+
for (const el of [...container.querySelectorAll("script, style, link, meta, base")]) el.remove();
|
|
94
|
+
for (const el of container.querySelectorAll("*")) {
|
|
95
|
+
for (const { name, value } of [...el.attributes]) {
|
|
96
|
+
const attr = name.toLowerCase();
|
|
97
|
+
if (attr.startsWith("on") || attr === "clay" || attr === "merge" || attr === "live" || attr === "srcdoc" || attr.startsWith("data-clay") || attr.startsWith("clay-")) {
|
|
98
|
+
el.removeAttribute(name);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (/^(href|src|action|formaction|poster|data-src|xlink:href)$/.test(attr) && !carriable(squash(value))) el.removeAttribute(name);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The table of contents reads as a nested list of its entries; its anchors point at
|
|
107
|
+
// a page that is not running here.
|
|
108
|
+
function plainToc(container) {
|
|
109
|
+
for (const nav of container.querySelectorAll('nav[data-type="toc"]')) {
|
|
110
|
+
for (const link of [...nav.querySelectorAll("a")]) link.replaceWith(link.ownerDocument.createTextNode(link.textContent));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const openToggles = (model) => model.map((item) => (item.t === "toggle" ? { ...item, open: true } : item));
|
|
115
|
+
|
|
116
|
+
const hasStatic = (html, clean) => {
|
|
117
|
+
const cleaned = clean.fallback(String(html ?? ""));
|
|
118
|
+
return STATIC_MEDIA.test(cleaned) || cleaned.replace(STATIC_TEXT, "").replace(/<[^>]+>/g, "").trim() !== "";
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const escText = (text) => String(text).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
122
|
+
|
|
123
|
+
const READER_CSS = `:root { color-scheme: light dark; }
|
|
124
|
+
body { margin: 0; background: #ffffff; color: #37352f; font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.6; }
|
|
125
|
+
article[data-page] { max-width: 46rem; margin: 0 auto; padding: 3rem 1.25rem 6rem; }
|
|
126
|
+
article[data-page] > * { margin: 0.7em 0; }
|
|
127
|
+
header[data-page-heading] { position: relative; margin-bottom: 1.6em; }
|
|
128
|
+
header[data-page-heading] img[data-page-cover] { display: block; width: 100%; max-height: 18rem; object-fit: cover; border-radius: 6px; margin-bottom: 1em; }
|
|
129
|
+
header[data-page-heading] span[data-page-icon] { display: block; font-size: 2.6em; line-height: 1.2; }
|
|
130
|
+
h1, h2, h3, h4, h5, h6 { line-height: 1.25; font-weight: 650; margin: 1.2em 0 0.3em; }
|
|
131
|
+
h1 { font-size: 2em; }
|
|
132
|
+
h2 { font-size: 1.5em; }
|
|
133
|
+
h3 { font-size: 1.2em; }
|
|
134
|
+
p, li { max-width: 100%; }
|
|
135
|
+
a { color: #0b6bcb; }
|
|
136
|
+
blockquote { margin-left: 0; padding-left: 1em; border-left: 3px solid rgba(55, 53, 47, 0.2); color: #5b5952; }
|
|
137
|
+
aside.page-callout { border: 1px solid rgba(55, 53, 47, 0.14); border-radius: 6px; padding: 0.8em 1em; background: rgba(55, 53, 47, 0.04); }
|
|
138
|
+
pre { overflow-x: auto; padding: 0.9em 1em; border-radius: 6px; background: rgba(55, 53, 47, 0.06); font-size: 0.9em; }
|
|
139
|
+
code { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; font-size: 0.92em; }
|
|
140
|
+
pre code { font-size: inherit; }
|
|
141
|
+
details { margin: 0.4em 0; }
|
|
142
|
+
details > summary { cursor: pointer; font-weight: 600; }
|
|
143
|
+
details > summary h1, details > summary h2, details > summary h3 { display: inline; margin: 0; }
|
|
144
|
+
hr { border: 0; border-top: 1px solid rgba(55, 53, 47, 0.18); }
|
|
145
|
+
table { border-collapse: collapse; width: 100%; }
|
|
146
|
+
th, td { border: 1px solid rgba(55, 53, 47, 0.18); padding: 0.4em 0.6em; text-align: left; vertical-align: top; }
|
|
147
|
+
th { background: rgba(55, 53, 47, 0.04); font-weight: 600; }
|
|
148
|
+
figure { margin: 1em 0; }
|
|
149
|
+
figure img { max-width: 100%; height: auto; border-radius: 4px; }
|
|
150
|
+
figcaption { font-size: 0.88em; color: #78766f; margin-top: 0.4em; }
|
|
151
|
+
figure[data-type="chart"] svg { max-width: 100%; height: auto; }
|
|
152
|
+
section[data-type="board"] { display: flex; gap: 1em; overflow-x: auto; align-items: flex-start; }
|
|
153
|
+
section[data-type="board"] > div { min-width: 12rem; border: 1px solid rgba(55, 53, 47, 0.14); border-radius: 6px; padding: 0.6em 0.8em; }
|
|
154
|
+
section[data-type="board"] h4 { margin: 0 0 0.4em; font-size: 0.95em; }
|
|
155
|
+
section[data-type="board"] ul { list-style: none; margin: 0; padding: 0; }
|
|
156
|
+
section[data-type="board"] li { border: 1px solid rgba(55, 53, 47, 0.14); border-radius: 4px; padding: 0.4em 0.5em; margin-bottom: 0.4em; }
|
|
157
|
+
nav[data-type="toc"] ul { list-style: none; padding-left: 0; }
|
|
158
|
+
nav[data-type="toc"] ul ul { padding-left: 1.2em; }
|
|
159
|
+
li > input[type="checkbox"] { margin-right: 0.4em; }
|
|
160
|
+
[data-type="file"] a { display: inline-block; padding: 0.4em 0.7em; border: 1px solid rgba(55, 53, 47, 0.18); border-radius: 4px; }
|
|
161
|
+
@media (prefers-color-scheme: dark) {
|
|
162
|
+
body { background: #191919; color: #d4d4d4; }
|
|
163
|
+
aside.page-callout, th, pre { background: rgba(255, 255, 255, 0.06); }
|
|
164
|
+
th, td, section[data-type="board"] > div, section[data-type="board"] li, [data-type="file"] a { border-color: rgba(255, 255, 255, 0.16); }
|
|
165
|
+
a { color: #6cb0f5; }
|
|
166
|
+
}`;
|
|
167
|
+
|
|
168
|
+
function standalone(title, body) {
|
|
169
|
+
return `<!DOCTYPE html>
|
|
170
|
+
<html lang="en">
|
|
171
|
+
<head>
|
|
172
|
+
<meta charset="utf-8">
|
|
173
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
174
|
+
<title>${escText(title)}</title>
|
|
175
|
+
<style>
|
|
176
|
+
${READER_CSS}
|
|
177
|
+
</style>
|
|
178
|
+
</head>
|
|
179
|
+
<body>
|
|
180
|
+
<article data-page="1">
|
|
181
|
+
${body}
|
|
182
|
+
</article>
|
|
183
|
+
</body>
|
|
184
|
+
</html>
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* One readable HTML document for the page model, and the resources it references.
|
|
190
|
+
* An address the file cannot carry, or a tool with no static rendering, throws a
|
|
191
|
+
* PageExportError naming what stopped it rather than producing a truncated file.
|
|
192
|
+
*/
|
|
193
|
+
export function renderExportHtml(model, { title = null, fallbackOf = null, doc = globalThis.document } = {}) {
|
|
194
|
+
if (!Array.isArray(model)) throw new TypeError("page: an export renders a projection model");
|
|
195
|
+
const clean = fragmentCleaner(doc);
|
|
196
|
+
const fallback = fallbackOf ?? ((id) => model.find((item) => item.id === id)?.artifact?.fallbackHtml ?? "");
|
|
197
|
+
for (const item of model) {
|
|
198
|
+
if (item.t === "tool" && !hasStatic(fallback(item.id), clean)) {
|
|
199
|
+
fail("unsupported-tool", `Block ${item.id} holds a tool with no static rendering, which a reading copy cannot show`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const container = doc.createElement("div");
|
|
203
|
+
container.innerHTML = renderModel(openToggles(model), { fallbackOf: fallback, clean });
|
|
204
|
+
const resources = resourcesOf(container);
|
|
205
|
+
scrub(container);
|
|
206
|
+
plainToc(container);
|
|
207
|
+
const name = title ?? documentTitle(model);
|
|
208
|
+
return { html: standalone(name, container.innerHTML), title: name, resources };
|
|
209
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// Markdown export: the live page record as Markdown text, and the report of what the
|
|
2
|
+
// format could not carry. Nothing is dropped silently: a construct written as semantic
|
|
3
|
+
// HTML from the canonical projection is listed as an extension, and a construct whose
|
|
4
|
+
// meaning goes away is listed as a loss, each named per block. No parser is involved:
|
|
5
|
+
// the record is read once and the text is built from it.
|
|
6
|
+
import { project, renderModel, tocHtml, unitsOf } from "./projection.js";
|
|
7
|
+
import { descriptors } from "./format-codec.js";
|
|
8
|
+
|
|
9
|
+
const ATOM = "\uFFFC";
|
|
10
|
+
const BREAK = '@{"type":"hard_break"}';
|
|
11
|
+
const CODES = { b: "strong", i: "em", s: "strike", c: "code" };
|
|
12
|
+
const TEXTUAL = new Set(["paragraph", "heading", "list_item", "toggle", "quote", "callout", "code_block"]);
|
|
13
|
+
const KNOWN = new Set([
|
|
14
|
+
"paragraph", "heading", "list_item", "quote", "callout", "code_block", "toggle",
|
|
15
|
+
"divider", "toc", "image", "file", "table", "chart", "board", "tool", "preserved",
|
|
16
|
+
]);
|
|
17
|
+
// The characters that could start a construct wherever they stand, and the ones that
|
|
18
|
+
// could only at the start of a line: a list marker, a setext rule, a fence, and an
|
|
19
|
+
// ordered marker's dot. A backslash before one of them is Markdown's own escape, and
|
|
20
|
+
// the import reads it back as the character, so the text round-trips.
|
|
21
|
+
const SPECIAL = new Set(["\\", "`", "*", "_", "[", "]", "(", ")", "#", "!", ">", "|", "<", "&"]);
|
|
22
|
+
const STARTS = new Set(["-", "+", "=", "~"]);
|
|
23
|
+
function escText(value) {
|
|
24
|
+
const text = String(value ?? "");
|
|
25
|
+
let out = "";
|
|
26
|
+
let line = "";
|
|
27
|
+
for (const ch of text) {
|
|
28
|
+
let escape = SPECIAL.has(ch);
|
|
29
|
+
if (!escape && line === "" && STARTS.has(ch)) escape = true;
|
|
30
|
+
if (!escape && ch === "." && line !== "" && /^\d+$/.test(line)) escape = true;
|
|
31
|
+
out += escape ? `\\${ch}` : ch;
|
|
32
|
+
line = ch === "\n" ? "" : line + ch;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// A code span of any content: a fence longer than the longest backtick run inside it,
|
|
38
|
+
// padded when the first or last character would otherwise touch the fence.
|
|
39
|
+
function codeSpan(text) {
|
|
40
|
+
const value = String(text);
|
|
41
|
+
const longest = (value.match(/`+/g) ?? []).reduce((long, run) => Math.max(long, run.length), 0);
|
|
42
|
+
const fence = "`".repeat(longest + 1);
|
|
43
|
+
const pad = /^[`\s]|[`\s]$/.test(value) ? " " : "";
|
|
44
|
+
return `${fence}${pad}${value}${pad}${fence}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fence(body, lang = "") {
|
|
48
|
+
const value = String(body ?? "").replace(/\r\n?/g, "\n").replace(/\n$/, "");
|
|
49
|
+
const longest = (value.match(/`+/g) ?? []).reduce((long, run) => Math.max(long, run.length), 0);
|
|
50
|
+
const mark = "`".repeat(Math.max(3, longest + 1));
|
|
51
|
+
const info = String(lang ?? "").trim().split(/\s+/)[0].replace(/`/g, "");
|
|
52
|
+
return `${mark}${info}\n${value}\n${mark}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// A link destination that would end the link at its first space or parenthesis is
|
|
56
|
+
// written in angle brackets instead.
|
|
57
|
+
const hrefText = (href) => (/[\s()<>]/.test(href) ? `<${href.trim().replace(/</g, "%3C").replace(/>/g, "%3E")}>` : href);
|
|
58
|
+
|
|
59
|
+
// One unit's mark key as what Markdown can write: the codes it has, the link it
|
|
60
|
+
// carries, and every mark it would have to drop.
|
|
61
|
+
function parseKey(key) {
|
|
62
|
+
const codes = {};
|
|
63
|
+
let href = null;
|
|
64
|
+
const dropped = [];
|
|
65
|
+
for (const desc of descriptors(key)) {
|
|
66
|
+
if (desc.name === undefined) {
|
|
67
|
+
if (desc.code === "a") href = String(desc.attrs?.[0] ?? "");
|
|
68
|
+
else if (CODES[desc.code]) codes[CODES[desc.code]] = true;
|
|
69
|
+
else dropped.push(desc.code);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (desc.name === "mention") {
|
|
73
|
+
dropped.push("mention");
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (desc.name === "date") continue;
|
|
77
|
+
dropped.push(desc.name);
|
|
78
|
+
}
|
|
79
|
+
return { codes, href, dropped };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A text block's inline content as Markdown. `note` names what is being dropped in
|
|
83
|
+
// this block, once per construct.
|
|
84
|
+
function inline(item, note) {
|
|
85
|
+
const c = typeof item.c === "string" ? item.c : "";
|
|
86
|
+
const units = unitsOf(item.m, c.length) ?? [];
|
|
87
|
+
let out = "";
|
|
88
|
+
for (let i = 0; i < c.length;) {
|
|
89
|
+
const key = units[i] ?? "";
|
|
90
|
+
if (c[i] === ATOM && key[0] === "@") {
|
|
91
|
+
if (key === BREAK) out += "\\\n";
|
|
92
|
+
else note("inline object");
|
|
93
|
+
i++;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
let j = i + 1;
|
|
97
|
+
while (j < c.length && (units[j] ?? "") === key && !(c[j] === ATOM && key[0] === "@")) j++;
|
|
98
|
+
const raw = c.slice(i, j);
|
|
99
|
+
const { codes, href, dropped } = parseKey(key);
|
|
100
|
+
for (const name of dropped) note(name);
|
|
101
|
+
let piece;
|
|
102
|
+
if (codes.code) {
|
|
103
|
+
piece = codeSpan(raw);
|
|
104
|
+
for (const name of ["strong", "em", "strike"]) if (codes[name]) note(name);
|
|
105
|
+
} else {
|
|
106
|
+
const text = escText(raw);
|
|
107
|
+
const lead = text.slice(0, text.length - text.trimStart().length);
|
|
108
|
+
const trail = text.slice(text.trimEnd().length);
|
|
109
|
+
let core = text.slice(lead.length, text.length - trail.length);
|
|
110
|
+
if (core) {
|
|
111
|
+
if (codes.em) core = `*${core}*`;
|
|
112
|
+
if (codes.strong) core = `**${core}**`;
|
|
113
|
+
if (codes.strike) core = `~~${core}~~`;
|
|
114
|
+
}
|
|
115
|
+
piece = lead + core + trail;
|
|
116
|
+
}
|
|
117
|
+
if (href) piece = `[${piece}](${hrefText(href)})`;
|
|
118
|
+
out += piece;
|
|
119
|
+
i = j;
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const levelOf = (value, fallback = 2) => Math.min(6, Math.max(1, Number.isInteger(value) ? value : fallback));
|
|
125
|
+
|
|
126
|
+
// The canonical projection of one block, or of a toggle with its whole body. A chart
|
|
127
|
+
// is drawn against its source table, which is rendered after it and cut away again.
|
|
128
|
+
function htmlFor(item, model) {
|
|
129
|
+
if (item.t === "toc") return tocHtml(item, model);
|
|
130
|
+
if (item.t === "chart") {
|
|
131
|
+
const html = renderModel([item, ...model.filter((one) => one.t === "table")]);
|
|
132
|
+
const cut = html.indexOf("\n<table");
|
|
133
|
+
return cut < 0 ? html : html.slice(0, cut);
|
|
134
|
+
}
|
|
135
|
+
return renderModel([item]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const decorated = (item) => {
|
|
139
|
+
const names = [];
|
|
140
|
+
if (item.pageIcon != null && item.pageIcon !== "") names.push("Page icon");
|
|
141
|
+
if (item.pageCoverSrc != null || item.pageCoverAlt != null || item.pageCoverX != null || item.pageCoverY != null) names.push("Page cover");
|
|
142
|
+
return names;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// The blocks a toggle owns, which the projection nests inside it. The model lists a
|
|
146
|
+
// toggle before its body, in order, so the body is the run that names it as an ancestor.
|
|
147
|
+
function bodyOf(items, item) {
|
|
148
|
+
const out = [];
|
|
149
|
+
const at = items.indexOf(item);
|
|
150
|
+
const byId = new Map(items.map((one) => [one.id, one]));
|
|
151
|
+
for (let i = at + 1; i < items.length; i++) {
|
|
152
|
+
let up = items[i].parent ?? null;
|
|
153
|
+
let inside = false;
|
|
154
|
+
for (let depth = 0; up && depth < 64; depth++) {
|
|
155
|
+
if (up === item.id) { inside = true; break; }
|
|
156
|
+
up = byId.get(up)?.parent ?? null;
|
|
157
|
+
}
|
|
158
|
+
if (!inside) break;
|
|
159
|
+
out.push(items[i]);
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function exportMarkdown(blocks) {
|
|
165
|
+
const model = project(blocks ?? {});
|
|
166
|
+
const items = model.filter((one) => one && typeof one.t === "string");
|
|
167
|
+
const title = items.find((one) => one.t === "heading" && (one.level ?? 2) === 1 && (one.parent ?? null) === null) ?? null;
|
|
168
|
+
const extensions = [];
|
|
169
|
+
const losses = [];
|
|
170
|
+
const extension = (item, name) => extensions.push({ block: item.id ?? null, type: item.t, name });
|
|
171
|
+
const noteFor = (item) => {
|
|
172
|
+
const seen = new Set();
|
|
173
|
+
return (name) => {
|
|
174
|
+
if (seen.has(name)) return;
|
|
175
|
+
seen.add(name);
|
|
176
|
+
losses.push({ block: item.id ?? null, type: item.t, name });
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const lines = [];
|
|
181
|
+
let listOpen = false;
|
|
182
|
+
const push = (text) => {
|
|
183
|
+
if (lines.length && lines[lines.length - 1] !== "") lines.push("");
|
|
184
|
+
lines.push(...String(text).split("\n"));
|
|
185
|
+
listOpen = false;
|
|
186
|
+
};
|
|
187
|
+
const pushItem = (text) => {
|
|
188
|
+
if (!listOpen && lines.length && lines[lines.length - 1] !== "") lines.push("");
|
|
189
|
+
lines.push(text);
|
|
190
|
+
listOpen = true;
|
|
191
|
+
};
|
|
192
|
+
const skip = new Set();
|
|
193
|
+
|
|
194
|
+
for (const raw of model) {
|
|
195
|
+
if (!raw || typeof raw.t !== "string" || skip.has(raw.id)) continue;
|
|
196
|
+
const item = raw;
|
|
197
|
+
const note = noteFor(item);
|
|
198
|
+
const t = item.t;
|
|
199
|
+
|
|
200
|
+
// A block type Markdown has no form for keeps only its text, and says so.
|
|
201
|
+
if (t === "opaque_block" || !KNOWN.has(t)) {
|
|
202
|
+
note(`opaque record (${String(item.raw?.t ?? t)})`);
|
|
203
|
+
const text = TEXTUAL.has(t) ? inline(item, note) : typeof item.c === "string" ? escText(item.c) : "";
|
|
204
|
+
if (text.trim()) push(text);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (t === "paragraph") {
|
|
209
|
+
push(inline(item, note));
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (t === "heading") {
|
|
214
|
+
const names = item.id === title?.id ? decorated(item) : [];
|
|
215
|
+
if (names.length) {
|
|
216
|
+
for (const name of names) extension(item, name);
|
|
217
|
+
push(htmlFor(item, model));
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (item.pageIcon != null && item.pageIcon !== "") note("heading icon");
|
|
221
|
+
push(`${"#".repeat(levelOf(item.level))} ${inline(item, note)}`.trimEnd());
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (t === "toggle") {
|
|
226
|
+
const level = item.level === 1 || item.level === 2 || item.level === 3 ? item.level : null;
|
|
227
|
+
if (level) {
|
|
228
|
+
note("toggle collapse");
|
|
229
|
+
push(`${"#".repeat(level)} ${inline(item, note)}`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
extension(item, "Toggle");
|
|
233
|
+
const body = bodyOf(items, item);
|
|
234
|
+
for (const one of body) skip.add(one.id);
|
|
235
|
+
push(renderModel([item, ...body]));
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (t === "list_item") {
|
|
240
|
+
if (item.owner != null) note("owner");
|
|
241
|
+
if (item.due != null) note("due date");
|
|
242
|
+
const indent = Math.max(0, Math.min(8, Number.isInteger(item.indent) ? item.indent : 0));
|
|
243
|
+
const marker = item.kind === "ordered" ? "1." : "-";
|
|
244
|
+
const box = item.kind === "check" ? (item.checked ? " [x]" : " [ ]") : "";
|
|
245
|
+
pushItem(`${" ".repeat(indent)}${marker}${box} ${inline(item, note)}`.trimEnd());
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (t === "quote") {
|
|
250
|
+
const text = inline(item, note).split("\n").map((line) => (line ? `> ${line}` : ">")).join("\n");
|
|
251
|
+
push(text);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (t === "code_block") {
|
|
256
|
+
push(fence(item.c, item.lang));
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (t === "divider") {
|
|
261
|
+
push("---");
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (t === "image") {
|
|
266
|
+
const src = String(item.src ?? "");
|
|
267
|
+
if (item.width != null) note("image width");
|
|
268
|
+
if (item.align != null) note("image alignment");
|
|
269
|
+
if (!src) {
|
|
270
|
+
note("image address");
|
|
271
|
+
const alt = String(item.alt ?? "");
|
|
272
|
+
if (alt) push(escText(alt));
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const alt = escText(item.alt ?? "");
|
|
276
|
+
const caption = item.caption ? ` "${String(item.caption).replace(/"/g, '\\"')}"` : "";
|
|
277
|
+
push(`}${caption})`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (t === "file") {
|
|
282
|
+
if (item.size != null) note("file size");
|
|
283
|
+
if (item.mime != null) note("file type");
|
|
284
|
+
const src = String(item.src ?? "");
|
|
285
|
+
const name = escText(item.name || "file");
|
|
286
|
+
push(src ? `[${name}](${hrefText(src)})` : name);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (t === "table") {
|
|
291
|
+
const cols = Array.isArray(item.cols) ? item.cols : [];
|
|
292
|
+
const rows = Array.isArray(item.rows) ? item.rows : [];
|
|
293
|
+
for (const col of cols) {
|
|
294
|
+
if (col.kind != null) note("column kind");
|
|
295
|
+
if (col.width != null) note("column width");
|
|
296
|
+
}
|
|
297
|
+
if (item.sort != null) note("sort");
|
|
298
|
+
if (!cols.length) {
|
|
299
|
+
note("empty table");
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const cell = (value) => {
|
|
303
|
+
const one = typeof value === "string" ? escText(value) : value && typeof value === "object" ? inline(value, note) : "";
|
|
304
|
+
return one.replace(/\n/g, " ").trim();
|
|
305
|
+
};
|
|
306
|
+
const header = cols.map((col) => escText(col.name ?? "")).join(" | ");
|
|
307
|
+
const rule = cols.map(() => "---").join(" | ");
|
|
308
|
+
const body = rows.map((row) => `| ${cols.map((col) => cell(row.cells?.[col.id])).join(" | ")} |`);
|
|
309
|
+
push([`| ${header} |`, `| ${rule} |`, ...body].join("\n"));
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (t === "toc") {
|
|
314
|
+
extension(item, "Table of contents");
|
|
315
|
+
push(tocHtml(item, model));
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (t === "chart") {
|
|
320
|
+
extension(item, "Chart");
|
|
321
|
+
push(htmlFor(item, model));
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (t === "board") {
|
|
326
|
+
extension(item, "Board");
|
|
327
|
+
push(htmlFor(item, model));
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (t === "tool") {
|
|
332
|
+
extension(item, "Tool");
|
|
333
|
+
push(htmlFor(item, model));
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (t === "preserved") {
|
|
338
|
+
extension(item, "Raw HTML");
|
|
339
|
+
push(htmlFor(item, model));
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (t === "callout") {
|
|
344
|
+
extension(item, "Callout");
|
|
345
|
+
push(htmlFor(item, model));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const text = lines.join("\n").replace(/^\n+/, "").replace(/\n+$/, "");
|
|
350
|
+
return { text: text ? `${text}\n` : "", extensions, losses, blocks: items.length };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// A name a download can carry: the page's, with the characters a filesystem refuses
|
|
354
|
+
// replaced, never a leading or trailing dot or space.
|
|
355
|
+
export function markdownFileName(name = "page") {
|
|
356
|
+
const base = String(name ?? "")
|
|
357
|
+
.replace(/[\\/:*?"<>|\u0000-\u001f]+/g, "-")
|
|
358
|
+
.replace(/^[.\s]+/, "")
|
|
359
|
+
.replace(/[.\s]+$/, "");
|
|
360
|
+
return `${base || "page"}.md`;
|
|
361
|
+
}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { BOARD_CSS } from "../board-style.js";
|
|
2
2
|
import { BoardView } from "../board-view.js";
|
|
3
3
|
import { cardPanel } from "../board-panel.js";
|
|
4
|
+
import { cardPanels } from "../board-views.js";
|
|
5
|
+
import { addViewHook } from "../view-hooks.js";
|
|
4
6
|
|
|
5
|
-
export function createFeature({ board = null, undo = null, redo = null } = {}) {
|
|
7
|
+
export function createFeature({ view = null, board = null, undo = null, redo = null } = {}) {
|
|
8
|
+
const off = view ? addViewHook(view, {
|
|
9
|
+
update: (editorView) => cardPanels.get(editorView)?.sync(),
|
|
10
|
+
destroy: (editorView) => {
|
|
11
|
+
cardPanels.get(editorView)?.hide(false, false);
|
|
12
|
+
cardPanels.delete(editorView);
|
|
13
|
+
},
|
|
14
|
+
}) : () => {};
|
|
6
15
|
return {
|
|
7
16
|
id: "board",
|
|
8
|
-
nodeViews: board ? { board: (node,
|
|
17
|
+
nodeViews: board ? { board: (node, editorView, getPos) => new BoardView(node, editorView, getPos, { commands: board, undo, redo, panel: cardPanel(editorView) }) } : null,
|
|
9
18
|
css: board ? BOARD_CSS : null,
|
|
10
|
-
dispose() {},
|
|
19
|
+
dispose() { off(); },
|
|
11
20
|
};
|
|
12
21
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ChartView, refreshCharts } from "../chart.js";
|
|
2
|
+
import { addViewHook } from "../view-hooks.js";
|
|
3
|
+
|
|
4
|
+
export function createFeature({ view = null } = {}) {
|
|
5
|
+
const off = view ? addViewHook(view, { update: refreshCharts }) : () => {};
|
|
6
|
+
return {
|
|
7
|
+
id: "chart",
|
|
8
|
+
nodeViews: { chart: (node, editorView, getPos) => new ChartView(node, editorView, getPos) },
|
|
9
|
+
dispose() { off(); },
|
|
10
|
+
};
|
|
11
|
+
}
|