@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
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { isUtf8 } from "node:buffer";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { parseFragment, serializeOuter } from "parse5";
|
|
7
7
|
import { isInstance, parseCompound, parsePart, parseSelector, partOf } from "./components/index.mjs";
|
|
8
|
-
import { ATTRIBUTE, NO_TEXT, PACKAGE_ID, PACKAGE_MARK, SLOT, instanceMatch, refusedInCopy, settingState } from "./components/installed.mjs";
|
|
8
|
+
import { ATTRIBUTE, NO_TEXT, PACKAGE_ID, PACKAGE_MARK, SLOT, inDocumentMenus, instanceMatch, refusedInCopy, settingState } from "./components/installed.mjs";
|
|
9
9
|
import { writeAtomic } from "./files.mjs";
|
|
10
10
|
import { attr, walk } from "./html-tree.mjs";
|
|
11
|
+
import { HOST, NET, netHostsOf } from "./net-policy.mjs";
|
|
11
12
|
import { inLanguage, refusal, said, saying, told } from "./i18n/index.mjs";
|
|
13
|
+
import { PACK_FILE, PACK_ID, readPackManifest } from "./pack-manifest.mjs";
|
|
12
14
|
import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf, markerOf, packageFolders } from "./workspace.mjs";
|
|
13
15
|
|
|
14
|
-
//
|
|
16
|
+
// 组件包, custom blocks (自定义块) as the UI and the Agent's documents call them: what the workspace's
|
|
17
|
+
// component packages say of themselves to Gallery, read from
|
|
15
18
|
// <workspace>/components/<scope>/<name>/component.json, plain data, whose code no system runs here. A
|
|
16
19
|
// package is a description as a kit component's is (src/shared/components/): its id, where its
|
|
17
20
|
// instances are, its parts and what it leaves on a page, so that what reads a built-in's reads a
|
|
@@ -19,19 +22,22 @@ import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf
|
|
|
19
22
|
// A manifest offers only FIELDS, no unit, box, host, rank or category: those are the engines', and a
|
|
20
23
|
// field it does not know keeps its package from loading.
|
|
21
24
|
//
|
|
22
|
-
// The catalog is { packages, problems, settings, stamp }. `packages`, by id, are those that load. A
|
|
25
|
+
// The catalog is { packages, problems, settings, packs, stamp }. `packages`, by id, are those that load. A
|
|
23
26
|
// package that does not load (a manifest that does not read as one, one for an apiVersion this Gallery
|
|
24
27
|
// does not run, which counts as disabled, or one the settings turned off) stands in `problems`, by its
|
|
25
28
|
// id in lowercase, as an instance names it, with why (`problem`, said()'s, in either language by
|
|
26
29
|
// problemIn), and touches nothing else: its instances show as those of a package that is not there,
|
|
27
30
|
// and whoever reads them says why. `settings` is what gallery.json says of the components (设置 › 组件列表,
|
|
28
|
-
// componentSettings): `hidden` and `
|
|
29
|
-
// component, and `
|
|
30
|
-
//
|
|
31
|
-
//
|
|
31
|
+
// componentSettings): `hidden`, `disabled`, `shown` and `unlisted`, by the id of a package (`hidden` the name of a
|
|
32
|
+
// built-in component too), `packs` and `packsOff`, the packs' switches (a pack off: none of its blocks offered,
|
|
33
|
+
// as one unlisted), and `problem` where the file cannot be read as saying anything (settingsNow). `packs`, by
|
|
34
|
+
// the scope of a block (a pack: ./pack-manifest.mjs), what its pack.json names it, or null for a scope with
|
|
35
|
+
// none or one that does not read (who lists it says it by its scope).
|
|
36
|
+
// A package the document's menus do not offer (inDocumentMenus: hidden, unlisted, or a custom page's alone and not
|
|
37
|
+
// shown) runs as any other and is left out of those menus, which read its state (menuOf, packageStates); one disabled is one that does not load, so that nothing that loads a
|
|
32
38
|
// package has a case for it. A stamp is a hash of what files hold: `stamp`, of a package, over what
|
|
33
39
|
// reaches a page of it (STAMPED and the files `assets` names), of one that does not load over those
|
|
34
|
-
// and why, and of the catalog, over its packages', what is hidden and whether gallery.json reads;
|
|
40
|
+
// and why, and of the catalog, over its packages', what is hidden, shown and unlisted and whether gallery.json reads;
|
|
35
41
|
// `definition`, over the manifest alone, is what a page's commit is to be held to.
|
|
36
42
|
//
|
|
37
43
|
// readCatalog reads the disk; what reads a catalog for use (the scan, the checks, a page) asks
|
|
@@ -43,14 +49,38 @@ import { DEVICE_NAMES, PACKAGE_MANIFEST, WORKSPACE_MARK, findWorkspace, layoutOf
|
|
|
43
49
|
/** The apiVersion this Gallery runs. */
|
|
44
50
|
export const API_VERSION = 1;
|
|
45
51
|
|
|
46
|
-
/** The kit's files a package may ask for by id in `assets`, as src/render/assets.mjs names them (the others are on every page). */
|
|
52
|
+
/** The kit's files a package may ask for by id in `assets`, as src/render/assets.mjs names them (the others are on every page, or, the board's, on every custom page). */
|
|
47
53
|
export const KIT_ASSETS = new Set(["code", "state", "sheets", "tokens", "dialogs", "katex-styles", "katex", "katex-auto-render", "math", "d3", "charts", "tables"]);
|
|
48
54
|
|
|
49
55
|
// What a manifest may hold, a part of one and its `runtime`.
|
|
50
|
-
const FIELDS = ["id", "apiVersion", "version", "name", "description", "words", "parts", "runtime", "assets", "markdown", "schema"];
|
|
56
|
+
const FIELDS = ["id", "apiVersion", "version", "name", "description", "words", "parts", "runtime", "assets", "markdown", "schema", "places", "card", "settings", "needs"];
|
|
51
57
|
const PART_FIELDS = ["at", "edit", "optional", "min", "max"];
|
|
52
58
|
const RUNTIME_FIELDS = ["generated", "attrs"];
|
|
53
59
|
|
|
60
|
+
// Where a block may stand (`places`, notes/plans/custom-blocks-spec-2026-11-07.md section 1): in a
|
|
61
|
+
// document's flow, on a custom page's board as a card. One that says nothing is a document's, as every
|
|
62
|
+
// package before the field was.
|
|
63
|
+
const PLACES = ["document", "page"];
|
|
64
|
+
const DOCUMENT_ONLY = ["document"];
|
|
65
|
+
// What `card` says of a card on a board, in cells of the grid: a row is 12 of them, and a card at most
|
|
66
|
+
// 24 high.
|
|
67
|
+
const CARD_FIELDS = ["sizes", "w", "h", "min", "max", "resize", "fit", "bare"];
|
|
68
|
+
const GRID = { w: 12, h: 24 };
|
|
69
|
+
const FITS = ["fixed", "content"];
|
|
70
|
+
const NO_CARD = { sizes: null, any: false, w: 4, h: null, min: { w: 1, h: 1 }, max: { ...GRID }, resize: false, fit: "fixed", bare: false };
|
|
71
|
+
// A card setting: its key, a JavaScript name as a block reads it from its settings part; its kinds,
|
|
72
|
+
// each a field of the form the board draws; `required`: the card shows nothing until the setting holds a
|
|
73
|
+
// value (a weather card's city), which gallery page check says of a card that has none.
|
|
74
|
+
const SETTING_FIELDS = ["key", "label", "type", "options", "default", "required"];
|
|
75
|
+
const SETTING_KEY = /^[a-z][a-zA-Z0-9]*$/;
|
|
76
|
+
const SETTING_TYPES = ["text", "number", "select", "date", "switch", "document"];
|
|
77
|
+
const DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
78
|
+
/** The data part a block with settings keeps their values in, JSON, as the board's form writes them. */
|
|
79
|
+
export const SETTINGS_PART = "settings";
|
|
80
|
+
// What a block asks to use: the workspace's data, the keyed tables of its document, the network of a
|
|
81
|
+
// host (lowercase, no port and no path, "*." first for its subdomains: ./net-policy.mjs HOST).
|
|
82
|
+
const NEEDS = ["workspace", "tables"];
|
|
83
|
+
|
|
54
84
|
// What a package's part may be edited as: its text, its data through the kit's edit, the document's
|
|
55
85
|
// blocks (a slot, marked data-doc-slot="<name>" in its markup), or not at all.
|
|
56
86
|
const EDITS = ["text", "data", "flow", "none"];
|
|
@@ -149,7 +179,7 @@ const notTooLong = (bytes, name) => {
|
|
|
149
179
|
};
|
|
150
180
|
|
|
151
181
|
const onlyFields = (table, fields, where = "") => {
|
|
152
|
-
for (const key of Object.keys(table)) if (!fields.includes(key)) fail(said("{0}
|
|
182
|
+
for (const key of Object.keys(table)) if (!fields.includes(key)) fail(said("{0} 不是自定义块的清单能写的字段", `${where}${key}`));
|
|
153
183
|
};
|
|
154
184
|
const required = (manifest, fields) => {
|
|
155
185
|
for (const field of fields) if (manifest[field] === undefined) fail(said("缺少 {0}", field));
|
|
@@ -210,7 +240,7 @@ function runtimeOf(runtime, id) {
|
|
|
210
240
|
if (!isTable(attrs)) fail(said("{0} 要写成一张表", "runtime.attrs"));
|
|
211
241
|
for (const names of Object.values(attrs)) {
|
|
212
242
|
if (!Array.isArray(names) || !names.every(isWords)) fail(said("{0} 要写成一串文字", "runtime.attrs"));
|
|
213
|
-
for (const name of names) if (OWN_NAME.test(name)) fail(said("{0} 是 Gallery
|
|
243
|
+
for (const name of names) if (OWN_NAME.test(name)) fail(said("{0} 是 Gallery 自己的名字,自定义块不能用", name));
|
|
214
244
|
}
|
|
215
245
|
const made = (selector) => {
|
|
216
246
|
const written = scoped(selector, "runtime.generated");
|
|
@@ -223,14 +253,136 @@ function runtimeOf(runtime, id) {
|
|
|
223
253
|
};
|
|
224
254
|
}
|
|
225
255
|
|
|
256
|
+
const isCount = (value, most) => Number.isInteger(value) && value >= 1 && value <= most;
|
|
257
|
+
const twice = (list) => list.find((value, index) => list.indexOf(value) !== index);
|
|
258
|
+
|
|
259
|
+
// `places`: one place or both, each once.
|
|
260
|
+
function placesOf(places) {
|
|
261
|
+
if (places === undefined) return DOCUMENT_ONLY;
|
|
262
|
+
if (!Array.isArray(places) || places.length === 0 || !places.every((place) => PLACES.includes(place))) fail(said("places 要写成 document、page 中的一个或两个,如 [\"document\"]"));
|
|
263
|
+
if (twice(places)) fail(said("places 里 {0} 写了两次", twice(places)));
|
|
264
|
+
return [...places];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// `card`: as a board reads it, every field there (NO_CARD's where the manifest says nothing; `h` null:
|
|
268
|
+
// the fewest whole cells its content takes). With `sizes`, the first is the default, and no w or h is
|
|
269
|
+
// written beside them; a min or a max beside them frees the card (`any`): the sizes are the common ones
|
|
270
|
+
// its 大小 menu lists, any whole size between min and max is its too (resizing by the corner, a place),
|
|
271
|
+
// and each of the sizes stands inside them.
|
|
272
|
+
function cardOf(card) {
|
|
273
|
+
if (card === undefined) return NO_CARD;
|
|
274
|
+
if (!isTable(card)) fail(said("{0} 要写成一张表", "card"));
|
|
275
|
+
onlyFields(card, CARD_FIELDS, "card.");
|
|
276
|
+
const sized = (pair) => Array.isArray(pair) && pair.length === 2 && isCount(pair[0], GRID.w) && isCount(pair[1], GRID.h);
|
|
277
|
+
if (card.sizes !== undefined) {
|
|
278
|
+
if (!Array.isArray(card.sizes) || card.sizes.length === 0 || !card.sizes.every(sized)) fail(said("card.sizes 要写成 [宽, 高] 的一串,宽 1 到 {0} 格,高 1 到 {1} 格", GRID.w, GRID.h));
|
|
279
|
+
const repeated = twice(card.sizes.map((pair) => pair.join("x")));
|
|
280
|
+
if (repeated) fail(said("card.sizes 里 {0} 写了两次", repeated));
|
|
281
|
+
const beside = ["w", "h"].find((field) => card[field] !== undefined);
|
|
282
|
+
if (beside) fail(said("写了 card.sizes,就不再写 card.{0}:第一种大小是默认的", beside));
|
|
283
|
+
}
|
|
284
|
+
for (const axis of ["w", "h"]) if (card[axis] !== undefined && !isCount(card[axis], GRID[axis])) fail(said("{0} 要写成 1 到 {1} 的整数", `card.${axis}`, GRID[axis]));
|
|
285
|
+
const bounds = {};
|
|
286
|
+
for (const bound of ["min", "max"]) {
|
|
287
|
+
const value = card[bound];
|
|
288
|
+
if (value === undefined) continue;
|
|
289
|
+
if (!isTable(value)) fail(said("{0} 要写成 { w, h },单位是格", `card.${bound}`));
|
|
290
|
+
onlyFields(value, ["w", "h"], `card.${bound}.`);
|
|
291
|
+
for (const axis of ["w", "h"]) if (value[axis] !== undefined && !isCount(value[axis], GRID[axis])) fail(said("{0} 要写成 1 到 {1} 的整数", `card.${bound}.${axis}`, GRID[axis]));
|
|
292
|
+
bounds[bound] = value;
|
|
293
|
+
}
|
|
294
|
+
const min = { w: bounds.min?.w ?? 1, h: bounds.min?.h ?? 1 };
|
|
295
|
+
const max = { w: bounds.max?.w ?? GRID.w, h: bounds.max?.h ?? GRID.h };
|
|
296
|
+
const w = card.sizes ? card.sizes[0][0] : card.w ?? NO_CARD.w;
|
|
297
|
+
const h = card.sizes ? card.sizes[0][1] : card.h ?? null;
|
|
298
|
+
const any = Boolean(card.sizes) && (card.min !== undefined || card.max !== undefined);
|
|
299
|
+
if (any) {
|
|
300
|
+
const outside = card.sizes.find(([width, height]) => width < min.w || width > max.w || height < min.h || height > max.h);
|
|
301
|
+
if (outside) fail(said("card.sizes 里 {0} 不在 card.min 到 card.max 之间", outside.join("x")));
|
|
302
|
+
}
|
|
303
|
+
for (const axis of ["w", "h"]) {
|
|
304
|
+
if (min[axis] > max[axis]) fail(said("card.min.{0} 比 card.max.{0} 大", axis));
|
|
305
|
+
const value = { w, h }[axis];
|
|
306
|
+
if (value !== null && !card.sizes && (value < min[axis] || value > max[axis])) fail(said("默认的 card.{0} 是 {1},不在 card.min 到 card.max 之间({2} 到 {3})", axis, value, min[axis], max[axis]));
|
|
307
|
+
}
|
|
308
|
+
for (const flag of ["resize", "bare"]) if (card[flag] !== undefined && typeof card[flag] !== "boolean") fail(said("{0} 要写成 true 或 false", `card.${flag}`));
|
|
309
|
+
if (card.fit !== undefined && !FITS.includes(card.fit)) fail(said("card.fit 只能是 fixed 或 content"));
|
|
310
|
+
return {
|
|
311
|
+
sizes: card.sizes ? card.sizes.map(([width, height]) => [width, height]) : null,
|
|
312
|
+
any, w, h, min, max,
|
|
313
|
+
resize: card.resize ?? false,
|
|
314
|
+
fit: card.fit ?? "fixed",
|
|
315
|
+
bare: card.bare ?? false,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Whether `value` can be the default of a setting of `type` (with its `options`).
|
|
320
|
+
const fitsSetting = (value, { type, options }) => ({
|
|
321
|
+
text: typeof value === "string",
|
|
322
|
+
number: typeof value === "number" && Number.isFinite(value),
|
|
323
|
+
select: options?.includes(value),
|
|
324
|
+
date: typeof value === "string" && DATE.test(value),
|
|
325
|
+
switch: typeof value === "boolean",
|
|
326
|
+
document: typeof value === "string",
|
|
327
|
+
})[type];
|
|
328
|
+
|
|
329
|
+
// `settings`: the fields of a card's form, each key once; a block with any keeps their values in a JSON
|
|
330
|
+
// data part of its own (SETTINGS_PART), which its parts are to name.
|
|
331
|
+
function settingsOf(settings, parts) {
|
|
332
|
+
if (settings === undefined) return [];
|
|
333
|
+
if (!Array.isArray(settings)) fail(said("{0} 要写成一串字段", "settings"));
|
|
334
|
+
const keys = [];
|
|
335
|
+
settings.forEach((setting, index) => {
|
|
336
|
+
const where = `settings[${index}]`;
|
|
337
|
+
if (!isTable(setting)) fail(said("{0} 要写成一张表", where));
|
|
338
|
+
onlyFields(setting, SETTING_FIELDS, `${where}.`);
|
|
339
|
+
if (typeof setting.key !== "string" || !SETTING_KEY.test(setting.key)) fail(said("{0} 的 key 以小写字母开头,只用字母和数字(如 city、startDate)", where));
|
|
340
|
+
if (keys.includes(setting.key)) fail(said("settings 里 key {0} 写了两次", setting.key));
|
|
341
|
+
keys.push(setting.key);
|
|
342
|
+
bothLanguages(setting.label, `${where}.label`);
|
|
343
|
+
if (!SETTING_TYPES.includes(setting.type)) fail(said("{0} 的 type 只能是 {1}", where, SETTING_TYPES.join(", ")));
|
|
344
|
+
if (setting.type === "select") {
|
|
345
|
+
if (!Array.isArray(setting.options) || setting.options.length === 0 || !setting.options.every(isWords)) fail(said("{0} 是 select,要有 options:一串文字", where));
|
|
346
|
+
if (twice(setting.options)) fail(said("{0} 的 options 里 {1} 写了两次", where, twice(setting.options)));
|
|
347
|
+
} else if (setting.options !== undefined) fail(said("{0} 不是 select,不写 options", where));
|
|
348
|
+
if (setting.default !== undefined && !fitsSetting(setting.default, setting)) fail(said("{0} 的 default 和它的 type {1} 不符", where, setting.type));
|
|
349
|
+
if (setting.required !== undefined && typeof setting.required !== "boolean") fail(said("{0} 的 required 要写 true 或 false", where));
|
|
350
|
+
});
|
|
351
|
+
const part = parts[SETTINGS_PART];
|
|
352
|
+
if (settings.length > 0 && !(part?.edit === "data" && tailTags(part.at).every((tag) => tag === "script") && tailTags(part.at).length > 0)) {
|
|
353
|
+
fail(said("写了 settings,parts 里就要有一个名为 settings、edit 为 data 的部分,它是一个 <script type=\"application/json\">,存卡片设置的值"));
|
|
354
|
+
}
|
|
355
|
+
return settings.map((setting) => ({ ...setting }));
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// `needs`: what a block asks to use, each once.
|
|
359
|
+
function needsOf(needs) {
|
|
360
|
+
if (needs === undefined) return [];
|
|
361
|
+
if (!Array.isArray(needs)) fail(said("{0} 要写成一串文字", "needs"));
|
|
362
|
+
for (const need of needs) {
|
|
363
|
+
if (typeof need === "string" && (NEEDS.includes(need) || (need.startsWith(NET) && HOST.test(need.slice(NET.length))))) continue;
|
|
364
|
+
fail(said("needs 里的 {0} 不认识:只认 workspace、tables 和 net:<域名>(小写的主机名,不带端口和路径,可以以 *. 开头)", typeof need === "string" ? need : JSON.stringify(need)));
|
|
365
|
+
}
|
|
366
|
+
if (twice(needs)) fail(said("needs 里 {0} 写了两次", twice(needs)));
|
|
367
|
+
return [...needs];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// What a block that may stand on a custom page cannot hold: a slot, whose blocks are a document's,
|
|
371
|
+
// which no page has the editor and the rendering of.
|
|
372
|
+
function pageParts(places, parts) {
|
|
373
|
+
if (!places.includes("page")) return;
|
|
374
|
+
const slot = Object.entries(parts).find(([, part]) => part.edit === "flow");
|
|
375
|
+
if (slot) fail(said("places 里有 page,就不能有插槽:{0} 的 edit 是 flow,插槽放的是文档的块,自定义页上没有", `parts.${slot[0]}`));
|
|
376
|
+
}
|
|
377
|
+
|
|
226
378
|
// `assets`: the kit's files a package asks for, by id, and files of its own, by their path in its folder.
|
|
227
379
|
function assetsOf(assets, folder) {
|
|
228
380
|
if (assets === undefined) return { kit: [], own: [] };
|
|
229
381
|
if (!Array.isArray(assets) || !assets.every(isWords)) fail(said("{0} 要写成一串文字", "assets"));
|
|
230
382
|
const own = assets.filter((asset) => !KIT_ASSETS.has(asset));
|
|
231
383
|
for (const asset of own) {
|
|
232
|
-
if (!OWN_FILE.test(asset)) fail(said("assets 里的 {0}
|
|
233
|
-
if (!ownPath(folder, asset)) fail(said("assets 写了 {0}
|
|
384
|
+
if (!OWN_FILE.test(asset)) fail(said("assets 里的 {0} 既不是套件的资源,也不是块的文件夹里的文件", asset));
|
|
385
|
+
if (!ownPath(folder, asset)) fail(said("assets 写了 {0},块的文件夹里没有这个文件", asset));
|
|
234
386
|
}
|
|
235
387
|
return { kit: assets.filter((asset) => KIT_ASSETS.has(asset)), own };
|
|
236
388
|
}
|
|
@@ -245,12 +397,12 @@ function packageOf(id, folder, text) {
|
|
|
245
397
|
fail(said("component.json 不是 JSON:{0}", error.message));
|
|
246
398
|
}
|
|
247
399
|
if (!isTable(manifest)) fail(said("component.json 写的不是一个对象"));
|
|
248
|
-
if (!PACKAGE_ID.test(id)) fail(said("{0}
|
|
249
|
-
for (const name of id.split("/")) if (DEVICE_NAMES.has(name)) fail(said("{0}
|
|
400
|
+
if (!PACKAGE_ID.test(id)) fail(said("{0} 不能做自定义块的 id:范围和名字都只用小写字母、数字和连字符", id));
|
|
401
|
+
for (const name of id.split("/")) if (DEVICE_NAMES.has(name)) fail(said("{0} 不能做自定义块的 id:{1} 是 Windows 留给设备的名字", id, name));
|
|
250
402
|
if (manifest.id !== id) fail(said("清单的 id 是 {0},和它的文件夹 {1} 不一样", String(manifest.id), id));
|
|
251
403
|
required(manifest, ["apiVersion"]);
|
|
252
404
|
if (!Number.isInteger(manifest.apiVersion) || manifest.apiVersion < 1) fail(said("apiVersion 要写成一个正整数"));
|
|
253
|
-
if (manifest.apiVersion !== API_VERSION) throw refusal(said("
|
|
405
|
+
if (manifest.apiVersion !== API_VERSION) throw refusal(said("这个自定义块要 apiVersion {0},这一版 Gallery 只支持 {1}", manifest.apiVersion, API_VERSION), { disabled: true, version: isWords(manifest.version) ? manifest.version : null });
|
|
254
406
|
onlyFields(manifest, FIELDS);
|
|
255
407
|
required(manifest, ["version", "name", "description", "parts"]);
|
|
256
408
|
if (!isWords(manifest.version)) fail(said("{0} 要写成文字", "version"));
|
|
@@ -263,6 +415,11 @@ function packageOf(id, folder, text) {
|
|
|
263
415
|
const { kit, own } = assetsOf(manifest.assets, folder);
|
|
264
416
|
if (manifest.markdown !== undefined && !MARKDOWN.includes(manifest.markdown)) fail(said("markdown 只能是 parts 或 text"));
|
|
265
417
|
if (manifest.schema !== undefined && !(Number.isInteger(manifest.schema) && manifest.schema >= 1)) fail(said("schema 要写成一个正整数"));
|
|
418
|
+
const places = placesOf(manifest.places);
|
|
419
|
+
pageParts(places, manifest.parts);
|
|
420
|
+
const card = cardOf(manifest.card);
|
|
421
|
+
const settings = settingsOf(manifest.settings, manifest.parts);
|
|
422
|
+
const needs = needsOf(manifest.needs);
|
|
266
423
|
const template = ownFile(folder, TEMPLATE);
|
|
267
424
|
if (template) notTooLong(template, TEMPLATE);
|
|
268
425
|
return {
|
|
@@ -278,6 +435,10 @@ function packageOf(id, folder, text) {
|
|
|
278
435
|
words: manifest.words ?? "",
|
|
279
436
|
assets: kit,
|
|
280
437
|
markdown: manifest.markdown ?? "parts",
|
|
438
|
+
places,
|
|
439
|
+
card,
|
|
440
|
+
settings,
|
|
441
|
+
needs,
|
|
281
442
|
files: [...new Set([...SERVED.filter((file) => ownPath(folder, file)), ...own])],
|
|
282
443
|
template: template && wordsOf(template),
|
|
283
444
|
folder,
|
|
@@ -305,10 +466,20 @@ const workspaceRoot = () => findWorkspace().root;
|
|
|
305
466
|
|
|
306
467
|
// A file edited by hand holds what it holds: of its lists, what can be an id is read, once each, and
|
|
307
468
|
// the rest is passed over, one that names nothing there doing nothing. An id may be of a package that
|
|
308
|
-
// comes back, so it stays. Only a package can be disabled.
|
|
469
|
+
// comes back, so it stays. Only a package can be disabled or unlisted.
|
|
309
470
|
const NAMED = /^[a-z0-9][a-z0-9-]*(?:\/[a-z0-9][a-z0-9-]*)?$/;
|
|
310
471
|
const idsIn = (list, shape) => [...new Set(Array.isArray(list) ? list.filter((id) => typeof id === "string" && shape.test(id)) : [])];
|
|
311
|
-
|
|
472
|
+
// `net`, the reader's consents (a block's card, 设置 › 组件列表): of each package by its id, the hosts of its
|
|
473
|
+
// needs it was allowed to reach, those that can be a needs' host (./net-policy.mjs HOST), once each; a
|
|
474
|
+
// package with none named none.
|
|
475
|
+
const consentsIn = (net) => Object.fromEntries(Object.entries(isTable(net) ? net : {}).filter(([id]) => PACKAGE_ID.test(id)).map(([id, hosts]) => [id, idsIn(hosts, HOST)]).filter(([, hosts]) => hosts.length > 0));
|
|
476
|
+
// `packs`, the packs' switches (设置 › 组件列表's pack cards): { "<pack id>": { "enabled": false } }, by id, a
|
|
477
|
+
// pack it names none of on; what is no pack's id or says no boolean is passed over.
|
|
478
|
+
const switchesIn = (packs) => Object.fromEntries(Object.entries(isTable(packs) ? packs : {}).filter(([id, entry]) => PACK_ID.test(id) && typeof entry?.enabled === "boolean").map(([id, entry]) => [id, entry.enabled]));
|
|
479
|
+
const settingsIn = (file) => {
|
|
480
|
+
const packs = switchesIn(file?.components?.packs);
|
|
481
|
+
return { hidden: idsIn(file?.components?.hidden, NAMED), disabled: idsIn(file?.components?.disabled, PACKAGE_ID), shown: idsIn(file?.components?.shown, PACKAGE_ID), unlisted: idsIn(file?.components?.unlisted, PACKAGE_ID), net: consentsIn(file?.components?.net), packs, packsOff: Object.keys(packs).filter((id) => packs[id] === false).sort() };
|
|
482
|
+
};
|
|
312
483
|
|
|
313
484
|
// gallery.json's text (null: no file) as what it holds, `value`, or `problem`, why it cannot be taken
|
|
314
485
|
// for a workspace's: what the catalog goes on through (settingsNow) and no writer writes over.
|
|
@@ -329,8 +500,11 @@ function fileAt(root) {
|
|
|
329
500
|
}
|
|
330
501
|
|
|
331
502
|
/**
|
|
332
|
-
* What gallery.json says of the components: { hidden, disabled }, ids of packages and, in
|
|
333
|
-
* names of built-in components
|
|
503
|
+
* What gallery.json says of the components: { hidden, disabled, shown, unlisted, net, packs, packsOff }, ids of packages and, in
|
|
504
|
+
* `hidden`, names of built-in components (inDocumentMenus reads `hidden`, `shown` and `unlisted`; a block
|
|
505
|
+
* unlisted, 设置 › 组件列表's master switch off, is offered by no menu and no library); `net` the hosts
|
|
506
|
+
* each package was allowed to reach, by its id (consentsIn); `packs` each pack's switch named there, by its id (true on,
|
|
507
|
+
* false off), `packsOff` the ids of those off. One that is not there says nothing, and neither does one that does not
|
|
334
508
|
* read or is another program's: what goes on using a workspace goes by the catalog (readCatalog).
|
|
335
509
|
*/
|
|
336
510
|
export const componentSettings = (root = workspaceRoot()) => settingsIn(fileAt(root).value);
|
|
@@ -339,12 +513,17 @@ export const componentSettings = (root = workspaceRoot()) => settingsIn(fileAt(r
|
|
|
339
513
|
const NOT_WRITABLE = ["EPERM", "EACCES", "EBUSY", "EROFS"];
|
|
340
514
|
|
|
341
515
|
/**
|
|
342
|
-
* gallery.json with `
|
|
343
|
-
*
|
|
516
|
+
* gallery.json with the pack `pack` on or off, as `enabled` says (components.packs, its entry's other fields
|
|
517
|
+
* kept), or with `id` hidden or not, shown or not, disabled or not and unlisted or not, as `hidden`,
|
|
518
|
+
* `shown`, `disabled` and `unlisted` say (true, false, or left out for as it is), written at once and with
|
|
519
|
+
* all else it holds kept; `shown` and `unlisted` are written where they hold an id or were there already
|
|
520
|
+
* (a block no entry names is listed). `net`, the hosts `id` is allowed
|
|
521
|
+
* to reach (a list: its consent, all it names in place of what was there; [] takes it back), written in
|
|
522
|
+
* components.net under its id, which goes with its last host, and components.net with its last id. A file that does
|
|
344
523
|
* not read, is another program's, changed under this write or cannot be written is refused, and stays
|
|
345
524
|
* as it is: it is not this write's to mend.
|
|
346
525
|
*/
|
|
347
|
-
export async function writeComponentSettings(root, { id, hidden, disabled }) {
|
|
526
|
+
export async function writeComponentSettings(root, { id, hidden, shown, disabled, unlisted, net, pack, enabled }) {
|
|
348
527
|
const file = layoutOf(root).marker;
|
|
349
528
|
const text = await fs.readFile(file, "utf8").catch((error) => {
|
|
350
529
|
if (error.code === "ENOENT") return null;
|
|
@@ -353,8 +532,26 @@ export async function writeComponentSettings(root, { id, hidden, disabled }) {
|
|
|
353
532
|
const { value, problem } = fileOf(text);
|
|
354
533
|
if (problem) fail(problem);
|
|
355
534
|
const named = (list, on) => (on === undefined ? list : on ? [...new Set([...list, id])] : list.filter((other) => other !== id));
|
|
356
|
-
const { hidden: wasHidden, disabled: wasDisabled } = settingsIn(value);
|
|
357
|
-
const
|
|
535
|
+
const { hidden: wasHidden, disabled: wasDisabled, shown: wasShown, unlisted: wasUnlisted } = settingsIn(value);
|
|
536
|
+
const before = isTable(value?.components) ? value.components : {};
|
|
537
|
+
const nowShown = named(wasShown, shown);
|
|
538
|
+
const nowUnlisted = named(wasUnlisted, unlisted);
|
|
539
|
+
const components = {
|
|
540
|
+
...before, hidden: named(wasHidden, hidden), disabled: named(wasDisabled, disabled),
|
|
541
|
+
...(nowShown.length > 0 || Object.hasOwn(before, "shown") ? { shown: nowShown } : {}),
|
|
542
|
+
...(nowUnlisted.length > 0 || Object.hasOwn(before, "unlisted") ? { unlisted: nowUnlisted } : {}),
|
|
543
|
+
};
|
|
544
|
+
if (pack !== undefined) {
|
|
545
|
+
const switches = isTable(before.packs) ? before.packs : {};
|
|
546
|
+
components.packs = { ...switches, [pack]: { ...(isTable(switches[pack]) ? switches[pack] : {}), enabled: enabled === true } };
|
|
547
|
+
}
|
|
548
|
+
if (net !== undefined) {
|
|
549
|
+
const consents = { ...(isTable(before.net) ? before.net : {}) };
|
|
550
|
+
if (net.length > 0) consents[id] = [...new Set(net)];
|
|
551
|
+
else delete consents[id];
|
|
552
|
+
if (Object.keys(consents).length > 0) components.net = consents;
|
|
553
|
+
else delete components.net;
|
|
554
|
+
}
|
|
358
555
|
const written = await writeAtomic(file, `${JSON.stringify({ ...(value ?? WORKSPACE_MARK), components }, null, 2)}\n`, { expect: text }).catch((error) => {
|
|
359
556
|
if (NOT_WRITABLE.includes(error.code)) fail(said("gallery.json 写不进去:可能是只读的,或被别的程序占着"));
|
|
360
557
|
throw error;
|
|
@@ -368,31 +565,60 @@ const turnedOff = (found) => ({ ...found, off: true, disabled: true, problem: sa
|
|
|
368
565
|
|
|
369
566
|
const NO_SETTINGS = settingsIn(null);
|
|
370
567
|
|
|
371
|
-
/** The stamp of a catalog: of what its packages are, those that do not load and why too, of what is hidden and of why gallery.json does not read,
|
|
372
|
-
export const catalogStamp = ({ packages, problems, settings = NO_SETTINGS }) => hashOf((hash) => {
|
|
568
|
+
/** The stamp of a catalog: of what its packages are, those that do not load and why too, of what is hidden, shown and unlisted, the packs off and their names, and of why gallery.json does not read, none of which changes a package. */
|
|
569
|
+
export const catalogStamp = ({ packages, problems, settings = NO_SETTINGS, packs = new Map() }) => hashOf((hash) => {
|
|
373
570
|
for (const { id, stamp } of [...packages.values(), ...problems.values()].sort(byId)) hash.update(`${id} ${stamp}\n`);
|
|
374
571
|
hash.update(`hidden ${[...settings.hidden].sort().join(" ")}\n`);
|
|
572
|
+
hash.update(`shown ${[...settings.shown].sort().join(" ")}\n`);
|
|
573
|
+
hash.update(`unlisted ${[...(settings.unlisted ?? [])].sort().join(" ")}\n`);
|
|
574
|
+
hash.update(`net ${consentPairs(settings).join(" ")}\n`);
|
|
575
|
+
hash.update(`packs off ${(settings.packsOff ?? []).join(" ")}\n`);
|
|
576
|
+
for (const [scope, name] of [...packs].sort(([a], [b]) => (a < b ? -1 : 1))) hash.update(`pack ${scope} ${JSON.stringify(name)}\n`);
|
|
375
577
|
hash.update(`settings ${settings.problem?.message ?? ""}\n`);
|
|
376
578
|
});
|
|
377
579
|
|
|
580
|
+
// The consents of `settings` as "<id>@<host>", sorted: what the stamp and the settings line say of them.
|
|
581
|
+
const consentPairs = ({ net = {} }) => Object.entries(net).flatMap(([id, hosts]) => hosts.map((host) => `${id}@${host}`)).sort();
|
|
582
|
+
|
|
378
583
|
// The settings a catalog goes by: what gallery.json says or, while it cannot be read as saying
|
|
379
584
|
// anything, what was in use (`held`, a broken manifest's hold, for a slip by hand), with why (`problem`).
|
|
380
585
|
function settingsNow(root, held) {
|
|
381
586
|
const { value, problem } = fileAt(root);
|
|
382
|
-
return problem ? { hidden: held.hidden, disabled: held.disabled, problem } : settingsIn(value);
|
|
587
|
+
return problem ? { hidden: held.hidden, disabled: held.disabled, shown: held.shown ?? [], unlisted: held.unlisted ?? [], net: held.net ?? {}, packs: held.packs ?? {}, packsOff: held.packsOff ?? [], problem } : settingsIn(value);
|
|
383
588
|
}
|
|
384
589
|
|
|
590
|
+
// What runs before the catalog reads the blocks of a workspace that holds Gallery's own (the scopes of
|
|
591
|
+
// Gallery's packs, `ownScopes`): (root, ids) -> anything. A server sets it as it starts
|
|
592
|
+
// (shared/shipped-blocks.mjs followOnRead: a copy as it was added brought up to Gallery's first), so that
|
|
593
|
+
// this file imports nothing of the package's paths, reading whichever workspace it is given; a command sets none.
|
|
594
|
+
let beforeRead = null;
|
|
595
|
+
let ownScopes = [];
|
|
596
|
+
|
|
597
|
+
/** `run(root, ids)` runs before each read of a workspace's blocks that finds Gallery's own (`ids`, those of `scopes`, the scopes of Gallery's packs), from now on in this process. */
|
|
598
|
+
export const beforeCatalogRead = (run, scopes = ["gallery"]) => {
|
|
599
|
+
beforeRead = run;
|
|
600
|
+
ownScopes = [...scopes];
|
|
601
|
+
};
|
|
602
|
+
|
|
385
603
|
/**
|
|
386
604
|
* The workspace's packages as they are on disk now: the catalog, whatever a package or gallery.json
|
|
387
605
|
* holds. `held`: the settings of the catalog in use, for a file that does not read; a process with
|
|
388
606
|
* none has nothing to go by. `on`: a package read as its files say whatever gallery.json says of it,
|
|
389
|
-
* for what holds it to them (gallery
|
|
607
|
+
* for what holds it to them (gallery block check). In a server, Gallery's own blocks as they were
|
|
608
|
+
* added are brought up to Gallery's first (beforeCatalogRead).
|
|
390
609
|
*/
|
|
391
610
|
export function readCatalog(root = workspaceRoot(), held = NO_SETTINGS, { on = null } = {}) {
|
|
392
611
|
const settings = settingsNow(root, held);
|
|
393
612
|
const packages = new Map();
|
|
394
613
|
const problems = new Map();
|
|
395
|
-
|
|
614
|
+
const folders = packageFolders(root);
|
|
615
|
+
const own = folders.map(({ id }) => id).filter((id) => ownScopes.includes(id.split("/")[0]));
|
|
616
|
+
if (beforeRead && own.length > 0) {
|
|
617
|
+
try {
|
|
618
|
+
beforeRead(root, own);
|
|
619
|
+
} catch {} // what it could not do leaves the blocks as they are
|
|
620
|
+
}
|
|
621
|
+
for (const { id, folder } of folders) {
|
|
396
622
|
const found = readPackage(id, folder);
|
|
397
623
|
(found.problem ? problems : packages).set(found.id, found);
|
|
398
624
|
}
|
|
@@ -402,7 +628,30 @@ export function readCatalog(root = workspaceRoot(), held = NO_SETTINGS, { on = n
|
|
|
402
628
|
problems.set(id, turnedOff(packages.get(id)));
|
|
403
629
|
packages.delete(id);
|
|
404
630
|
}
|
|
405
|
-
|
|
631
|
+
const packs = packNames(root, folders.map(({ id }) => id.split("/")[0]));
|
|
632
|
+
return { packages, problems, settings, packs, stamp: catalogStamp({ packages, problems, settings, packs }) };
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* What the pack.json of each scope of `scopes` (and of each folder of components/ that holds one) names
|
|
637
|
+
* it: a Map, by scope, of { zh, en }, or null where it has none or one that does not read (a scope of
|
|
638
|
+
* blocks with no pack.json is a pack of its own by its scope's name: ./block-packs.mjs).
|
|
639
|
+
*/
|
|
640
|
+
export function packNames(root, scopes = []) {
|
|
641
|
+
const { components } = layoutOf(root);
|
|
642
|
+
let withFile = [];
|
|
643
|
+
try {
|
|
644
|
+
withFile = readdirSync(components).filter((name) => PACK_ID.test(name) && existsSync(path.join(components, name, PACK_FILE)));
|
|
645
|
+
} catch {}
|
|
646
|
+
const names = new Map();
|
|
647
|
+
for (const scope of [...new Set([...scopes, ...withFile])].sort()) {
|
|
648
|
+
let name = null;
|
|
649
|
+
try {
|
|
650
|
+
name = readPackManifest(readFileSync(path.join(components, scope, PACK_FILE), "utf8")).name;
|
|
651
|
+
} catch {}
|
|
652
|
+
names.set(scope, name);
|
|
653
|
+
}
|
|
654
|
+
return names;
|
|
406
655
|
}
|
|
407
656
|
|
|
408
657
|
const kept = new Map(); // a workspace's root -> the catalog in use
|
|
@@ -429,12 +678,18 @@ const bothProblems = (problem) => ({ zh: problemIn("zh", problem), en: problemIn
|
|
|
429
678
|
/** Why gallery.json cannot be read, in both languages, as 设置 › 组件列表 says it, or null. */
|
|
430
679
|
export const settingsProblem = ({ settings }) => (settings.problem ? bothProblems(settings.problem) : null);
|
|
431
680
|
|
|
432
|
-
// What a package is now: "normal"
|
|
433
|
-
// does not run), or "broken", one that
|
|
434
|
-
|
|
681
|
+
// What a package is now: "normal" (the document's menus offer it: inDocumentMenus), "hidden" (they do
|
|
682
|
+
// not), "disabled" (turned off, or for an apiVersion this Gallery does not run), or "broken", one that
|
|
683
|
+
// does not load of itself.
|
|
684
|
+
const stateOf = (found, settings) => (found.problem ? (found.disabled ? "disabled" : "broken") : settingState(found.id, settings, found.places));
|
|
435
685
|
|
|
436
|
-
/**
|
|
437
|
-
|
|
686
|
+
/**
|
|
687
|
+
* What the kit is told of a package: where its instances are, its parts, what it leaves on a page, how far
|
|
688
|
+
* its content's shape goes, and what it uses (`needs`, where it names any: the kit gives an instance
|
|
689
|
+
* context.workspace for "workspace", context.fetch for a host of the net and context.table() for
|
|
690
|
+
* "tables").
|
|
691
|
+
*/
|
|
692
|
+
export const descriptionOf = ({ id, match, parts, runtime, schema, needs }) => ({ id, match, parts, ...(runtime ? { runtime } : {}), ...(schema !== undefined ? { schema } : {}), ...(needs?.length ? { needs } : {}) });
|
|
438
693
|
|
|
439
694
|
/**
|
|
440
695
|
* What the kit is told of a catalog, each by DocKit.describe, the one way a page learns a package's
|
|
@@ -509,7 +764,7 @@ export function instanceMarkup(text, id) {
|
|
|
509
764
|
function readTemplate({ id, template, parts }) {
|
|
510
765
|
if (!template) return {};
|
|
511
766
|
const read = instanceMarkup(template, id);
|
|
512
|
-
if (!read) return { problem: said("template.html
|
|
767
|
+
if (!read) return { problem: said("template.html 要是这个自定义块的一个实例,有自己的结束标签,此外什么都没有") };
|
|
513
768
|
const held = [];
|
|
514
769
|
walk({ childNodes: [read.root] }, (node) => { if (refusedInCopy(node.tagName, node.attrs)) held.push(node.tagName); });
|
|
515
770
|
if (held.length > 0) return { problem: said("template.html 里的 <{0}> 进不了新建的块:脚本、事件、样式和 Gallery 自己的标记都不写在模板里", held[0]) };
|
|
@@ -520,7 +775,7 @@ function readTemplate({ id, template, parts }) {
|
|
|
520
775
|
return short ? { problem: said("template.html 的 {0} 和清单写的数目不符", short[0]) } : { markup: read.markup };
|
|
521
776
|
}
|
|
522
777
|
|
|
523
|
-
/** Why no menu offers the template of `found`, said()'s, for `gallery
|
|
778
|
+
/** Why no menu offers the template of `found`, said()'s, for `gallery block check` to say, or null: it has none, or one no reason keeps out. */
|
|
524
779
|
export const templateProblem = (found) => readTemplate(found).problem ?? null;
|
|
525
780
|
|
|
526
781
|
/** The markup of an instance of `found` as a new one is written, or null: it has no template a menu offers. */
|
|
@@ -530,12 +785,15 @@ export const templateOf = (found) => readTemplate(found).markup ?? null;
|
|
|
530
785
|
* What the page the editor runs on holds of a catalog: its MAP's `components` (src/editor/service.mjs),
|
|
531
786
|
* no route and no dynamic import. One entry for each package that loads, by id: { id, version, name:
|
|
532
787
|
* { zh, en }, description: { zh, en }, words, definition, stamp, template (the markup "/" puts in: one
|
|
533
|
-
* instance of it, or null), preview (the address of its picture, or null), state ("normal", or "hidden": a menu offers what is "normal" and reads no setting),
|
|
788
|
+
* instance of it, or null), preview (the address of its picture, or null), state ("normal", or "hidden": a menu offers what is "normal" and reads no setting), places (which the page's
|
|
789
|
+
* settingState reads with gallery.json's lists as its event stream says them), needs, markdown, assets (the kit's files it asks for,
|
|
534
790
|
* by id), tags (packageTags: what loads it in a page that was served without it, the line that tells the
|
|
535
|
-
* kit of its parts among them)
|
|
791
|
+
* kit of its parts among them), pack ({ id, name }: the pack it is of, its scope, and what its pack.json names
|
|
792
|
+
* it, null where none does: a menu writes the pack's name after a block's in grey where two packs offer
|
|
793
|
+
* blocks of one name) }. A page knows what it holds of a package's parts from the kit
|
|
536
794
|
* (descriptionsOf), and of one that came after it was served from these tags.
|
|
537
795
|
*/
|
|
538
|
-
export const menuOf = ({ packages, settings = NO_SETTINGS }) => [...packages.values()].sort(byId).map((found) => ({
|
|
796
|
+
export const menuOf = ({ packages, settings = NO_SETTINGS, packs = new Map() }) => [...packages.values()].sort(byId).map((found) => ({
|
|
539
797
|
id: found.id,
|
|
540
798
|
version: found.version,
|
|
541
799
|
name: found.name,
|
|
@@ -546,28 +804,47 @@ export const menuOf = ({ packages, settings = NO_SETTINGS }) => [...packages.val
|
|
|
546
804
|
template: templateOf(found),
|
|
547
805
|
preview: found.files.includes("preview.svg") ? packageAddress(found, "preview.svg") : null,
|
|
548
806
|
state: stateOf(found, settings),
|
|
807
|
+
places: found.places,
|
|
808
|
+
needs: found.needs,
|
|
549
809
|
markdown: found.markdown,
|
|
550
810
|
assets: found.assets,
|
|
551
811
|
tags: packageTags(found),
|
|
812
|
+
pack: { id: found.id.split("/")[0], name: packs.get(found.id.split("/")[0]) ?? null },
|
|
552
813
|
}));
|
|
553
814
|
|
|
554
815
|
/**
|
|
555
816
|
* The workspace's packages as who lists them says them, by id: { id, version, name, description (what
|
|
556
817
|
* a manifest says, null where it does not read), state ("normal", "hidden", "disabled" or "broken":
|
|
557
818
|
* stateOf), reason ({ zh, en }, why it does not load of itself, or null), template (whether it has one
|
|
558
|
-
* a menu can put in: with none, none can),
|
|
819
|
+
* a menu can put in: with none, none can), places and needs (what its manifest says, null where it does
|
|
820
|
+
* not read), hidden, shown, disabled and unlisted (what gallery.json says of it), allowed (the hosts of its needs
|
|
821
|
+
* the reader allowed it to reach, gallery.json's components.net: its switch 允许联网 is on while it holds
|
|
822
|
+
* every host its needs name) and menu (whether its switch
|
|
823
|
+
* 在文档菜单里显示 is on: inDocumentMenus, whatever `disabled` and `unlisted` say, which are switches of their
|
|
824
|
+
* own: the row's master switch is on while it is not unlisted, nor its pack's) and pack (the pack it is of: its scope) }.
|
|
559
825
|
*/
|
|
560
826
|
export const packageStates = ({ packages, problems, settings = NO_SETTINGS }) => [...packages.values(), ...problems.values()].sort(byId).map((found) => ({
|
|
561
827
|
id: found.id,
|
|
828
|
+
pack: found.id.split("/")[0],
|
|
562
829
|
version: found.version ?? null,
|
|
563
830
|
name: found.name ?? null,
|
|
564
831
|
description: found.description ?? null,
|
|
565
832
|
state: stateOf(found, settings),
|
|
566
833
|
reason: found.problem && !found.off ? bothProblems(found.problem) : null,
|
|
567
834
|
template: Boolean(templateOf(found)),
|
|
835
|
+
places: found.places ?? null,
|
|
836
|
+
needs: found.needs ?? null,
|
|
568
837
|
hidden: settings.hidden.includes(found.id),
|
|
838
|
+
shown: settings.shown.includes(found.id),
|
|
569
839
|
disabled: settings.disabled.includes(found.id),
|
|
840
|
+
unlisted: (settings.unlisted ?? []).includes(found.id),
|
|
841
|
+
allowed: (settings.net?.[found.id] ?? []).filter((host) => netHostsOf(found.needs).includes(host)),
|
|
842
|
+
menu: inDocumentMenus(found.places, { ...settings, disabled: [], unlisted: [], packsOff: [] }, found.id),
|
|
570
843
|
}));
|
|
571
844
|
|
|
572
|
-
/**
|
|
573
|
-
|
|
845
|
+
/**
|
|
846
|
+
* The message that tells a page of `settings` on its event stream, its six lists by commas (hidden,
|
|
847
|
+
* disabled, shown, the consents, "<id>@<host>" each, unlisted, and the packs off): what it offers follows
|
|
848
|
+
* them, whatever it holds, and an instance whose consent changed starts again (the kit's net.js).
|
|
849
|
+
*/
|
|
850
|
+
export const settingsLine = (settings) => `settings ${settings.hidden.join(",")} ${settings.disabled.join(",")} ${settings.shown.join(",")} ${consentPairs(settings).join(",")} ${(settings.unlisted ?? []).join(",")} ${(settings.packsOff ?? []).join(",")}`;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// What a component that hears `source` hears of attributes (src/render/kit/runtime/kernel.js;
|
|
2
|
+
// docs/render.md "A custom block on a page"): a change of an attribute in a part or a slot that says
|
|
3
|
+
// something of the file, and of a class only where a look class changes, so that what the kit and the
|
|
4
|
+
// editor toggle as they lay the page out is no change of the file's.
|
|
5
|
+
|
|
6
|
+
import { COLOR_NAMES } from "./colors.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The attributes that say something of the file in a part or a slot, which a component hearing
|
|
10
|
+
* `source` hears change (runtime/kernel.js): a to-do item checked, a cell's spans, what a table's
|
|
11
|
+
* header and cells say of its sort (the table description's data-sort-*, listed one by one, as an
|
|
12
|
+
* observer takes them). A class is one only where a look class changes (lookChanged); `hidden` and
|
|
13
|
+
* `open` are the reader's, which `reveal` tells.
|
|
14
|
+
*/
|
|
15
|
+
export const SOURCE_ATTRIBUTES = ["data-checked", "rowspan", "colspan", "data-sort-status", "data-sort-type", "data-sort-key", "data-sort-default", "data-sort-ignore", "data-sort-value", "data-sort-rank"];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The classes that are a block's or a cell's look, which a reader sets and the file keeps: a text color
|
|
19
|
+
* or a fill (doc-text-<color>, doc-highlight-<color>), a cell's alignments and its bold (table.mjs's
|
|
20
|
+
* CELL_ALIGN_CLASSES, CELL_VALIGN_CLASSES, CELL_BOLD_CLASS, the editor's CELL_LOOKS). What the kit and
|
|
21
|
+
* the editor toggle as they lay a page out (a table's number and doc-table--auto, a code block's
|
|
22
|
+
* folds) is none, so that no component hears its own layout as a change of the file, and draws again
|
|
23
|
+
* for ever.
|
|
24
|
+
*/
|
|
25
|
+
export const LOOK_CLASSES = new Set([
|
|
26
|
+
...COLOR_NAMES.flatMap((name) => [`doc-text-${name}`, `doc-highlight-${name}`]),
|
|
27
|
+
// table.mjs's CELL_ALIGN_CLASSES, CELL_VALIGN_CLASSES and CELL_BOLD_CLASS, written out: an import of that
|
|
28
|
+
// file would bring its description, prose and all, into the kit's runtime (tests/unit/installed.mjs
|
|
29
|
+
// holds the two lists together).
|
|
30
|
+
"doc-align-left", "doc-align-center", "doc-align-right", "doc-valign-top", "doc-valign-middle", "doc-valign-bottom", "doc-bold",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/** Whether a class attribute's look classes (LOOK_CLASSES) differ from `before` to `after`, either null for none. */
|
|
34
|
+
export function lookChanged(before, after) {
|
|
35
|
+
const looks = (value) => [...new Set(String(value ?? "").split(/\s+/).filter((name) => LOOK_CLASSES.has(name)))].sort().join(" ");
|
|
36
|
+
return looks(before) !== looks(after);
|
|
37
|
+
}
|