@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,162 @@
|
|
|
1
|
+
// The emoji a picker offers: a small curated set, grouped the way somebody looks for one.
|
|
2
|
+
// Each row is [glyph, name, keywords], and the glyph is the whole record: a variation
|
|
3
|
+
// selector, a skin tone, a flag's regional pair, a keycap or a joined family is written as
|
|
4
|
+
// the one grapheme a person sees, and nothing here is a font, a sprite, an image or a URL.
|
|
5
|
+
// It is fetched by `emoji.js` the first time the picker opens, never by a reader and never
|
|
6
|
+
// with the editor.
|
|
7
|
+
export const EMOJI = {
|
|
8
|
+
"Smileys": [
|
|
9
|
+
["😀", "grinning", "face smile happy"],
|
|
10
|
+
["😃", "grinning big eyes", "face smile happy"],
|
|
11
|
+
["😄", "grinning smiling eyes", "face laugh happy"],
|
|
12
|
+
["😁", "beaming", "face grin happy"],
|
|
13
|
+
["😆", "laughing", "face happy haha"],
|
|
14
|
+
["😅", "sweat smile", "face relief nervous"],
|
|
15
|
+
["😂", "joy", "face tears laugh lol"],
|
|
16
|
+
["🙂", "slight smile", "face ok"],
|
|
17
|
+
["🙃", "upside down", "face silly"],
|
|
18
|
+
["😉", "wink", "face flirt"],
|
|
19
|
+
["😊", "blush", "face smile warm"],
|
|
20
|
+
["😇", "halo", "face angel innocent"],
|
|
21
|
+
["🥰", "smiling hearts", "face love"],
|
|
22
|
+
["😍", "heart eyes", "face love crush"],
|
|
23
|
+
["🤩", "star struck", "face wow excited"],
|
|
24
|
+
["😎", "cool", "face sunglasses"],
|
|
25
|
+
["🤔", "thinking", "face hmm question"],
|
|
26
|
+
["😐", "neutral", "face flat"],
|
|
27
|
+
["😴", "sleeping", "face tired zzz"],
|
|
28
|
+
["😢", "crying", "face sad tear"],
|
|
29
|
+
["😭", "sobbing", "face sad tears"],
|
|
30
|
+
["😡", "angry", "face mad red"],
|
|
31
|
+
["😱", "scream", "face fear shock"],
|
|
32
|
+
["🤯", "exploding head", "face mind blown"],
|
|
33
|
+
["🥳", "party face", "celebration birthday"],
|
|
34
|
+
["😷", "mask", "face sick medical"],
|
|
35
|
+
["🤒", "thermometer", "face sick ill"],
|
|
36
|
+
],
|
|
37
|
+
"Gestures and people": [
|
|
38
|
+
["👋", "wave", "hand hello hi bye"],
|
|
39
|
+
["👋🏽", "wave medium skin tone", "hand hello hi"],
|
|
40
|
+
["👍", "thumbs up", "yes like approve"],
|
|
41
|
+
["👍🏿", "thumbs up dark skin tone", "yes like approve"],
|
|
42
|
+
["👎", "thumbs down", "no dislike"],
|
|
43
|
+
["👏", "clapping", "hands applause bravo"],
|
|
44
|
+
["🙌", "raised hands", "hooray praise"],
|
|
45
|
+
["🙏", "folded hands", "please thanks pray"],
|
|
46
|
+
["💪", "flexed arm", "strong muscle"],
|
|
47
|
+
["🤝", "handshake", "deal agreement"],
|
|
48
|
+
["👨👩👧👦", "family", "people children parents"],
|
|
49
|
+
["🧑💻", "technologist", "person laptop work code"],
|
|
50
|
+
["🏃", "runner", "person sport hurry"],
|
|
51
|
+
["🧘", "lotus", "person meditation yoga"],
|
|
52
|
+
],
|
|
53
|
+
"Animals and nature": [
|
|
54
|
+
["🐶", "dog", "animal pet puppy"],
|
|
55
|
+
["🐱", "cat", "animal pet kitten"],
|
|
56
|
+
["🐭", "mouse", "animal rodent"],
|
|
57
|
+
["🐰", "rabbit", "animal bunny"],
|
|
58
|
+
["🦊", "fox", "animal"],
|
|
59
|
+
["🐻", "bear", "animal"],
|
|
60
|
+
["🐼", "panda", "animal"],
|
|
61
|
+
["🐨", "koala", "animal"],
|
|
62
|
+
["🐯", "tiger", "animal"],
|
|
63
|
+
["🦁", "lion", "animal"],
|
|
64
|
+
["🐮", "cow", "animal"],
|
|
65
|
+
["🐷", "pig", "animal"],
|
|
66
|
+
["🐸", "frog", "animal"],
|
|
67
|
+
["🐵", "monkey", "animal"],
|
|
68
|
+
["🐔", "chicken", "animal bird"],
|
|
69
|
+
["🐧", "penguin", "animal bird"],
|
|
70
|
+
["🐦", "bird", "animal"],
|
|
71
|
+
["🦄", "unicorn", "animal magic"],
|
|
72
|
+
["🐝", "bee", "animal insect honey"],
|
|
73
|
+
["🦋", "butterfly", "animal insect"],
|
|
74
|
+
["🌸", "cherry blossom", "flower plant spring"],
|
|
75
|
+
["🌻", "sunflower", "flower plant summer"],
|
|
76
|
+
["🌈", "rainbow", "weather sky"],
|
|
77
|
+
["🌲", "evergreen tree", "plant forest"],
|
|
78
|
+
["🌵", "cactus", "plant desert"],
|
|
79
|
+
["🍁", "maple leaf", "plant autumn"],
|
|
80
|
+
],
|
|
81
|
+
"Food and drink": [
|
|
82
|
+
["☕", "coffee", "drink cup hot"],
|
|
83
|
+
["🍵", "tea", "drink cup green"],
|
|
84
|
+
["🍺", "beer", "drink mug"],
|
|
85
|
+
["🍷", "wine", "drink glass"],
|
|
86
|
+
["🍎", "apple", "fruit food red"],
|
|
87
|
+
["🍌", "banana", "fruit food"],
|
|
88
|
+
["🍇", "grapes", "fruit food"],
|
|
89
|
+
["🍓", "strawberry", "fruit food"],
|
|
90
|
+
["🍑", "peach", "fruit food"],
|
|
91
|
+
["🍕", "pizza", "food slice"],
|
|
92
|
+
["🍔", "burger", "food"],
|
|
93
|
+
["🍟", "fries", "food"],
|
|
94
|
+
["🌮", "taco", "food"],
|
|
95
|
+
["🍣", "sushi", "food fish"],
|
|
96
|
+
["🍜", "noodles", "food ramen"],
|
|
97
|
+
["🍰", "cake", "food dessert"],
|
|
98
|
+
["🍪", "cookie", "food sweet"],
|
|
99
|
+
["🍫", "chocolate", "food sweet"],
|
|
100
|
+
["🥑", "avocado", "food fruit"],
|
|
101
|
+
["🥕", "carrot", "food vegetable"],
|
|
102
|
+
["🍞", "bread", "food loaf"],
|
|
103
|
+
],
|
|
104
|
+
"Travel and places": [
|
|
105
|
+
["✈️", "airplane", "travel flight"],
|
|
106
|
+
["🚗", "car", "travel drive"],
|
|
107
|
+
["🚲", "bicycle", "travel bike"],
|
|
108
|
+
["🚂", "train", "travel locomotive"],
|
|
109
|
+
["🚀", "rocket", "travel launch space"],
|
|
110
|
+
["⛵", "sailboat", "travel boat"],
|
|
111
|
+
["🏠", "house", "place home"],
|
|
112
|
+
["🏢", "office", "place building work"],
|
|
113
|
+
["🗽", "statue of liberty", "place new york"],
|
|
114
|
+
["🗼", "tower", "place tokyo"],
|
|
115
|
+
["🏔", "mountain", "place snow"],
|
|
116
|
+
["🏖", "beach", "place holiday sea"],
|
|
117
|
+
["🌍", "globe", "world earth"],
|
|
118
|
+
["🗺", "map", "place world"],
|
|
119
|
+
["🚦", "traffic light", "place street"],
|
|
120
|
+
],
|
|
121
|
+
"Objects": [
|
|
122
|
+
["💡", "bulb", "idea light"],
|
|
123
|
+
["🔥", "fire", "hot flame"],
|
|
124
|
+
["⭐", "star", "favourite rating"],
|
|
125
|
+
["💯", "hundred", "score perfect"],
|
|
126
|
+
["✅", "check", "done yes"],
|
|
127
|
+
["❌", "cross", "no wrong"],
|
|
128
|
+
["⚠️", "warning", "caution alert"],
|
|
129
|
+
["📌", "pushpin", "pin note"],
|
|
130
|
+
["📎", "paperclip", "attach"],
|
|
131
|
+
["📅", "calendar", "date schedule"],
|
|
132
|
+
["📈", "chart up", "graph growth"],
|
|
133
|
+
["📉", "chart down", "graph loss"],
|
|
134
|
+
["💰", "money bag", "cash"],
|
|
135
|
+
["🔑", "key", "access"],
|
|
136
|
+
["🔒", "lock", "secure private"],
|
|
137
|
+
["🔔", "bell", "notify alert"],
|
|
138
|
+
["🎉", "party popper", "celebrate"],
|
|
139
|
+
["🎁", "gift", "present"],
|
|
140
|
+
["🏆", "trophy", "win award"],
|
|
141
|
+
["🎯", "target", "goal aim"],
|
|
142
|
+
["⏰", "alarm clock", "time"],
|
|
143
|
+
["⌛", "hourglass", "time wait"],
|
|
144
|
+
],
|
|
145
|
+
"Symbols": [
|
|
146
|
+
["❤️", "red heart", "love"],
|
|
147
|
+
["💔", "broken heart", "love sad"],
|
|
148
|
+
["✨", "sparkles", "magic new"],
|
|
149
|
+
["1️⃣", "keycap one", "number"],
|
|
150
|
+
["2️⃣", "keycap two", "number"],
|
|
151
|
+
["#️⃣", "keycap hash", "number channel"],
|
|
152
|
+
["🇫🇷", "flag france", "country"],
|
|
153
|
+
["🇺🇸", "flag united states", "country"],
|
|
154
|
+
["🇯🇵", "flag japan", "country"],
|
|
155
|
+
["♻️", "recycle", "reuse"],
|
|
156
|
+
["➡️", "arrow right", "direction next"],
|
|
157
|
+
["⬅️", "arrow left", "direction back"],
|
|
158
|
+
["🔁", "repeat", "loop again"],
|
|
159
|
+
["🔀", "shuffle", "random"],
|
|
160
|
+
["🆗", "ok button", "yes"],
|
|
161
|
+
],
|
|
162
|
+
};
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
// The readable snapshot's inventory, made self-contained: every displayed image and
|
|
2
|
+
// included attachment the snapshot names is read once, within limits, and written
|
|
3
|
+
// into the document as a data URL, so the file that comes out needs nothing fetched
|
|
4
|
+
// again. What cannot be carried is named in a loss list rather than dropped in
|
|
5
|
+
// silence: a cross-origin read the other origin refuses, a host that is not
|
|
6
|
+
// reachable, a missing file, a type a reading copy does not carry, a resource over
|
|
7
|
+
// its limit, an embed with no static rendering. Addresses are canonical before the
|
|
8
|
+
// duplicate check, so one picture named twice is one request.
|
|
9
|
+
/** The limits one export reads under: per resource, in total, and how long each waits. */
|
|
10
|
+
export const EXPORT_LIMITS = Object.freeze({
|
|
11
|
+
perResource: 20 * 1024 * 1024,
|
|
12
|
+
total: 50 * 1024 * 1024,
|
|
13
|
+
timeout: 10_000,
|
|
14
|
+
inFlight: 3,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// What a reading copy carries inside itself: images, fonts and media, never anything
|
|
18
|
+
// a browser would treat as a document, a script or a style. An active type such as
|
|
19
|
+
// SVG or HTML is refused by name rather than inlined.
|
|
20
|
+
const ALLOWED_MIME = [
|
|
21
|
+
/^image\/(?:png|jpe?g|gif|webp|avif|bmp|x-icon|vnd\.microsoft\.icon)$/i,
|
|
22
|
+
/^font\/(?:woff2?|ttf|otf|sfnt|collection|eot)$/i,
|
|
23
|
+
/^application\/(?:font-woff2?|vnd\.ms-fontobject|x-font-ttf|x-font-opentype|vnd\.ms-opentype)$/i,
|
|
24
|
+
/^audio\/[a-z0-9][a-z0-9.+-]*$/i,
|
|
25
|
+
/^video\/[a-z0-9][a-z0-9.+-]*$/i,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// What no attachment may carry: anything a browser would run or render as a document.
|
|
29
|
+
const ACTIVE_MIME = /^(?:text\/(?:html|javascript|css|xml|xsl)|application\/(?:xhtml\+xml|xml|javascript|ecmascript|x-javascript|wasm)|image\/svg\+xml)$|\+xml$/i;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether a response's declared type is one a reading copy carries inside itself. An image
|
|
33
|
+
* is drawn, so only the drawn types; an attachment is a link to a file, so any type that is
|
|
34
|
+
* not active.
|
|
35
|
+
*/
|
|
36
|
+
export function isAllowedMime(type, kind = "image") {
|
|
37
|
+
const base = String(type ?? "").split(";")[0].trim();
|
|
38
|
+
if (base === "") return false;
|
|
39
|
+
if (kind === "attachment") return !ACTIVE_MIME.test(base);
|
|
40
|
+
return ALLOWED_MIME.some((pattern) => pattern.test(base));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* One address, as the duplicate check and the request both see it: resolved against
|
|
45
|
+
* the captured base URL, with the fragment gone. A value that names nothing, or that
|
|
46
|
+
* is not an address at all, answers null.
|
|
47
|
+
*/
|
|
48
|
+
export function canonicalUrl(raw, base = globalThis.document?.baseURI ?? null) {
|
|
49
|
+
const text = String(raw ?? "").trim();
|
|
50
|
+
if (!text || text.startsWith("#")) return null;
|
|
51
|
+
try {
|
|
52
|
+
const url = new URL(text, base ?? undefined);
|
|
53
|
+
url.hash = "";
|
|
54
|
+
return url.href;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const squash = (value) => String(value ?? "").trim().replace(/[\u0000-\u0020]/g, "");
|
|
61
|
+
const isLocalHost = (hostname) => /^(?:localhost|127(?:\.\d{1,3}){3}|0\.0\.0\.0|\[::1\]|::1)$/i.test(hostname || "") || /\.local$/i.test(hostname || "");
|
|
62
|
+
const originOf = (href) => { try { return new URL(href).origin; } catch { return null; } };
|
|
63
|
+
const hostOf = (href) => { try { return new URL(href).hostname; } catch { return null; } };
|
|
64
|
+
const mb = (bytes) => (bytes >= 1024 * 1024 ? `${Math.round(bytes / (1024 * 1024))} MiB` : bytes >= 1024 ? `${Math.round(bytes / 1024)} KiB` : `${Math.round(bytes)} B`);
|
|
65
|
+
|
|
66
|
+
const subject = (target) => (target.blockId ? `The ${target.kind} in block ${target.blockId}` : `The ${target.kind}`);
|
|
67
|
+
const loss = (code, target, message) => ({ code, kind: target.kind ?? null, url: target.url ?? null, blockId: target.blockId ?? null, message });
|
|
68
|
+
|
|
69
|
+
function embedLoss(entry, url) {
|
|
70
|
+
const blockId = entry.blockId ?? null;
|
|
71
|
+
const where = blockId ? ` in block ${blockId}` : "";
|
|
72
|
+
return {
|
|
73
|
+
code: "unsupported-embed",
|
|
74
|
+
kind: "embed",
|
|
75
|
+
url: entry.url ?? null,
|
|
76
|
+
blockId,
|
|
77
|
+
message: `The embed${where} is a live frame, which a reading copy cannot carry${url ? ` (${url})` : ""}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function urlLoss(entry) {
|
|
82
|
+
const blockId = entry.blockId ?? null;
|
|
83
|
+
const where = blockId ? ` in block ${blockId}` : "";
|
|
84
|
+
return {
|
|
85
|
+
code: "unsupported-url",
|
|
86
|
+
kind: entry.kind ?? null,
|
|
87
|
+
url: entry.url ?? null,
|
|
88
|
+
blockId,
|
|
89
|
+
message: `The ${entry.kind}${where} points at ${String(entry.url ?? "").trim().slice(0, 120)}, which is not an address a reading copy can fetch`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const bytesOf = (value) => {
|
|
94
|
+
if (!value) return new Uint8Array(0);
|
|
95
|
+
if (value instanceof Uint8Array) return value;
|
|
96
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
97
|
+
if (ArrayBuffer.isView(value)) return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
98
|
+
return new Uint8Array(value);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
function toBase64(bytes) {
|
|
102
|
+
let binary = "";
|
|
103
|
+
for (let at = 0; at < bytes.length; at += 0x8000) binary += String.fromCharCode(...bytes.subarray(at, at + 0x8000));
|
|
104
|
+
return globalThis.btoa(binary);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const dataUrlOf = (type, bytes) => `data:${String(type).split(";")[0].trim()};base64,${toBase64(bytes)}`;
|
|
108
|
+
|
|
109
|
+
// The body as bytes, never as a whole file at once: each chunk is counted against
|
|
110
|
+
// both limits, so an oversized resource, or one that would push the file past its
|
|
111
|
+
// total, stops at the chunk that crossed the line instead of after the download.
|
|
112
|
+
async function readBytes(response, task, limits, budget) {
|
|
113
|
+
const declared = Number(response.headers?.get?.("content-length"));
|
|
114
|
+
if (Number.isFinite(declared) && declared > limits.perResource) {
|
|
115
|
+
throw loss("too-large", task.target, `${subject(task.target)} at ${task.target.url} is larger than ${mb(limits.perResource)}`);
|
|
116
|
+
}
|
|
117
|
+
const charge = () => {
|
|
118
|
+
if (task.bytes > limits.perResource) throw loss("too-large", task.target, `${subject(task.target)} at ${task.target.url} is larger than ${mb(limits.perResource)}`);
|
|
119
|
+
if (budget.exhausted || budget.spent + budget.live > budget.total) {
|
|
120
|
+
budget.exhausted = true;
|
|
121
|
+
throw loss("total-too-large", task.target, `The page's resources together are larger than ${mb(budget.total)}`);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const reader = response.body?.getReader?.();
|
|
125
|
+
if (!reader) {
|
|
126
|
+
const whole = bytesOf(typeof response.arrayBuffer === "function" ? await response.arrayBuffer() : response.body);
|
|
127
|
+
task.bytes += whole.byteLength;
|
|
128
|
+
budget.live += whole.byteLength;
|
|
129
|
+
try {
|
|
130
|
+
charge();
|
|
131
|
+
} finally {
|
|
132
|
+
budget.live -= whole.byteLength;
|
|
133
|
+
if (task.bytes <= limits.perResource) budget.spent += whole.byteLength;
|
|
134
|
+
}
|
|
135
|
+
return whole;
|
|
136
|
+
}
|
|
137
|
+
const chunks = [];
|
|
138
|
+
try {
|
|
139
|
+
for (;;) {
|
|
140
|
+
const { done, value } = await reader.read();
|
|
141
|
+
if (done) break;
|
|
142
|
+
const part = bytesOf(value);
|
|
143
|
+
task.bytes += part.byteLength;
|
|
144
|
+
budget.live += part.byteLength;
|
|
145
|
+
charge();
|
|
146
|
+
chunks.push(part);
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
budget.live -= task.bytes;
|
|
150
|
+
task.bytes = 0;
|
|
151
|
+
try { await reader.cancel?.(); } catch {}
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
budget.live -= task.bytes;
|
|
155
|
+
budget.spent += task.bytes;
|
|
156
|
+
const out = new Uint8Array(task.bytes);
|
|
157
|
+
let at = 0;
|
|
158
|
+
for (const part of chunks) { out.set(part, at); at += part.byteLength; }
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// One resource: one request with the credentials its origin is owed, one timeout, one
|
|
163
|
+
// cancellation, and one named loss if it cannot be carried. Nothing here throws past
|
|
164
|
+
// the loss: an export reports every failure it found rather than the first.
|
|
165
|
+
async function fetchOne(task, { fetchImpl, baseOrigin, baseHost, limits, budget, run }) {
|
|
166
|
+
const { target, controller } = task;
|
|
167
|
+
if (run.signal.aborted) return { target, loss: loss("cancelled", target, `Reading ${target.url} was cancelled`) };
|
|
168
|
+
const onCancel = () => controller.abort();
|
|
169
|
+
if (run.signal.aborted) controller.abort();
|
|
170
|
+
else run.signal.addEventListener("abort", onCancel, { once: true });
|
|
171
|
+
const timer = setTimeout(() => { task.timedOut = true; controller.abort(); }, limits.timeout);
|
|
172
|
+
const sameOrigin = baseOrigin != null && originOf(target.url) === baseOrigin;
|
|
173
|
+
try {
|
|
174
|
+
const response = await fetchImpl(target.url, {
|
|
175
|
+
credentials: sameOrigin ? "include" : "omit",
|
|
176
|
+
signal: controller.signal,
|
|
177
|
+
});
|
|
178
|
+
if (!response || response.ok === false) {
|
|
179
|
+
const status = response?.status ?? 0;
|
|
180
|
+
throw loss("missing-resource", target, `${subject(target)} is not at ${target.url} (the host answered ${status || "nothing"})`);
|
|
181
|
+
}
|
|
182
|
+
const type = response.headers?.get?.("content-type") ?? "";
|
|
183
|
+
if (!isAllowedMime(type, target.kind)) {
|
|
184
|
+
throw loss("mime-refused", target, `${subject(target)} at ${target.url} is ${String(type).split(";")[0].trim() || "of no stated type"}, which a reading copy does not carry`);
|
|
185
|
+
}
|
|
186
|
+
const bytes = await readBytes(response, task, limits, budget);
|
|
187
|
+
return { target, hit: { data: dataUrlOf(type, bytes), type: String(type).split(";")[0].trim(), bytes: bytes.byteLength } };
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (error && typeof error.code === "string" && typeof error.message === "string") return { target, loss: error };
|
|
190
|
+
if (task.timedOut) return { target, loss: loss("timeout", target, `${subject(target)} at ${target.url} did not arrive within ${Math.round(limits.timeout / 1000)} s`) };
|
|
191
|
+
if (run.signal.aborted || controller.signal.aborted) return { target, loss: loss("cancelled", target, `Reading ${target.url} was cancelled`) };
|
|
192
|
+
if (!sameOrigin) return { target, loss: loss("cross-origin-cors", target, `${subject(target)} at ${target.url} could not be read: ${originOf(target.url) ?? "the other origin"} refused a cross-origin read`) };
|
|
193
|
+
if (isLocalHost(baseHost)) return { target, loss: loss("offline-host", target, `${subject(target)} at ${target.url} could not be read: this copy's host is not reachable`) };
|
|
194
|
+
return { target, loss: loss("unreachable", target, `${subject(target)} at ${target.url} could not be read`) };
|
|
195
|
+
} finally {
|
|
196
|
+
clearTimeout(timer);
|
|
197
|
+
run.signal.removeEventListener("abort", onCancel);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function pool(tasks, inFlight, worker) {
|
|
202
|
+
const queue = tasks.slice();
|
|
203
|
+
const run = async () => {
|
|
204
|
+
for (;;) {
|
|
205
|
+
const task = queue.shift();
|
|
206
|
+
if (!task) return;
|
|
207
|
+
await worker(task);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
await Promise.all(Array.from({ length: Math.max(1, Math.min(inFlight, queue.length)) }, run));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// The document with the fetched bytes inside it and every remote reference gone: a
|
|
214
|
+
// data URL where a picture or an attachment was, and nothing at all where a resource
|
|
215
|
+
// could not be carried. A stylesheet or script element has no place in a reading copy,
|
|
216
|
+
// and a `url()` or `@import` leaves a style declaration alone.
|
|
217
|
+
function rewrite(html, hits, base) {
|
|
218
|
+
const parsed = new DOMParser().parseFromString(String(html ?? ""), "text/html");
|
|
219
|
+
for (const el of [...parsed.querySelectorAll("script, link, base, style[media]")]) el.remove();
|
|
220
|
+
for (const el of [...parsed.querySelectorAll("iframe, embed, object, video, audio, source, track, picture")]) el.remove();
|
|
221
|
+
for (const el of parsed.querySelectorAll("img")) {
|
|
222
|
+
const hit = hits.get(canonicalUrl(el.getAttribute("src"), base));
|
|
223
|
+
if (hit) el.setAttribute("src", hit.data);
|
|
224
|
+
else el.removeAttribute("src");
|
|
225
|
+
el.removeAttribute("srcset");
|
|
226
|
+
el.removeAttribute("poster");
|
|
227
|
+
}
|
|
228
|
+
for (const el of parsed.querySelectorAll('[data-type="file"]')) {
|
|
229
|
+
const hit = hits.get(canonicalUrl(el.getAttribute("data-src"), base)) ?? hits.get(canonicalUrl(el.querySelector("a[href]")?.getAttribute("href"), base));
|
|
230
|
+
if (hit) {
|
|
231
|
+
el.setAttribute("data-src", hit.data);
|
|
232
|
+
const link = el.querySelector("a[href]");
|
|
233
|
+
if (link) link.setAttribute("href", hit.data);
|
|
234
|
+
} else {
|
|
235
|
+
el.removeAttribute("data-src");
|
|
236
|
+
el.querySelector("a[href]")?.removeAttribute("href");
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
for (const el of parsed.querySelectorAll("[style]")) {
|
|
240
|
+
const kept = String(el.getAttribute("style")).split(";").filter((part) => !/url\s*\(|@import/i.test(part)).join(";").trim();
|
|
241
|
+
if (kept) el.setAttribute("style", kept);
|
|
242
|
+
else el.removeAttribute("style");
|
|
243
|
+
}
|
|
244
|
+
return `<!DOCTYPE html>\n${parsed.documentElement.outerHTML}`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const CANDIDATES = [...String("src srcset poster data data-src xlink:href").split(" "), "href"];
|
|
248
|
+
const carriesRemote = (value) => {
|
|
249
|
+
const s = squash(value);
|
|
250
|
+
if (!s || s.startsWith("#")) return false;
|
|
251
|
+
return !/^data:/i.test(s);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Every reference in a document that would make the browser request something the
|
|
256
|
+
* file does not hold: a source, a source set, a poster, an object's data, a
|
|
257
|
+
* stylesheet's or a script's address, a `url()` or `@import` in a style. An ordinary
|
|
258
|
+
* hyperlink is not one, and neither is a data URL or a fragment.
|
|
259
|
+
*/
|
|
260
|
+
export function remoteDependencies(html) {
|
|
261
|
+
const parsed = new DOMParser().parseFromString(String(html ?? ""), "text/html");
|
|
262
|
+
const out = [];
|
|
263
|
+
for (const el of parsed.querySelectorAll("*")) {
|
|
264
|
+
const tag = el.tagName.toLowerCase();
|
|
265
|
+
for (const name of CANDIDATES) {
|
|
266
|
+
if (!el.hasAttribute(name)) continue;
|
|
267
|
+
if (name === "href" && tag !== "link" && tag !== "script" && tag !== "base") continue;
|
|
268
|
+
if (name === "data" && tag !== "object") continue;
|
|
269
|
+
if (name === "data-src" && !el.closest?.('[data-type="file"]')) continue;
|
|
270
|
+
const value = el.getAttribute(name);
|
|
271
|
+
const candidates = name === "srcset" ? String(value).split(",").map((part) => part.trim().split(/\s+/)[0]) : [value];
|
|
272
|
+
for (const candidate of candidates) if (carriesRemote(candidate)) out.push({ tag, attr: name, value: candidate });
|
|
273
|
+
}
|
|
274
|
+
const style = el.getAttribute("style");
|
|
275
|
+
if (style && /url\s*\(|@import/i.test(style)) out.push({ tag, attr: "style", value: style });
|
|
276
|
+
}
|
|
277
|
+
for (const el of parsed.querySelectorAll("style")) {
|
|
278
|
+
if (/url\s*\(|@import/i.test(el.textContent ?? "")) out.push({ tag: "style", attr: "text", value: el.textContent });
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* One readable snapshot made self-contained: `html` with its images and attachments
|
|
285
|
+
* inside it, the named `losses` that stopped anything else being carried, and the
|
|
286
|
+
* `remote` references that still point off the file (empty when nothing does). The
|
|
287
|
+
* caller downloads only when both lists are empty.
|
|
288
|
+
*/
|
|
289
|
+
export async function inlineExportResources(html, {
|
|
290
|
+
resources = [],
|
|
291
|
+
base = globalThis.document?.baseURI ?? null,
|
|
292
|
+
fetch: fetchImpl = globalThis.fetch,
|
|
293
|
+
signal = null,
|
|
294
|
+
limits = EXPORT_LIMITS,
|
|
295
|
+
} = {}) {
|
|
296
|
+
const capped = { ...EXPORT_LIMITS, ...(limits ?? {}) };
|
|
297
|
+
const losses = [];
|
|
298
|
+
const named = new Set();
|
|
299
|
+
const add = (entry) => {
|
|
300
|
+
const key = `${entry.code}\u0000${entry.blockId ?? ""}\u0000${entry.message}`;
|
|
301
|
+
if (named.has(key)) return;
|
|
302
|
+
named.add(key);
|
|
303
|
+
losses.push(entry);
|
|
304
|
+
};
|
|
305
|
+
const targets = new Map();
|
|
306
|
+
for (const entry of resources ?? []) {
|
|
307
|
+
if (!entry || entry.kind === "chart" || entry.inline) continue;
|
|
308
|
+
const url = entry.url ? canonicalUrl(entry.url, base) : null;
|
|
309
|
+
if (entry.kind === "embed") {
|
|
310
|
+
add(embedLoss(entry, url));
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
if (!url) {
|
|
314
|
+
add(urlLoss(entry));
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const target = targets.get(url) ?? { url, kind: entry.kind, blockId: entry.blockId ?? null };
|
|
318
|
+
target.blockId ??= entry.blockId ?? null;
|
|
319
|
+
targets.set(url, target);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const listed = [...targets.values()];
|
|
323
|
+
const hits = new Map();
|
|
324
|
+
const budget = { spent: 0, live: 0, total: capped.total, exhausted: false };
|
|
325
|
+
if (listed.length && typeof fetchImpl !== "function") {
|
|
326
|
+
for (const target of listed) add(loss("unavailable", target, `${subject(target)} at ${target.url} could not be read: this page cannot fetch it`));
|
|
327
|
+
} else if (listed.length) {
|
|
328
|
+
const run = new AbortController();
|
|
329
|
+
const onCancel = () => run.abort();
|
|
330
|
+
if (signal) {
|
|
331
|
+
if (signal.aborted) run.abort();
|
|
332
|
+
else signal.addEventListener("abort", onCancel, { once: true });
|
|
333
|
+
}
|
|
334
|
+
const baseOrigin = base ? originOf(canonicalUrl(base, base) ?? base) : null;
|
|
335
|
+
const baseHost = base ? hostOf(canonicalUrl(base, base) ?? base) : null;
|
|
336
|
+
const answers = new Map();
|
|
337
|
+
try {
|
|
338
|
+
const tasks = listed.map((target) => ({ target, controller: new AbortController(), bytes: 0, timedOut: false }));
|
|
339
|
+
await pool(tasks, capped.inFlight, async (task) => {
|
|
340
|
+
if (run.signal.aborted) return;
|
|
341
|
+
answers.set(task.target.url, await fetchOne(task, { fetchImpl, baseOrigin, baseHost, limits: capped, budget, run }));
|
|
342
|
+
});
|
|
343
|
+
} finally {
|
|
344
|
+
signal?.removeEventListener("abort", onCancel);
|
|
345
|
+
}
|
|
346
|
+
for (const target of listed) {
|
|
347
|
+
const answer = answers.get(target.url);
|
|
348
|
+
if (answer?.hit) hits.set(target.url, answer.hit);
|
|
349
|
+
else if (answer?.loss) add(answer.loss);
|
|
350
|
+
else if (run.signal.aborted) add(loss("cancelled", target, `Reading ${target.url} was cancelled`));
|
|
351
|
+
else add(loss("unreachable", target, `${subject(target)} at ${target.url} could not be read`));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const out = rewrite(html, hits, base);
|
|
356
|
+
return { html: out, losses, remote: remoteDependencies(out), embedded: hits.size, bytes: budget.spent };
|
|
357
|
+
}
|