@heycys/gallery 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +2 -2
- package/browser/editor/components/align.mjs +58 -0
- package/browser/editor/components/code/index.mjs +1 -1
- package/browser/editor/components/columns/page.mjs +4 -4
- package/browser/editor/components/embed/index.mjs +2 -2
- package/browser/editor/components/embed/page.mjs +1 -1
- package/browser/editor/components/file/page.mjs +4 -4
- 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 +7 -6
- package/browser/editor/components/icons.mjs +11 -0
- package/browser/editor/components/image/crop.mjs +6 -9
- package/browser/editor/components/image/index.mjs +2 -0
- package/browser/editor/components/image/page.mjs +172 -90
- package/browser/editor/components/installed/index.mjs +15 -4
- package/browser/editor/components/installed/page.mjs +103 -25
- 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/media/icons.mjs +13 -8
- package/browser/editor/components/media/page.mjs +5 -3
- package/browser/editor/components/media/toolbar.mjs +12 -3
- package/browser/editor/components/paragraph/index.mjs +4 -3
- package/browser/editor/components/registry.mjs +30 -9
- package/browser/editor/components/table/index.mjs +61 -2
- package/browser/editor/components/table/page.mjs +1 -1
- package/browser/editor/page/after/index.mjs +288 -0
- package/browser/editor/page/align.mjs +91 -0
- package/browser/editor/page/annotate/card.mjs +3 -6
- package/browser/editor/page/autosave.mjs +310 -37
- package/browser/editor/page/beside.mjs +5 -6
- package/browser/editor/page/block-menu/index.mjs +6 -3
- package/browser/editor/page/blocks.mjs +3 -1
- package/browser/editor/page/board/client.mjs +57 -0
- package/browser/editor/page/board/hint.mjs +182 -0
- package/browser/editor/page/board/history.mjs +119 -0
- package/browser/editor/page/board/index.mjs +1059 -0
- package/browser/editor/page/board/motion.mjs +177 -0
- package/browser/editor/page/board/settings.mjs +210 -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 +133 -15
- package/browser/editor/page/clipboard/own/blocks.mjs +13 -14
- package/browser/editor/page/clipboard/own/chart-keys.mjs +41 -28
- package/browser/editor/page/clipboard/own/describe.mjs +6 -2
- 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 +34 -16
- package/browser/editor/page/custom-page.mjs +110 -0
- package/browser/editor/page/editor.mjs +71 -17
- 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 +41 -6
- package/browser/editor/page/insert/index.mjs +20 -22
- 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 +33 -14
- package/browser/editor/page/notices.mjs +2 -2
- package/browser/editor/page/page-objects.mjs +9 -2
- package/browser/editor/page/panel.mjs +4 -9
- package/browser/editor/page/present-links.mjs +15 -5
- package/browser/editor/page/present-shell.mjs +63 -0
- package/browser/editor/page/resize/index.mjs +42 -29
- package/browser/editor/page/selection/index.mjs +10 -2
- 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/binding.mjs +87 -0
- package/browser/editor/page/table/cells.mjs +102 -46
- package/browser/editor/page/table/index.mjs +24 -9
- package/browser/editor/page/table/pictures.mjs +299 -0
- package/browser/editor/page/table/views.mjs +103 -0
- package/browser/editor/page/table/widths.mjs +82 -10
- 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 +97 -15
- package/browser/editor/page/typing/leave.mjs +19 -5
- package/browser/editor/page/ui.mjs +13 -0
- package/browser/shared/block-packs.mjs +419 -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 +330 -46
- package/browser/shared/component-source.mjs +39 -0
- package/browser/shared/components/heading.mjs +2 -1
- package/browser/shared/components/index.mjs +12 -3
- package/browser/shared/components/installed.mjs +35 -3
- package/browser/shared/components/page.mjs +20 -8
- package/browser/shared/components/paragraph.mjs +8 -1
- package/browser/shared/components/state.mjs +5 -1
- package/browser/shared/components/table.mjs +1 -0
- package/browser/shared/document-look.mjs +10 -5
- package/browser/shared/drafts.mjs +432 -0
- package/browser/shared/emoji/line-icons.mjs +37 -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/http.mjs +9 -0
- package/browser/shared/i18n/en.mjs +330 -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 +359 -67
- package/browser/shared/pack-manifest.mjs +88 -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 +500 -0
- package/browser/shared/paths.mjs +6 -0
- package/browser/shared/pinyin.mjs +92 -0
- package/browser/shared/present-step.mjs +17 -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/sidebar-head.mjs +70 -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 +139 -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-2OHANJ6Q.mjs +2 -0
- package/dist/chunk-3MBRL2PM.mjs +16 -0
- package/dist/chunk-4OKWZY5E.mjs +2 -0
- package/dist/chunk-4OZWXZE5.mjs +5 -0
- package/dist/chunk-5K44HLTZ.mjs +2 -0
- package/dist/{chunk-5ZKJ3H4P.mjs → chunk-5XA2ZBAE.mjs} +3 -3
- package/dist/{chunk-4FS7Z7WR.mjs → chunk-67N2QJYT.mjs} +1 -1
- package/dist/chunk-6KBYRA2C.mjs +2 -0
- package/dist/chunk-6N3PX4XX.mjs +8 -0
- package/dist/{chunk-V5JTW2LH.mjs → chunk-6QBHU4DF.mjs} +1 -1
- package/dist/chunk-7JDX4E65.mjs +13 -0
- package/dist/chunk-7M2BQQGZ.mjs +2 -0
- package/dist/chunk-7OG26PGF.mjs +2 -0
- package/dist/chunk-AHN3NFKJ.mjs +3 -0
- package/dist/chunk-ALZ6ZENZ.mjs +3 -0
- package/dist/chunk-AR5D57ZD.mjs +170 -0
- package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
- package/dist/{chunk-LS4F7JUM.mjs → chunk-BZ5RAF7L.mjs} +1 -1
- package/dist/chunk-CMA72MAG.mjs +2 -0
- package/dist/{chunk-LCTHTZIL.mjs → chunk-DAZL73VK.mjs} +91 -91
- package/dist/{chunk-Y6RLRPG3.mjs → chunk-DJISHPUL.mjs} +1 -1
- package/dist/chunk-DR73Y2UJ.mjs +3 -0
- package/dist/{chunk-FDLXYYG4.mjs → chunk-EAIFITAV.mjs} +1 -1
- package/dist/{chunk-TMYZB6AV.mjs → chunk-EKNHM3P4.mjs} +1 -1
- package/dist/chunk-EP25HDAJ.mjs +2 -0
- package/dist/{chunk-GB5SOYQJ.mjs → chunk-ERLAIBOA.mjs} +2 -2
- package/dist/chunk-EURLTOTZ.mjs +4 -0
- package/dist/chunk-F4ZA6JRZ.mjs +3 -0
- package/dist/chunk-F6HVDZR4.mjs +2 -0
- package/dist/chunk-FDMAXMQA.mjs +2 -0
- package/dist/chunk-FETSREUN.mjs +5 -0
- package/dist/chunk-GHOYKMQ3.mjs +4 -0
- package/dist/{chunk-2B5E4CDJ.mjs → chunk-GPIWQ4X4.mjs} +1 -1
- package/dist/chunk-HDWTZ763.mjs +12 -0
- package/dist/chunk-HPOI4ZX7.mjs +6 -0
- package/dist/{chunk-BGGDRNYQ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
- package/dist/chunk-IA4PSIMD.mjs +2 -0
- package/dist/{chunk-E7LWZRXY.mjs → chunk-ITOH5EGO.mjs} +1 -1
- package/dist/chunk-IV64FFI4.mjs +2 -0
- package/dist/chunk-J43Z6LSS.mjs +2 -0
- package/dist/chunk-J5PUIV3Y.mjs +4 -0
- package/dist/chunk-JA7G53PC.mjs +2 -0
- package/dist/chunk-JEUK3ZO7.mjs +2 -0
- package/dist/chunk-JLTZF4LA.mjs +2 -0
- package/dist/chunk-JZT7I2OJ.mjs +2 -0
- package/dist/chunk-K5XATEOO.mjs +2 -0
- package/dist/chunk-L2SUIS5S.mjs +2 -0
- package/dist/chunk-L7UP52JO.mjs +2 -0
- package/dist/chunk-LM4XFQHZ.mjs +2 -0
- package/dist/chunk-MN7BZL4R.mjs +2 -0
- package/dist/chunk-MTYYHM2G.mjs +15 -0
- package/dist/chunk-MXUBKDRO.mjs +15 -0
- package/dist/chunk-NAMN3RAF.mjs +2 -0
- package/dist/{chunk-SYGN7GLU.mjs → chunk-OEXWRJME.mjs} +1 -1
- package/dist/chunk-OFHXGWOX.mjs +2 -0
- package/dist/chunk-ORID3AE7.mjs +166 -0
- package/dist/chunk-PAREWQGB.mjs +4 -0
- package/dist/chunk-PGDE24N5.mjs +2 -0
- package/dist/chunk-POTA2FV4.mjs +2 -0
- package/dist/chunk-PUOQ65NC.mjs +2 -0
- package/dist/chunk-Q4UEPUWF.mjs +2 -0
- package/dist/chunk-QE5RG76S.mjs +549 -0
- package/dist/chunk-R2ZDLJYT.mjs +10 -0
- package/dist/{chunk-7DTINW6U.mjs → chunk-SQ6Y2ZMA.mjs} +2 -2
- package/dist/chunk-SWUTTZCN.mjs +2 -0
- package/dist/chunk-TANZRINB.mjs +2 -0
- package/dist/chunk-TR7TWGHB.mjs +2 -0
- package/dist/chunk-U5XL7D6A.mjs +3 -0
- package/dist/{chunk-XNROSH2T.mjs → chunk-U7NZI6N4.mjs} +2 -2
- package/dist/chunk-UGWMMEJ5.mjs +15 -0
- package/dist/chunk-UJQJKNGK.mjs +2 -0
- package/dist/chunk-UYVCQK3H.mjs +22 -0
- package/dist/chunk-VFX2KNHP.mjs +4 -0
- package/dist/chunk-VS7JIGJW.mjs +24 -0
- package/dist/chunk-W7665ZEC.mjs +6 -0
- package/dist/chunk-WQDQWCX5.mjs +2 -0
- package/dist/chunk-YI43RG4P.mjs +2 -0
- package/dist/chunk-YLPSMDIS.mjs +5 -0
- package/dist/chunk-YNAWBAYN.mjs +3 -0
- package/dist/chunk-YROX2O22.mjs +2 -0
- package/dist/chunk-ZDLPUOAG.mjs +6 -0
- package/dist/{chunk-IVDLMMBD.mjs → chunk-ZHEHPMBK.mjs} +1 -1
- package/dist/chunk-ZQS6H473.mjs +16 -0
- package/dist/chunk-ZTHICSSF.mjs +2 -0
- package/dist/chunk-ZWWC4KLF.mjs +20 -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 +17 -17
- package/kit/report.css +335 -18
- package/kit/runtime.js +1638 -125
- package/kit/state.js +10 -1
- package/kit/tables.js +130 -43
- package/kit/ui.js +79 -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 +216 -0
- package/packs/gallery/countdown/preview.svg +1 -0
- package/packs/gallery/countdown/style.css +118 -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 +334 -0
- package/packs/gallery/habit/preview.svg +1 -0
- package/packs/gallery/habit/style.css +174 -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 +513 -0
- package/packs/gallery/heatmap/preview.svg +1 -0
- package/packs/gallery/heatmap/style.css +189 -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 +129 -0
- package/packs/gallery/note/preview.svg +1 -0
- package/packs/gallery/note/style.css +97 -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 +293 -0
- package/packs/gallery/recent/preview.svg +1 -0
- package/packs/gallery/recent/style.css +85 -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 +171 -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 +347 -0
- package/packs/gallery/stats/preview.svg +1 -0
- package/packs/gallery/stats/style.css +73 -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 +220 -0
- package/packs/gallery/today/preview.svg +1 -0
- package/packs/gallery/today/style.css +149 -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 +489 -0
- package/packs/gallery/todos/preview.svg +1 -0
- package/packs/gallery/todos/style.css +214 -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 +387 -0
- package/packs/gallery/weather/preview.svg +1 -0
- package/packs/gallery/weather/style.css +140 -0
- package/packs/gallery/weather/template.html +3 -0
- package/packs/gallery/weather/writing.md +26 -0
- package/packs/gallery-soft/countdown/component.json +18 -0
- package/packs/gallery-soft/countdown/example.html +3 -0
- package/packs/gallery-soft/countdown/kit.js +206 -0
- package/packs/gallery-soft/countdown/preview.svg +1 -0
- package/packs/gallery-soft/countdown/style.css +211 -0
- package/packs/gallery-soft/countdown/template.html +3 -0
- package/packs/gallery-soft/countdown/writing.md +24 -0
- package/packs/gallery-soft/habit/component.json +14 -0
- package/packs/gallery-soft/habit/example.html +3 -0
- package/packs/gallery-soft/habit/kit.js +366 -0
- package/packs/gallery-soft/habit/preview.svg +1 -0
- package/packs/gallery-soft/habit/style.css +251 -0
- package/packs/gallery-soft/habit/template.html +3 -0
- package/packs/gallery-soft/habit/writing.md +23 -0
- package/packs/gallery-soft/heatmap/component.json +18 -0
- package/packs/gallery-soft/heatmap/example.html +3 -0
- package/packs/gallery-soft/heatmap/kit.js +498 -0
- package/packs/gallery-soft/heatmap/preview.svg +1 -0
- package/packs/gallery-soft/heatmap/style.css +181 -0
- package/packs/gallery-soft/heatmap/template.html +3 -0
- package/packs/gallery-soft/heatmap/writing.md +23 -0
- package/packs/gallery-soft/note/component.json +14 -0
- package/packs/gallery-soft/note/example.html +3 -0
- package/packs/gallery-soft/note/kit.js +162 -0
- package/packs/gallery-soft/note/preview.svg +1 -0
- package/packs/gallery-soft/note/style.css +148 -0
- package/packs/gallery-soft/note/template.html +3 -0
- package/packs/gallery-soft/note/writing.md +20 -0
- package/packs/gallery-soft/pack.json +8 -0
- package/packs/gallery-soft/recent/component.json +13 -0
- package/packs/gallery-soft/recent/example.html +1 -0
- package/packs/gallery-soft/recent/kit.js +335 -0
- package/packs/gallery-soft/recent/preview.svg +1 -0
- package/packs/gallery-soft/recent/style.css +160 -0
- package/packs/gallery-soft/recent/template.html +1 -0
- package/packs/gallery-soft/recent/writing.md +17 -0
- package/packs/gallery-soft/static/component.json +13 -0
- package/packs/gallery-soft/static/example.html +12 -0
- package/packs/gallery-soft/static/preview.svg +1 -0
- package/packs/gallery-soft/static/style.css +186 -0
- package/packs/gallery-soft/static/template.html +1 -0
- package/packs/gallery-soft/static/writing.md +20 -0
- package/packs/gallery-soft/stats/component.json +13 -0
- package/packs/gallery-soft/stats/example.html +1 -0
- package/packs/gallery-soft/stats/kit.js +341 -0
- package/packs/gallery-soft/stats/preview.svg +1 -0
- package/packs/gallery-soft/stats/style.css +119 -0
- package/packs/gallery-soft/stats/template.html +1 -0
- package/packs/gallery-soft/stats/writing.md +13 -0
- package/packs/gallery-soft/today/component.json +12 -0
- package/packs/gallery-soft/today/example.html +1 -0
- package/packs/gallery-soft/today/kit.js +222 -0
- package/packs/gallery-soft/today/preview.svg +1 -0
- package/packs/gallery-soft/today/style.css +178 -0
- package/packs/gallery-soft/today/template.html +1 -0
- package/packs/gallery-soft/today/writing.md +16 -0
- package/packs/gallery-soft/todos/component.json +19 -0
- package/packs/gallery-soft/todos/example.html +3 -0
- package/packs/gallery-soft/todos/kit.js +527 -0
- package/packs/gallery-soft/todos/preview.svg +1 -0
- package/packs/gallery-soft/todos/style.css +212 -0
- package/packs/gallery-soft/todos/template.html +3 -0
- package/packs/gallery-soft/todos/writing.md +32 -0
- package/packs/gallery-soft/weather/component.json +19 -0
- package/packs/gallery-soft/weather/example.html +3 -0
- package/packs/gallery-soft/weather/kit.js +411 -0
- package/packs/gallery-soft/weather/preview.svg +1 -0
- package/packs/gallery-soft/weather/style.css +185 -0
- package/packs/gallery-soft/weather/template.html +3 -0
- package/packs/gallery-soft/weather/writing.md +28 -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 +225 -0
- package/skill/references/writing-html.md +8 -4
- package/skill/sdk/doc-kit.d.ts +396 -5
- package/skill/sdk/doc-state.d.ts +2 -0
- package/web/assets/index-BM_K-Auu.css +1 -0
- package/web/assets/index-DRslOBiv.js +15 -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-JVHHHGEA.mjs +0 -3
- 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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gallery/note",
|
|
3
|
+
"apiVersion": 1,
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"name": { "zh": "便签", "en": "Note" },
|
|
6
|
+
"description": { "zh": "几行随手记,在卡片上直接写,存在页面里", "en": "A few lines jotted down, typed on the card and kept in the page" },
|
|
7
|
+
"words": "bianqian note memo sticky 便签 笔记 随手记 记事",
|
|
8
|
+
"parts": {
|
|
9
|
+
"note": { "at": ":scope > script", "edit": "data" }
|
|
10
|
+
},
|
|
11
|
+
"runtime": { "generated": [":scope > .gallery-note-head", ":scope > .gallery-note-field", ":scope > .gallery-note-foot"] },
|
|
12
|
+
"places": ["page"],
|
|
13
|
+
"card": { "sizes": [[4, 2], [2, 2], [4, 4]], "min": { "w": 2, "h": 2 }, "max": { "w": 4, "h": 8 }, "resize": true }
|
|
14
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// gallery/note (便签): a few lines of free text, kept in the instance's data part, a script of JSON,
|
|
2
|
+
// { "text": "…" }, and typed on the card itself, in a <textarea> the block draws under its head.
|
|
3
|
+
// The field is the kit's (DocKit.input): what the reader typed goes to the file once they are done with
|
|
4
|
+
// it, as they leave the field or press Ctrl+Enter (⌘Enter on a Mac; Enter alone is a new line), never
|
|
5
|
+
// while an input method composes, and never a value it holds already; Esc puts back what the file holds.
|
|
6
|
+
// Each of those is one context.editJSON, one undo step, the JSON written back on one line as it was. It
|
|
7
|
+
// draws from the part on every `source` (an Agent's write, an undo), the words the reader is typing kept
|
|
8
|
+
// where they are. Where nothing takes its writes (an export, a page that has not come to edit yet:
|
|
9
|
+
// context.canEdit() false) the field is read-only, and shows the same words. An empty note says 写点什么….
|
|
10
|
+
// Above the field its head (the pack's design B: the kit's card tokens): the chip, a pencil, its name,
|
|
11
|
+
// 便签, and its quiet meta after it, how many characters the field holds (its blanks not counted, said
|
|
12
|
+
// again as the reader types; none on a card two cells wide, `data-layout` "narrow" by context.size, read
|
|
13
|
+
// again on `layout`). Its lines are ruled faintly, a hint that it takes writing (style.css); while the
|
|
14
|
+
// reader types, a line under the field says the two keys (Ctrl+Enter 保存, Esc 还原; ⌘Enter on a Mac), and
|
|
15
|
+
// the card is ringed in the accent's wash; scrolled down, the field's top fades (data-scrolled). Nothing of
|
|
16
|
+
// the head or the foot is written.
|
|
17
|
+
|
|
18
|
+
const WORDS = {
|
|
19
|
+
zh: { empty: "写点什么…", label: "便签", count: (/** @type {number} */ count) => `${count} 字`, save: "保存", undo: "还原" },
|
|
20
|
+
en: { empty: "Write something…", label: "Note", count: (/** @type {number} */ count) => `${count} ${count === 1 ? "character" : "characters"}`, save: "save", undo: "revert" },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
|
|
24
|
+
function languageOf(/** @type {HTMLElement} */ root) {
|
|
25
|
+
const ui = root.getAttribute("data-ge-language");
|
|
26
|
+
return (ui !== null ? ui === "en" : /^en(?:-|$)/i.test(root.lang)) ? "en" : "zh";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The words a note's data holds (context.readJSON's): its `text`, "" where it has none yet; throws
|
|
31
|
+
* saying why for data that is no note, which the block does not draw over.
|
|
32
|
+
* @param {any} data
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function textOf(data) {
|
|
36
|
+
if (data === null || typeof data !== "object" || Array.isArray(data)) throw new Error("its data is no object: a note holds { \"text\": \"…\" }");
|
|
37
|
+
if (data.text === undefined) return "";
|
|
38
|
+
if (typeof data.text !== "string") throw new Error("its text is no string");
|
|
39
|
+
return data.text;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The note made to hold `text`, one write (context.editJSON): what a reader's words, once they are done
|
|
44
|
+
* with the field, call, and the example too. Whether it was taken.
|
|
45
|
+
* @param {DocComponentContext} context
|
|
46
|
+
* @param {string} text
|
|
47
|
+
*/
|
|
48
|
+
function writeNote(context, text) {
|
|
49
|
+
return context.editJSON("note", (data) => {
|
|
50
|
+
textOf(data);
|
|
51
|
+
data.text = text;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** How many characters `text` holds, its blanks (spaces, new lines) not counted. @param {string} text */
|
|
56
|
+
const countOf = (text) => [...text.replace(/\s+/g, "")].length;
|
|
57
|
+
|
|
58
|
+
DocKit.component({
|
|
59
|
+
id: "gallery/note",
|
|
60
|
+
on: ["source", "editable", "layout"],
|
|
61
|
+
mount(host, context) {
|
|
62
|
+
const words = WORDS[languageOf(document.documentElement)];
|
|
63
|
+
const head = document.createElement("div");
|
|
64
|
+
head.className = "gallery-note-head";
|
|
65
|
+
head.setAttribute("aria-hidden", "true");
|
|
66
|
+
const chip = document.createElement("span");
|
|
67
|
+
chip.className = "gallery-note-chip";
|
|
68
|
+
chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 19.5h3.8L19 8.8a2.4 2.4 0 0 0-3.4-3.4L4.9 16.1z"/><path d="M14 7l3 3"/></svg>';
|
|
69
|
+
const title = document.createElement("span");
|
|
70
|
+
title.className = "gallery-note-title";
|
|
71
|
+
title.textContent = words.label;
|
|
72
|
+
// Its quiet meta after the title: how many characters the field holds (its blanks not counted), as the reader types too.
|
|
73
|
+
const meta = document.createElement("span");
|
|
74
|
+
meta.className = "gallery-note-meta";
|
|
75
|
+
head.append(chip, title, meta);
|
|
76
|
+
const field = document.createElement("textarea");
|
|
77
|
+
field.className = "gallery-note-field";
|
|
78
|
+
field.placeholder = words.empty;
|
|
79
|
+
field.spellcheck = false;
|
|
80
|
+
field.setAttribute("aria-label", words.label);
|
|
81
|
+
// While the reader types: the two keys, Ctrl+Enter (⌘Enter on a Mac) and Esc.
|
|
82
|
+
const foot = document.createElement("div");
|
|
83
|
+
foot.className = "gallery-note-foot";
|
|
84
|
+
foot.setAttribute("aria-hidden", "true");
|
|
85
|
+
const key = (/** @type {string} */ name, /** @type {string} */ does) => {
|
|
86
|
+
const said = document.createElement("span");
|
|
87
|
+
const bold = document.createElement("b");
|
|
88
|
+
bold.textContent = name;
|
|
89
|
+
said.append(bold, ` ${does}`);
|
|
90
|
+
return said;
|
|
91
|
+
};
|
|
92
|
+
foot.append(key(document.documentElement.dataset.docPlatform === "mac" ? "⌘Enter" : "Ctrl+Enter", words.save), key("Esc", words.undo));
|
|
93
|
+
host.append(head, field, foot);
|
|
94
|
+
const typed = DocKit.input(field, { signal: context.signal, commit: (value) => writeNote(context, value) });
|
|
95
|
+
const recount = () => {
|
|
96
|
+
const count = countOf(field.value);
|
|
97
|
+
const said = count > 0 ? words.count(count) : "";
|
|
98
|
+
if (meta.textContent !== said) meta.textContent = said;
|
|
99
|
+
};
|
|
100
|
+
field.addEventListener("input", recount);
|
|
101
|
+
context.signal?.addEventListener("abort", () => field.removeEventListener("input", recount), { once: true });
|
|
102
|
+
// Scrolled down, its top fades (style.css), so that a line it scrolled past is never cut hard.
|
|
103
|
+
const scrolled = () => field.toggleAttribute("data-scrolled", field.scrollTop > 0);
|
|
104
|
+
field.addEventListener("scroll", scrolled, { passive: true });
|
|
105
|
+
context.signal?.addEventListener("abort", () => field.removeEventListener("scroll", scrolled), { once: true });
|
|
106
|
+
const draw = (/** @type {string} */ reason = "") => {
|
|
107
|
+
const layout = context.size && context.size.w <= 2 ? "narrow" : "wide";
|
|
108
|
+
if (head.dataset.layout !== layout) head.dataset.layout = layout;
|
|
109
|
+
if (reason === "layout") return;
|
|
110
|
+
const data = context.readJSON("note");
|
|
111
|
+
if (data === undefined) return; // no JSON: the instance has failed, and Gallery shows what it holds
|
|
112
|
+
let text;
|
|
113
|
+
try {
|
|
114
|
+
text = textOf(data);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return context.fail(error);
|
|
117
|
+
}
|
|
118
|
+
field.readOnly = !context.canEdit();
|
|
119
|
+
typed.set(text);
|
|
120
|
+
recount();
|
|
121
|
+
scrolled();
|
|
122
|
+
};
|
|
123
|
+
draw();
|
|
124
|
+
return { update: draw };
|
|
125
|
+
},
|
|
126
|
+
example(host, context) {
|
|
127
|
+
writeNote(context, `${textOf(context.readJSON("note"))}\n再加一行`);
|
|
128
|
+
},
|
|
129
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="16.5" y="12.5" width="207" height="95" rx="8" fill="#ffffff" stroke="#e6e6e3"/><rect x="28" y="22" width="10" height="10" rx="3" fill="#efefed"/><rect x="42" y="24.5" width="22" height="5" rx="2.5" fill="#37352f"/><rect x="68" y="24.5" width="18" height="5" rx="2.5" fill="#d4d3cf"/><line x1="28" x2="212" y1="57" y2="57" stroke="#ededeb"/><rect x="28" y="47" width="24" height="6" rx="3" fill="#37352f"/><line x1="28" x2="212" y1="73" y2="73" stroke="#ededeb"/><rect x="28" y="63" width="112" height="6" rx="3" fill="#37352f"/><line x1="28" x2="212" y1="89" y2="89" stroke="#ededeb"/><rect x="28" y="79" width="96" height="6" rx="3" fill="#37352f"/></svg>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* Every selector starts at the instance and goes down a step at a time (>), so that nothing here
|
|
2
|
+
reaches the rest of the page. The pack's design B, in the kit's card tokens: its head (the chip, a
|
|
3
|
+
pencil, and 便签, how many characters it holds its quiet meta after it, none on a narrow card), its end
|
|
4
|
+
clear of the card's grip, and under it the field the script draws, the rest of the note's face: no
|
|
5
|
+
border and no fill of its own, words half a size larger than the other cards' on wide lines (26px, a
|
|
6
|
+
faint rule under each, scrolling with them: a hint that it takes writing), its placeholder (写点什么…)
|
|
7
|
+
faint, the caret the accent's. While the reader types the card is ringed in the accent's wash and a line
|
|
8
|
+
under the field says its two keys; scrolled down, the field's top fades over 8px. On a custom page it is the card's whole face, taken out over the card's padding so that
|
|
9
|
+
its ring is the card's edge; where no card is around it (a document, as gallery block check shows it) the
|
|
10
|
+
instance frames itself, a few lines high. Light and dark. */
|
|
11
|
+
[data-doc-component="gallery/note"] {
|
|
12
|
+
--gallery-note-rule: light-dark(rgb(55 53 47 / 7.5%), rgb(255 255 255 / 6%));
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
padding: 16px;
|
|
17
|
+
border-radius: var(--gallery-card-radius);
|
|
18
|
+
box-shadow: var(--gallery-card-shadow);
|
|
19
|
+
font-family: var(--gallery-font);
|
|
20
|
+
font-size: 13px;
|
|
21
|
+
line-height: 1.5;
|
|
22
|
+
color: var(--gallery-text);
|
|
23
|
+
> .gallery-note-head {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
flex: none;
|
|
28
|
+
height: 20px;
|
|
29
|
+
min-width: 0;
|
|
30
|
+
padding-inline-end: var(--gallery-grip-room, 0px);
|
|
31
|
+
}
|
|
32
|
+
> .gallery-note-head > .gallery-note-chip {
|
|
33
|
+
flex: none;
|
|
34
|
+
display: grid;
|
|
35
|
+
place-items: center;
|
|
36
|
+
width: var(--gallery-chip-size);
|
|
37
|
+
height: var(--gallery-chip-size);
|
|
38
|
+
border-radius: var(--gallery-chip-radius);
|
|
39
|
+
background: var(--gallery-fill);
|
|
40
|
+
color: var(--gallery-muted);
|
|
41
|
+
}
|
|
42
|
+
> .gallery-note-head > .gallery-note-chip > svg { display: block; width: var(--gallery-chip-icon); height: var(--gallery-chip-icon); }
|
|
43
|
+
> .gallery-note-head > .gallery-note-title {
|
|
44
|
+
min-width: 0;
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
white-space: nowrap;
|
|
47
|
+
text-overflow: ellipsis;
|
|
48
|
+
font-size: var(--gallery-title-size);
|
|
49
|
+
line-height: var(--gallery-title-line);
|
|
50
|
+
font-weight: 600;
|
|
51
|
+
color: var(--gallery-text);
|
|
52
|
+
}
|
|
53
|
+
> .gallery-note-head > .gallery-note-meta { flex: none; font-size: var(--gallery-meta-size); color: var(--gallery-faint); white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
54
|
+
> .gallery-note-head[data-layout="narrow"] > .gallery-note-meta { display: none; }
|
|
55
|
+
> .gallery-note-field {
|
|
56
|
+
display: block;
|
|
57
|
+
flex: 1 1 auto;
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
width: 100%;
|
|
60
|
+
min-height: 104px;
|
|
61
|
+
margin: 8px 0 0;
|
|
62
|
+
padding: 0;
|
|
63
|
+
border: 0;
|
|
64
|
+
outline: 0;
|
|
65
|
+
resize: none;
|
|
66
|
+
background: transparent linear-gradient(to bottom, transparent 25px, var(--gallery-note-rule) 25px, var(--gallery-note-rule) 26px, transparent 26px) 0 0 / 100% 26px local;
|
|
67
|
+
font: inherit;
|
|
68
|
+
font-size: 13.5px;
|
|
69
|
+
line-height: 26px;
|
|
70
|
+
color: var(--gallery-text);
|
|
71
|
+
caret-color: var(--gallery-accent);
|
|
72
|
+
overflow-wrap: break-word;
|
|
73
|
+
scrollbar-width: none;
|
|
74
|
+
}
|
|
75
|
+
> .gallery-note-field[data-scrolled] { mask-image: linear-gradient(to bottom, transparent, #000 8px); }
|
|
76
|
+
> .gallery-note-head[data-layout="narrow"] ~ .gallery-note-field { font-size: 13px; }
|
|
77
|
+
> .gallery-note-field::placeholder { color: var(--gallery-faint); opacity: 1; }
|
|
78
|
+
> .gallery-note-field:read-only { cursor: default; }
|
|
79
|
+
> .gallery-note-foot { display: none; flex: none; gap: 12px; padding-top: 6px; font-size: 11.5px; line-height: 16px; color: var(--gallery-faint); white-space: nowrap; }
|
|
80
|
+
> .gallery-note-foot b { font-weight: 600; color: var(--gallery-muted); }
|
|
81
|
+
> .gallery-note-field:focus:not(:read-only) + .gallery-note-foot { display: flex; }
|
|
82
|
+
&:has(> .gallery-note-field:focus:not(:read-only)) { box-shadow: var(--gallery-card-shadow), 0 0 0 2px var(--gallery-accent-wash); }
|
|
83
|
+
}
|
|
84
|
+
/* On a custom page the board draws the frame: the note is the card's whole face, out over its padding, its
|
|
85
|
+
ring (while the reader types) at the card's edge, inside it. */
|
|
86
|
+
.gallery-card > [data-doc-component="gallery/note"] {
|
|
87
|
+
height: calc(100% + 2 * var(--gallery-card-pad));
|
|
88
|
+
margin: calc(-1 * var(--gallery-card-pad));
|
|
89
|
+
padding: var(--gallery-card-pad);
|
|
90
|
+
box-shadow: none;
|
|
91
|
+
/* The field as high as whole lines fit under the head (20px and 8px), and over the keys' line (22px) while
|
|
92
|
+
the reader types, so that a line scrolled to is never cut at the top: on a 4 × 2 card three lines,
|
|
93
|
+
two while typing. */
|
|
94
|
+
> .gallery-note-field { flex: none; min-height: 0; height: max(26px, round(down, calc(100% - 28px), 26px)); }
|
|
95
|
+
> .gallery-note-field:focus:not(:read-only) { height: max(26px, round(down, calc(100% - 50px), 26px)); }
|
|
96
|
+
&:has(> .gallery-note-field:focus:not(:read-only)) { box-shadow: inset 0 0 0 2px var(--gallery-accent-wash); }
|
|
97
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 便签 (gallery/note)
|
|
2
|
+
|
|
3
|
+
A card for a custom page (its places are `page`): a few lines the user types on the card itself, kept in the page. Use it where the user wants to jot something down beside the rest of a page (a reminder, a list for today).
|
|
4
|
+
|
|
5
|
+
## How to write it
|
|
6
|
+
|
|
7
|
+
Put it on a custom page as a card of its board, `<section class="gallery-card" data-card-id="…" data-x data-y data-w data-h>`, holding one instance, after `gallery block add note`, the instance as template.html has it: its data part, a `<script type="application/json">` holding `{"text":"…"}` (on one line and compact, as the block writes it back), the note's words, lines apart with `\n`; `{"text":""}` for an empty one, which shows 写点什么.
|
|
8
|
+
|
|
9
|
+
- Leave the words of a note that is there alone: they are the user's. To start one with words of yours (a list the user asked for), write them in `text`, as example.html does.
|
|
10
|
+
- Sizes: 4 × 2 (the default), 2 × 2 (no count of characters) and 4 × 4 are the ones its menu lists; any height from 2 to 8 cells, 2 to 4 wide, is one too (the card's corner reaches each). Words that do not fit scroll inside the card.
|
|
11
|
+
|
|
12
|
+
## What the user does
|
|
13
|
+
|
|
14
|
+
They click the card and type. What they typed is written to the page once they leave the card, or press Ctrl+Enter (⌘Enter on a Mac); Enter alone starts a new line, and Esc puts back what was there. Each write is one step of the page's undo. Nothing is written while a Chinese input method is composing.
|
|
15
|
+
|
|
16
|
+
## How it looks
|
|
17
|
+
|
|
18
|
+
In Gallery 风格's look (the kit's card tokens, light and dark): a head of a small gray chip and a title, its end clear of the card's grip; one hero; the rest small and gray; the accent on the data and on today alone. Its head is a pencil's chip, 便签 and how many characters it holds; under it the words, half a size larger than the other cards' on wide 26px lines, each ruled faintly (a hint that it takes writing, not a yellow paper). While the reader types the card is ringed in the accent's wash and a line under the field says the two keys (Ctrl+Enter 保存, Esc 还原; ⌘Enter on a Mac). Empty, it says 写点什么… faintly.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gallery",
|
|
3
|
+
"name": { "zh": "Gallery 风格", "en": "Gallery style" },
|
|
4
|
+
"description": { "zh": "Gallery 自己的样子:克制、清楚的首页卡片,以及文档里用的看板、指标、图表、卡片和图表卡。", "en": "Gallery's own look: quiet, clear cards for the home page, and the board, metric, chart, card and chart card for documents." },
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"author": "Gallery",
|
|
7
|
+
"icon": "🗂️"
|
|
8
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "gallery/recent",
|
|
3
|
+
"apiVersion": 1,
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"name": { "zh": "最近编辑", "en": "Recently edited" },
|
|
6
|
+
"description": { "zh": "最近改过的文档,点一篇就打开它", "en": "The documents edited last: click one to open it" },
|
|
7
|
+
"words": "zuijinbianji zuijin bianji recent recently edited documents 最近 最近编辑 文档 改过",
|
|
8
|
+
"parts": {},
|
|
9
|
+
"runtime": { "generated": [":scope > .gallery-recent-face"] },
|
|
10
|
+
"places": ["page"],
|
|
11
|
+
"card": { "sizes": [[4, 4], [4, 2], [4, 6]], "min": { "w": 4, "h": 2 }, "max": { "w": 4, "h": 8 }, "resize": true },
|
|
12
|
+
"needs": ["workspace"]
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div data-doc-component="gallery/recent"></div>
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// gallery/recent (最近编辑): the workspace's documents edited last, the last first, read through
|
|
2
|
+
// context.workspace.query("recent") (its manifest's needs hold "workspace"; and "stats", for how many
|
|
3
|
+
// there are in all): each its page icon, its title and when it was edited. A click opens the document
|
|
4
|
+
// (context.workspace.open: the workbench goes there). It reads again whenever the workspace says the list
|
|
5
|
+
// changed (the `data` signal, "recent": a document written, renamed, given another icon, made or gone), and
|
|
6
|
+
// says the times again each minute (the kit's clock). It holds no part and writes nothing: all it shows is
|
|
7
|
+
// drawn (the manifest's `runtime`). Where the workspace cannot be read (an exported file, where it has no
|
|
8
|
+
// server; a server that does not answer) it says so. The words are the page's language (the UI's where
|
|
9
|
+
// Gallery says it, else the page's lang).
|
|
10
|
+
//
|
|
11
|
+
// Its face (the pack's design B, in the kit's card tokens): a head of the chip (a clock), 最近编辑 and how
|
|
12
|
+
// many of them were edited today, its quiet meta (今天 3 篇); then rows like the sidebar's, grouped by
|
|
13
|
+
// when (今天, 昨天, 本周, 更早: `groupOf`), each its icon, its title and its time at its end, said by its
|
|
14
|
+
// group (`whenOf`: today's 12 分钟前, or the hour once an hour has gone, 08:05; yesterday's its hour; this
|
|
15
|
+
// week's its weekday, 周二; earlier ones the date, 11月7日); under them, faint, how many more there are (还有
|
|
16
|
+
// 20 篇). The card shows whole rows only, as many as the room under its head holds with their groups'
|
|
17
|
+
// titles (`fit`, measured again as the card changes size: its own ResizeObserver on the face). A card two
|
|
18
|
+
// cells high (`data-layout` "short", by context.size) lists the last three, no groups, their times as a
|
|
19
|
+
// person says them (昨天, 周二). No document yet: the cards' empty face.
|
|
20
|
+
//
|
|
21
|
+
// A document's icon is drawn as Gallery's sidebar draws it, from the server that shows the page (its icon
|
|
22
|
+
// route, /__gallery/edit/icon, one picture of any icon's value): an emoji or a picture of its folder as a
|
|
23
|
+
// picture, one of Gallery's glyphs as a mask in the muted color, which follows the theme; a document with
|
|
24
|
+
// none a page drawn in its line.
|
|
25
|
+
|
|
26
|
+
const WORDS = {
|
|
27
|
+
zh: {
|
|
28
|
+
title: "最近编辑",
|
|
29
|
+
empty: ["还没有文档", "新建一篇,或者让 Agent 写一篇"],
|
|
30
|
+
offline: ["在 Gallery 里打开时显示", "这里会列出最近改过的文档"],
|
|
31
|
+
failed: ["读不到工作区的文档", "过一会儿会再读"],
|
|
32
|
+
more: (/** @type {number} */ count) => `还有 ${count} 篇`,
|
|
33
|
+
today: (/** @type {number} */ count) => `今天 ${count} 篇`,
|
|
34
|
+
open: (/** @type {string} */ title) => `打开「${title}」`,
|
|
35
|
+
groups: { today: "今天", yesterday: "昨天", week: "本周", earlier: "更早" },
|
|
36
|
+
now: "刚刚",
|
|
37
|
+
minutes: (/** @type {number} */ count) => `${count} 分钟前`,
|
|
38
|
+
yesterday: "昨天",
|
|
39
|
+
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
|
|
40
|
+
date: (/** @type {Date} */ date, /** @type {boolean} */ thisYear) => (thisYear ? `${date.getMonth() + 1}月${date.getDate()}日` : `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`),
|
|
41
|
+
},
|
|
42
|
+
en: {
|
|
43
|
+
title: "Recently edited",
|
|
44
|
+
empty: ["No documents yet", "Make one, or ask your Agent to write one"],
|
|
45
|
+
offline: ["Shown when opened in Gallery", "The documents edited last show here"],
|
|
46
|
+
failed: ["The workspace's documents could not be read", "It reads them again in a while"],
|
|
47
|
+
more: (/** @type {number} */ count) => `${count} more`,
|
|
48
|
+
today: (/** @type {number} */ count) => `${count} today`,
|
|
49
|
+
open: (/** @type {string} */ title) => `Open “${title}”`,
|
|
50
|
+
groups: { today: "Today", yesterday: "Yesterday", week: "This week", earlier: "Earlier" },
|
|
51
|
+
now: "just now",
|
|
52
|
+
minutes: (/** @type {number} */ count) => `${count} min ago`,
|
|
53
|
+
yesterday: "Yesterday",
|
|
54
|
+
weekdays: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
|
55
|
+
date: (/** @type {Date} */ date, /** @type {boolean} */ thisYear) => `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][date.getMonth()]} ${date.getDate()}${thisYear ? "" : `, ${date.getFullYear()}`}`,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
/** The documents it asks for: as many as the tallest card shows, and more. */
|
|
59
|
+
const LIMIT = 50;
|
|
60
|
+
/** A group's title and a row, in px (style.css). */
|
|
61
|
+
const GROUP_HEIGHT = 26;
|
|
62
|
+
const ROW_HEIGHT = 30;
|
|
63
|
+
|
|
64
|
+
/** The language the page speaks: the UI's where Gallery says it (data-ge-language), else its lang's. */
|
|
65
|
+
function languageOf(/** @type {HTMLElement} */ root) {
|
|
66
|
+
const ui = root.getAttribute("data-ge-language");
|
|
67
|
+
return (ui !== null ? ui === "en" : /^en(?:-|$)/i.test(root.lang)) ? "en" : "zh";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** A local day, as a number of days. @param {Date} date */
|
|
71
|
+
const dayNumber = (date) => Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 86_400_000;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Which group a document edited at `edited` (an ISO time) is in, from `now` (the reader's clock): "today",
|
|
75
|
+
* "yesterday", "week" (earlier this week, from its Monday) or "earlier".
|
|
76
|
+
* @param {string} edited
|
|
77
|
+
* @param {Date} now
|
|
78
|
+
*/
|
|
79
|
+
function groupOf(edited, now) {
|
|
80
|
+
const then = new Date(edited);
|
|
81
|
+
const days = dayNumber(now) - dayNumber(then);
|
|
82
|
+
if (days <= 0) return "today";
|
|
83
|
+
if (days === 1) return "yesterday";
|
|
84
|
+
return days <= (now.getDay() + 6) % 7 ? "week" : "earlier";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* When `edited` (an ISO time) was, from `now` (the reader's clock), in `language`: within the hour 刚刚
|
|
89
|
+
* and N 分钟前, then today's hour (08:05); yesterday's hour where its group says yesterday (`grouped`),
|
|
90
|
+
* else 昨天; a day earlier this week its weekday (周二); else its date, the year said when it is another.
|
|
91
|
+
* @param {string} edited
|
|
92
|
+
* @param {Date} now
|
|
93
|
+
* @param {"zh" | "en"} language
|
|
94
|
+
* @param {boolean} [grouped]
|
|
95
|
+
*/
|
|
96
|
+
function whenOf(edited, now, language, grouped = true) {
|
|
97
|
+
const words = WORDS[language];
|
|
98
|
+
const then = new Date(edited);
|
|
99
|
+
const minutes = Math.floor((now.getTime() - then.getTime()) / 60_000);
|
|
100
|
+
if (!Number.isFinite(minutes)) return "";
|
|
101
|
+
const two = (/** @type {number} */ value) => String(value).padStart(2, "0");
|
|
102
|
+
const hour = `${two(then.getHours())}:${two(then.getMinutes())}`;
|
|
103
|
+
const group = groupOf(edited, now);
|
|
104
|
+
if (group === "today") return minutes < 1 ? words.now : minutes < 60 ? words.minutes(minutes) : hour;
|
|
105
|
+
if (group === "yesterday") return grouped ? hour : words.yesterday;
|
|
106
|
+
if (group === "week") return words.weekdays[then.getDay()];
|
|
107
|
+
return words.date(then, then.getFullYear() === now.getFullYear());
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** What a document's icon is, as Gallery writes its value: a glyph (/icons/…), a picture of its folder (assets/…), an emoji, or none. @param {string | null} value */
|
|
111
|
+
function iconKindOf(value) {
|
|
112
|
+
if (typeof value !== "string" || !value) return null;
|
|
113
|
+
if (value.startsWith("/icons/")) return "glyph";
|
|
114
|
+
if (value.startsWith("assets/")) return "image";
|
|
115
|
+
return "emoji";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* An element of the face, of `kind` (its class).
|
|
120
|
+
* @template {keyof HTMLElementTagNameMap} K
|
|
121
|
+
* @param {K} tag
|
|
122
|
+
* @param {string} kind
|
|
123
|
+
* @param {string} [text]
|
|
124
|
+
*/
|
|
125
|
+
function made(tag, kind, text) {
|
|
126
|
+
const node = document.createElement(tag);
|
|
127
|
+
node.className = `gallery-recent-${kind}`;
|
|
128
|
+
if (text !== undefined) node.textContent = text;
|
|
129
|
+
return node;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// A document's icon: its picture from the server's icon route, a glyph masked, or none (the page CSS draws).
|
|
133
|
+
function iconFor(/** @type {string} */ slug, /** @type {string | null} */ value) {
|
|
134
|
+
const kind = iconKindOf(value);
|
|
135
|
+
const icon = made("span", "icon");
|
|
136
|
+
if (!kind || value === null) return icon;
|
|
137
|
+
const address = `/__gallery/edit/icon?value=${encodeURIComponent(value)}&slug=${encodeURIComponent(slug)}`;
|
|
138
|
+
if (kind === "glyph") {
|
|
139
|
+
icon.classList.add("gallery-recent-glyph");
|
|
140
|
+
icon.style.setProperty("--gallery-recent-icon", `url("${address}")`);
|
|
141
|
+
return icon;
|
|
142
|
+
}
|
|
143
|
+
const picture = document.createElement("img");
|
|
144
|
+
picture.alt = "";
|
|
145
|
+
picture.src = address;
|
|
146
|
+
picture.decoding = "async";
|
|
147
|
+
icon.classList.add("gallery-recent-picture");
|
|
148
|
+
icon.append(picture);
|
|
149
|
+
return icon;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
DocKit.component({
|
|
153
|
+
id: "gallery/recent",
|
|
154
|
+
on: ["data", "clock", "layout"],
|
|
155
|
+
mount(host, context) {
|
|
156
|
+
const language = languageOf(document.documentElement);
|
|
157
|
+
const words = WORDS[language];
|
|
158
|
+
const face = made("div", "face");
|
|
159
|
+
const list = made("ul", "list");
|
|
160
|
+
const head = made("div", "head");
|
|
161
|
+
const chip = made("span", "chip");
|
|
162
|
+
chip.setAttribute("aria-hidden", "true");
|
|
163
|
+
chip.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 1.8"/></svg>';
|
|
164
|
+
// Its quiet meta after the title: how many of the documents were edited today.
|
|
165
|
+
const meta = made("span", "meta");
|
|
166
|
+
head.append(chip, made("span", "title", words.title), meta);
|
|
167
|
+
const more = made("p", "more");
|
|
168
|
+
more.hidden = true;
|
|
169
|
+
// No document, or no workspace: the cards' empty face.
|
|
170
|
+
const empty = made("div", "empty");
|
|
171
|
+
const emptyPicture = made("span", "empty-picture");
|
|
172
|
+
emptyPicture.setAttribute("aria-hidden", "true");
|
|
173
|
+
emptyPicture.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M7 3.5h6.5L18 8v11a1.5 1.5 0 0 1-1.5 1.5h-9.5A1.5 1.5 0 0 1 5.5 19V5A1.5 1.5 0 0 1 7 3.5z"/><path d="M13.5 3.5V8H18"/></svg>';
|
|
174
|
+
const line = made("p", "line");
|
|
175
|
+
const hint = made("p", "hint");
|
|
176
|
+
empty.append(emptyPicture, line, hint);
|
|
177
|
+
empty.hidden = true;
|
|
178
|
+
face.append(head, list, more, empty);
|
|
179
|
+
host.append(face);
|
|
180
|
+
const workspace = context.workspace;
|
|
181
|
+
if (!workspace) return void context.fail(new Error("its manifest's needs hold \"workspace\": context.workspace is not there"));
|
|
182
|
+
/** @type {Array<{ slug: string, title: string, icon: string | null, edited: string }> | null} */
|
|
183
|
+
let documents = null;
|
|
184
|
+
let total = 0;
|
|
185
|
+
/** @type {Array<{ node: HTMLElement, edited: string }>} */
|
|
186
|
+
let times = [];
|
|
187
|
+
let shown = -1;
|
|
188
|
+
const short = () => Boolean(context.size && context.size.h <= 2);
|
|
189
|
+
const say = (/** @type {string[] | null} */ said) => {
|
|
190
|
+
empty.hidden = said === null;
|
|
191
|
+
list.hidden = said !== null;
|
|
192
|
+
if (said === null) return;
|
|
193
|
+
line.textContent = said[0];
|
|
194
|
+
hint.textContent = said[1] ?? "";
|
|
195
|
+
};
|
|
196
|
+
// Each time said again, only where its words are others; the head's count of today's too.
|
|
197
|
+
const retime = () => {
|
|
198
|
+
const now = new Date();
|
|
199
|
+
for (const { node, edited } of times) {
|
|
200
|
+
const said = whenOf(edited, now, language, !short());
|
|
201
|
+
if (node.textContent !== said) node.textContent = said;
|
|
202
|
+
}
|
|
203
|
+
const today = (documents ?? []).filter(({ edited }) => groupOf(edited, now) === "today").length;
|
|
204
|
+
const counted = today > 0 ? words.today(today) : "";
|
|
205
|
+
if (meta.textContent !== counted) meta.textContent = counted;
|
|
206
|
+
};
|
|
207
|
+
// The rows the room under the head holds, whole, with their groups' titles (none on a short card), and
|
|
208
|
+
// a line of how many more there are: drawn again only when that is another number.
|
|
209
|
+
const fit = (/** @type {boolean} */ again = false) => {
|
|
210
|
+
if (!documents) return;
|
|
211
|
+
const layout = short() ? "short" : "full";
|
|
212
|
+
face.dataset.layout = layout;
|
|
213
|
+
const now = new Date();
|
|
214
|
+
// The room: the face's height under the head and the line of more (none measured where nothing lays it out: every row).
|
|
215
|
+
const room = face.clientHeight > 0 ? face.clientHeight - head.offsetHeight - (layout === "short" ? 4 : 6) - (layout === "short" ? 0 : 24) : Infinity;
|
|
216
|
+
let count = 0;
|
|
217
|
+
if (layout === "short") count = Math.min(3, documents.length);
|
|
218
|
+
else {
|
|
219
|
+
let used = 0;
|
|
220
|
+
let group = "";
|
|
221
|
+
for (const entry of documents) {
|
|
222
|
+
const next = groupOf(entry.edited, now);
|
|
223
|
+
const cost = (next !== group ? GROUP_HEIGHT : 0) + ROW_HEIGHT;
|
|
224
|
+
if (used + cost > room) break;
|
|
225
|
+
used += cost;
|
|
226
|
+
group = next;
|
|
227
|
+
count++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const key = `${layout}:${count}`;
|
|
231
|
+
if (!again && String(shown) === key) return;
|
|
232
|
+
shown = /** @type {any} */ (key);
|
|
233
|
+
times = [];
|
|
234
|
+
let group = "";
|
|
235
|
+
list.replaceChildren(...documents.slice(0, count).flatMap((entry) => {
|
|
236
|
+
const rows = [];
|
|
237
|
+
const next = groupOf(entry.edited, now);
|
|
238
|
+
if (layout === "full" && next !== group) {
|
|
239
|
+
rows.push(made("li", "group", words.groups[/** @type {keyof typeof words.groups} */ (next)]));
|
|
240
|
+
group = next;
|
|
241
|
+
}
|
|
242
|
+
const item = made("li", "item");
|
|
243
|
+
const button = made("button", "document");
|
|
244
|
+
button.type = "button";
|
|
245
|
+
button.title = words.open(entry.title);
|
|
246
|
+
const when = made("time", "when");
|
|
247
|
+
when.dateTime = entry.edited;
|
|
248
|
+
times.push({ node: when, edited: entry.edited });
|
|
249
|
+
button.append(iconFor(entry.slug, entry.icon), made("span", "name", entry.title), when);
|
|
250
|
+
button.addEventListener("click", () => workspace.open({ slug: entry.slug }));
|
|
251
|
+
item.append(button);
|
|
252
|
+
rows.push(item);
|
|
253
|
+
return rows;
|
|
254
|
+
}));
|
|
255
|
+
const below = Math.max(0, total - count);
|
|
256
|
+
more.hidden = below === 0 || layout === "short";
|
|
257
|
+
more.textContent = below > 0 ? words.more(below) : "";
|
|
258
|
+
retime();
|
|
259
|
+
};
|
|
260
|
+
const watch = typeof ResizeObserver === "function" ? new ResizeObserver(() => fit()) : null;
|
|
261
|
+
watch?.observe(face);
|
|
262
|
+
context.signal?.addEventListener("abort", () => watch?.disconnect(), { once: true });
|
|
263
|
+
let asking = 0; // the last read asked: an older answer that comes after it is dropped
|
|
264
|
+
const read = () => {
|
|
265
|
+
const ticket = ++asking;
|
|
266
|
+
const counted = workspace.query("stats").then(({ data }) => data?.documents ?? null, () => null);
|
|
267
|
+
workspace.query("recent", { limit: LIMIT }).then(async ({ data }) => {
|
|
268
|
+
const all = await counted;
|
|
269
|
+
if (ticket !== asking || context.signal.aborted) return;
|
|
270
|
+
documents = data;
|
|
271
|
+
total = typeof all === "number" ? Math.max(all, data.length) : data.length;
|
|
272
|
+
say(data.length === 0 ? words.empty : null);
|
|
273
|
+
fit(true);
|
|
274
|
+
}, (/** @type {any} */ error) => {
|
|
275
|
+
if (ticket !== asking || context.signal.aborted) return;
|
|
276
|
+
documents = null;
|
|
277
|
+
times = [];
|
|
278
|
+
meta.textContent = "";
|
|
279
|
+
list.replaceChildren();
|
|
280
|
+
more.hidden = true;
|
|
281
|
+
say(error?.code === "offline" ? words.offline : words.failed);
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
read();
|
|
285
|
+
return {
|
|
286
|
+
update(reason, detail) {
|
|
287
|
+
if (reason === "data" && detail === "recent") read();
|
|
288
|
+
else if (reason === "clock") retime();
|
|
289
|
+
else if (reason === "layout") fit();
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 120"><rect width="240" height="120" rx="8" fill="#f3f3f1"/><rect x="60.5" y="6.5" width="119" height="107" rx="8" fill="#ffffff" stroke="#e6e6e3"/><rect x="70" y="14" width="10" height="10" rx="3" fill="#efefed"/><rect x="84" y="16.5" width="30" height="5" rx="2.5" fill="#37352f"/><rect x="118" y="16.5" width="16" height="5" rx="2.5" fill="#d4d3cf"/><rect x="70" y="32" width="12" height="4" rx="2" fill="#a6a5a1"/><rect x="70" y="41" width="7" height="7" rx="2" fill="#f6c264"/><rect x="81" y="42" width="52" height="5" rx="2.5" fill="#37352f"/><rect x="158" y="42.5" width="12" height="4" rx="2" fill="#d4d3cf"/><rect x="70" y="54" width="7" height="7" rx="2" fill="#e8a3a3"/><rect x="81" y="55" width="44" height="5" rx="2.5" fill="#37352f"/><rect x="158" y="55.5" width="12" height="4" rx="2" fill="#d4d3cf"/><rect x="70" y="67" width="7" height="7" rx="2" fill="#9fc3ef"/><rect x="81" y="68" width="38" height="5" rx="2.5" fill="#37352f"/><rect x="158" y="68.5" width="12" height="4" rx="2" fill="#d4d3cf"/><rect x="70" y="88" width="7" height="7" rx="2" fill="#b8d79a"/><rect x="81" y="89" width="48" height="5" rx="2.5" fill="#37352f"/><rect x="158" y="89.5" width="12" height="4" rx="2" fill="#d4d3cf"/><rect x="70" y="101" width="7" height="7" rx="2" fill="#c9b3ec"/><rect x="81" y="102" width="36" height="5" rx="2.5" fill="#37352f"/><rect x="158" y="102.5" width="12" height="4" rx="2" fill="#d4d3cf"/><rect x="70" y="79" width="12" height="4" rx="2" fill="#a6a5a1"/></svg>
|