@heycys/gallery 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +51 -1
- package/browser/editor/components/callout/index.mjs +40 -15
- package/browser/editor/components/columns/page.mjs +45 -32
- package/browser/editor/components/details/index.mjs +2 -1
- package/browser/editor/components/dialog/index.mjs +2 -1
- package/browser/editor/components/dialog/page.mjs +0 -18
- package/browser/editor/components/divider/index.mjs +1 -1
- package/browser/editor/components/embed/index.mjs +2 -2
- package/browser/editor/components/figure/page.mjs +10 -1
- package/browser/editor/components/file/page.mjs +2 -2
- package/browser/editor/components/flowchart/add.mjs +7 -6
- package/browser/editor/components/flowchart/page.mjs +1 -1
- package/browser/editor/components/heading/index.mjs +2 -0
- package/browser/editor/components/heading/page.mjs +6 -0
- package/browser/editor/components/highlight/index.mjs +4 -20
- package/browser/editor/components/icons.mjs +13 -2
- package/browser/editor/components/image/crop.mjs +6 -9
- package/browser/editor/components/image/page.mjs +2 -2
- package/browser/editor/components/installed/index.mjs +82 -29
- package/browser/editor/components/installed/page.mjs +146 -34
- package/browser/editor/components/installed/served.mjs +6 -5
- package/browser/editor/components/instances.mjs +18 -0
- package/browser/editor/components/kit.mjs +3 -0
- package/browser/editor/components/list/page.mjs +3 -6
- package/browser/editor/components/media/page.mjs +44 -14
- package/browser/editor/components/pages.mjs +2 -2
- package/browser/editor/components/registry.mjs +68 -33
- package/browser/editor/components/section/index.mjs +2 -1
- package/browser/editor/components/set-apart.mjs +24 -0
- package/browser/editor/components/table/index.mjs +64 -5
- package/browser/editor/components/table/page.mjs +30 -19
- package/browser/editor/page/annotate/anchors.mjs +4 -3
- package/browser/editor/page/annotate/card.mjs +3 -6
- package/browser/editor/page/annotate/index.mjs +61 -34
- package/browser/editor/page/autosave.mjs +357 -38
- package/browser/editor/page/beside.mjs +5 -5
- package/browser/editor/page/block-menu/colors.mjs +3 -1
- package/browser/editor/page/block-menu/index.mjs +8 -6
- package/browser/editor/page/blocks.mjs +16 -7
- package/browser/editor/page/board/client.mjs +57 -0
- package/browser/editor/page/board/hint.mjs +145 -0
- package/browser/editor/page/board/history.mjs +119 -0
- package/browser/editor/page/board/index.mjs +970 -0
- package/browser/editor/page/board/settings.mjs +209 -0
- package/browser/editor/page/caret.mjs +5 -2
- package/browser/editor/page/changes/bar.mjs +94 -0
- package/browser/editor/page/changes/ghost.mjs +206 -0
- package/browser/editor/page/changes/gone-words.mjs +94 -0
- package/browser/editor/page/changes/index.mjs +447 -0
- package/browser/editor/page/changes/words.mjs +27 -0
- package/browser/editor/page/clipboard/import/colors.mjs +3 -3
- package/browser/editor/page/clipboard/import/html.mjs +29 -6
- package/browser/editor/page/clipboard/import/inline.mjs +32 -0
- package/browser/editor/page/clipboard/import/markdown.mjs +58 -13
- package/browser/editor/page/clipboard/import/read.mjs +6 -1
- package/browser/editor/page/clipboard/import/to-blocks.mjs +44 -12
- package/browser/editor/page/clipboard/import/yuque.mjs +622 -0
- package/browser/editor/page/clipboard/index.mjs +156 -26
- package/browser/editor/page/clipboard/own/blocks.mjs +25 -16
- package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
- package/browser/editor/page/clipboard/own/describe.mjs +16 -7
- package/browser/editor/page/clipboard/own/export.mjs +6 -6
- package/browser/editor/page/clipboard/own/selection.mjs +3 -3
- package/browser/editor/page/clipboard/own/writers.mjs +0 -11
- package/browser/editor/page/columns/index.mjs +48 -46
- package/browser/editor/page/commit.mjs +98 -25
- package/browser/editor/page/conflicts.mjs +13 -33
- package/browser/editor/page/convert.mjs +8 -7
- package/browser/editor/page/core.mjs +81 -27
- package/browser/editor/page/custom-page.mjs +106 -0
- package/browser/editor/page/drag/index.mjs +40 -24
- package/browser/editor/page/drag/preview.mjs +9 -4
- package/browser/editor/page/editor.mjs +120 -28
- package/browser/editor/page/emoji-picker/emoji-tab.mjs +22 -13
- package/browser/editor/page/emoji-picker/grid.mjs +7 -5
- package/browser/editor/page/emoji-picker/icon-tab.mjs +4 -4
- package/browser/editor/page/emoji-picker/icons.mjs +0 -1
- package/browser/editor/page/emoji-picker/index.mjs +11 -9
- package/browser/editor/page/emoji-picker/styles.mjs +156 -2
- package/browser/editor/page/emoji-picker/support.mjs +6 -4
- package/browser/editor/page/emoji-picker/upload.mjs +14 -13
- package/browser/editor/page/fills.mjs +5 -3
- package/browser/editor/page/fold/index.mjs +348 -0
- package/browser/editor/page/fold/rule.mjs +125 -0
- package/browser/editor/page/folded.mjs +23 -0
- package/browser/editor/page/format-bar/bar.mjs +2 -1
- package/browser/editor/page/format-bar/index.mjs +1 -1
- package/browser/editor/page/geometry.mjs +8 -6
- package/browser/editor/page/handle/index.mjs +22 -14
- package/browser/editor/page/hints/index.mjs +1 -1
- package/browser/editor/page/history.mjs +24 -4
- package/browser/editor/page/insert/index.mjs +30 -17
- package/browser/editor/page/links/index.mjs +5 -3
- package/browser/editor/page/links/pages.mjs +27 -9
- package/browser/editor/page/long-runs.mjs +75 -0
- package/browser/editor/page/look.mjs +69 -6
- package/browser/editor/page/margin.mjs +63 -27
- package/browser/editor/page/media-menu/index.mjs +6 -7
- package/browser/editor/page/menu.mjs +86 -31
- package/browser/editor/page/merge.mjs +24 -0
- package/browser/editor/page/notices.mjs +3 -3
- package/browser/editor/page/offered.mjs +1 -1
- package/browser/editor/page/outline/folds.mjs +11 -10
- package/browser/editor/page/outline/index.mjs +131 -45
- package/browser/editor/page/outline/list.mjs +23 -11
- package/browser/editor/page/outline/read.mjs +1 -1
- package/browser/editor/page/outline/strip.mjs +7 -1
- package/browser/editor/page/page-objects.mjs +13 -2
- package/browser/editor/page/panel.mjs +9 -13
- package/browser/editor/page/present-links.mjs +11 -2
- package/browser/editor/page/resize/index.mjs +42 -29
- package/browser/editor/page/selection/delete.mjs +8 -6
- package/browser/editor/page/selection/index.mjs +10 -3
- package/browser/editor/page/served.mjs +14 -0
- package/browser/editor/page/session.mjs +101 -6
- package/browser/editor/page/slash-menu/index.mjs +3 -3
- package/browser/editor/page/table/cell-bar.mjs +4 -1
- package/browser/editor/page/table/cells.mjs +103 -47
- package/browser/editor/page/table/icons.mjs +3 -1
- package/browser/editor/page/table/index.mjs +40 -16
- package/browser/editor/page/table/pictures.mjs +299 -0
- package/browser/editor/page/table/views.mjs +150 -0
- package/browser/editor/page/table/widths.mjs +9 -4
- package/browser/editor/page/text-select/index.mjs +43 -14
- package/browser/editor/page/todo-writes.mjs +75 -0
- package/browser/editor/page/tooltip.mjs +1 -1
- package/browser/editor/page/type-grid.mjs +4 -3
- package/browser/editor/page/typing/code-edges.mjs +63 -17
- package/browser/editor/page/typing/index.mjs +65 -9
- package/browser/editor/page/typing/leave.mjs +1 -1
- package/browser/editor/page/ui.mjs +13 -0
- package/browser/shared/annotations.mjs +8 -10
- package/browser/shared/block-packs.mjs +404 -0
- package/browser/shared/board-places.mjs +224 -0
- package/browser/shared/card-size.mjs +59 -0
- package/browser/shared/code-folds.mjs +5 -1
- package/browser/shared/colors.mjs +17 -14
- package/browser/shared/component-catalog.mjs +345 -54
- package/browser/shared/component-source.mjs +37 -0
- package/browser/shared/components/callout.mjs +4 -2
- package/browser/shared/components/index.mjs +72 -9
- package/browser/shared/components/installed.mjs +110 -8
- package/browser/shared/components/page.mjs +22 -3
- package/browser/shared/components/state.mjs +5 -1
- package/browser/shared/components/table.mjs +1 -1
- package/browser/shared/components/text.mjs +2 -2
- package/browser/shared/document-look.mjs +183 -0
- package/browser/shared/drafts.mjs +432 -0
- package/browser/shared/emoji/data/LICENSE-lucide.txt +43 -0
- package/browser/shared/emoji/data/README.md +4 -2
- package/browser/shared/emoji/data/categories.json +1 -1
- package/browser/shared/emoji/data/keywords-en.json +1 -1
- package/browser/shared/emoji/data/keywords-zh.json +1 -1
- package/browser/shared/emoji/data/names.json +1 -1
- package/browser/shared/emoji/data/positions.json +1 -1
- package/browser/shared/emoji/data/twemoji-index.json +1 -1
- package/browser/shared/emoji/data/twemoji-svg.bin +0 -0
- package/browser/shared/emoji/data.mjs +10 -12
- package/browser/shared/emoji/emoji.mjs +8 -7
- package/browser/shared/emoji/icon-value.mjs +4 -3
- package/browser/shared/extension-sidebar.mjs +126 -0
- package/browser/shared/extensions.mjs +79 -0
- package/browser/shared/file-table.mjs +101 -0
- package/browser/shared/html-tree.mjs +11 -5
- package/browser/shared/i18n/en.mjs +374 -35
- package/browser/shared/json-layout.mjs +47 -0
- package/browser/shared/long-runs.mjs +39 -0
- package/browser/shared/net-cache.mjs +133 -0
- package/browser/shared/net-keys.mjs +32 -0
- package/browser/shared/net-policy.mjs +177 -0
- package/browser/shared/new-names.mjs +5 -2
- package/browser/shared/outline-shape.mjs +128 -0
- package/browser/shared/outline.mjs +458 -0
- package/browser/shared/pack-manifest.mjs +77 -0
- package/browser/shared/page-address.mjs +6 -3
- package/browser/shared/page-cards.mjs +308 -0
- package/browser/shared/page-links.mjs +69 -6
- package/browser/shared/pages.mjs +388 -0
- package/browser/shared/paths.mjs +6 -0
- package/browser/shared/preferences.mjs +10 -4
- package/browser/shared/preview-server.mjs +3 -7
- package/browser/shared/session.mjs +13 -5
- package/browser/shared/shipped-blocks.mjs +299 -0
- package/browser/shared/table-chart.mjs +33 -1
- package/browser/shared/todos.mjs +83 -0
- package/browser/shared/user-directory.mjs +24 -0
- package/browser/shared/versions.mjs +14 -0
- package/browser/shared/web-links.mjs +134 -0
- package/browser/shared/workspace-columns.mjs +120 -0
- package/browser/shared/workspace-look.mjs +91 -0
- package/browser/shared/workspace-name.mjs +154 -0
- package/browser/shared/workspace-query.mjs +125 -0
- package/browser/shared/workspace-registry.mjs +272 -0
- package/browser/shared/workspace.mjs +171 -20
- package/browser/shared/zoom.mjs +5 -5
- package/dist/chunk-2NRBY6AJ.mjs +2 -0
- package/dist/chunk-32ZUEFZ7.mjs +2 -0
- package/dist/chunk-35NQ4PLQ.mjs +2 -0
- package/dist/chunk-3KJHLHUO.mjs +2 -0
- package/dist/chunk-3NJACR4W.mjs +2 -0
- package/dist/chunk-4AV456RQ.mjs +2 -0
- package/dist/chunk-4OZWXZE5.mjs +5 -0
- package/dist/chunk-5XYHDBJ5.mjs +2 -0
- package/dist/chunk-5ZJA7CXT.mjs +2 -0
- package/dist/chunk-6A53MZYZ.mjs +2 -0
- package/dist/chunk-6EXH3ORZ.mjs +166 -0
- package/dist/chunk-6EZ7FNIK.mjs +3 -0
- package/dist/{chunk-UXUK4PMC.mjs → chunk-6QBHU4DF.mjs} +1 -1
- package/dist/{chunk-6U2SLSDN.mjs → chunk-6VTU6KEO.mjs} +1 -1
- package/dist/chunk-B4P2CM2J.mjs +3 -0
- package/dist/chunk-BPL64HGA.mjs +4 -0
- package/dist/chunk-BVEFGE4B.mjs +2 -0
- package/dist/chunk-BZ5RAF7L.mjs +2 -0
- package/dist/chunk-CRJEG7MT.mjs +2 -0
- package/dist/{chunk-B3VTYBYR.mjs → chunk-D4CKRD3V.mjs} +91 -91
- package/dist/chunk-D4EURAX4.mjs +4 -0
- package/dist/chunk-DQCE7TJY.mjs +5 -0
- package/dist/chunk-DR73Y2UJ.mjs +3 -0
- package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
- package/dist/{chunk-DB4WKGYF.mjs → chunk-ERLAIBOA.mjs} +2 -2
- package/dist/chunk-EURLTOTZ.mjs +4 -0
- package/dist/chunk-FJYN7RTH.mjs +4 -0
- package/dist/chunk-FWNRPMCJ.mjs +12 -0
- package/dist/chunk-FWW4CSAZ.mjs +2 -0
- package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
- package/dist/chunk-GY3FAUVM.mjs +2 -0
- package/dist/chunk-HLYV7RIH.mjs +2 -0
- package/dist/chunk-HMYTM3GT.mjs +15 -0
- package/dist/chunk-IA4PSIMD.mjs +2 -0
- package/dist/chunk-JEUK3ZO7.mjs +2 -0
- package/dist/chunk-JVHHHGEA.mjs +3 -0
- package/dist/chunk-KTKHRC6H.mjs +2 -0
- package/dist/chunk-L7UP52JO.mjs +2 -0
- package/dist/chunk-MHW7ZKOU.mjs +2 -0
- package/dist/chunk-N76HWBA3.mjs +10 -0
- package/dist/chunk-N7AZZLGT.mjs +548 -0
- package/dist/chunk-NAMN3RAF.mjs +2 -0
- package/dist/{chunk-YXAUQPVK.mjs → chunk-NFMCP2HI.mjs} +3 -3
- package/dist/chunk-NG375ILQ.mjs +2 -0
- package/dist/chunk-NVRNLK25.mjs +6 -0
- package/dist/chunk-NWFBE2YA.mjs +2 -0
- package/dist/chunk-NWR2LT5B.mjs +2 -0
- package/dist/chunk-OJKMINIU.mjs +20 -0
- package/dist/chunk-OUUP3WKT.mjs +2 -0
- package/dist/chunk-OW5NWPOJ.mjs +10 -0
- package/dist/chunk-PAREWQGB.mjs +4 -0
- package/dist/chunk-POQSE4C2.mjs +2 -0
- package/dist/chunk-PUOQ65NC.mjs +2 -0
- package/dist/{chunk-Z52HTUW7.mjs → chunk-PVER7LSC.mjs} +2 -2
- package/dist/chunk-PZDLJJTJ.mjs +3 -0
- package/dist/chunk-Q47PLKIJ.mjs +15 -0
- package/dist/chunk-Q4UEPUWF.mjs +2 -0
- package/dist/chunk-QKXMPUXC.mjs +2 -0
- package/dist/chunk-QOUEFUCH.mjs +16 -0
- package/dist/chunk-RFDZXFJF.mjs +13 -0
- package/dist/chunk-RKPWLGZ3.mjs +4 -0
- package/dist/chunk-RMECZA2K.mjs +2 -0
- package/dist/{chunk-EUX7IC5J.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
- package/dist/chunk-SX4LJYT6.mjs +8 -0
- package/dist/chunk-SXHBYZST.mjs +16 -0
- package/dist/chunk-TANZRINB.mjs +2 -0
- package/dist/chunk-TEZAWDSQ.mjs +2 -0
- package/dist/chunk-UBBRTJ2R.mjs +24 -0
- package/dist/chunk-UFNYFFKO.mjs +20 -0
- package/dist/chunk-UGWYKHBL.mjs +170 -0
- package/dist/chunk-UKSHDZZ6.mjs +2 -0
- package/dist/chunk-UMHUSOPJ.mjs +2 -0
- package/dist/chunk-UNOYVIYR.mjs +2 -0
- package/dist/chunk-V6RIYQZZ.mjs +4 -0
- package/dist/chunk-VJPFVNNW.mjs +15 -0
- package/dist/chunk-VS3ZMVGX.mjs +5 -0
- package/dist/chunk-VTKNEYT3.mjs +6 -0
- package/dist/chunk-VXACTLO3.mjs +3 -0
- package/dist/chunk-W3BGBJQW.mjs +2 -0
- package/dist/chunk-YI43RG4P.mjs +2 -0
- package/dist/chunk-YQGT4AB2.mjs +6 -0
- package/dist/chunk-ZB4H7ZN4.mjs +2 -0
- package/dist/{chunk-ACOPDCNB.mjs → chunk-ZHEHPMBK.mjs} +1 -1
- package/dist/chunk-ZTHICSSF.mjs +2 -0
- package/dist/chunk-ZUNVAXLH.mjs +2 -0
- package/dist/chunk-ZWPMCK7P.mjs +2 -0
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/{chunk-EVJ3BX3M.mjs → update-fetch.mjs} +1 -1
- package/dist/worker.mjs +1 -1
- package/kit/board.css +95 -0
- package/kit/board.js +250 -0
- package/kit/charts.js +5 -71
- package/kit/code.js +14 -14
- package/kit/math.js +18 -2
- package/kit/report.css +500 -151
- package/kit/runtime.js +1720 -170
- package/kit/state.js +10 -1
- package/kit/tables.js +172 -37
- package/kit/ui.js +166 -27
- package/package.json +2 -1
- package/packs/gallery/card/component.json +14 -0
- package/packs/gallery/card/example.html +10 -0
- package/packs/gallery/card/preview.svg +1 -0
- package/packs/gallery/card/style.css +6 -0
- package/packs/gallery/card/template.html +6 -0
- package/packs/gallery/card/writing.md +14 -0
- package/packs/gallery/chart-card/component.json +15 -0
- package/packs/gallery/chart-card/example.html +14 -0
- package/packs/gallery/chart-card/kit.js +130 -0
- package/packs/gallery/chart-card/preview.svg +1 -0
- package/packs/gallery/chart-card/style.css +42 -0
- package/packs/gallery/chart-card/template.html +13 -0
- package/packs/gallery/chart-card/writing.md +20 -0
- package/packs/gallery/chart-view/component.json +20 -0
- package/packs/gallery/chart-view/example.html +13 -0
- package/packs/gallery/chart-view/kit.js +355 -0
- package/packs/gallery/chart-view/preview.svg +1 -0
- package/packs/gallery/chart-view/style.css +94 -0
- package/packs/gallery/chart-view/template.html +3 -0
- package/packs/gallery/chart-view/writing.md +22 -0
- package/packs/gallery/countdown/component.json +18 -0
- package/packs/gallery/countdown/example.html +3 -0
- package/packs/gallery/countdown/kit.js +156 -0
- package/packs/gallery/countdown/preview.svg +1 -0
- package/packs/gallery/countdown/style.css +130 -0
- package/packs/gallery/countdown/template.html +3 -0
- package/packs/gallery/countdown/writing.md +22 -0
- package/packs/gallery/habit/component.json +14 -0
- package/packs/gallery/habit/example.html +3 -0
- package/packs/gallery/habit/kit.js +295 -0
- package/packs/gallery/habit/preview.svg +1 -0
- package/packs/gallery/habit/style.css +246 -0
- package/packs/gallery/habit/template.html +3 -0
- package/packs/gallery/habit/writing.md +21 -0
- package/packs/gallery/heatmap/component.json +18 -0
- package/packs/gallery/heatmap/example.html +3 -0
- package/packs/gallery/heatmap/kit.js +351 -0
- package/packs/gallery/heatmap/preview.svg +1 -0
- package/packs/gallery/heatmap/style.css +201 -0
- package/packs/gallery/heatmap/template.html +3 -0
- package/packs/gallery/heatmap/writing.md +21 -0
- package/packs/gallery/kanban/component.json +18 -0
- package/packs/gallery/kanban/example.html +12 -0
- package/packs/gallery/kanban/kit.js +540 -0
- package/packs/gallery/kanban/preview.svg +1 -0
- package/packs/gallery/kanban/style.css +212 -0
- package/packs/gallery/kanban/template.html +3 -0
- package/packs/gallery/kanban/writing.md +20 -0
- package/packs/gallery/metric/component.json +22 -0
- package/packs/gallery/metric/example.html +15 -0
- package/packs/gallery/metric/kit.js +248 -0
- package/packs/gallery/metric/preview.svg +1 -0
- package/packs/gallery/metric/style.css +92 -0
- package/packs/gallery/metric/template.html +3 -0
- package/packs/gallery/metric/writing.md +23 -0
- package/packs/gallery/note/component.json +14 -0
- package/packs/gallery/note/example.html +3 -0
- package/packs/gallery/note/kit.js +102 -0
- package/packs/gallery/note/preview.svg +1 -0
- package/packs/gallery/note/style.css +76 -0
- package/packs/gallery/note/template.html +3 -0
- package/packs/gallery/note/writing.md +18 -0
- package/packs/gallery/pack.json +8 -0
- package/packs/gallery/recent/component.json +13 -0
- package/packs/gallery/recent/example.html +1 -0
- package/packs/gallery/recent/kit.js +238 -0
- package/packs/gallery/recent/preview.svg +1 -0
- package/packs/gallery/recent/style.css +158 -0
- package/packs/gallery/recent/template.html +1 -0
- package/packs/gallery/recent/writing.md +15 -0
- package/packs/gallery/static/component.json +13 -0
- package/packs/gallery/static/example.html +11 -0
- package/packs/gallery/static/preview.svg +1 -0
- package/packs/gallery/static/style.css +138 -0
- package/packs/gallery/static/template.html +1 -0
- package/packs/gallery/static/writing.md +18 -0
- package/packs/gallery/stats/component.json +13 -0
- package/packs/gallery/stats/example.html +1 -0
- package/packs/gallery/stats/kit.js +166 -0
- package/packs/gallery/stats/preview.svg +1 -0
- package/packs/gallery/stats/style.css +151 -0
- package/packs/gallery/stats/template.html +1 -0
- package/packs/gallery/stats/writing.md +11 -0
- package/packs/gallery/today/component.json +12 -0
- package/packs/gallery/today/example.html +1 -0
- package/packs/gallery/today/kit.js +185 -0
- package/packs/gallery/today/preview.svg +1 -0
- package/packs/gallery/today/style.css +120 -0
- package/packs/gallery/today/template.html +1 -0
- package/packs/gallery/today/writing.md +14 -0
- package/packs/gallery/todos/component.json +19 -0
- package/packs/gallery/todos/example.html +3 -0
- package/packs/gallery/todos/kit.js +342 -0
- package/packs/gallery/todos/preview.svg +1 -0
- package/packs/gallery/todos/style.css +267 -0
- package/packs/gallery/todos/template.html +3 -0
- package/packs/gallery/todos/writing.md +30 -0
- package/packs/gallery/weather/component.json +19 -0
- package/packs/gallery/weather/example.html +3 -0
- package/packs/gallery/weather/kit.js +312 -0
- package/packs/gallery/weather/preview.svg +1 -0
- package/packs/gallery/weather/style.css +258 -0
- package/packs/gallery/weather/template.html +3 -0
- package/packs/gallery/weather/writing.md +26 -0
- package/packs/gallery-soft/pack.json +8 -0
- package/release.json +13 -11
- package/sdk/doc-kit.d.ts +404 -7
- package/sdk/doc-state.d.ts +2 -0
- package/skill/SKILL.md +71 -26
- package/skill/references/block-script.md +140 -0
- package/skill/references/building-blocks.md +257 -0
- package/skill/references/example-report.html +1 -1
- package/skill/references/extensions.md +59 -0
- package/skill/references/pages-and-data.md +221 -0
- package/skill/references/writing-html.md +9 -7
- package/skill/sdk/doc-kit.d.ts +404 -7
- package/skill/sdk/doc-state.d.ts +2 -0
- package/web/assets/index-BK2e_07w.js +13 -0
- package/web/assets/index-CWFI83mF.css +1 -0
- package/web/index.html +2 -2
- package/browser/editor/components/callout/page.mjs +0 -13
- package/dist/chunk-2PBALTR3.mjs +0 -57
- package/dist/chunk-3LA3EMDE.mjs +0 -15
- package/dist/chunk-5FRMCDPY.mjs +0 -16
- package/dist/chunk-5LEK2CBS.mjs +0 -2
- package/dist/chunk-5OFN5AHC.mjs +0 -2
- package/dist/chunk-5SIYS5HN.mjs +0 -2
- package/dist/chunk-5TEK3RAD.mjs +0 -2
- package/dist/chunk-6CYG3Z4V.mjs +0 -2
- package/dist/chunk-7GHT2UCA.mjs +0 -15
- package/dist/chunk-7QQBR7PB.mjs +0 -2
- package/dist/chunk-ATCJYAR4.mjs +0 -2
- package/dist/chunk-C7HWOBIF.mjs +0 -2
- package/dist/chunk-E7VYDG2X.mjs +0 -2
- package/dist/chunk-EZY3WC6Z.mjs +0 -15
- package/dist/chunk-FQGQH4KM.mjs +0 -2
- package/dist/chunk-G7TOXONE.mjs +0 -6
- package/dist/chunk-GPF4YWWB.mjs +0 -9
- package/dist/chunk-GYQV7Y4J.mjs +0 -120
- package/dist/chunk-HMHK3EQ6.mjs +0 -2
- package/dist/chunk-HQQMJCQJ.mjs +0 -10
- package/dist/chunk-IERUOGC3.mjs +0 -6
- package/dist/chunk-ITNLYXXV.mjs +0 -3
- package/dist/chunk-L4DVONOG.mjs +0 -2
- package/dist/chunk-LAE5NQ4B.mjs +0 -2
- package/dist/chunk-LC7YNQFG.mjs +0 -2
- package/dist/chunk-LHTFLRB6.mjs +0 -4
- package/dist/chunk-NFIBI4IP.mjs +0 -26
- package/dist/chunk-NSKUTQ5J.mjs +0 -5
- package/dist/chunk-O52JMOCZ.mjs +0 -2
- package/dist/chunk-P2CAZPDP.mjs +0 -3
- package/dist/chunk-QMQUXTU4.mjs +0 -12
- package/dist/chunk-QVWVFC5K.mjs +0 -6
- package/dist/chunk-QYKCJ7FG.mjs +0 -8
- package/dist/chunk-QZY6Q64C.mjs +0 -2
- package/dist/chunk-TDQP3DKQ.mjs +0 -10
- package/dist/chunk-TX6JH7TR.mjs +0 -2
- package/dist/chunk-UNUEWH4S.mjs +0 -2
- package/dist/chunk-V4CPWKMZ.mjs +0 -2
- package/dist/chunk-VACRB2NE.mjs +0 -3
- package/dist/chunk-W63L7367.mjs +0 -7
- package/dist/chunk-W7CXPCRV.mjs +0 -6
- package/dist/chunk-XINNG4F2.mjs +0 -4
- package/dist/chunk-YL2DPWVS.mjs +0 -4
- package/dist/chunk-YZ5Z6H4W.mjs +0 -4
- package/dist/chunk-ZBIM6ARY.mjs +0 -216
- package/skill/references/building-components.md +0 -327
- package/web/assets/index-B6Ai3wsa.js +0 -13
- package/web/assets/index-SYPC3EZy.css +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Z } from "../../../shared/layers.mjs";
|
|
2
|
+
import { language, t } from "../../../shared/i18n/index.mjs";
|
|
3
|
+
import { createMenu } from "../menu.mjs";
|
|
4
|
+
import { placeBox } from "../placement.mjs";
|
|
5
|
+
import { addStyle, button, element, mount } from "../ui.mjs";
|
|
6
|
+
|
|
7
|
+
// A card's 设置… (docs/render.md "A custom page"): a small form of the fields its block's manifest
|
|
8
|
+
// lists (`settings`: text, number, select, date, switch, document), Notion's popup look (the editor's
|
|
9
|
+
// palette, look.mjs), standing under the card's grip (its right edge at the grip's). What it shows is what the card's settings part holds,
|
|
10
|
+
// each field its manifest's default where it holds none; 保存 (or Enter in a field) hands the values
|
|
11
|
+
// that changed to `save`, 取消, Escape or a press outside it nothing. Its buttons are the editor's flat
|
|
12
|
+
// ones (CLAUDE.md 界面): no line round either, 取消 with no fill until the pointer is on it (grey), 保存 on
|
|
13
|
+
// a pale blue fill of its own. A document is chosen from the
|
|
14
|
+
// workspace's documents by title (the workbench's outline: GET /__gallery/workspace/outline, its web
|
|
15
|
+
// links no documents), the slug what is kept. A choice from a list (select, document) is a field that
|
|
16
|
+
// opens the editor's own menu card under it (menu.mjs: rows, the chosen one checked, ↑/↓ Enter Escape from
|
|
17
|
+
// the field), not a native <select>: the page cannot style that one's popup, which a browser draws in
|
|
18
|
+
// the system's colours (a white list with pale words in a dark page, the user's picture of 2026-11-09).
|
|
19
|
+
// While the form stands, the page is not shown again under it (the board's idle).
|
|
20
|
+
|
|
21
|
+
const STYLE = `
|
|
22
|
+
.ge-board-form { position: fixed; left: 0; top: 0; z-index: ${Z.menu}; box-sizing: border-box; width: 300px; max-height: calc(100vh - 24px); overflow-y: auto; padding: 12px 12px 10px;
|
|
23
|
+
border-radius: 10px; background: var(--ge-bg); color: var(--ge-tx); box-shadow: var(--ge-shadow-pop);
|
|
24
|
+
font: 14px/1.4 ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", Helvetica, "PingFang SC", "Microsoft YaHei", Arial, sans-serif; }
|
|
25
|
+
.ge-board-form-title { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px; font-size: 14px; font-weight: 600; }
|
|
26
|
+
.ge-board-form-title small { min-width: 0; overflow: hidden; color: var(--ge-tx3); font-size: 12px; font-weight: 400; text-overflow: ellipsis; white-space: nowrap; }
|
|
27
|
+
.ge-board-field { display: block; margin: 0 0 10px; }
|
|
28
|
+
.ge-board-field > span { display: block; margin: 0 0 4px; color: var(--ge-tx2); font-size: 12px; }
|
|
29
|
+
.ge-board-field :is(input, .ge-board-pick) { box-sizing: border-box; width: 100%; height: 28px; margin: 0; padding: 0 8px; border: 0; border-radius: 6px; outline: none;
|
|
30
|
+
background: var(--ge-field); color: var(--ge-tx); font: inherit; box-shadow: inset 0 0 0 1px var(--ge-edge); }
|
|
31
|
+
.ge-board-field :is(input, .ge-board-pick):focus, .ge-board-pick[aria-expanded="true"] { box-shadow: inset 0 0 0 1px var(--ge-focus), 0 0 0 2px var(--ge-halo); }
|
|
32
|
+
.ge-board-pick { display: flex; align-items: center; gap: 6px; text-align: left; cursor: pointer; }
|
|
33
|
+
.ge-board-pick-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
34
|
+
.ge-board-pick svg { flex: none; display: block; width: 16px; height: 16px; fill: none; stroke: var(--ge-tx3); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
35
|
+
.ge-board-switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 10px; }
|
|
36
|
+
.ge-board-switch > span { color: var(--ge-tx); font-size: 14px; }
|
|
37
|
+
.ge-board-switch button { position: relative; flex: none; width: 30px; height: 18px; padding: 0; border: 0; border-radius: 9px; background: var(--ge-line-strong); cursor: pointer; transition: background .12s; }
|
|
38
|
+
.ge-board-switch button::after { content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgb(15 15 15 / 20%); transition: transform .12s; }
|
|
39
|
+
.ge-board-switch button[aria-checked="true"] { background: var(--ge-accent); }
|
|
40
|
+
.ge-board-switch button[aria-checked="true"]::after { transform: translateX(12px); }
|
|
41
|
+
.ge-board-switch button:focus-visible { outline: 2px solid var(--ge-halo); }
|
|
42
|
+
.ge-board-form-foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
|
|
43
|
+
.ge-board-form-foot button { height: 28px; padding: 0 12px; border: 0; border-radius: 6px; background: transparent; color: var(--ge-tx); font: inherit; cursor: pointer; }
|
|
44
|
+
.ge-board-form-foot button:hover { background: var(--ge-hover); }
|
|
45
|
+
.ge-board-form-foot button[data-primary] { background: var(--ge-btn-primary); color: var(--ge-btn-primary-text); }
|
|
46
|
+
.ge-board-form-foot button[data-primary]:hover { background: var(--ge-btn-primary-hover); }
|
|
47
|
+
.ge-board-form-foot button:focus-visible { outline: 2px solid var(--ge-halo); }
|
|
48
|
+
@media (prefers-reduced-motion: reduce) { .ge-board-switch :is(button, button::after) { transition: none; } }
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const CHEVRON = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m4 6 4 4 4-4"/></svg>';
|
|
52
|
+
const said = (words) => (typeof words === "string" ? words : words?.[language] ?? words?.zh ?? words?.en ?? "");
|
|
53
|
+
let shown = null; // the form standing now: { node, close }
|
|
54
|
+
|
|
55
|
+
/** Whether the form stands now. */
|
|
56
|
+
export const formOpen = () => shown !== null;
|
|
57
|
+
|
|
58
|
+
/** The workspace's documents, by title: [{ slug, title }] (none where the outline cannot be read). */
|
|
59
|
+
async function documents() {
|
|
60
|
+
try {
|
|
61
|
+
const answer = await (await fetch("/__gallery/workspace/outline")).json();
|
|
62
|
+
return Object.values(answer.reports ?? {}).filter((report) => !report.instructions && report.kind !== "link").map(({ slug, title }) => ({ slug, title: title || slug }))
|
|
63
|
+
.sort((a, b) => a.title.localeCompare(b.title, "zh-CN"));
|
|
64
|
+
} catch {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The form for a card: `fields`, its manifest's settings; `values`, what its settings part holds;
|
|
71
|
+
* `name`, its block's name; `anchor`, the rect it stands by; `save(changed)`, the values that changed
|
|
72
|
+
* by key. Closes another that stands.
|
|
73
|
+
*/
|
|
74
|
+
export async function openSettings({ fields, values, name, anchor, save }) {
|
|
75
|
+
addStyle(STYLE);
|
|
76
|
+
shown?.close();
|
|
77
|
+
const kinds = new Map();
|
|
78
|
+
const pick = createMenu(); // the list of the field being chosen from (one at a time)
|
|
79
|
+
let picking = null; // the field it stands under
|
|
80
|
+
const node = element("form", { class: "ge-board-form", role: "dialog", "aria-label": t("卡片设置"), novalidate: "" });
|
|
81
|
+
node.append(element("div", { class: "ge-board-form-title" }, element("span", { text: t("设置") }), element("small", { text: said(name) })));
|
|
82
|
+
const listed = fields.some((field) => field.type === "document") ? await documents() : [];
|
|
83
|
+
const now = (field) => (Object.hasOwn(values ?? {}, field.key) ? values[field.key] : field.default);
|
|
84
|
+
for (const field of fields) {
|
|
85
|
+
const label = said(field.label) || field.key;
|
|
86
|
+
const value = now(field);
|
|
87
|
+
if (field.type === "switch") {
|
|
88
|
+
const toggle = element("button", { type: "button", role: "switch", "aria-checked": String(value === true), "aria-label": label, "data-key": field.key });
|
|
89
|
+
toggle.addEventListener("click", () => toggle.setAttribute("aria-checked", String(toggle.getAttribute("aria-checked") !== "true")));
|
|
90
|
+
node.append(element("div", { class: "ge-board-switch" }, element("span", { text: label }), toggle));
|
|
91
|
+
kinds.set(field.key, { field, read: () => toggle.getAttribute("aria-checked") === "true" });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
let control;
|
|
95
|
+
const choice = field.type === "select" || field.type === "document"; // chosen from a list
|
|
96
|
+
if (choice) {
|
|
97
|
+
const options = field.type === "select" ? field.options.map((option) => [String(option), String(option)]) : [["", t("(未选择)")], ...listed.map(({ slug, title }) => [slug, title])];
|
|
98
|
+
if (field.type === "document" && value && !listed.some(({ slug }) => slug === value)) options.push([String(value), String(value)]);
|
|
99
|
+
const text = element("span", { class: "ge-board-pick-text" });
|
|
100
|
+
control = element("button", { type: "button", class: "ge-board-pick", "data-key": field.key, "aria-haspopup": "menu", "aria-expanded": "false" }, text, element("span", { html: CHEVRON }));
|
|
101
|
+
// What the field holds is its `value` (the key of an option), shown as the option's words.
|
|
102
|
+
const show = (key) => {
|
|
103
|
+
control.value = key;
|
|
104
|
+
text.textContent = options.find(([option]) => option === key)?.[1] ?? key;
|
|
105
|
+
};
|
|
106
|
+
show(value === undefined || value === null ? (field.type === "select" ? String(field.options[0]) : "") : String(value));
|
|
107
|
+
const open = () => {
|
|
108
|
+
const wide = control.getBoundingClientRect();
|
|
109
|
+
picking = control;
|
|
110
|
+
control.setAttribute("aria-expanded", "true");
|
|
111
|
+
pick.open(options.map(([key, words]) => ({ label: words, selected: key === control.value, run: () => show(key) })), wide, {
|
|
112
|
+
label, width: Math.round(wide.width), icons: false, height: 280,
|
|
113
|
+
onClose: () => { if (picking === control) picking = null; control.setAttribute("aria-expanded", "false"); },
|
|
114
|
+
});
|
|
115
|
+
// The chosen row highlighted where there is one (the first otherwise: the menu's own).
|
|
116
|
+
const chosen = options.findIndex(([key]) => key === control.value);
|
|
117
|
+
for (let step = 0; step < Math.max(0, chosen); step++) pick.move(1);
|
|
118
|
+
};
|
|
119
|
+
control.addEventListener("click", () => { if (picking === control) pick.close(); else open(); });
|
|
120
|
+
control.addEventListener("keydown", (event) => {
|
|
121
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
122
|
+
if (picking !== control) {
|
|
123
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
open();
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (pick.arrow(event)) event.preventDefault();
|
|
130
|
+
else if (event.key === "Enter" || event.key === " ") {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
pick.choose();
|
|
133
|
+
} else if (event.key === "Escape") {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
event.stopPropagation(); // the list closes; the form stays
|
|
136
|
+
pick.close();
|
|
137
|
+
} else if (event.key === "Tab") pick.close();
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
control = element("input", { type: field.type === "number" ? "number" : field.type === "date" ? "date" : "text", "data-key": field.key, autocomplete: "off" });
|
|
141
|
+
control.value = value === undefined || value === null ? "" : String(value);
|
|
142
|
+
}
|
|
143
|
+
// A field to choose from is no <label> (a click on its words would open its list): its words name it by aria-labelledby.
|
|
144
|
+
const words = element("span", { text: label });
|
|
145
|
+
if (choice) {
|
|
146
|
+
words.id = `ge-board-label-${field.key}`;
|
|
147
|
+
control.setAttribute("aria-labelledby", words.id);
|
|
148
|
+
}
|
|
149
|
+
node.append(element(choice ? "div" : "label", { class: "ge-board-field" }, words, control));
|
|
150
|
+
kinds.set(field.key, {
|
|
151
|
+
field,
|
|
152
|
+
read: () => {
|
|
153
|
+
if (field.type === "number") return control.value.trim() === "" ? null : Number(control.value);
|
|
154
|
+
if (field.type === "select") return field.options.find((option) => String(option) === control.value) ?? field.options[0];
|
|
155
|
+
return control.value;
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
const close = () => {
|
|
160
|
+
if (shown?.node !== node) return;
|
|
161
|
+
shown = null;
|
|
162
|
+
pick.close();
|
|
163
|
+
node.remove();
|
|
164
|
+
removeEventListener("pointerdown", outside, true);
|
|
165
|
+
};
|
|
166
|
+
// What a field showed as it opened, as it reads it back (a field the part holds nothing of shows its default, or nothing).
|
|
167
|
+
const shownAt = (field) => now(field) ?? (field.type === "switch" ? false : field.type === "select" ? field.options[0] : field.type === "number" ? null : "");
|
|
168
|
+
// Only what the reader changed goes: a field left as it showed stays as the part holds it (its default unwritten).
|
|
169
|
+
const done = () => {
|
|
170
|
+
const changed = {};
|
|
171
|
+
for (const [key, { field, read }] of kinds) {
|
|
172
|
+
const value = read();
|
|
173
|
+
if (field.type === "number" && value !== null && !Number.isFinite(value)) continue;
|
|
174
|
+
if (JSON.stringify(value) !== JSON.stringify(shownAt(field))) changed[key] = value;
|
|
175
|
+
}
|
|
176
|
+
close();
|
|
177
|
+
if (Object.keys(changed).length) save(changed);
|
|
178
|
+
};
|
|
179
|
+
const outside = (event) => {
|
|
180
|
+
if (!node.contains(event.target) && !pick.contains(event.target)) close();
|
|
181
|
+
};
|
|
182
|
+
node.addEventListener("submit", (event) => {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
done();
|
|
185
|
+
});
|
|
186
|
+
node.addEventListener("keydown", (event) => {
|
|
187
|
+
if (event.isComposing || event.keyCode === 229) return;
|
|
188
|
+
if (event.key === "Escape") {
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
event.stopPropagation();
|
|
191
|
+
close();
|
|
192
|
+
} else if (event.key === "Enter" && event.target.localName === "input") {
|
|
193
|
+
event.preventDefault();
|
|
194
|
+
done();
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
node.append(element("div", { class: "ge-board-form-foot" }, button(t("取消"), close), element("button", { type: "submit", "data-primary": "", text: t("保存") })));
|
|
198
|
+
mount(node);
|
|
199
|
+
const { left, top } = placeBox(anchor, { width: node.offsetWidth, height: node.offsetHeight }, { placement: "bottom", align: "end", gap: 6 });
|
|
200
|
+
node.style.left = `${Math.round(left)}px`;
|
|
201
|
+
node.style.top = `${Math.round(top)}px`;
|
|
202
|
+
shown = { node, close };
|
|
203
|
+
addEventListener("pointerdown", outside, true);
|
|
204
|
+
node.querySelector("input, .ge-board-pick, button[role=switch]")?.focus({ preventScroll: true });
|
|
205
|
+
return node;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The form taken away, whatever it holds. */
|
|
209
|
+
export const closeSettings = () => shown?.close();
|
|
@@ -105,7 +105,7 @@ export function caretAtOffset(host, offset) {
|
|
|
105
105
|
rest -= node.length;
|
|
106
106
|
last = node;
|
|
107
107
|
}
|
|
108
|
-
|
|
108
|
+
caretAt(host, last ? "end" : "start"); // past its text, or in a block with none (emptied)
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/** The selection when it is non-empty and inside a single block: { host, range }, or null. */
|
|
@@ -507,7 +507,10 @@ export function reveal(host, line, margin = 24) {
|
|
|
507
507
|
const style = getComputedStyle(scroller ?? document.documentElement);
|
|
508
508
|
const above = kept(style.scrollPaddingTop, margin);
|
|
509
509
|
const below = kept(style.scrollPaddingBottom, margin);
|
|
510
|
-
const
|
|
510
|
+
const exact = line.bottom > view.bottom - below ? line.bottom - view.bottom + below : line.top < view.top + above ? line.top - view.top - above : 0;
|
|
511
|
+
// Whole pixels, away from the line: a scroll by a fraction is rounded by the browser, and rounded
|
|
512
|
+
// down it left a line at the window's bottom a fraction of a pixel past it.
|
|
513
|
+
const delta = exact > 0 ? Math.ceil(exact) : Math.floor(exact);
|
|
511
514
|
if (!delta) return;
|
|
512
515
|
if (scroller) scroller.scrollTop += delta;
|
|
513
516
|
else scrollBy(0, delta);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { t } from "../../../shared/i18n/index.mjs";
|
|
2
|
+
import { Z } from "../../../shared/layers.mjs";
|
|
3
|
+
import { PANEL_STYLE, onPanels, panelGone, panelShown } from "../panel.mjs";
|
|
4
|
+
import { addStyle, element, fadeIn, fadeOut, mount, textButton } from "../ui.mjs";
|
|
5
|
+
|
|
6
|
+
// The bar of the Agent's changes (docs/editor.md "Agent's changes"), Cursor's review bar: the third of
|
|
7
|
+
// the editor's bottom panels (panel.mjs), in the middle of the window's bottom, so that a notice
|
|
8
|
+
// stands above it as above the others. It says how many blocks the Agent added and changed (+) and
|
|
9
|
+
// changed and deleted (−), the numbers in the conflict card's green and red, and offers 撤回 and 确认
|
|
10
|
+
// (the primary one: the pale blue), in the editor's one button look (look.mjs BUTTONS: no line, no fill
|
|
11
|
+
// until the pointer is on it). Reverted, it says so and offers
|
|
12
|
+
// 撤销 for UNDO_SHOWN. A conflict card at the left or the lost texts at the right reaching under it
|
|
13
|
+
// lift it 8px above them.
|
|
14
|
+
|
|
15
|
+
const STYLE = `
|
|
16
|
+
.ge-changes { position: fixed; left: 50%; bottom: var(--ge-changes-bottom, 16px); transform: translateX(-50%); z-index: ${Z.panel};
|
|
17
|
+
display: flex; align-items: center; gap: 8px; height: 40px; padding: 0 6px 0 14px; box-sizing: border-box; max-width: calc(100vw - 32px);
|
|
18
|
+
border-radius: 10px; background: var(--ge-bg); color: var(--ge-tx); box-shadow: var(--ge-shadow);
|
|
19
|
+
font: 13px/1 var(--doc-sans-font, var(--doc-font, system-ui, sans-serif)); white-space: nowrap; }
|
|
20
|
+
.ge-changes[hidden] { display: none; }
|
|
21
|
+
.ge-changes-what { color: var(--ge-tx2); }
|
|
22
|
+
.ge-changes-count { display: inline-flex; gap: 6px; margin-right: 4px; font-variant-numeric: tabular-nums; font-weight: 500; }
|
|
23
|
+
.ge-changes-plus { color: var(--ge-diff-add-text); }
|
|
24
|
+
.ge-changes-minus { color: var(--ge-diff-del-text); }
|
|
25
|
+
@media print { .ge-changes { display: none; } }
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
export const UNDO_SHOWN = 6000; // how long a revert can be undone from the bar
|
|
29
|
+
const ABOVE = 8; // px between a bottom panel and the bar lifted over it
|
|
30
|
+
const BOTTOM = 16;
|
|
31
|
+
|
|
32
|
+
/** The bar: { counts(counts), reverted(left, undo), hide(), node }. revert(), accept(): what its buttons do. */
|
|
33
|
+
export function createBar({ revert, accept }) {
|
|
34
|
+
addStyle(PANEL_STYLE);
|
|
35
|
+
addStyle(STYLE);
|
|
36
|
+
let bar = null;
|
|
37
|
+
let timer = 0;
|
|
38
|
+
// Lifted over a bottom panel it would stand on.
|
|
39
|
+
const place = () => {
|
|
40
|
+
if (!bar || bar.hidden) return;
|
|
41
|
+
const own = bar.getBoundingClientRect();
|
|
42
|
+
let lift = BOTTOM;
|
|
43
|
+
for (const panel of document.querySelectorAll(".ge-conflicts:not([hidden]), .ge-panel")) {
|
|
44
|
+
const rect = panel.getBoundingClientRect();
|
|
45
|
+
if (rect.height > 0 && rect.left < own.right && rect.right > own.left) lift = Math.max(lift, Math.round(innerHeight - rect.top) + ABOVE);
|
|
46
|
+
}
|
|
47
|
+
bar.style.setProperty("--ge-changes-bottom", `${lift}px`);
|
|
48
|
+
};
|
|
49
|
+
onPanels(place);
|
|
50
|
+
addEventListener("resize", place, { passive: true });
|
|
51
|
+
const made = () => {
|
|
52
|
+
if (!bar) bar = mount(element("div", { class: "ge-changes", role: "region", "aria-label": t("Agent 的修改"), hidden: "" }));
|
|
53
|
+
return bar;
|
|
54
|
+
};
|
|
55
|
+
const show = (...children) => {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
const node = made();
|
|
58
|
+
node.replaceChildren(...children);
|
|
59
|
+
if (node.hidden) {
|
|
60
|
+
fadeIn(node);
|
|
61
|
+
panelShown(node);
|
|
62
|
+
}
|
|
63
|
+
place();
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
get node() { return bar; },
|
|
67
|
+
counts({ added = 0, changed = 0, removed = 0 }) {
|
|
68
|
+
const plus = added + changed;
|
|
69
|
+
const minus = removed + changed;
|
|
70
|
+
show(
|
|
71
|
+
element("span", { class: "ge-changes-what", text: t("Agent 已修改") }),
|
|
72
|
+
element("span", { class: "ge-changes-count", title: t("Agent 新增 {0} 块、改动 {1} 块、删除 {2} 块", added, changed, removed) },
|
|
73
|
+
...(plus ? [element("span", { class: "ge-changes-plus", text: `+${plus}` })] : []),
|
|
74
|
+
...(minus ? [element("span", { class: "ge-changes-minus", text: `−${minus}` })] : [])),
|
|
75
|
+
textButton(t("撤回"), revert, { title: t("撤回 Agent 的这些修改,你自己的修改会保留"), "data-action": "revert" }),
|
|
76
|
+
textButton(t("确认"), accept, { primary: true, title: t("保留这些修改,不再标出"), "data-action": "accept" }),
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
// Reverted `left` ms ago at most: 撤销 offered until UNDO_SHOWN is over.
|
|
80
|
+
reverted(left, undo) {
|
|
81
|
+
show(
|
|
82
|
+
element("span", { class: "ge-changes-what", text: t("已撤回 Agent 的修改") }),
|
|
83
|
+
textButton(t("撤销"), undo, { "data-action": "undo" }),
|
|
84
|
+
);
|
|
85
|
+
timer = setTimeout(() => this.hide(), Math.max(0, left));
|
|
86
|
+
},
|
|
87
|
+
hide() {
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
if (!bar || bar.hidden) return;
|
|
90
|
+
const node = bar;
|
|
91
|
+
fadeOut(node, 100, () => panelGone(node));
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { IN_LAYER } from "../../../shared/layers.mjs";
|
|
2
|
+
import { KIND_NAMES } from "../../components/registry.mjs";
|
|
3
|
+
import { identityOf, withLeads } from "../core.mjs";
|
|
4
|
+
import { own } from "../overlays.mjs";
|
|
5
|
+
import { drawAt, layerOf } from "../overlay-layer.mjs";
|
|
6
|
+
import { element } from "../ui.mjs";
|
|
7
|
+
|
|
8
|
+
// The blocks an Agent deleted, shown where they were (docs/editor.md "Agent's changes"): a ghost of
|
|
9
|
+
// them, their words in red struck through on a faint red fill, a line each (a heading larger and
|
|
10
|
+
// bold, an item with its marker, nested items further in, a table a line each row, a block with no
|
|
11
|
+
// words its kind's name), never a block of the page: nothing of it is in the document's flows, so no
|
|
12
|
+
// commit, undo step, copy, selection, drop or search ever meets it. Room is made for it where the
|
|
13
|
+
// blocks were by a margin of the block after the place (`before`; at the document's end the last
|
|
14
|
+
// block's below it, `after`; with no block left at all, the body's top, `into`), written in a
|
|
15
|
+
// stylesheet of the editor's own keyed by that block's identity (data-ge-id: never a style
|
|
16
|
+
// attribute, which the file would get), and the ghost is drawn in that room in the block's overlay
|
|
17
|
+
// layer (overlay-layer.mjs), measured first: the room is its height and the gap the page had
|
|
18
|
+
// there, the gap kept above it and below it (a collapsed margin made good on a second measure). A
|
|
19
|
+
// block that stands beside others (a Sheet's tab in its bar, a flowchart's node in its row, a column
|
|
20
|
+
// of 分栏) has the room made before the row it stands in, which a margin of its own would push out of
|
|
21
|
+
// line; what stands at one place is one ghost in one room (two groups before one row; one before a
|
|
22
|
+
// block and one before the first of its blocks that stays, a paragraph before a list and the list's
|
|
23
|
+
// first item: a margin of the inner one would fold into the outer one's, the two drawn over each
|
|
24
|
+
// other). Placed again whenever the page may have moved (the overlays' frame loop), the room made
|
|
25
|
+
// again when its height changes (the window narrowed, its lines wrapped anew).
|
|
26
|
+
|
|
27
|
+
const REACH = 2; // px past the block at its sides, as a text block's fill
|
|
28
|
+
const GAP_LEAST = 6;
|
|
29
|
+
const GAP_MOST = 20;
|
|
30
|
+
|
|
31
|
+
export const GHOST_STYLE = `
|
|
32
|
+
.ge-diff-ghost { left: 0; top: 0; z-index: ${IN_LAYER.halo}; box-sizing: border-box; padding: 2px ${REACH}px; border-radius: 4px; background: var(--ge-diff-del-fill); color: var(--ge-diff-del-text); pointer-events: none; user-select: none; overflow-wrap: anywhere; }
|
|
33
|
+
.ge-diff-ghost-line { position: relative; white-space: pre-line; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; }
|
|
34
|
+
.ge-diff-ghost-line + .ge-diff-ghost-line { margin-top: 2px; }
|
|
35
|
+
.ge-diff-ghost-words { text-decoration: line-through; text-decoration-color: var(--ge-diff-del-text); }
|
|
36
|
+
.ge-diff-ghost-line[data-tag="h1"], .ge-diff-ghost-line[data-tag="h2"] { font-size: 1.25em; font-weight: 600; }
|
|
37
|
+
.ge-diff-ghost-line:is([data-tag="h3"], [data-tag="h4"]) { font-size: 1.1em; font-weight: 600; }
|
|
38
|
+
.ge-diff-ghost-line[data-tag="pre"] { font-family: var(--doc-mono, ui-monospace, SFMono-Regular, Consolas, monospace); font-size: .875em; }
|
|
39
|
+
.ge-diff-ghost-marker { position: absolute; transform: translateX(-100%); padding-right: .45em; font-variant-numeric: tabular-nums; }
|
|
40
|
+
.ge-diff-ghost-line[data-empty] .ge-diff-ghost-words { text-decoration: none; opacity: .8; }
|
|
41
|
+
@media print { .ge-diff-ghost { display: none; } }
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const shows = (node) => node.isConnected && node.getClientRects().length > 0 && node.getBoundingClientRect().width > 0;
|
|
45
|
+
|
|
46
|
+
// Whether `node` stands beside the blocks next to it rather than under them: in a row of a flex box,
|
|
47
|
+
// a grid of more than one column, or a line of text.
|
|
48
|
+
function beside(node) {
|
|
49
|
+
const parent = node.parentElement;
|
|
50
|
+
if (!parent || parent.localName === "main") return false;
|
|
51
|
+
const style = getComputedStyle(parent);
|
|
52
|
+
if (style.display.includes("flex")) return style.flexDirection.startsWith("row");
|
|
53
|
+
if (style.display.includes("grid")) return style.gridTemplateColumns.trim().split(/\s+/).length > 1;
|
|
54
|
+
return getComputedStyle(node).display.startsWith("inline");
|
|
55
|
+
}
|
|
56
|
+
// The block a room is made before or after for `node`: itself, or the row it stands in.
|
|
57
|
+
function rowOf(node) {
|
|
58
|
+
while (beside(node)) node = node.parentElement;
|
|
59
|
+
return node;
|
|
60
|
+
}
|
|
61
|
+
const level = (a, b) => Math.abs(a.getBoundingClientRect().top - b.getBoundingClientRect().top) < 1;
|
|
62
|
+
|
|
63
|
+
/** A line of the ghost: one deleted block (source/changes.mjs ghostRow). */
|
|
64
|
+
function lineOf(row) {
|
|
65
|
+
const words = (row.text ?? "").trim();
|
|
66
|
+
const line = element("div", { class: "ge-diff-ghost-line", "data-tag": row.tag ?? "" });
|
|
67
|
+
line.style.paddingLeft = `${(row.depth ?? 0) * 1.5 + (row.marker ? 1.5 : 0)}em`;
|
|
68
|
+
if (row.marker) line.append(element("span", { class: "ge-diff-ghost-marker", text: row.marker === "ol" ? `${row.number ?? 1}.` : "•" }));
|
|
69
|
+
if (!words) line.setAttribute("data-empty", "");
|
|
70
|
+
line.append(element("span", { class: "ge-diff-ghost-words", text: words || KIND_NAMES[row.kind] || "…" }));
|
|
71
|
+
return line;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The ghosts of a review's deleted blocks. Returns { add(group, anchor, edge), place(), clear(),
|
|
76
|
+
* drop(gone?), count, entries }: add makes one (drawn at the next place), place puts each where its
|
|
77
|
+
* room is (made or made again as needed), clear takes them and their rooms away.
|
|
78
|
+
*/
|
|
79
|
+
export function createGhosts() {
|
|
80
|
+
const sheet = element("style", { "data-doc-dev": "", "data-ge-changes-room": "" });
|
|
81
|
+
let entries = []; // { drawn, group, anchor, edge, target, height, width, gap, room }
|
|
82
|
+
|
|
83
|
+
// The rooms, one rule each, for the screen alone (printed, the page is as the file has it). A
|
|
84
|
+
// room's margin is !important: the kit's spacing (a flow's first block's none, the gap between two
|
|
85
|
+
// kinds of block) is said by rules more specific than one attribute, and the room is added to it.
|
|
86
|
+
function writeRules() {
|
|
87
|
+
const text = entries.map((entry) => entry.room).filter(Boolean).join("\n");
|
|
88
|
+
const css = text ? `@media screen {\n${text}\n}` : "";
|
|
89
|
+
if (sheet.textContent !== css) sheet.textContent = css;
|
|
90
|
+
if (css && !sheet.isConnected) document.head.append(sheet);
|
|
91
|
+
if (!css && sheet.isConnected) sheet.remove();
|
|
92
|
+
}
|
|
93
|
+
const selectorOf = (target) => (target.localName === "main" ? "main" : `[data-ge-id="${CSS.escape(identityOf(target))}"]`);
|
|
94
|
+
const setRule = (entry, declaration) => {
|
|
95
|
+
entry.room = declaration ? `${selectorOf(entry.target)} { ${declaration} }` : null;
|
|
96
|
+
writeRules();
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Room for `height` px where `entry`'s blocks were, the gap the page had there kept on either side.
|
|
100
|
+
function makeRoom(entry, height) {
|
|
101
|
+
const { target, edge } = entry;
|
|
102
|
+
setRule(entry, null);
|
|
103
|
+
const style = getComputedStyle(target);
|
|
104
|
+
if (edge === "after") {
|
|
105
|
+
entry.gap = Math.min(GAP_MOST, Math.max(GAP_LEAST, parseFloat(style.marginBottom) || 0));
|
|
106
|
+
setRule(entry, `margin-bottom: ${Math.round((parseFloat(style.marginBottom) || 0) + entry.gap + height + entry.gap)}px !important;`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (edge === "into") {
|
|
110
|
+
entry.gap = GAP_LEAST;
|
|
111
|
+
setRule(entry, `padding-top: ${Math.round((parseFloat(style.paddingTop) || 0) + height + 2 * entry.gap)}px !important;`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const top = target.getBoundingClientRect().top;
|
|
115
|
+
let before = target.previousElementSibling;
|
|
116
|
+
while (before && (before.hasAttribute("data-doc-dev") || !shows(before))) before = before.previousElementSibling;
|
|
117
|
+
const above = before ? top - before.getBoundingClientRect().bottom : null;
|
|
118
|
+
entry.gap = Math.min(GAP_MOST, Math.max(GAP_LEAST, above ?? GAP_LEAST));
|
|
119
|
+
const want = height + entry.gap; // how far the block goes down
|
|
120
|
+
let margin = (parseFloat(style.marginTop) || 0) + want;
|
|
121
|
+
setRule(entry, `margin-top: ${Math.round(margin)}px !important;`);
|
|
122
|
+
const moved = target.getBoundingClientRect().top - top;
|
|
123
|
+
if (Math.abs(moved - want) > 0.5) { // its margin collapsed with the one above it
|
|
124
|
+
margin += want - moved;
|
|
125
|
+
setRule(entry, `margin-top: ${Math.round(margin)}px !important;`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
get count() { return entries.length; },
|
|
131
|
+
get entries() { return entries; },
|
|
132
|
+
/** A ghost of `group` (a review's `removed` entry) at `anchor`'s `edge` ("before", "after", "into"). */
|
|
133
|
+
add(group, anchor, edge) {
|
|
134
|
+
const lines = group.rows.map(lineOf);
|
|
135
|
+
if (group.more || group.rows.length === 0) lines.push(lineOf({ text: "…", tag: "", depth: 0 }));
|
|
136
|
+
const target = edge === "into" ? anchor : rowOf(edge === "before" ? withLeads(anchor)[0] : anchor);
|
|
137
|
+
// At the place of one made already (the groups come in the page's order): its lines go on in that one.
|
|
138
|
+
const one = entries.find((entry) => entry.edge === edge && (entry.target === target || (edge === "before" && entry.target.contains(target) && level(entry.target, target))));
|
|
139
|
+
if (one) {
|
|
140
|
+
one.drawn.append(...lines);
|
|
141
|
+
one.height = -1;
|
|
142
|
+
return one;
|
|
143
|
+
}
|
|
144
|
+
const drawn = element("div", { class: "ge-diff-ghost", "data-doc-dev": "", "aria-hidden": "true" }, ...lines);
|
|
145
|
+
const entry = { drawn, group, anchor, edge, target, height: -1, width: -1, gap: GAP_LEAST, room: null };
|
|
146
|
+
own(drawn, "changes", { owner: anchor, alive: () => anchor.isConnected && entries.includes(entry) });
|
|
147
|
+
entries.push(entry);
|
|
148
|
+
return entry;
|
|
149
|
+
},
|
|
150
|
+
/** Each ghost where its room is; a block not shown (a closed disclosure, another tab) neither ghost nor room. */
|
|
151
|
+
place() {
|
|
152
|
+
let changed = false;
|
|
153
|
+
for (const entry of entries) {
|
|
154
|
+
const { drawn: node, target } = entry;
|
|
155
|
+
if (!entry.anchor.isConnected || !shows(target)) {
|
|
156
|
+
node.remove();
|
|
157
|
+
if (entry.room) {
|
|
158
|
+
entry.room = null;
|
|
159
|
+
entry.height = -1;
|
|
160
|
+
changed = true;
|
|
161
|
+
}
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const layer = layerOf(target);
|
|
165
|
+
if (node.parentNode !== layer) {
|
|
166
|
+
const font = getComputedStyle(target.closest("main") ?? document.body);
|
|
167
|
+
Object.assign(node.style, { fontFamily: font.fontFamily, fontSize: font.fontSize, lineHeight: font.lineHeight });
|
|
168
|
+
layer.append(node);
|
|
169
|
+
}
|
|
170
|
+
let rect = target.getBoundingClientRect();
|
|
171
|
+
const width = Math.round(rect.width + 2 * REACH);
|
|
172
|
+
if (width !== entry.width) {
|
|
173
|
+
node.style.width = `${width}px`;
|
|
174
|
+
entry.width = width;
|
|
175
|
+
}
|
|
176
|
+
const height = node.offsetHeight;
|
|
177
|
+
if (height !== entry.height) {
|
|
178
|
+
makeRoom(entry, height);
|
|
179
|
+
entry.height = height;
|
|
180
|
+
rect = target.getBoundingClientRect();
|
|
181
|
+
}
|
|
182
|
+
const top = entry.edge === "before" ? rect.top - entry.gap - height
|
|
183
|
+
: entry.edge === "after" ? rect.bottom + entry.gap
|
|
184
|
+
: rect.top + (parseFloat(getComputedStyle(target).paddingTop) || 0) - height - entry.gap;
|
|
185
|
+
const origin = layer.getBoundingClientRect();
|
|
186
|
+
drawAt(node, layer, rect.left - REACH - origin.left, top - origin.top);
|
|
187
|
+
}
|
|
188
|
+
if (changed) writeRules();
|
|
189
|
+
return entries.length > 0;
|
|
190
|
+
},
|
|
191
|
+
/** Ghosts whose block left the page go, and their rooms. */
|
|
192
|
+
settle() {
|
|
193
|
+
const gone = entries.filter((entry) => !entry.anchor.isConnected);
|
|
194
|
+
if (gone.length === 0) return;
|
|
195
|
+
for (const entry of gone) entry.drawn.remove();
|
|
196
|
+
entries = entries.filter((entry) => !gone.includes(entry));
|
|
197
|
+
writeRules();
|
|
198
|
+
},
|
|
199
|
+
/** Every ghost and room taken away. */
|
|
200
|
+
clear() {
|
|
201
|
+
for (const entry of entries) entry.drawn.remove();
|
|
202
|
+
entries = [];
|
|
203
|
+
writeRules();
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { isPad } from "../pads.mjs";
|
|
2
|
+
|
|
3
|
+
// The words an Agent took out of a text block, shown where they were (docs/editor.md "Agent's
|
|
4
|
+
// changes"): an empty inline span of the editor's own in the block's text (`span.ge-diff-del`,
|
|
5
|
+
// `data-ge-gone-words` holding the words, drawn by its ::before, contenteditable="false",
|
|
6
|
+
// aria-hidden, data-doc-dev). It holds no text node, so whatever reads the block's words (its
|
|
7
|
+
// textContent, a selection's text, readText, the search, the outline, a copy's plain text) reads none
|
|
8
|
+
// of them; what reads its markup leaves it out (core.mjs serializeNodes, writing the text on either side
|
|
9
|
+
// of it as one; ownHTML, which undo records; a copy's clipboard.own, which drops data-doc-dev). Put in, it
|
|
10
|
+
// splits the text node it stands in (the live ranges, the selection and the added words' highlight
|
|
11
|
+
// among them, follow); taken out, the two halves are one text node again, the caret where it was (and
|
|
12
|
+
// the live ranges it is given: a selection across blocks', which keeps its ends as points of its own).
|
|
13
|
+
|
|
14
|
+
const ATTRIBUTE = "data-ge-gone-words";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A mark of `words` taken out (not on the page yet). `before`, `after`: the characters of the text on
|
|
18
|
+
* either side of where it goes. A space at either end of the words is drawn apart from them, neither
|
|
19
|
+
* struck nor filled (data-ge-gone-lead, -tail), and only where the text beside has none: "words
|
|
20
|
+
* [that were removed] stay", never a struck space.
|
|
21
|
+
*/
|
|
22
|
+
export function goneMark(words, doc = document, { before, after } = {}) {
|
|
23
|
+
const span = doc.createElement("span");
|
|
24
|
+
span.className = "ge-diff-del";
|
|
25
|
+
const bare = words.trim();
|
|
26
|
+
span.setAttribute(ATTRIBUTE, bare || words);
|
|
27
|
+
if (bare && /^\s/.test(words) && before !== undefined && !/\s/.test(before)) span.setAttribute("data-ge-gone-lead", "");
|
|
28
|
+
if (bare && /\s$/.test(words) && after !== undefined && !/\s/.test(after)) span.setAttribute("data-ge-gone-tail", "");
|
|
29
|
+
span.setAttribute("contenteditable", "false");
|
|
30
|
+
span.setAttribute("aria-hidden", "true");
|
|
31
|
+
span.setAttribute("data-doc-dev", "");
|
|
32
|
+
return span;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Where words taken out at character `at` of `read` (annotate/anchors.mjs readText of the block)
|
|
37
|
+
* stand: before that character, or after the one before it when it is a line's end or there is
|
|
38
|
+
* none (the words taken out at the block's end). { place, side }, or null for a block with no text.
|
|
39
|
+
*/
|
|
40
|
+
export function placeOf(read, at) {
|
|
41
|
+
if (read.at[at]) return { place: read.at[at], side: "before" };
|
|
42
|
+
if (at > 0 && read.at[at - 1]) return { place: read.at[at - 1], side: "after" };
|
|
43
|
+
for (let index = at + 1; index < read.at.length; index++) if (read.at[index]) return { place: read.at[index], side: "before" };
|
|
44
|
+
for (let index = Math.min(at, read.at.length) - 1; index >= 0; index--) if (read.at[index]) return { place: read.at[index], side: "after" };
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Put `mark` at `where` (placeOf): in a text node split there, or beside a formula or a chip, never inside one. */
|
|
49
|
+
export function putMark(mark, { place, side }) {
|
|
50
|
+
if (place.node) {
|
|
51
|
+
const { node } = place;
|
|
52
|
+
const at = side === "before" ? place.offset : place.offset + 1;
|
|
53
|
+
if (at <= 0) node.before(mark);
|
|
54
|
+
else if (at >= node.length) node.after(mark);
|
|
55
|
+
else node.splitText(at).before(mark);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const beside = place.element.closest("[data-ge-atom]") ?? place.element;
|
|
59
|
+
if (side === "before") beside.before(mark);
|
|
60
|
+
else beside.after(mark);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Take `mark` off the page, the text on either side of it one text node again, the selection where it
|
|
65
|
+
* was, and so each of `ranges` (live ranges a tool keeps: a selection across blocks' pieces,
|
|
66
|
+
* text-select/), whose ends in the second half would otherwise fall out of the text with it.
|
|
67
|
+
*/
|
|
68
|
+
export function takeMark(mark, ranges = []) {
|
|
69
|
+
const first = mark.previousSibling;
|
|
70
|
+
const second = mark.nextSibling;
|
|
71
|
+
mark.remove();
|
|
72
|
+
if (first?.nodeType !== 3 || second?.nodeType !== 3 || isPad(first) || isPad(second)) return;
|
|
73
|
+
const selection = first.ownerDocument.getSelection();
|
|
74
|
+
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
|
|
75
|
+
const length = first.length;
|
|
76
|
+
const held = ranges.map((range) => [range, range.startContainer === second ? range.startOffset : -1, range.endContainer === second ? range.endOffset : -1]);
|
|
77
|
+
first.appendData(second.data);
|
|
78
|
+
second.remove();
|
|
79
|
+
for (const [range, start, end] of held) {
|
|
80
|
+
if (start >= 0) range.setStart(first, length + start);
|
|
81
|
+
if (end >= 0) range.setEnd(first, length + end);
|
|
82
|
+
}
|
|
83
|
+
if (anchorNode !== second && focusNode !== second) return; // where it is still says the same (a point in `first`, or in their parent)
|
|
84
|
+
const point = (node, offset) => (node === second ? [first, length + offset] : [node, offset]);
|
|
85
|
+
selection.setBaseAndExtent(...point(anchorNode, anchorOffset), ...point(focusNode, focusOffset));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// The reader edits a block holding marks: they go first, before the edit touches it (changes/index.mjs).
|
|
89
|
+
// A key edits unless it only moves the caret or the view, or copies, selects all, finds, saves or prints.
|
|
90
|
+
const MOVES = /^(Arrow(Up|Down|Left|Right)|Home|End|PageUp|PageDown|Escape|Shift|Control|Alt|Meta|AltGraph|CapsLock|NumLock|ScrollLock|ContextMenu|F\d{1,2})$/;
|
|
91
|
+
const KEEPS = /^[acfsp]$/;
|
|
92
|
+
|
|
93
|
+
/** Whether a keydown may edit the block the caret is in (an input method's Process key does). */
|
|
94
|
+
export const editsBy = (event) => !MOVES.test(event.key) && !((event.ctrlKey || event.metaKey) && !event.altKey && !event.shiftKey && KEEPS.test(event.key));
|