@heycys/gallery 0.1.1 → 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 +1 -1
- package/browser/editor/components/columns/page.mjs +4 -4
- package/browser/editor/components/embed/index.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/icons.mjs +8 -0
- package/browser/editor/components/image/crop.mjs +6 -9
- package/browser/editor/components/installed/index.mjs +11 -4
- package/browser/editor/components/installed/page.mjs +27 -16
- package/browser/editor/components/installed/served.mjs +6 -5
- package/browser/editor/components/instances.mjs +18 -0
- package/browser/editor/components/list/page.mjs +3 -6
- package/browser/editor/components/registry.mjs +13 -4
- package/browser/editor/components/table/index.mjs +59 -2
- package/browser/editor/components/table/page.mjs +1 -1
- package/browser/editor/page/annotate/card.mjs +3 -6
- package/browser/editor/page/autosave.mjs +310 -37
- 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 +1 -1
- package/browser/editor/page/changes/bar.mjs +6 -14
- package/browser/editor/page/changes/index.mjs +2 -2
- 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 +124 -13
- package/browser/editor/page/clipboard/own/blocks.mjs +11 -11
- package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
- package/browser/editor/page/clipboard/own/selection.mjs +1 -1
- package/browser/editor/page/columns/index.mjs +48 -46
- package/browser/editor/page/commit.mjs +80 -22
- package/browser/editor/page/conflicts.mjs +7 -7
- package/browser/editor/page/core.mjs +31 -15
- package/browser/editor/page/custom-page.mjs +106 -0
- package/browser/editor/page/editor.mjs +51 -14
- package/browser/editor/page/emoji-picker/styles.mjs +7 -12
- package/browser/editor/page/emoji-picker/upload.mjs +2 -2
- package/browser/editor/page/handle/index.mjs +5 -8
- package/browser/editor/page/history.mjs +24 -4
- package/browser/editor/page/links/index.mjs +5 -3
- package/browser/editor/page/links/pages.mjs +27 -9
- package/browser/editor/page/look.mjs +54 -3
- package/browser/editor/page/margin.mjs +18 -3
- package/browser/editor/page/media-menu/index.mjs +6 -7
- package/browser/editor/page/menu.mjs +18 -7
- package/browser/editor/page/notices.mjs +2 -2
- package/browser/editor/page/page-objects.mjs +13 -2
- package/browser/editor/page/panel.mjs +4 -9
- package/browser/editor/page/present-links.mjs +11 -2
- package/browser/editor/page/resize/index.mjs +42 -29
- package/browser/editor/page/served.mjs +14 -0
- package/browser/editor/page/session.mjs +48 -4
- package/browser/editor/page/slash-menu/index.mjs +3 -3
- package/browser/editor/page/table/cells.mjs +102 -46
- package/browser/editor/page/table/icons.mjs +2 -0
- package/browser/editor/page/table/index.mjs +29 -10
- package/browser/editor/page/table/pictures.mjs +299 -0
- package/browser/editor/page/table/views.mjs +150 -0
- package/browser/editor/page/text-select/index.mjs +26 -12
- package/browser/editor/page/todo-writes.mjs +75 -0
- package/browser/editor/page/typing/index.mjs +53 -3
- package/browser/editor/page/ui.mjs +13 -0
- 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/colors.mjs +3 -2
- package/browser/shared/component-catalog.mjs +323 -46
- package/browser/shared/component-source.mjs +37 -0
- package/browser/shared/components/index.mjs +12 -3
- package/browser/shared/components/installed.mjs +35 -3
- package/browser/shared/components/page.mjs +18 -8
- package/browser/shared/components/state.mjs +5 -1
- package/browser/shared/document-look.mjs +10 -5
- package/browser/shared/drafts.mjs +432 -0
- 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/i18n/en.mjs +307 -27
- package/browser/shared/json-layout.mjs +47 -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/outline-shape.mjs +128 -0
- package/browser/shared/outline.mjs +331 -67
- 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/preview-server.mjs +3 -7
- package/browser/shared/session.mjs +8 -1
- 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-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/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-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
- package/dist/{chunk-Y6RLRPG3.mjs → chunk-6VTU6KEO.mjs} +1 -1
- package/dist/chunk-B4P2CM2J.mjs +3 -0
- package/dist/{chunk-FDLXYYG4.mjs → chunk-BPL64HGA.mjs} +1 -1
- package/dist/chunk-BVEFGE4B.mjs +2 -0
- package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
- package/dist/chunk-CRJEG7MT.mjs +2 -0
- package/dist/{chunk-LCTHTZIL.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-GB5SOYQJ.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-KTKHRC6H.mjs +2 -0
- package/dist/chunk-L7UP52JO.mjs +2 -0
- package/dist/{chunk-SYGN7GLU.mjs → chunk-MHW7ZKOU.mjs} +1 -1
- package/dist/chunk-N76HWBA3.mjs +10 -0
- package/dist/chunk-N7AZZLGT.mjs +548 -0
- package/dist/chunk-NAMN3RAF.mjs +2 -0
- package/dist/{chunk-5ZKJ3H4P.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-4FS7Z7WR.mjs → chunk-OUUP3WKT.mjs} +1 -1
- package/dist/{chunk-BGGDRNYQ.mjs → chunk-OW5NWPOJ.mjs} +1 -1
- package/dist/chunk-PAREWQGB.mjs +4 -0
- package/dist/chunk-POQSE4C2.mjs +2 -0
- package/dist/chunk-PUOQ65NC.mjs +2 -0
- package/dist/{chunk-XNROSH2T.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-7DTINW6U.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-UNOYVIYR.mjs +2 -0
- package/dist/{chunk-E7LWZRXY.mjs → chunk-V6RIYQZZ.mjs} +1 -1
- 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-YI43RG4P.mjs +2 -0
- package/dist/chunk-YQGT4AB2.mjs +6 -0
- package/dist/chunk-ZB4H7ZN4.mjs +2 -0
- package/dist/{chunk-IVDLMMBD.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-U6KNPJPR.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/report.css +255 -17
- package/kit/runtime.js +1600 -125
- package/kit/state.js +10 -1
- package/kit/tables.js +128 -43
- package/kit/ui.js +77 -35
- 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 +12 -10
- package/sdk/doc-kit.d.ts +396 -5
- package/sdk/doc-state.d.ts +2 -0
- package/skill/SKILL.md +56 -23
- package/skill/references/block-script.md +140 -0
- package/skill/references/building-blocks.md +257 -0
- package/skill/references/extensions.md +59 -0
- package/skill/references/pages-and-data.md +221 -0
- package/skill/references/writing-html.md +6 -4
- package/skill/sdk/doc-kit.d.ts +396 -5
- 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/dist/chunk-3HIRPVLU.mjs +0 -2
- package/dist/chunk-5MHMDLZD.mjs +0 -4
- package/dist/chunk-64K7IPLO.mjs +0 -255
- package/dist/chunk-6QXGZM36.mjs +0 -8
- package/dist/chunk-6RGJOMJP.mjs +0 -16
- package/dist/chunk-AJ73XJF3.mjs +0 -2
- package/dist/chunk-AUZGSZJH.mjs +0 -13
- package/dist/chunk-BFE45AKL.mjs +0 -2
- package/dist/chunk-BXO2ZY5P.mjs +0 -2
- package/dist/chunk-CEH4MRCC.mjs +0 -2
- package/dist/chunk-CY2FNNAB.mjs +0 -16
- package/dist/chunk-DFUDV4LX.mjs +0 -2
- package/dist/chunk-DKXG4XDG.mjs +0 -2
- package/dist/chunk-EZQ34M3F.mjs +0 -124
- package/dist/chunk-EZY3WC6Z.mjs +0 -15
- package/dist/chunk-FUKQNOQS.mjs +0 -9
- package/dist/chunk-JPOSLGOC.mjs +0 -6
- package/dist/chunk-L3QKH4OK.mjs +0 -26
- package/dist/chunk-LAE5NQ4B.mjs +0 -2
- package/dist/chunk-LEF6DRCK.mjs +0 -59
- package/dist/chunk-NO5NJCHO.mjs +0 -2
- package/dist/chunk-NQPVAU63.mjs +0 -2
- package/dist/chunk-NSKUTQ5J.mjs +0 -5
- package/dist/chunk-O35FIXRB.mjs +0 -2
- package/dist/chunk-P2CAZPDP.mjs +0 -3
- package/dist/chunk-PL6GHLGD.mjs +0 -15
- package/dist/chunk-QJIR2BLP.mjs +0 -6
- package/dist/chunk-QUPLVPB2.mjs +0 -7
- package/dist/chunk-R7BNENLY.mjs +0 -4
- package/dist/chunk-S6O4UHLC.mjs +0 -2
- package/dist/chunk-SGJWQKEI.mjs +0 -2
- package/dist/chunk-TX6JH7TR.mjs +0 -2
- package/dist/chunk-TYK6VNAB.mjs +0 -10
- package/dist/chunk-UALRNVSE.mjs +0 -4
- package/dist/chunk-V3HXVQEM.mjs +0 -2
- package/dist/chunk-V5VXUJKG.mjs +0 -2
- package/dist/chunk-VBXMADMP.mjs +0 -2
- package/dist/chunk-VNV55RUA.mjs +0 -2
- package/dist/chunk-VPD2PS35.mjs +0 -2
- package/dist/chunk-W4VKN3GF.mjs +0 -7
- package/dist/chunk-W5BXXPDJ.mjs +0 -3
- package/dist/chunk-XUNHRCOG.mjs +0 -6
- package/skill/references/building-components.md +0 -367
- package/web/assets/index-Bh74DfsV.css +0 -1
- package/web/assets/index-UW0RyFFt.js +0 -13
|
@@ -7,7 +7,7 @@ import { rangeAt, rangeOffsets } from "./marks.mjs";
|
|
|
7
7
|
import { pageOfKind, pageOfUnit } from "./page-objects.mjs";
|
|
8
8
|
import { containerTypeOf, pack, project } from "./sync-state.mjs";
|
|
9
9
|
import { outsideKey, randomBlockId } from "../../shared/ids.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import { CHART_SOURCE, TABLE_READERS } from "../../shared/table-chart.mjs";
|
|
11
11
|
|
|
12
12
|
// The page's commits (autosave; docs/editor.md, "Commits"). A commit is what changed on the page
|
|
13
13
|
// since the last one, as collectEdits finds it against the layout and baselines the last commit
|
|
@@ -803,12 +803,18 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
803
803
|
baselines.set(id, html);
|
|
804
804
|
return id;
|
|
805
805
|
};
|
|
806
|
+
// How many blocks holding text blocks of their own (a list item's paragraphs) asLoaded made anew, those
|
|
807
|
+
// in them made anew with them: pathIndex finds a unit's blocks again then.
|
|
808
|
+
let rewrites = 0;
|
|
809
|
+
const holdsBlocks = (host) => host.querySelector("[contenteditable='true']") !== null;
|
|
806
810
|
// A text block as a load of the page shows the file's (its inline elements of the report's own
|
|
807
811
|
// atoms, written back as they are: core.mjs prepareBlock), the caret where it stood.
|
|
808
812
|
const asLoaded = (host, { inner, atoms = [], kind, caps }) => {
|
|
809
813
|
const selection = host.ownerDocument.getSelection();
|
|
810
814
|
const place = selection?.rangeCount && host.contains(selection.anchorNode) ? rangeOffsets(host, selection.getRangeAt(0)) : null;
|
|
815
|
+
const held = holdsBlocks(host);
|
|
811
816
|
prepareBlock(host, { inner, atoms, kind, caps });
|
|
817
|
+
if (held || holdsBlocks(host)) rewrites++;
|
|
812
818
|
if (place) select(rangeAt(host, place));
|
|
813
819
|
};
|
|
814
820
|
// The selection in a text block someone else's words are about to be merged into, as it stands among the
|
|
@@ -883,14 +889,26 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
883
889
|
const hosts = [...element.querySelectorAll("[contenteditable='true']")].filter((host) => host.parentElement?.closest("[data-ge-unit], [data-ge-unit-kind]") === element);
|
|
884
890
|
return hosts.length === blocks.length ? hosts : null;
|
|
885
891
|
};
|
|
886
|
-
// The text block of `element` (a unit of the page) that is the file's { self } or { path } one
|
|
887
|
-
|
|
892
|
+
// The text block of `element` (a unit of the page) that is the file's { self } or { path } one: the first
|
|
893
|
+
// of its own whose path (pathFrom) is that one. `find`: the unit's text blocks by path (pathIndex), which a
|
|
894
|
+
// caller asking of each of a unit's blocks makes once: found anew for each block, a table of 500 rows had
|
|
895
|
+
// its thousand cells walked, each past its row's 500 siblings, for each of the thousand the answer named,
|
|
896
|
+
// the page standing still for a minute after each commit there.
|
|
897
|
+
const blockElement = (element, block, find = pathIndex(element)) => {
|
|
888
898
|
// Its own text: the element itself, or the span of its own its description's `title` names (a Sheet tab's name).
|
|
889
899
|
if (block.self) return element.matches("[contenteditable='true']") ? element : BLOCK_TYPES[blockTypeOf(element)]?.title?.(element) ?? null;
|
|
890
900
|
if (!Array.isArray(block.path) || block.path[0] !== "unit") return null;
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
901
|
+
return find(JSON.stringify(block.path));
|
|
902
|
+
};
|
|
903
|
+
// The text blocks of `element` by path, for asking of many in turn: found once (hostsByPath), and again only
|
|
904
|
+
// once a block holding blocks of its own was made anew as the file gives it (asLoaded's `rewrites`).
|
|
905
|
+
const pathIndex = (element) => {
|
|
906
|
+
let index = null;
|
|
907
|
+
let made = -1;
|
|
908
|
+
return (want) => {
|
|
909
|
+
if (made !== rewrites) [index, made] = [hostsByPath(element), rewrites];
|
|
910
|
+
return index.get(want) ?? null;
|
|
911
|
+
};
|
|
894
912
|
};
|
|
895
913
|
|
|
896
914
|
/**
|
|
@@ -1003,8 +1021,9 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1003
1021
|
// of the page's (its words would go over theirs): it keeps the one the page had, but where nobody else wrote.
|
|
1004
1022
|
const matched = new Set();
|
|
1005
1023
|
const inOrder = inFileOrder(element, fact.blocks);
|
|
1024
|
+
const find = pathIndex(element);
|
|
1006
1025
|
for (const [index, block] of fact.blocks.entries()) {
|
|
1007
|
-
const host = blockElement(element, block) ?? inOrder?.[index];
|
|
1026
|
+
const host = blockElement(element, block, find) ?? inOrder?.[index];
|
|
1008
1027
|
if (!host) continue;
|
|
1009
1028
|
const fresh = blockOf(host) === null;
|
|
1010
1029
|
const own = registerBlock(host, pending, block, unit);
|
|
@@ -1196,7 +1215,8 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1196
1215
|
* Take in the answer to a commit (build's `pending`, the endpoint's receipt; `alone`: nobody but
|
|
1197
1216
|
* this page wrote the file since it showed it, see adopt). Returns { conflicts, merged: hosts now
|
|
1198
1217
|
* showing a merge, stale: a merge the page could not show, outdated: it runs a manifest that is no
|
|
1199
|
-
* more, forced: the page units a change the reader said to make anyway went in for, all it held
|
|
1218
|
+
* more, forced: the page units a change the reader said to make anyway went in for, all it held,
|
|
1219
|
+
* rekeyed: the keys of tables it set otherwise on the page, [element, name, old, new | null] }.
|
|
1200
1220
|
* A conflict of a change sent as the reader said to make it anyway says so (`again`).
|
|
1201
1221
|
*/
|
|
1202
1222
|
function apply(pending, receipt, { alone = true } = {}) {
|
|
@@ -1221,24 +1241,31 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1221
1241
|
else if (element) proposals.set(identityOf(element), id);
|
|
1222
1242
|
}
|
|
1223
1243
|
for (const [key, hash] of Object.entries(receipt.hashes ?? {})) if (map.units[Number(key)]) map.units[Number(key)].hash = hash;
|
|
1224
|
-
// The keys of
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1244
|
+
// The keys of tables the file gave the blocks the commit made and the tables it keyed (a view's
|
|
1245
|
+
// 视图 ›: the key its frame took, an attribute of the unit), other than the page chose
|
|
1246
|
+
// (source/chart-keys.mjs: one the file had meanwhile): theirs on the page too, so that each card and
|
|
1247
|
+
// view reads the table the file links it to ({ old: new | null }, null: off a table); `rekeyed` says
|
|
1248
|
+
// which it changed ([element, name, old, new | null]), for the undo steps that recorded them (history.mjs).
|
|
1249
|
+
const rekeyed = [];
|
|
1227
1250
|
if (receipt.chartKeys) {
|
|
1228
|
-
|
|
1251
|
+
const keyed = [...pending.units.values()].filter(({ change }) => change?.attribute === CHART_SOURCE).map(({ id }) => unitElement(id)).filter(Boolean);
|
|
1252
|
+
for (const element of [...pending.made.values(), ...keyed]) {
|
|
1229
1253
|
// A copy not written yet sends its keys again: the file's now.
|
|
1230
1254
|
const sent = element?.getAttribute(COPY_KEYS);
|
|
1231
1255
|
if (sent) {
|
|
1232
1256
|
const keys = Object.fromEntries(Object.entries(JSON.parse(sent)).map(([from, to]) => [from, to !== null && Object.hasOwn(receipt.chartKeys, to) ? receipt.chartKeys[to] : to]));
|
|
1233
1257
|
element.setAttribute(COPY_KEYS, JSON.stringify(keys));
|
|
1234
1258
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1259
|
+
const names = [CHART_SOURCE, ...TABLE_READERS];
|
|
1260
|
+
for (const node of element ? [element, ...element.querySelectorAll(names.map((name) => `[${name}]`).join(", "))] : []) {
|
|
1261
|
+
for (const name of names) {
|
|
1237
1262
|
const key = node.getAttribute(name);
|
|
1238
1263
|
if (key === null || !Object.hasOwn(receipt.chartKeys, key)) continue;
|
|
1239
1264
|
const next = receipt.chartKeys[key];
|
|
1240
1265
|
if (typeof next === "string") node.setAttribute(name, next);
|
|
1241
1266
|
else if (next === null && name === CHART_SOURCE) node.removeAttribute(name);
|
|
1267
|
+
else continue;
|
|
1268
|
+
rekeyed.push([node, name, key, next]);
|
|
1242
1269
|
}
|
|
1243
1270
|
}
|
|
1244
1271
|
}
|
|
@@ -1498,13 +1525,14 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1498
1525
|
}
|
|
1499
1526
|
setLayout(next);
|
|
1500
1527
|
marks();
|
|
1501
|
-
return { conflicts, merged, stale, outdated: shownAgain, forced: [...landed] };
|
|
1528
|
+
return { conflicts, merged, stale, outdated: shownAgain, forced: [...landed], rekeyed };
|
|
1502
1529
|
}
|
|
1503
1530
|
|
|
1504
1531
|
// A text committed: what it replaced (the first time this page committed it), what it is now, and what
|
|
1505
1532
|
// the commits of it between left it as (`earlier`, the newest TEXTS_KEPT): someone writing back what
|
|
1506
1533
|
// they read then puts back what the page committed after. Back as it was first, it is asked of what it
|
|
1507
1534
|
// was between, as a part of what a component writes is (noteWrite): an undo is a commit like any other.
|
|
1535
|
+
// `at`: when it was last committed, which puts the journals of two tabs' pages in order (autosave.mjs).
|
|
1508
1536
|
function committedText(key, sent) {
|
|
1509
1537
|
const mine = sources.get(sent.id);
|
|
1510
1538
|
const was = journal.get(key);
|
|
@@ -1512,7 +1540,7 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1512
1540
|
if (mine === undefined) return void journal.delete(key);
|
|
1513
1541
|
const earlier = [...new Set([...(was?.earlier ?? []), ...(was ? [was.mine] : [])])].filter((text) => text !== mine && text !== original).slice(-TEXTS_KEPT);
|
|
1514
1542
|
if (mine === original && earlier.length === 0) return void journal.delete(key);
|
|
1515
|
-
journal.set(key, { key, block: blockRef(sent.id), original, mine, ...(earlier.length > 0 ? { earlier } : {}) });
|
|
1543
|
+
journal.set(key, { key, block: blockRef(sent.id), original, mine, ...(earlier.length > 0 ? { earlier } : {}), at: Date.now() });
|
|
1516
1544
|
}
|
|
1517
1545
|
|
|
1518
1546
|
// A part of what a unit's component writes, written by a commit (`before`, `after`: the hashes the file
|
|
@@ -1626,6 +1654,12 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1626
1654
|
/** Whether unit `unit` stands inside one of `units` on the page: what someone else's write took out with it is decided with it. */
|
|
1627
1655
|
const heldWithin = (unit, units) => units.some((other) => other !== unit && unitsWithin(other).includes(unit));
|
|
1628
1656
|
|
|
1657
|
+
/** The unit the page shows with file id `id` (not deleted on the page), or null: a block an earlier load made, still there. */
|
|
1658
|
+
const unitOf = (id) => {
|
|
1659
|
+
const unit = typeof id === "string" ? map.units.findIndex((each, index) => each?.blockId === id && !removed.has(index) && unitElement(index)) : -1;
|
|
1660
|
+
return unit < 0 ? null : unit;
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1629
1663
|
/** The text of the blocks in a unit the page shows (the reader's words), or "". */
|
|
1630
1664
|
const unitText = (unit) => {
|
|
1631
1665
|
const element = unitElement(unit);
|
|
@@ -1741,7 +1775,7 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1741
1775
|
}
|
|
1742
1776
|
|
|
1743
1777
|
return {
|
|
1744
|
-
build, apply, collect, current, marks, counts, buffer, texts, resolve, committed, unjournal, placedUnits, putBack, heldWithin, unitText, written, carry, dataOfUnit, heldOtherwise, rewrite, issues, removed, pageIds, idFor,
|
|
1778
|
+
build, apply, collect, current, marks, counts, buffer, texts, resolve, committed, unjournal, placedUnits, putBack, heldWithin, unitOf, unitText, written, carry, dataOfUnit, heldOtherwise, rewrite, issues, removed, pageIds, idFor,
|
|
1745
1779
|
/** The id a unit of the page has in the file, or the one it proposes for it; null for none. */
|
|
1746
1780
|
fileId: (unit) => map.units[unit]?.blockId ?? map.units[unit]?.proposed ?? null,
|
|
1747
1781
|
/** The hash a unit of the page has in the file now (its map's, kept by every answer); null for none (changes/ finds the Agent's blocks by it). */
|
|
@@ -1803,11 +1837,35 @@ export const nodeHTML = (node) => [node?.title, node?.html, node?.restore?.sourc
|
|
|
1803
1837
|
// Where a text block of the page sits in its unit, as the server's scan writes it (pathIn): the
|
|
1804
1838
|
// element steps from the unit down, each [tag, index among the parent's children of that tag, how
|
|
1805
1839
|
// many there are]. Only to find the element a path of the file names (blockElement), never to say where a block is.
|
|
1806
|
-
|
|
1807
|
-
|
|
1840
|
+
// `steps`: each element's step, kept for the next path asked of the same unit (hostsByPath): its parent's
|
|
1841
|
+
// children are counted once, for all of them.
|
|
1842
|
+
function pathFrom(unit, host, steps = new Map()) {
|
|
1843
|
+
const path = [];
|
|
1808
1844
|
for (let node = host; node && node !== unit; node = node.parentElement) {
|
|
1809
|
-
|
|
1810
|
-
|
|
1845
|
+
if (!steps.has(node)) {
|
|
1846
|
+
const children = node.parentElement?.children ?? [node];
|
|
1847
|
+
const counts = new Map();
|
|
1848
|
+
for (const child of children) {
|
|
1849
|
+
const at = counts.get(child.localName) ?? 0;
|
|
1850
|
+
counts.set(child.localName, at + 1);
|
|
1851
|
+
steps.set(child, [child.localName, node.parentElement ? at : -1]);
|
|
1852
|
+
}
|
|
1853
|
+
for (const child of children) steps.get(child).push(node.parentElement ? counts.get(child.localName) : 0);
|
|
1854
|
+
}
|
|
1855
|
+
path.unshift(steps.get(node));
|
|
1856
|
+
}
|
|
1857
|
+
return ["unit", ...path];
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// The text blocks of `unit` (a unit of the page) that are its own, no unit's inside it, by their path (pathFrom,
|
|
1861
|
+
// as JSON): the first of each, in the page's order.
|
|
1862
|
+
function hostsByPath(unit) {
|
|
1863
|
+
const steps = new Map();
|
|
1864
|
+
const found = new Map();
|
|
1865
|
+
for (const host of unit.querySelectorAll("[contenteditable='true']")) {
|
|
1866
|
+
if (host.parentElement?.closest("[data-ge-unit], [data-ge-unit-kind]") !== unit) continue;
|
|
1867
|
+
const path = JSON.stringify(pathFrom(unit, host, steps));
|
|
1868
|
+
if (!found.has(path)) found.set(path, host);
|
|
1811
1869
|
}
|
|
1812
|
-
return
|
|
1870
|
+
return found;
|
|
1813
1871
|
}
|
|
@@ -2,7 +2,7 @@ import { blocksText, partOwner, plainText } from "./core.mjs";
|
|
|
2
2
|
import { reveal } from "./folded.mjs";
|
|
3
3
|
import { wordDiff } from "./merge.mjs";
|
|
4
4
|
import { PANEL_STYLE, panelGone, panelShown } from "./panel.mjs";
|
|
5
|
-
import { addStyle,
|
|
5
|
+
import { addStyle, element, mount, textButton } from "./ui.mjs";
|
|
6
6
|
import { t } from "../../shared/i18n/index.mjs";
|
|
7
7
|
|
|
8
8
|
// The conflicts the page holds back (docs/editor.md, "Conflicts"), each a card in one panel: what
|
|
@@ -66,7 +66,7 @@ function describe(entry, restorable) {
|
|
|
66
66
|
if (entry.reason === "changed") return { text: t("这一段你和 AI 同时改了同一处。"), both: true, diff: true, actions: [[t("保留我的"), "mine", true], [t("使用最新版本"), "theirs"]] };
|
|
67
67
|
if (entry.reason === "missing") return { text: t("你修改的这一块已被 AI 删除。"), actions: [...(restorable ? [[t("恢复我的"), "restore", true]] : []), [t("接受删除"), "accept"], copy] };
|
|
68
68
|
if (entry.reason === "structure") return { text: t("你修改的这一处所在的组件已被 AI 改过结构,没法确定对应的位置。"), actions: [[t("使用最新版本"), "theirs"], copy] };
|
|
69
|
-
if (packageChanged(entry)) return { text: t("
|
|
69
|
+
if (packageChanged(entry)) return { text: t("这个自定义块刚刚变了,这处文字不再能修改,你的修改没有写进文件。"), actions: [[t("使用最新版本"), "theirs"], copy] };
|
|
70
70
|
return { text: t("这一处在文件里找不到唯一对应的位置,没有保存。"), actions: [[t("使用最新版本"), "theirs"], copy] };
|
|
71
71
|
}
|
|
72
72
|
if (entry.kind === "unit") {
|
|
@@ -123,7 +123,7 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
|
|
|
123
123
|
panelShown(panel);
|
|
124
124
|
}
|
|
125
125
|
const conflicts = [...cards.values()].every(({ entry }) => !unfit(entry));
|
|
126
|
-
const head = element("div", { class: "ge-conflicts-head" }, element("span", { text: conflicts ? t("{0} 处冲突待处理", cards.size) : t("{0} 处内容待处理", cards.size) }),
|
|
126
|
+
const head = element("div", { class: "ge-conflicts-head" }, element("span", { text: conflicts ? t("{0} 处冲突待处理", cards.size) : t("{0} 处内容待处理", cards.size) }), textButton(t("收起"), () => { panel.hidden = true; }, { quiet: true }));
|
|
127
127
|
panel.replaceChildren(head, ...[...cards.values()].map(({ node }) => node));
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -139,7 +139,7 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
|
|
|
139
139
|
const buttons = element("div", { class: "ge-actions" });
|
|
140
140
|
const quiet = element("span", { class: "ge-conflict-quiet" }); // looking, not deciding: on the right
|
|
141
141
|
for (const [label, action, primary] of actions) {
|
|
142
|
-
buttons.append(
|
|
142
|
+
buttons.append(textButton(label, async () => {
|
|
143
143
|
if (action === "copy-block") {
|
|
144
144
|
const node = nodeOf(entry);
|
|
145
145
|
if (!node?.isConnected || !copyBlock(node)) say(t("没能完成,请再试一次。"), "error");
|
|
@@ -166,16 +166,16 @@ export function createConflicts({ act, nodeOf, restorable, copyBlock, say }) {
|
|
|
166
166
|
if (diff && mine && theirs) {
|
|
167
167
|
const legend = element("div", { class: "ge-conflict-label" }, element("span", { class: "ge-chip", "data-mine": "", text: t("你的") }), element("span", { class: "ge-chip", "data-theirs": "", text: t("AI 的") }));
|
|
168
168
|
const view = element("div", { hidden: "" }, legend, element("div", { class: "ge-version" }));
|
|
169
|
-
const toggle =
|
|
169
|
+
const toggle = textButton(t("查看局部差异"), () => {
|
|
170
170
|
view.hidden = !view.hidden;
|
|
171
171
|
versions.hidden = !view.hidden;
|
|
172
172
|
toggle.textContent = view.hidden ? t("查看局部差异") : t("查看两个版本");
|
|
173
173
|
if (!view.hidden) view.lastElementChild.replaceChildren(...diffNodes(mine, theirs));
|
|
174
|
-
}, {
|
|
174
|
+
}, { quiet: true });
|
|
175
175
|
quiet.append(toggle);
|
|
176
176
|
node.append(view);
|
|
177
177
|
}
|
|
178
|
-
if (nodeOf(entry)) quiet.append(
|
|
178
|
+
if (nodeOf(entry)) quiet.append(textButton(t("定位"), () => locate(entry.key), { quiet: true }));
|
|
179
179
|
if (quiet.childElementCount > 0) buttons.append(quiet);
|
|
180
180
|
node.append(buttons);
|
|
181
181
|
return node;
|
|
@@ -40,7 +40,7 @@ const WRAPPERS = new Set(["UL", "OL", "BLOCKQUOTE"]);
|
|
|
40
40
|
|
|
41
41
|
// An inline element of the report's own, as the scanner judges it (source/blocks.mjs): its
|
|
42
42
|
// attributes as the file writes them (not the page's data-ge-* markers).
|
|
43
|
-
const isOwnAtom =
|
|
43
|
+
const isOwnAtom =(node) => ownInline(node.localName, [...node.attributes].filter(({ name }) => !MARKER.test(name)), node.textContent.trim() !== "");
|
|
44
44
|
|
|
45
45
|
// A block's atoms in `root` (its source parsed), in the order of the file, as the scanner lists
|
|
46
46
|
// them (source/blocks.mjs inlineMode): the outermost formulas, links shown with their icon and inline
|
|
@@ -88,6 +88,7 @@ export function prepareBlock(element, { inner, atoms, kind = "flow", caps = [] }
|
|
|
88
88
|
const template = element.ownerDocument.createElement("template");
|
|
89
89
|
template.innerHTML = inner;
|
|
90
90
|
template.content.querySelectorAll("*").forEach((node) => node.setAttribute("data-ge-src", ""));
|
|
91
|
+
HOSTS[kind]?.pictures?.show(template.content); // a cell's pictures shown where the server serves them
|
|
91
92
|
const sourceAtoms = atomsIn(template.content, kind === "text" && caps.includes("plain"));
|
|
92
93
|
const drawnAtoms = sourceAtoms.filter((atom) => atom.matches(ATOM_SELECTOR)); // a formula as the kit drew it
|
|
93
94
|
// The blocks a list item holds are blocks of their own: only its text before them is rebuilt.
|
|
@@ -342,15 +343,16 @@ export function matchesSource(element, inner) {
|
|
|
342
343
|
export function serializeInner(element) {
|
|
343
344
|
const inner = HOSTS[element.getAttribute?.("data-ge-kind")]?.inner;
|
|
344
345
|
if (inner) return inner(element);
|
|
345
|
-
// A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own.
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
// A table cell's lists are its text (单元格里的列表); any other block's are blocks of their own. A
|
|
347
|
+
// kind of text block that holds pictures (a cell's: HOSTS `pictures`) writes them; any other none.
|
|
348
|
+
const kind = element.getAttribute?.("data-ge-kind");
|
|
349
|
+
return serializeNodes(ownNodes(element), kind === "cell", HOSTS[kind]?.pictures ?? null).replace(/<br>\s*$/, "");
|
|
348
350
|
}
|
|
349
351
|
|
|
350
352
|
// A list in a table cell, its items' inline content (a line break holding an empty item's caret left out).
|
|
351
|
-
function serializeList(list) {
|
|
353
|
+
function serializeList(list, pictures = null) {
|
|
352
354
|
const tag = list.tagName.toLowerCase();
|
|
353
|
-
const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${serializeNodes([...item.childNodes]).replace(/<br>\s*$/, "")}</li>`);
|
|
355
|
+
const items = [...list.children].filter((item) => item.tagName === "LI").map((item) => `<li>${serializeNodes([...item.childNodes], false, pictures).replace(/<br>\s*$/, "")}</li>`);
|
|
354
356
|
return items.length > 0 ? `<${tag}>${items.join("")}</${tag}>` : "";
|
|
355
357
|
}
|
|
356
358
|
|
|
@@ -438,7 +440,7 @@ const spaced = (text) => escapeTyped(text).replace(/ *\u00a0[ \u00a0]*/g, " ");
|
|
|
438
440
|
// shows where they were, changes/) are no content: left out, the text on either side of one is
|
|
439
441
|
// written as one text, as it was before the node split it (a space either side of it the one space
|
|
440
442
|
// it was, never two).
|
|
441
|
-
function serializeNodes(nodes, lists = false) {
|
|
443
|
+
function serializeNodes(nodes, lists = false, pictures = null) {
|
|
442
444
|
let html = "";
|
|
443
445
|
let text = null; // a text node's, not written yet: the text after an editor's node joins it
|
|
444
446
|
let across = false; // an editor's node stands between it and what comes next
|
|
@@ -456,17 +458,20 @@ function serializeNodes(nodes, lists = false) {
|
|
|
456
458
|
if (text !== null) html += spaced(text);
|
|
457
459
|
text = null;
|
|
458
460
|
across = false;
|
|
459
|
-
html += serializeNode(node, lists);
|
|
461
|
+
html += serializeNode(node, lists, pictures);
|
|
460
462
|
}
|
|
461
463
|
return text === null ? html : html + spaced(text);
|
|
462
464
|
}
|
|
463
465
|
|
|
464
|
-
|
|
466
|
+
// `pictures`: the host's (HOSTS `pictures`: write(element) its markup, "" for one of no file, null for no picture).
|
|
467
|
+
function serializeNode(node, lists = false, pictures = null) {
|
|
465
468
|
// A text node holding nothing but a zero-width space keeps the caret after a mark the editor
|
|
466
469
|
// made (markdown/, typing shortcuts); it is never content.
|
|
467
470
|
if (node.nodeType === 3) return node.data === ZWSP ? "" : spaced(node.data);
|
|
468
471
|
if (node.nodeType !== 1) return "";
|
|
469
|
-
if (isList(node)) return lists ? serializeList(node) : "";
|
|
472
|
+
if (isList(node)) return lists ? serializeList(node, pictures) : "";
|
|
473
|
+
const picture = pictures?.write(node) ?? null;
|
|
474
|
+
if (picture !== null) return picture;
|
|
470
475
|
const atom = node.getAttribute("data-ge-atom");
|
|
471
476
|
if (atom !== null && ATOM_SOURCE.has(atom)) return ATOM_SOURCE.get(atom);
|
|
472
477
|
// Elements from the source keep the author's tag and attributes; only elements the browser
|
|
@@ -475,7 +480,7 @@ function serializeNode(node, lists = false) {
|
|
|
475
480
|
// and their text kept.
|
|
476
481
|
const fromSource = node.hasAttribute("data-ge-src") && node.tagName in TAGS;
|
|
477
482
|
const tag = fromSource ? node.tagName.toLowerCase() : TAGS[node.tagName];
|
|
478
|
-
const inner = serializeNodes([...node.childNodes]);
|
|
483
|
+
const inner = serializeNodes([...node.childNodes], false, pictures);
|
|
479
484
|
const ink = node.tagName === "SPAN" ? [...node.classList].filter(isInkClass) : [];
|
|
480
485
|
if (ink.length > 0) return inner ? `<span class="${ink.join(" ")}">${inner}</span>` : "";
|
|
481
486
|
// Small text's end part (its Tab: components/paragraph/page.mjs), at the line's right end.
|
|
@@ -574,6 +579,9 @@ export function newTail(container, type) {
|
|
|
574
579
|
// New blocks split off `from` are paragraphs, or list items in a list.
|
|
575
580
|
const newBlock = (doc, tag, from) => newHost(doc, tag === "li" ? "item" : "paragraph", from);
|
|
576
581
|
|
|
582
|
+
/** A new, empty paragraph, as a split makes one outside a list (the page's title's Enter: typing/ enterBody). */
|
|
583
|
+
export const newParagraphHost = (doc) => newBlock(doc, "p", null);
|
|
584
|
+
|
|
577
585
|
// The element a text block made on the page is part of when it is not a block of its own: the
|
|
578
586
|
// summary of a new disclosure, a new code block (its code's pre, its title's block), the header of a
|
|
579
587
|
// new detail card.
|
|
@@ -720,10 +728,12 @@ const isFormula = (node) => node.matches(".doc-math-inline");
|
|
|
720
728
|
* text with the format bar's marks (marks: false drops them, links: false drops links), a formula
|
|
721
729
|
* of this page as its chip (another one, KaTeX's or the kit's \(…\), as the chip formula(tex) makes,
|
|
722
730
|
* or else as its TeX source; `formulas` false, each as its source), a link shown with its icon as the
|
|
723
|
-
* chip mention(mentionOf(it)) makes (else a link of its title)
|
|
724
|
-
*
|
|
731
|
+
* chip mention(mentionOf(it)) makes (else a link of its title), an element what picture(element)
|
|
732
|
+
* makes of it (a cell's picture: page/table/pictures.mjs; null nothing, undefined no picture: none
|
|
733
|
+
* given, no picture is one); blocks and line breaks start new lines, and everything else gives only
|
|
734
|
+
* its text.
|
|
725
735
|
*/
|
|
726
|
-
export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null } = {}) {
|
|
736
|
+
export function pastedLines(doc, html, { marks = true, links = true, formula = null, formulas = true, mention = null, picture = null } = {}) {
|
|
727
737
|
const template = doc.createElement("template");
|
|
728
738
|
template.innerHTML = html;
|
|
729
739
|
const lines = [[]];
|
|
@@ -777,6 +787,12 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
|
|
|
777
787
|
}
|
|
778
788
|
}
|
|
779
789
|
if (/^(STYLE|SCRIPT|TEMPLATE|META|TITLE|LINK|NOSCRIPT|BUTTON|SVG)$/i.test(child.tagName) || child.matches('[aria-hidden="true"], .ge-code-gutter, [data-doc-dev]')) continue;
|
|
790
|
+
// A picture, where `picture` takes it (undefined: no picture): what it makes of it, else nothing.
|
|
791
|
+
const made = picture?.(child);
|
|
792
|
+
if (made !== undefined) {
|
|
793
|
+
if (made) push(target, made);
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
780
796
|
if (child.tagName === "BR") {
|
|
781
797
|
if (target) push(target, doc.createTextNode(" "));
|
|
782
798
|
else newLine();
|
|
@@ -794,7 +810,7 @@ export function pastedLines(doc, html, { marks = true, links = true, formula = n
|
|
|
794
810
|
continue;
|
|
795
811
|
}
|
|
796
812
|
walk(child, mark);
|
|
797
|
-
if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]")) push(target, mark);
|
|
813
|
+
if (mark.textContent.trim() || mark.querySelector("[data-ge-atom]") || (picture && mark.firstElementChild)) push(target, mark);
|
|
798
814
|
else if (mark.textContent) push(target, doc.createTextNode(mark.textContent));
|
|
799
815
|
}
|
|
800
816
|
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { startBoard } from "./board/index.mjs";
|
|
2
|
+
import { relayTo } from "./notices.mjs";
|
|
3
|
+
import { connectWorkbench } from "./session.mjs";
|
|
4
|
+
|
|
5
|
+
// A custom page inside a workbench (src/server/routes.mjs serves it at /pages/<slug> with this module;
|
|
6
|
+
// docs/workbench.md "Custom pages"): no document editor, the session, by which the workbench shows it
|
|
7
|
+
// as it shows a document's page, and its boards' editing (./board/: the cards moved, resized, added,
|
|
8
|
+
// taken out, their settings and their blocks' writes, through the page service, by the page map the
|
|
9
|
+
// server writes in its head), which shows the page again only once nothing of it waits. Its session's
|
|
10
|
+
// slug is "@<slug>", the page's address in the workbench (#@<slug>), which no document's slug can be.
|
|
11
|
+
// It answers the session's own commands (theme, keys, scroll-top: src/editor/page/session.mjs),
|
|
12
|
+
// prepare-close (once the board's ops sent are answered: each is written at once, nothing else waits),
|
|
13
|
+
// add-card (the top bar's 添加自定义块: the library of the page's first board, under the button's place that
|
|
14
|
+
// the workbench sends) and, on the home page, restore-defaults (恢复默认卡片, the sidebar's: the board's, one step of its
|
|
15
|
+
// undo; its answer { ok, added, skipped } the workbench says) and undo-restore ({ cards }: that step
|
|
16
|
+
// undone while it is the board's last, the workbench's 撤销),
|
|
17
|
+
// and sends ready, reload (its live script heard that page.html or page.json changed, or a block it
|
|
18
|
+
// holds: the workbench loads it again in a hidden frame and swaps it in once drawn), open (a link to a
|
|
19
|
+
// document or another page, or a block's context.workspace.open: doc-open-request) and key. Opened on its own (not in a frame of the workbench), it has no
|
|
20
|
+
// session: its board edits all the same, and the live script reloads it.
|
|
21
|
+
|
|
22
|
+
const slug = decodeURIComponent(location.pathname.split("/")[2] ?? "");
|
|
23
|
+
const IDLE = { editing: false, pending: false, saving: false, offline: false, conflicts: 0, unfit: 0, changed: false };
|
|
24
|
+
// Nothing stands over a custom page as a modal.
|
|
25
|
+
const NO_MODALS = { open: () => [], listen() {}, closeTop: () => false };
|
|
26
|
+
|
|
27
|
+
let board = null;
|
|
28
|
+
const session = connectWorkbench({
|
|
29
|
+
slug: `@${slug}`,
|
|
30
|
+
snapshot: () => ({ title: document.title, icon: null, state: IDLE }),
|
|
31
|
+
commands: {
|
|
32
|
+
"prepare-close": async () => {
|
|
33
|
+
await board?.idle();
|
|
34
|
+
return { ok: true };
|
|
35
|
+
},
|
|
36
|
+
// 添加自定义块 of the workbench's top bar: the library opens in the page, at `anchor` (the button's place in
|
|
37
|
+
// this frame's coordinates; none: in the middle of the window), the first board's.
|
|
38
|
+
"add-card": async ({ anchor } = {}) => (board ? board.addCard({ anchor }) : { ok: false, reason: "board" }),
|
|
39
|
+
...(slug === "home" ? {
|
|
40
|
+
"restore-defaults": async () => (board ? board.restoreDefaults() : { ok: false, reason: "board" }),
|
|
41
|
+
"undo-restore": async ({ cards }) => (board ? board.undoRestore(cards) : { ok: false, reason: "board" }),
|
|
42
|
+
} : {}),
|
|
43
|
+
},
|
|
44
|
+
modals: NO_MODALS,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// The page map (routes.mjs: its slug, its token, the version it was served at, its blocks): the boards edit.
|
|
48
|
+
let map = null;
|
|
49
|
+
try {
|
|
50
|
+
map = JSON.parse(document.querySelector("script[data-ge-page]")?.textContent ?? "null");
|
|
51
|
+
} catch {}
|
|
52
|
+
board = map?.token ? startBoard({ map, reload: () => (session ? session.reload() : location.reload()), hosted: Boolean(session) }) : null;
|
|
53
|
+
// The live script (routes.mjs) shows the page again through the workbench, which swaps a new page in
|
|
54
|
+
// once drawn: no reload seen; once the board's own writes, a drag, a form or a field typed in are done.
|
|
55
|
+
if (board) window.__galleryEditReload = () => board.swap();
|
|
56
|
+
|
|
57
|
+
if (session) {
|
|
58
|
+
// Its notices (a card someone else changed) are the workbench's toasts, as a document's page's are.
|
|
59
|
+
relayTo(session.notice, session.noticeRoom);
|
|
60
|
+
if (!board) window.__galleryEditReload = () => session.reload();
|
|
61
|
+
|
|
62
|
+
// A block's context.workspace.open (the kit's runtime/workspace.js: 我的待办's item, 最近编辑's
|
|
63
|
+
// document): a document, at its block where it names one, or a custom page, opened in the workbench.
|
|
64
|
+
addEventListener("doc-open-request", (event) => {
|
|
65
|
+
const { slug = null, block = null, page = null } = event.detail ?? {};
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
session.open(page ? { hash: `@${page}` } : { target: slug, block });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// A link to a document or a page of the workbench goes there in the workbench, never in this frame:
|
|
71
|
+
// a workbench's address (#<link>, #@<slug>, /#…), a document's page (/generated/<slug>.html) or its
|
|
72
|
+
// file (../<slug>/doc.html), a custom page's served path (/pages/<slug>, the page alone that gallery
|
|
73
|
+
// page open gives, or written relative to this one: "reading" from /pages/home). Another site's
|
|
74
|
+
// opens in a tab of its own.
|
|
75
|
+
addEventListener("click", (event) => {
|
|
76
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
77
|
+
const link = event.target instanceof Element ? event.target.closest("a[href]") : null;
|
|
78
|
+
if (!link || link.hasAttribute("download")) return;
|
|
79
|
+
const raw = link.getAttribute("href") ?? "";
|
|
80
|
+
// A place in this page (an element of that id) is the browser's to go to.
|
|
81
|
+
if (raw.startsWith("#") && raw.length > 1 && document.getElementById(decodeURIComponent(raw.slice(1)))) return;
|
|
82
|
+
let url;
|
|
83
|
+
try {
|
|
84
|
+
url = new URL(raw, location.href);
|
|
85
|
+
} catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (url.origin !== location.origin) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
window.open(url.href, "_blank", "noopener");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const custom = /^\/pages\/([a-z0-9-]+)\/?$/.exec(url.pathname)?.[1];
|
|
94
|
+
const page = /^\/generated\/([a-z0-9-]+)\.html$/.exec(url.pathname)?.[1] ?? /^\/([a-z0-9-]+)\/(?:doc|report)\.html$/.exec(url.pathname)?.[1];
|
|
95
|
+
if (custom) {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
session.open({ hash: `@${custom}` });
|
|
98
|
+
} else if (page) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
session.open({ target: page, block: url.hash.slice(1) || null });
|
|
101
|
+
} else if ((url.pathname === "/" || raw.startsWith("#")) && url.hash.length > 1) {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
session.open({ hash: decodeURIComponent(url.hash.slice(1)) });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|