@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,458 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { writeAtomic } from "./files.mjs";
|
|
5
|
+
import { extensionColumnIds } from "./extensions.mjs";
|
|
6
|
+
import { DOCS, entryPattern, extensionGroupId, GROUP_LIMITS, groupOf, isForeign, isTop, newGroupId, topIn, topOf, validGroups } from "./outline-shape.mjs";
|
|
7
|
+
import { shownWorkspaces, snapshotOf } from "./workspace-columns.mjs";
|
|
8
|
+
import { layoutOf, listDocumentSlugs, listLinkSlugs, pageSlug } from "./workspace.mjs";
|
|
9
|
+
|
|
10
|
+
// The workspace's outline: the sidebar's columns and which entry sits under which, in what order
|
|
11
|
+
// (<workspace>/.gallery/outline.json, docs/workbench.md "Workspace commands"; its shape: ./outline-shape.mjs).
|
|
12
|
+
// The file, version 2:
|
|
13
|
+
// { version: 2, children: { "": [the 文档 column's top], "<slug>": [entries under it] },
|
|
14
|
+
// groups: [{ id: "docs", title? }, { id, title?, role?, workspace?, children: { "": [its top], "<slug>": […] } }] }
|
|
15
|
+
// The 文档 column's tree is the top-level `children`, as in version 1 ({ version: 1, children }, the
|
|
16
|
+
// 文档 column alone, read as such), so that an older Gallery reads the documents' order as before.
|
|
17
|
+
// Hierarchy and order only: an entry's folder never moves with it, and no file of a document names
|
|
18
|
+
// where it is (links between documents are relative links between their files). Read, it lists every
|
|
19
|
+
// document and web link of the workspace once but the 写作偏好 page (listed apart, in .gallery/pages/):
|
|
20
|
+
// entries that are gone drop out, documents it does not know yet come last in the own column (文档's
|
|
21
|
+
// place, top key ""), links last in a column of the links an older Gallery made (else in the own column).
|
|
22
|
+
// Every other registered workspace has a column (./workspace-columns.mjs; the plan §7.2): one the file
|
|
23
|
+
// holds not yet is made as it is read (the default workspace's right after the own column, the others
|
|
24
|
+
// last), and a "whole" column (not `picked`) holds every document of its workspace the outline holds not
|
|
25
|
+
// but those the user took out (its `left`): under its own parent's entry when the outline holds that,
|
|
26
|
+
// else last in the column, the first time laid out as its own sidebar has them. A draft of another
|
|
27
|
+
// workspace ("~<ws id>/<slug>": ./drafts.mjs) stays where the file has it while its document is there;
|
|
28
|
+
// one gone from a workspace the sidebar shows leaves, one of a workspace it does not show (not
|
|
29
|
+
// registered, a clash of ids, its folder away) stays, shown grey (找不到底稿), until it is removed; one of a
|
|
30
|
+
// workspace whose folder did not read this time (an error of the disk: otherWorkspaces' `failed`) stays
|
|
31
|
+
// too, and its column is not filled. A column the sidebar does not draw (the user removed it, `hidden`;
|
|
32
|
+
// its workspace not shown) keeps its workspace's drafts alone: any other entry there (a document of this
|
|
33
|
+
// workspace, a web link, a draft of a third, dragged in) comes last in the own column with what is under it,
|
|
34
|
+
// so that nothing the user put there is out of sight. `others: false` reads the workspace's tree alone (what
|
|
35
|
+
// another workspace's sidebar reads of it, and what the commands of that workspace's own tree read).
|
|
36
|
+
// The columns extensions provide (extensions/<id>/sidebar.json: ./extensions.mjs) are among the groups as
|
|
37
|
+
// { id: "ext-<id>", extension: "<id>" }, each there while its extension has a sidebar.json: one it has not
|
|
38
|
+
// got yet comes last (not written until the outline is), one whose extension is gone drops out, and what an
|
|
39
|
+
// outline says of its entries is passed over (an extension's entries are its own, never the outline's).
|
|
40
|
+
// Their place is what the outline keeps of them.
|
|
41
|
+
// Written by the workspace's server (src/workbench/service.mjs: the workbench's moves and columns, a new,
|
|
42
|
+
// copied, restored or imported document, a new link, gallery new --under, gallery move and gallery link
|
|
43
|
+
// add while it runs, in the queue its saves go through), and by a gallery command when no server runs
|
|
44
|
+
// (src/cli/tree.mjs), with the same functions, each change read, made and written under the outline's
|
|
45
|
+
// lock (withOutlineLock: a file that two processes cannot both make), so that two writers never lose
|
|
46
|
+
// each other's change.
|
|
47
|
+
|
|
48
|
+
/** The outline file's lock (.gallery/outline.lock): who holds it, until when it counts. */
|
|
49
|
+
const LOCK_STALE = 10_000; // a lock older than this was left by a process that ended: taken over
|
|
50
|
+
const LOCK_WAIT = 15_000;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Run `task` holding the outline's lock of workspace `root`: a file made with "wx", which one
|
|
54
|
+
* process alone can make, removed after; one older than 10 s is a crashed writer's and is taken over.
|
|
55
|
+
* Waits for it 15 s at most, then takes it all the same (a writer that holds it that long holds
|
|
56
|
+
* nothing any more).
|
|
57
|
+
*/
|
|
58
|
+
export async function withOutlineLock(root, task) {
|
|
59
|
+
const { gallery } = layoutOf(root);
|
|
60
|
+
const lock = path.join(gallery, "outline.lock");
|
|
61
|
+
const mine = `${process.pid} ${randomBytes(4).toString("hex")}`;
|
|
62
|
+
await fs.mkdir(gallery, { recursive: true });
|
|
63
|
+
for (const until = Date.now() + LOCK_WAIT; ;) {
|
|
64
|
+
try {
|
|
65
|
+
await fs.writeFile(lock, mine, { flag: "wx" });
|
|
66
|
+
break;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (error.code !== "EEXIST") throw error;
|
|
69
|
+
const age = await fs.stat(lock).then(({ mtimeMs }) => Date.now() - mtimeMs, () => 0);
|
|
70
|
+
if (age > LOCK_STALE || Date.now() > until) await fs.rm(lock, { force: true });
|
|
71
|
+
else await new Promise((resolve) => setTimeout(resolve, 20));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return await task();
|
|
76
|
+
} finally {
|
|
77
|
+
// Removed only while it is still this writer's (one taken over meanwhile is the next writer's).
|
|
78
|
+
if ((await fs.readFile(lock, "utf8").catch(() => null)) === mine) await fs.rm(lock, { force: true });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const readJson = (file) => fs.readFile(file, "utf8").then(JSON.parse).catch(() => null);
|
|
83
|
+
|
|
84
|
+
/** The outline of workspace `root` as it is read: every entry once (above). `others`: with the other workspaces' columns. */
|
|
85
|
+
export async function readOutline(root, { others = true } = {}) {
|
|
86
|
+
return (await outlineAndFile(root, { others })).outline;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The other workspaces the sidebar of workspace `root` shows a column of, each with what its own sidebar
|
|
91
|
+
* holds (./workspace-columns.mjs shownWorkspaces, snapshotOf): [{ id, root, isDefault, documents, order,
|
|
92
|
+
* parentOf, facts, failed }]. `failed`: its folder did not read this time (EPERM, EBUSY, EIO: a network or
|
|
93
|
+
* removable drive), none of its documents known, so that the outline drops none of its drafts for it.
|
|
94
|
+
*/
|
|
95
|
+
export async function otherWorkspaces(root, { env = process.env } = {}) {
|
|
96
|
+
const shown = shownWorkspaces(root, { env });
|
|
97
|
+
const readOwn = (other) => readOutline(other, { others: false });
|
|
98
|
+
const unread = () => ({ documents: new Set(), order: [], parentOf: new Map(), facts: new Map(), failed: true });
|
|
99
|
+
return Promise.all(shown.map(async (each) => ({ ...each, failed: false, ...(await snapshotOf(each.root, readOwn).catch(unread)) })));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The outline as it is read and what its file holds: { outline: { version: 2, groups, children,
|
|
104
|
+
* links: the slugs of its web links, drafts: the entries of other workspaces' drafts, shown: the other
|
|
105
|
+
* workspaces whose columns it shows ([{ id, root, isDefault }]: otherWorkspaces) }, stored: the file's
|
|
106
|
+
* columns and children as the outline has them (storedOf), or null; others: otherWorkspaces' answer }. A
|
|
107
|
+
* change is written when what it makes is not what the file holds (writtenAs), a document or a link the
|
|
108
|
+
* file does not hold yet too (one made since, placed only as it is read), so that the file keeps every
|
|
109
|
+
* place a command or the workbench gave. `others: false`: the workspace's own tree alone (no column of
|
|
110
|
+
* another workspace made, none filled).
|
|
111
|
+
*/
|
|
112
|
+
export async function outlineAndFile(root, { others: withOthers = true } = {}) {
|
|
113
|
+
const instructions = await pageSlug(root);
|
|
114
|
+
const documents = (await listDocumentSlugs(root)).filter((slug) => slug !== instructions);
|
|
115
|
+
const links = await listLinkSlugs(root);
|
|
116
|
+
const extensions = await extensionColumnIds(root);
|
|
117
|
+
const stored = storedOf(await readJson(layoutOf(root).outline));
|
|
118
|
+
const others = withOthers ? await otherWorkspaces(root) : [];
|
|
119
|
+
const shown = ordered(stored, documents, links, extensions, others, { drawn: withOthers });
|
|
120
|
+
const outline = {
|
|
121
|
+
version: 2, ...shown, links, drafts: Object.values(shown.children).flat().filter(isForeign),
|
|
122
|
+
shown: others.map(({ id, root: other, isDefault }) => ({ id, root: other, isDefault })),
|
|
123
|
+
};
|
|
124
|
+
return { outline, stored, others };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// The own column's name before it was 默认文档 or the workspace's (文档, Documents in English), which a
|
|
128
|
+
// rename of it confirmed unchanged wrote as its title (the field opens with the name shown): read as no
|
|
129
|
+
// title, so that the name it goes by now shows. A title of another column, or any other, is the user's.
|
|
130
|
+
const OLD_DOCS_NAMES = ["文档", "Documents"];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* What outline.json's value `file` holds, as the outline has it: { groups, children } (the 文档 column's
|
|
134
|
+
* tree from the file's own `children`, every other column's from its `children`, its top under its top
|
|
135
|
+
* key), or null for a file that is not there or not an outline. A file of version 1 is the 文档 column alone.
|
|
136
|
+
* The own column titled with its old name (OLD_DOCS_NAMES) has none.
|
|
137
|
+
*/
|
|
138
|
+
export function storedOf(file) {
|
|
139
|
+
if (!file || typeof file !== "object" || Array.isArray(file)) return null;
|
|
140
|
+
const groups = [];
|
|
141
|
+
const children = {};
|
|
142
|
+
const take = (id, own) => {
|
|
143
|
+
if (!own || typeof own !== "object" || Array.isArray(own)) return;
|
|
144
|
+
for (const [key, list] of Object.entries(own)) {
|
|
145
|
+
if (!Array.isArray(list)) continue;
|
|
146
|
+
const at = key === "" ? topOf(id) : key;
|
|
147
|
+
if (!children[at]) children[at] = list.filter((entry) => typeof entry === "string");
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
for (const value of Array.isArray(file.groups) ? file.groups : []) {
|
|
151
|
+
const group = groupOf(value);
|
|
152
|
+
if (!group || groups.some(({ id }) => id === group.id)) continue;
|
|
153
|
+
if (group.id === DOCS && OLD_DOCS_NAMES.includes(group.title)) delete group.title;
|
|
154
|
+
groups.push(group);
|
|
155
|
+
take(group.id, group.id === DOCS ? file.children : value.children);
|
|
156
|
+
}
|
|
157
|
+
if (!groups.some(({ id }) => id === DOCS)) {
|
|
158
|
+
groups.unshift({ id: DOCS });
|
|
159
|
+
take(DOCS, file.children);
|
|
160
|
+
}
|
|
161
|
+
return { groups, children };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The keys of `children` whose lists are not empty: what two outlines are compared by. */
|
|
165
|
+
const lists = (children) => Object.keys(children ?? {}).filter((key) => Array.isArray(children[key]) && children[key].length > 0);
|
|
166
|
+
|
|
167
|
+
/** The part of `children` the 文档 column holds: its top and the lists under its entries. */
|
|
168
|
+
function docsPart(children) {
|
|
169
|
+
const part = {};
|
|
170
|
+
const walk = (key) => {
|
|
171
|
+
const list = children?.[key];
|
|
172
|
+
if (!Array.isArray(list) || list.length === 0 || Object.hasOwn(part, key)) return;
|
|
173
|
+
part[key] = list;
|
|
174
|
+
list.forEach(walk);
|
|
175
|
+
};
|
|
176
|
+
walk("");
|
|
177
|
+
return part;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Whether outline `value` ({ groups?, children }) is what `stored` holds (outlineAndFile's, or an outline
|
|
182
|
+
* read): the same columns in the same order, and the same lists (an empty one as none), in whatever order
|
|
183
|
+
* the keys come. One without `groups` (an outline of version 1: a test's, an older workbench's) is held
|
|
184
|
+
* to the 文档 column alone. Also whether an outline a workbench changed (POST outline's `base`) is the one read now.
|
|
185
|
+
*/
|
|
186
|
+
export function writtenAs(value, stored) {
|
|
187
|
+
if (!stored || typeof stored !== "object" || !value || typeof value !== "object") return false;
|
|
188
|
+
const ours = value.children ?? {};
|
|
189
|
+
const theirs = value.groups ? stored.children : docsPart(stored.children);
|
|
190
|
+
const [a, b] = [lists(ours), lists(theirs)];
|
|
191
|
+
if (a.length !== b.length || !a.every((key) => JSON.stringify(ours[key]) === JSON.stringify(theirs[key]))) return false;
|
|
192
|
+
if (!value.groups) return true;
|
|
193
|
+
const same = (groups) => JSON.stringify((groups ?? []).map(groupOf));
|
|
194
|
+
return same(value.groups) === same(stored.groups);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* `groups` with the columns of the extensions `ids` (those with a sidebar.json): the extension columns the
|
|
199
|
+
* outline kept stay where it put them, once each and only while their extension is there; an extension
|
|
200
|
+
* with none comes last, while there is room (50 columns at most: validGroups, or no change of the outline
|
|
201
|
+
* would be written).
|
|
202
|
+
*/
|
|
203
|
+
function withExtensions(groups, ids) {
|
|
204
|
+
const wanted = new Set(ids);
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
const kept = groups.filter((group) => {
|
|
207
|
+
if (!group.extension) return true;
|
|
208
|
+
if (!wanted.has(group.extension) || seen.has(group.extension)) return false;
|
|
209
|
+
seen.add(group.extension);
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
212
|
+
const coming = ids.filter((id) => !seen.has(id)).map((id) => ({ id: extensionGroupId(id), extension: id }));
|
|
213
|
+
return [...kept, ...coming.slice(0, Math.max(0, GROUP_LIMITS.groups - kept.length))];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* `groups` with a column for each of the workspaces `others` (otherWorkspaces') that none has yet (hidden or
|
|
218
|
+
* not): a whole one (no `picked`), its id the workspace's when no column has that, the default workspace's
|
|
219
|
+
* right after the own column, the others last, in the registry's order; `limit` columns at most (the room
|
|
220
|
+
* the extensions' columns to come leave of the 50).
|
|
221
|
+
*/
|
|
222
|
+
export function withWorkspaces(groups, others, limit = GROUP_LIMITS.groups) {
|
|
223
|
+
const result = [...groups];
|
|
224
|
+
const lacking = others.filter(({ id }) => !result.some((group) => group.workspace === id));
|
|
225
|
+
for (const { id, isDefault } of lacking) {
|
|
226
|
+
if (result.length >= limit) break; // 50 columns at most (validGroups)
|
|
227
|
+
const column = { id: result.some((group) => group.id === id) ? newGroupId(result) : id, workspace: id };
|
|
228
|
+
if (isDefault) result.splice(result.findIndex((group) => group.id === DOCS) + 1, 0, column);
|
|
229
|
+
else result.push(column);
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const DRAFT = /^~(ws-[0-9a-f]{6})\/(.+)$/;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* The outline of the entries `documents` and `links` (the web links), in the order `stored` (storedOf's)
|
|
238
|
+
* gives, with the columns of the other workspaces `others` (otherWorkspaces'): { groups, children }, every
|
|
239
|
+
* column's top key there (an empty list for an empty column). Each entry once, under the first place it
|
|
240
|
+
* has; one of this workspace's that is gone is left out, a draft of another workspace ("~<ws id>/<slug>")
|
|
241
|
+
* left out when its workspace is among `others` and holds it no more (its folder read), else kept wherever
|
|
242
|
+
* the file has it. A document no column holds comes last in the own column; a link, last in a column of the
|
|
243
|
+
* links (role "links", an older Gallery's), else last in the own column; a document of another workspace
|
|
244
|
+
* whose column is whole, under its own parent's entry when that is held, else last in that column, in its
|
|
245
|
+
* own sidebar's order, but one the user took out of it (the column's `left`, kept to those still there).
|
|
246
|
+
* `drawn` (the sidebar's read, not a workspace's own tree alone): a workspace's column the sidebar does not
|
|
247
|
+
* draw (`hidden`, or its workspace not among `others`) keeps that workspace's drafts alone, any other entry
|
|
248
|
+
* there coming last in the own column with what is under it.
|
|
249
|
+
*/
|
|
250
|
+
function ordered(stored, documents, links, extensions = [], others = [], { drawn = false } = {}) {
|
|
251
|
+
const listed = new Set([...documents, ...links]);
|
|
252
|
+
const shownById = new Map(others.map((each) => [each.id, each]));
|
|
253
|
+
const known = {
|
|
254
|
+
has: (slug) => {
|
|
255
|
+
if (listed.has(slug)) return true;
|
|
256
|
+
if (!isForeign(slug) || !entryPattern.test(slug)) return false;
|
|
257
|
+
const [, ws, own] = DRAFT.exec(slug);
|
|
258
|
+
const other = shownById.get(ws);
|
|
259
|
+
return other && !other.failed ? other.documents.has(own) : true;
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
// A whole column's `left`: those its workspace still has (its folder read), none when none is.
|
|
263
|
+
const kept = (group) => {
|
|
264
|
+
const other = group.left ? shownById.get(group.workspace) : null;
|
|
265
|
+
if (!other || other.failed) return group;
|
|
266
|
+
const { left, ...rest } = group;
|
|
267
|
+
const still = left.filter((slug) => other.documents.has(slug));
|
|
268
|
+
return still.length ? { ...rest, left: still } : rest;
|
|
269
|
+
};
|
|
270
|
+
const base = stored?.groups ?? [{ id: DOCS }];
|
|
271
|
+
// The room of the 50 columns an extension's column to come takes first (one the outline has not got yet).
|
|
272
|
+
const coming = extensions.filter((id) => !base.some((group) => group.extension === id)).length;
|
|
273
|
+
const groups = withExtensions(withWorkspaces(base, others, GROUP_LIMITS.groups - coming), extensions).map(kept);
|
|
274
|
+
const children = Object.fromEntries(groups.map(({ id }) => [topOf(id), []]));
|
|
275
|
+
const seen = new Set();
|
|
276
|
+
// Drawn not: the user removed it, or its workspace is not shown (forgotten, its folder away, a clash of ids).
|
|
277
|
+
const undrawn = (group) => drawn && Boolean(group.workspace) && (group.hidden === true || !shownById.has(group.workspace));
|
|
278
|
+
const astray = []; // entries of a column not drawn that are not its workspace's drafts: last in the own column
|
|
279
|
+
const place = (parent, only = null) => {
|
|
280
|
+
for (const slug of stored?.children?.[parent] ?? []) {
|
|
281
|
+
if (!known.has(slug) || seen.has(slug)) continue;
|
|
282
|
+
if (only && !slug.startsWith(`~${only}/`)) {
|
|
283
|
+
astray.push(slug);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
seen.add(slug);
|
|
287
|
+
(children[parent] ??= []).push(slug);
|
|
288
|
+
place(slug, only);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
for (const group of groups) if (!group.extension) place(topOf(group.id), undrawn(group) ? group.workspace : null);
|
|
292
|
+
for (const slug of astray) {
|
|
293
|
+
if (seen.has(slug)) continue;
|
|
294
|
+
seen.add(slug);
|
|
295
|
+
children[""].push(slug);
|
|
296
|
+
place(slug);
|
|
297
|
+
}
|
|
298
|
+
const linked = groups.find(({ role }) => role === "links");
|
|
299
|
+
for (const slug of documents) if (!seen.has(slug)) children[""].push(slug);
|
|
300
|
+
for (const slug of links) if (!seen.has(slug)) children[linked ? topOf(linked.id) : ""].push(slug);
|
|
301
|
+
for (const other of others) {
|
|
302
|
+
const column = groups.find((group) => group.workspace === other.id);
|
|
303
|
+
if (!column || column.picked || other.failed) continue;
|
|
304
|
+
const left = new Set(column.left ?? []);
|
|
305
|
+
for (const slug of other.order) {
|
|
306
|
+
const entry = `~${other.id}/${slug}`;
|
|
307
|
+
if (seen.has(entry) || left.has(slug)) continue;
|
|
308
|
+
const parent = other.parentOf.get(slug);
|
|
309
|
+
const above = parent ? `~${other.id}/${parent}` : null;
|
|
310
|
+
(children[above !== null && seen.has(above) ? above : topOf(column.id)] ??= []).push(entry);
|
|
311
|
+
seen.add(entry);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return { groups, children };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Whether `value` ({ groups?, children }) is an outline to store: its columns valid (validGroups: 文档
|
|
319
|
+
* among them), each list's key a column's top or an entry, each entry at most once, never under itself.
|
|
320
|
+
* Without `groups` (an outline of version 1) the only top is 文档's ("").
|
|
321
|
+
*/
|
|
322
|
+
export function validOutline(value) {
|
|
323
|
+
const children = value?.children;
|
|
324
|
+
if (!children || typeof children !== "object" || Array.isArray(children)) return false;
|
|
325
|
+
if (value.groups !== undefined && !validGroups(value.groups)) return false;
|
|
326
|
+
const tops = new Set((value.groups ?? [{ id: DOCS }]).map(({ id }) => topOf(id)));
|
|
327
|
+
// An extension's column holds none of the outline's entries: they are the extension's own.
|
|
328
|
+
const extensionTops = new Set((value.groups ?? []).filter((group) => groupOf(group)?.extension).map(({ id }) => topOf(id)));
|
|
329
|
+
const listed = new Set();
|
|
330
|
+
for (const [parent, list] of Object.entries(children)) {
|
|
331
|
+
if (!tops.has(parent) && !entryPattern.test(parent)) return false;
|
|
332
|
+
if (!Array.isArray(list) || (extensionTops.has(parent) && list.length > 0)) return false;
|
|
333
|
+
for (const slug of list) {
|
|
334
|
+
if (typeof slug !== "string" || !entryPattern.test(slug) || listed.has(slug) || slug === parent) return false;
|
|
335
|
+
listed.add(slug);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Outline `value` ({ groups?, children }) as outline.json holds it, version 2: the 文档 column's tree in
|
|
343
|
+
* `children`, where version 1 had the whole tree (an older Gallery reads its documents' order as before:
|
|
344
|
+
* it sees no column, and writes version 1 back, which this one reads as 文档 alone), and every column in
|
|
345
|
+
* `groups`, in the sidebar's order, the others each with its own `children` ("" its top).
|
|
346
|
+
*/
|
|
347
|
+
export function fileOf(value) {
|
|
348
|
+
const groups = value.groups ?? [{ id: DOCS }];
|
|
349
|
+
const own = (top) => {
|
|
350
|
+
const tree = { "": [] };
|
|
351
|
+
const walk = (key, as) => {
|
|
352
|
+
const list = value.children[key];
|
|
353
|
+
if (!Array.isArray(list) || list.length === 0) return;
|
|
354
|
+
tree[as] = [...list];
|
|
355
|
+
for (const entry of list) if (!Object.hasOwn(tree, entry)) walk(entry, entry);
|
|
356
|
+
};
|
|
357
|
+
walk(top, "");
|
|
358
|
+
return tree;
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
version: 2,
|
|
362
|
+
children: own(""),
|
|
363
|
+
groups: groups.map((group) => {
|
|
364
|
+
const kept = groupOf(group);
|
|
365
|
+
return kept.id === DOCS || kept.extension ? kept : { ...kept, children: own(topOf(kept.id)) };
|
|
366
|
+
}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Write outline `value` ({ groups?, children }) of workspace `root` (.gallery/ made when there is none yet). */
|
|
371
|
+
export async function writeOutline(root, value) {
|
|
372
|
+
const file = layoutOf(root).outline;
|
|
373
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
374
|
+
return writeAtomic(file, `${JSON.stringify(fileOf(value), null, 2)}\n`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** The key `slug` sits under in `children` (a column's top key at a top level), or null when the outline holds it not. */
|
|
378
|
+
export function parentIn(children, slug) {
|
|
379
|
+
for (const [parent, list] of Object.entries(children)) if (list.includes(slug)) return parent;
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** The entries above `slug` in `children`, from the top down (itself and its column not among them). */
|
|
384
|
+
export function ancestorsIn(children, slug) {
|
|
385
|
+
const above = [];
|
|
386
|
+
for (let parent = parentIn(children, slug); parent !== null && !isTop(parent) && !above.includes(parent); parent = parentIn(children, parent)) above.unshift(parent);
|
|
387
|
+
return above;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Every entry under `slug` in `children` (a top key: the column's), depth first, in the sidebar's order. */
|
|
391
|
+
export function descendantsIn(children, slug) {
|
|
392
|
+
const found = [];
|
|
393
|
+
const walk = (parent) => (children[parent] ?? []).forEach((child) => { found.push(child); walk(child); });
|
|
394
|
+
walk(slug);
|
|
395
|
+
return found;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Entry `slug` moved in `children` (an outline as read: changed in place) with the entries under it, to
|
|
400
|
+
* where `where` says: `parent` ("" the top level of the column it is in; a column's top key, that
|
|
401
|
+
* column's; undefined: where it is now, or `after`'s parent), right after `after` (one of that parent's),
|
|
402
|
+
* first (`first`), else last. Resolves to { ok: true, moved: whether its place changed, parent, index,
|
|
403
|
+
* from: { parent, index } } or { ok: false, reason }: "missing" (no such entry in the outline), "parent"
|
|
404
|
+
* (no such parent), "after" (no such entry to go after), "level" (`after` is not under `parent`),
|
|
405
|
+
* "itself" (after itself), "inside" (under itself or one of the entries under it).
|
|
406
|
+
*/
|
|
407
|
+
export function moveIn(children, slug, { parent = undefined, after = null, first = false } = {}) {
|
|
408
|
+
const from = parentIn(children, slug);
|
|
409
|
+
if (from === null) return { ok: false, reason: "missing" };
|
|
410
|
+
let to = parent === "" ? topIn(children, slug) ?? "" : parent;
|
|
411
|
+
if (after !== null) {
|
|
412
|
+
if (after === slug) return { ok: false, reason: "itself" };
|
|
413
|
+
const its = parentIn(children, after);
|
|
414
|
+
if (its === null) return { ok: false, reason: "after" };
|
|
415
|
+
if (to !== undefined && to !== its) return { ok: false, reason: "level" };
|
|
416
|
+
to = its;
|
|
417
|
+
}
|
|
418
|
+
to ??= from;
|
|
419
|
+
if (isTop(to) ? !Object.hasOwn(children, to) : parentIn(children, to) === null) return { ok: false, reason: "parent" };
|
|
420
|
+
if (to === slug || ancestorsIn(children, to).includes(slug)) return { ok: false, reason: "inside" };
|
|
421
|
+
const index = children[from].indexOf(slug);
|
|
422
|
+
children[from].splice(index, 1);
|
|
423
|
+
const list = (children[to] ??= []);
|
|
424
|
+
const at = after !== null ? list.indexOf(after) + 1 : first ? 0 : list.length;
|
|
425
|
+
list.splice(at, 0, slug);
|
|
426
|
+
for (const key of Object.keys(children)) if (!isTop(key) && children[key].length === 0) delete children[key];
|
|
427
|
+
return { ok: true, moved: to !== from || at !== index, parent: to, index: at, from: { parent: from, index } };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Move entry `slug` of workspace `root` where `where` says (moveIn), under the outline's lock: the
|
|
432
|
+
* outline read, changed and written when that is not what its file holds (writtenAs: a move, or an
|
|
433
|
+
* entry the file does not hold yet placed where it is read). Resolves to moveIn's answer, with
|
|
434
|
+
* `written`: whether the file was written.
|
|
435
|
+
*/
|
|
436
|
+
export function moveDocument(root, slug, where) {
|
|
437
|
+
return withOutlineLock(root, async () => {
|
|
438
|
+
const { outline, stored } = await outlineAndFile(root);
|
|
439
|
+
const result = moveIn(outline.children, slug, where);
|
|
440
|
+
const written = result.ok && !writtenAs(outline, stored);
|
|
441
|
+
if (written) await writeOutline(root, outline);
|
|
442
|
+
return { ...result, ...(result.ok ? { written } : {}) };
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Entry `slug` put in `outline` (changed in place: on disk already, so that the outline as read lists
|
|
448
|
+
* it somewhere) under `parent` (a column's top key or an entry; one the outline holds not: the 文档
|
|
449
|
+
* column's top) at `index` (the end when past it).
|
|
450
|
+
*/
|
|
451
|
+
export function placeIn(outline, slug, parent = "", index = Infinity) {
|
|
452
|
+
const { children } = outline;
|
|
453
|
+
for (const list of Object.values(children)) if (list.includes(slug)) list.splice(list.indexOf(slug), 1);
|
|
454
|
+
const known = parent !== slug && (isTop(parent) ? Object.hasOwn(children, parent) : Object.values(children).some((list) => list.includes(parent)));
|
|
455
|
+
const list = (children[known ? parent : ""] ??= []);
|
|
456
|
+
list.splice(Math.max(0, Math.min(index, list.length)), 0, slug);
|
|
457
|
+
for (const key of Object.keys(children)) if (!isTop(key) && children[key].length === 0) delete children[key];
|
|
458
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { refusal, said } from "./i18n/index.mjs";
|
|
2
|
+
import { DEVICE_NAMES } from "./workspace.mjs";
|
|
3
|
+
|
|
4
|
+
// A block pack's manifest (自定义块包: notes/plans/block-packs-plan-2026-11-09.md §1; docs/render.md "Block
|
|
5
|
+
// packs"): pack.json at the top of a pack, a folder of components/ whose blocks are its scope's
|
|
6
|
+
// (components/<id>/<name>/, the block's id <id>/<name>), what a pack says of itself to whoever lists it
|
|
7
|
+
// (设置 › 组件列表's pack cards, gallery block pack list) and to whoever installs it (./block-packs.mjs).
|
|
8
|
+
// Plain data, as a block's manifest is: a field it does not know keeps it from reading, so that a pack
|
|
9
|
+
// made for a later Gallery says so rather than half reads.
|
|
10
|
+
//
|
|
11
|
+
// id the scope its blocks are of: lowercase letters, digits and hyphens (my-cards); not
|
|
12
|
+
// local, the workspace's own blocks' scope, nor a name Windows keeps for a device
|
|
13
|
+
// name what a reader calls it: words, or { zh, en } in both languages
|
|
14
|
+
// description what it is, the same way (optional)
|
|
15
|
+
// version x.y.z
|
|
16
|
+
// author who made it, words (optional)
|
|
17
|
+
// homepage where it comes from, an http(s) address (optional)
|
|
18
|
+
// icon an emoji or a character or two its card shows (optional; a card has the custom block's mark)
|
|
19
|
+
|
|
20
|
+
/** The file that makes a folder of components/ a pack. */
|
|
21
|
+
export const PACK_FILE = "pack.json";
|
|
22
|
+
|
|
23
|
+
/** What a pack's id, a scope, may be: as a block id's scope (shared/components/installed.mjs PACKAGE_ID). */
|
|
24
|
+
export const PACK_ID = /^[a-z0-9][a-z0-9-]*$/;
|
|
25
|
+
|
|
26
|
+
/** The scope a block of the workspace's own is in (gallery block new local/<name>, or a <name> alone), which no pack takes. */
|
|
27
|
+
export const OWN_SCOPE = "local";
|
|
28
|
+
|
|
29
|
+
const FIELDS = ["id", "name", "description", "version", "author", "homepage", "icon"];
|
|
30
|
+
const VERSION = /^\d+\.\d+\.\d+$/;
|
|
31
|
+
const isTable = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
|
+
const isWords = (value) => typeof value === "string" && value.trim() !== "";
|
|
33
|
+
const fail = (message) => {
|
|
34
|
+
throw refusal(message, { code: "manifest" });
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Words a reader reads, in both languages: one string for both, or { zh, en }.
|
|
38
|
+
function bothOf(value, field) {
|
|
39
|
+
if (isWords(value) && value.length <= 300) return { zh: value.trim(), en: value.trim() };
|
|
40
|
+
if (isTable(value) && Object.keys(value).length === 2 && isWords(value.zh) && isWords(value.en) && value.zh.length <= 300 && value.en.length <= 300) return { zh: value.zh.trim(), en: value.en.trim() };
|
|
41
|
+
fail(said("pack.json 的 {0} 要写成一段文字,或 { zh, en } 两种语言的文字", field));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* pack.json's text read as a pack's manifest: { id, name: { zh, en }, description: { zh, en } | null,
|
|
46
|
+
* version, author, homepage, icon (each null where it says none) }. One that does not read as one is
|
|
47
|
+
* refused (code "manifest") with why, said()'s.
|
|
48
|
+
*/
|
|
49
|
+
export function readPackManifest(text) {
|
|
50
|
+
let value;
|
|
51
|
+
try {
|
|
52
|
+
value = JSON.parse(String(text).replace(/^/, ""));
|
|
53
|
+
} catch {
|
|
54
|
+
fail(said("pack.json 不是 JSON"));
|
|
55
|
+
}
|
|
56
|
+
if (!isTable(value)) fail(said("pack.json 要写成一张表"));
|
|
57
|
+
for (const key of Object.keys(value)) if (!FIELDS.includes(key)) fail(said("{0} 不是 pack.json 能写的字段", key));
|
|
58
|
+
const { id, name, description, version, author, homepage, icon } = value;
|
|
59
|
+
if (id === undefined) fail(said("pack.json 缺少 {0}", "id"));
|
|
60
|
+
if (typeof id !== "string" || !PACK_ID.test(id) || DEVICE_NAMES.has(id)) fail(said("pack.json 的 id 只用小写字母、数字和连字符,如 my-cards"));
|
|
61
|
+
if (id === OWN_SCOPE) fail(said("{0} 留给工作区自己的块,块包换一个 id", OWN_SCOPE));
|
|
62
|
+
if (name === undefined) fail(said("pack.json 缺少 {0}", "name"));
|
|
63
|
+
if (version === undefined) fail(said("pack.json 缺少 {0}", "version"));
|
|
64
|
+
if (typeof version !== "string" || !VERSION.test(version)) fail(said("pack.json 的 version 要写成 1.0.0 这样的三段数字"));
|
|
65
|
+
if (author !== undefined && !(isWords(author) && author.length <= 100)) fail(said("pack.json 的 {0} 要写成一段文字", "author"));
|
|
66
|
+
if (homepage !== undefined && !(typeof homepage === "string" && /^https?:\/\/\S+$/.test(homepage) && homepage.length <= 500)) fail(said("pack.json 的 homepage 要写成 http 或 https 开头的地址"));
|
|
67
|
+
if (icon !== undefined && !(isWords(icon) && [...icon].length <= 8 && !/[<>&"]/.test(icon))) fail(said("pack.json 的 icon 要写成一个表情或一两个字"));
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
name: bothOf(name, "name"),
|
|
71
|
+
description: description === undefined ? null : bothOf(description, "description"),
|
|
72
|
+
version,
|
|
73
|
+
author: author?.trim() ?? null,
|
|
74
|
+
homepage: homepage ?? null,
|
|
75
|
+
icon: icon?.trim() ?? null,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pageLink, pageOfLink } from "./page-links.mjs";
|
|
1
|
+
import { earlierLink, pageLink, pageOfLink } from "./page-links.mjs";
|
|
2
2
|
|
|
3
3
|
// A document's address, as a link, a paste or the address bar may write it (docs/workbench.md, "A
|
|
4
4
|
// document's addresses"), read in one place: { slug, block }, block null for the page itself. Pure: the
|
|
@@ -19,7 +19,8 @@ import { pageLink, pageOfLink } from "./page-links.mjs";
|
|
|
19
19
|
// which has the list. pageAddress reads no port, as a link outlives a server; but this computer runs
|
|
20
20
|
// other programs too, and other workspaces, whose folder of one name has one page id, so an address
|
|
21
21
|
// on another port (elsewhere) is a document's only if the list holds it, by its folder name or its
|
|
22
|
-
// link of now (addressOn)
|
|
22
|
+
// link of now (addressOn), or for a folder named doc- and eight characters, the link it had before
|
|
23
|
+
// those eight were its id (shared/page-links.mjs earlierId).
|
|
23
24
|
|
|
24
25
|
const SLUG = "[a-z0-9]+(?:-[a-z0-9]+)*"; // shared/workspace.mjs slugPattern, which needs Node
|
|
25
26
|
const FILE = "(?:doc|report)\\.html";
|
|
@@ -96,7 +97,9 @@ export function workbenchHash(text) {
|
|
|
96
97
|
export function addressOn(text, links, origin = "http://127.0.0.1") {
|
|
97
98
|
const place = pageAddress(text, links);
|
|
98
99
|
const name = place && elsewhere(text, origin) ? workbenchHash(text)?.split("/")[0] : undefined;
|
|
99
|
-
|
|
100
|
+
if (name === undefined || links.has(name)) return place;
|
|
101
|
+
const link = links.get(place.slug);
|
|
102
|
+
return link === name || earlierLink(place.slug, link) === name ? place : null;
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/** Whether `text` is an address of a document of this workspace, as far as can be told with no list. */
|