@heycys/gallery 0.1.2 → 0.2.1
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/embed/page.mjs +1 -1
- package/browser/editor/components/file/page.mjs +4 -4
- package/browser/editor/components/heading/index.mjs +7 -6
- package/browser/editor/components/icon/page.mjs +6 -4
- package/browser/editor/components/icons.mjs +5 -2
- 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 +6 -2
- package/browser/editor/components/installed/page.mjs +80 -13
- 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 +17 -5
- package/browser/editor/components/table/index.mjs +4 -2
- package/browser/editor/page/after/index.mjs +288 -0
- package/browser/editor/page/align.mjs +91 -0
- 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/hint.mjs +64 -27
- package/browser/editor/page/board/index.mjs +160 -71
- package/browser/editor/page/board/motion.mjs +177 -0
- package/browser/editor/page/board/settings.mjs +4 -3
- package/browser/editor/page/clipboard/import/read.mjs +20 -2
- package/browser/editor/page/clipboard/index.mjs +10 -3
- package/browser/editor/page/clipboard/own/blocks.mjs +3 -4
- package/browser/editor/page/clipboard/own/describe.mjs +6 -2
- package/browser/editor/page/commit.mjs +2 -2
- package/browser/editor/page/core.mjs +3 -1
- package/browser/editor/page/custom-page.mjs +15 -11
- package/browser/editor/page/editor.mjs +31 -14
- package/browser/editor/page/history.mjs +19 -4
- package/browser/editor/page/insert/index.mjs +20 -22
- package/browser/editor/page/menu.mjs +15 -7
- package/browser/editor/page/page-objects.mjs +3 -7
- package/browser/editor/page/present-links.mjs +5 -4
- package/browser/editor/page/present-shell.mjs +63 -0
- package/browser/editor/page/selection/index.mjs +10 -2
- package/browser/editor/page/table/binding.mjs +87 -0
- package/browser/editor/page/table/icons.mjs +0 -2
- package/browser/editor/page/table/index.mjs +10 -14
- package/browser/editor/page/table/views.mjs +5 -52
- package/browser/editor/page/table/widths.mjs +82 -10
- package/browser/editor/page/typing/index.mjs +44 -12
- package/browser/editor/page/typing/leave.mjs +19 -5
- package/browser/shared/block-packs.mjs +23 -8
- package/browser/shared/component-catalog.mjs +15 -8
- package/browser/shared/component-source.mjs +3 -1
- package/browser/shared/components/heading.mjs +2 -1
- package/browser/shared/components/installed.mjs +1 -1
- package/browser/shared/components/page.mjs +2 -0
- package/browser/shared/components/paragraph.mjs +8 -1
- package/browser/shared/components/table.mjs +1 -0
- package/browser/shared/document-copy.mjs +43 -0
- package/browser/shared/drafts.mjs +10 -3
- package/browser/shared/emoji/icon-value.mjs +2 -2
- package/browser/shared/emoji/line-icons.mjs +37 -0
- package/browser/shared/extensions.mjs +83 -7
- package/browser/shared/http.mjs +9 -0
- package/browser/shared/i18n/en.mjs +50 -17
- package/browser/shared/outline-shape.mjs +6 -0
- package/browser/shared/outline.mjs +165 -12
- package/browser/shared/pack-manifest.mjs +12 -1
- package/browser/shared/pages.mjs +169 -57
- package/browser/shared/pinyin.mjs +92 -0
- package/browser/shared/present-step.mjs +17 -0
- package/browser/shared/sidebar-head.mjs +70 -0
- package/browser/shared/web-links.mjs +5 -0
- package/browser/shared/workspace-columns.mjs +11 -7
- package/dist/{chunk-N7AZZLGT.mjs → chunk-2GKGEVMR.mjs} +29 -24
- package/dist/chunk-2OHANJ6Q.mjs +2 -0
- package/dist/{chunk-HYMEEXKN.mjs → chunk-2RKIHJI2.mjs} +1 -1
- package/dist/{chunk-742D2WWJ.mjs → chunk-36FIRJIF.mjs} +2 -2
- package/dist/{chunk-BPL64HGA.mjs → chunk-3A5I2GJK.mjs} +1 -1
- package/dist/{chunk-RFDZXFJF.mjs → chunk-3OFXMN6N.mjs} +1 -1
- package/dist/chunk-46ACXOU5.mjs +2 -0
- package/dist/{chunk-PUOQ65NC.mjs → chunk-4AMTKWDO.mjs} +1 -1
- package/dist/{chunk-VTKNEYT3.mjs → chunk-4DNHHJSL.mjs} +1 -1
- package/dist/chunk-5DPABPBI.mjs +2 -0
- package/dist/chunk-5IIH335D.mjs +3 -0
- package/dist/chunk-5L26JUYA.mjs +2 -0
- package/dist/{chunk-3NJACR4W.mjs → chunk-5MEXZ2UF.mjs} +1 -1
- package/dist/{chunk-ZHEHPMBK.mjs → chunk-6MPKCH3P.mjs} +1 -1
- package/dist/{chunk-C564NU2H.mjs → chunk-74RPP2M2.mjs} +1 -1
- package/dist/chunk-7M2BQQGZ.mjs +2 -0
- package/dist/{chunk-VXACTLO3.mjs → chunk-7M7UPWU6.mjs} +1 -1
- package/dist/{chunk-ZB4H7ZN4.mjs → chunk-7N2LPG33.mjs} +1 -1
- package/dist/{chunk-UBBRTJ2R.mjs → chunk-7XDYK3UC.mjs} +1 -1
- package/dist/chunk-AEMVZYZQ.mjs +166 -0
- package/dist/{chunk-POQSE4C2.mjs → chunk-AFGUR7SY.mjs} +1 -1
- package/dist/{chunk-QKXMPUXC.mjs → chunk-AMDGVDNV.mjs} +1 -1
- package/dist/{chunk-KTKHRC6H.mjs → chunk-ASAXY2DJ.mjs} +1 -1
- package/dist/{chunk-NVRNLK25.mjs → chunk-BYSF5UMM.mjs} +2 -2
- package/dist/{chunk-U3SSPL4K.mjs → chunk-CCSGFCLZ.mjs} +1 -1
- package/dist/{chunk-NWFBE2YA.mjs → chunk-CG3UHDKH.mjs} +1 -1
- package/dist/chunk-CPS4SRPF.mjs +5 -0
- package/dist/{chunk-B4P2CM2J.mjs → chunk-CZ5KJSGK.mjs} +1 -1
- package/dist/chunk-DAMVDXR3.mjs +2 -0
- package/dist/{chunk-NG375ILQ.mjs → chunk-DH6GMDNN.mjs} +1 -1
- package/dist/chunk-DHB7UZ7F.mjs +3 -0
- package/dist/{chunk-MHW7ZKOU.mjs → chunk-DKR7LFIY.mjs} +1 -1
- package/dist/chunk-DLFZJJEE.mjs +2 -0
- package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
- package/dist/{chunk-6VTU6KEO.mjs → chunk-FCK6NXON.mjs} +1 -1
- package/dist/{chunk-ZWPMCK7P.mjs → chunk-FX42XU6J.mjs} +1 -1
- package/dist/chunk-FXQBADMN.mjs +5 -0
- package/dist/{chunk-W3BGBJQW.mjs → chunk-G4YP7GVC.mjs} +1 -1
- package/dist/{chunk-V6XZ4VIR.mjs → chunk-GWOYMENX.mjs} +1 -1
- package/dist/chunk-HATA2W7X.mjs +2 -0
- package/dist/{chunk-YI43RG4P.mjs → chunk-HBAUKKQH.mjs} +1 -1
- package/dist/{chunk-MAFIGVXY.mjs → chunk-HM4E43VM.mjs} +1 -1
- package/dist/{chunk-CRJEG7MT.mjs → chunk-JI6T3A4Y.mjs} +1 -1
- package/dist/{chunk-NFMCP2HI.mjs → chunk-JJNRIFND.mjs} +2 -2
- package/dist/chunk-JL6R4FG4.mjs +4 -0
- package/dist/{chunk-UMHUSOPJ.mjs → chunk-JXLSHVJ2.mjs} +1 -1
- package/dist/{chunk-FJYN7RTH.mjs → chunk-KFZBYDIS.mjs} +1 -1
- package/dist/{chunk-ZTHICSSF.mjs → chunk-KH24LWAT.mjs} +1 -1
- package/dist/{chunk-RKPWLGZ3.mjs → chunk-KUILYI7H.mjs} +1 -1
- package/dist/{chunk-RMECZA2K.mjs → chunk-L7W624FD.mjs} +1 -1
- package/dist/chunk-LE44MD3O.mjs +4 -0
- package/dist/{chunk-4K5KC6V2.mjs → chunk-LV24RY2E.mjs} +1 -1
- package/dist/{chunk-43B75J5T.mjs → chunk-MKTX5PGV.mjs} +1 -1
- package/dist/{chunk-LKX55H76.mjs → chunk-NAGMRVW4.mjs} +1 -1
- package/dist/{chunk-D4CKRD3V.mjs → chunk-NFABH3QW.mjs} +2 -2
- package/dist/{chunk-PVER7LSC.mjs → chunk-NIW7F7LU.mjs} +2 -2
- package/dist/{chunk-PAREWQGB.mjs → chunk-NWRZXELO.mjs} +1 -1
- package/dist/{chunk-OJKMINIU.mjs → chunk-OU3S466R.mjs} +15 -15
- package/dist/{chunk-5ZJA7CXT.mjs → chunk-PAFSFVYB.mjs} +1 -1
- package/dist/chunk-QCEATQC4.mjs +2 -0
- package/dist/{chunk-Y34NR5JL.mjs → chunk-QJ3TFJLQ.mjs} +1 -1
- package/dist/{chunk-IA4PSIMD.mjs → chunk-QK3VISCA.mjs} +1 -1
- package/dist/{chunk-ZUNVAXLH.mjs → chunk-QXVNUASA.mjs} +1 -1
- package/dist/{chunk-GRAXKKG4.mjs → chunk-QZ35UAB4.mjs} +1 -1
- package/dist/{chunk-UGWYKHBL.mjs → chunk-S2Y7VI4H.mjs} +2 -2
- package/dist/{chunk-OW5NWPOJ.mjs → chunk-SAHS56QM.mjs} +1 -1
- package/dist/{chunk-QOUEFUCH.mjs → chunk-SOD23RGL.mjs} +1 -1
- package/dist/chunk-TDWJKOLN.mjs +2 -0
- package/dist/{chunk-V6RIYQZZ.mjs → chunk-TOE6DPLH.mjs} +1 -1
- package/dist/chunk-UGO5N3EI.mjs +15 -0
- package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
- package/dist/{chunk-NWR2LT5B.mjs → chunk-V5JRSGZF.mjs} +1 -1
- package/dist/{chunk-BZ5RAF7L.mjs → chunk-VA3KAQZD.mjs} +1 -1
- package/dist/chunk-VG5JEIUQ.mjs +12 -0
- package/dist/{chunk-UNOYVIYR.mjs → chunk-VGTMLQWZ.mjs} +1 -1
- package/dist/chunk-VKSRM7EY.mjs +22 -0
- package/dist/{chunk-YQGT4AB2.mjs → chunk-VNUWREXU.mjs} +1 -1
- package/dist/{chunk-AGDILNWB.mjs → chunk-VPVOAJ7X.mjs} +1 -1
- package/dist/{chunk-HLYV7RIH.mjs → chunk-WCWYNFDF.mjs} +1 -1
- package/dist/chunk-WTEGUPFG.mjs +16 -0
- package/dist/{chunk-N76HWBA3.mjs → chunk-XKOHV4H6.mjs} +1 -1
- package/dist/{chunk-HMYTM3GT.mjs → chunk-XMKHCUI6.mjs} +1 -1
- package/dist/{chunk-OUUP3WKT.mjs → chunk-XQRUG4G3.mjs} +1 -1
- package/dist/{chunk-32ZUEFZ7.mjs → chunk-YBB5LWZK.mjs} +1 -1
- package/dist/{chunk-SX4LJYT6.mjs → chunk-YFJU3O7G.mjs} +1 -1
- package/dist/chunk-YPPJVDQI.mjs +2 -0
- package/dist/chunk-ZANMKAWS.mjs +2 -0
- package/dist/{chunk-6QBHU4DF.mjs → chunk-ZCP7BTOZ.mjs} +1 -1
- package/dist/{chunk-ERLAIBOA.mjs → chunk-ZEY2RLBT.mjs} +2 -2
- package/dist/{chunk-SQ6Y2ZMA.mjs → chunk-ZO7SFB2E.mjs} +2 -2
- package/dist/cli.mjs +1 -1
- package/dist/server.mjs +1 -1
- package/dist/worker.mjs +1 -1
- package/kit/board.css +5 -5
- package/kit/code.js +15 -15
- package/kit/math.js +1 -1
- package/kit/report.css +123 -44
- package/kit/runtime.js +45 -7
- package/kit/tables.js +2 -0
- package/kit/ui.js +2 -0
- package/package.json +1 -1
- package/packs/gallery/chart-view/kit.js +3 -3
- package/packs/gallery/chart-view/writing.md +1 -1
- package/packs/gallery/countdown/component.json +1 -1
- package/packs/gallery/countdown/kit.js +89 -29
- package/packs/gallery/countdown/preview.svg +1 -1
- package/packs/gallery/countdown/style.css +88 -100
- package/packs/gallery/countdown/writing.md +2 -2
- package/packs/gallery/habit/component.json +3 -3
- package/packs/gallery/habit/kit.js +114 -75
- package/packs/gallery/habit/preview.svg +1 -1
- package/packs/gallery/habit/style.css +133 -205
- package/packs/gallery/habit/writing.md +2 -2
- package/packs/gallery/heatmap/component.json +2 -2
- package/packs/gallery/heatmap/kit.js +335 -173
- package/packs/gallery/heatmap/preview.svg +1 -1
- package/packs/gallery/heatmap/style.css +158 -170
- package/packs/gallery/heatmap/writing.md +2 -2
- package/packs/gallery/kanban/kit.js +3 -3
- package/packs/gallery/kanban/writing.md +1 -1
- package/packs/gallery/metric/kit.js +3 -3
- package/packs/gallery/metric/writing.md +1 -1
- package/packs/gallery/note/component.json +3 -3
- package/packs/gallery/note/kit.js +40 -13
- package/packs/gallery/note/preview.svg +1 -1
- package/packs/gallery/note/style.css +51 -30
- package/packs/gallery/note/writing.md +2 -2
- package/packs/gallery/recent/component.json +2 -2
- package/packs/gallery/recent/kit.js +141 -86
- package/packs/gallery/recent/preview.svg +1 -1
- package/packs/gallery/recent/style.css +53 -126
- package/packs/gallery/recent/writing.md +2 -2
- package/packs/gallery/static/component.json +2 -2
- package/packs/gallery/static/preview.svg +1 -1
- package/packs/gallery/static/style.css +99 -66
- package/packs/gallery/static/writing.md +2 -2
- package/packs/gallery/stats/component.json +3 -3
- package/packs/gallery/stats/kit.js +253 -72
- package/packs/gallery/stats/preview.svg +1 -1
- package/packs/gallery/stats/style.css +64 -142
- package/packs/gallery/stats/writing.md +2 -2
- package/packs/gallery/today/component.json +2 -2
- package/packs/gallery/today/kit.js +62 -27
- package/packs/gallery/today/preview.svg +1 -1
- package/packs/gallery/today/style.css +101 -72
- package/packs/gallery/today/writing.md +2 -2
- package/packs/gallery/todos/component.json +1 -1
- package/packs/gallery/todos/kit.js +218 -71
- package/packs/gallery/todos/preview.svg +1 -1
- package/packs/gallery/todos/style.css +170 -223
- package/packs/gallery/todos/writing.md +1 -1
- package/packs/gallery/weather/component.json +2 -2
- package/packs/gallery/weather/kit.js +165 -90
- package/packs/gallery/weather/preview.svg +1 -1
- package/packs/gallery/weather/style.css +100 -218
- package/packs/gallery/weather/writing.md +1 -1
- 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/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 +10 -10
- package/skill/SKILL.md +15 -14
- package/skill/references/building-blocks.md +4 -4
- package/skill/references/extensions.md +11 -7
- package/skill/references/pages-and-data.md +13 -9
- package/skill/references/writing-html.md +2 -0
- package/web/assets/index-BHC5ZOMh.js +15 -0
- package/web/assets/index-xP0uw3a0.css +1 -0
- package/web/index.html +2 -2
- package/dist/chunk-2NRBY6AJ.mjs +0 -2
- package/dist/chunk-35NQ4PLQ.mjs +0 -2
- package/dist/chunk-3KJHLHUO.mjs +0 -2
- package/dist/chunk-4AV456RQ.mjs +0 -2
- package/dist/chunk-5XYHDBJ5.mjs +0 -2
- package/dist/chunk-6A53MZYZ.mjs +0 -2
- package/dist/chunk-6EXH3ORZ.mjs +0 -166
- package/dist/chunk-BVEFGE4B.mjs +0 -2
- package/dist/chunk-D4EURAX4.mjs +0 -4
- package/dist/chunk-DQCE7TJY.mjs +0 -5
- package/dist/chunk-FWNRPMCJ.mjs +0 -12
- package/dist/chunk-FWW4CSAZ.mjs +0 -2
- package/dist/chunk-GY3FAUVM.mjs +0 -2
- package/dist/chunk-JVHHHGEA.mjs +0 -3
- package/dist/chunk-L7UP52JO.mjs +0 -2
- package/dist/chunk-NAMN3RAF.mjs +0 -2
- package/dist/chunk-PZDLJJTJ.mjs +0 -3
- package/dist/chunk-SXHBYZST.mjs +0 -16
- package/dist/chunk-TEZAWDSQ.mjs +0 -2
- package/dist/chunk-UFNYFFKO.mjs +0 -20
- package/dist/chunk-UKSHDZZ6.mjs +0 -2
- package/dist/chunk-VJPFVNNW.mjs +0 -15
- package/dist/chunk-VS3ZMVGX.mjs +0 -5
- package/web/assets/index-BK2e_07w.js +0 -13
- package/web/assets/index-CWFI83mF.css +0 -1
- /package/dist/{chunk-YEY3RSFQ.mjs → chunk-5MMN3N5J.mjs} +0 -0
- /package/dist/{chunk-UH4SIXTR.mjs → chunk-74UW7QSL.mjs} +0 -0
- /package/dist/{chunk-W7XHE4CT.mjs → chunk-EKGYYOSP.mjs} +0 -0
- /package/dist/{chunk-QAIHRH3I.mjs → chunk-KNZJT4ET.mjs} +0 -0
- /package/dist/{chunk-A55HN5OK.mjs → chunk-N3DSBH5S.mjs} +0 -0
- /package/dist/{chunk-QR6A54KJ.mjs → chunk-ZW2A6GBW.mjs} +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// How a board's cards move while they are edited (./index.mjs; docs/render.md "Its cards edited"), the user's
|
|
2
|
+
// word of 2026-11-09: as smooth as the widgets of an iPhone's home screen, nothing that jumps or stutters.
|
|
3
|
+
//
|
|
4
|
+
// - glide(): the cards a change of the layout moves (a draft while a card is dragged or resized, a drop, an
|
|
5
|
+
// undo, 大小 ›) glide from where they were seen to where the layout now puts them (FLIP: their box before
|
|
6
|
+
// and after, a transform from the one to the other run to none) on a spring's ease (spring: a little
|
|
7
|
+
// less than critically damped, its overshoot under 2%), GLIDE ms; a card whose size changed glides its
|
|
8
|
+
// width and height too. A card already gliding to where the layout still puts it is left to go on; one
|
|
9
|
+
// the change sends elsewhere starts again from where it is seen then, so a target that changes under a
|
|
10
|
+
// drag never makes a card jump (interruptible).
|
|
11
|
+
// - createSettle(): a held card's place (or a resized card's size), axis by axis: the pointer's, or within
|
|
12
|
+
// `snap` px of the cells (or the size) a drop gives, those; where that changes (the card comes within the
|
|
13
|
+
// snap or leaves it, or the cells it stands on move) it settles from where it was carried to the new
|
|
14
|
+
// place in SETTLE ms on the same spring, the pointer's own motion never delayed.
|
|
15
|
+
// - createEdgeScroll(): near the window's top or bottom edge a gesture scrolls the page, timed by the frames'
|
|
16
|
+
// clock (not by the pointer's events): within BAND px of the edge, faster as the pointer is nearer it (the
|
|
17
|
+
// square of how far into the band, past the edge the most), FASTEST px a second at the most (12 a frame
|
|
18
|
+
// at 60 frames a second), and growing from nothing over RAMP ms once the pointer comes into the band, so
|
|
19
|
+
// that a card picked up near the edge does not set the page running.
|
|
20
|
+
// - prefers-reduced-motion: no glide and no settling (the layout and the snap at once); the scroll as it is.
|
|
21
|
+
|
|
22
|
+
export const GLIDE = 280; // ms a card glides to where a layout puts it
|
|
23
|
+
export const SETTLE = 180; // ms a held (or resized) card settles on what it snaps to, or comes off it
|
|
24
|
+
export const BAND = 64; // px from the window's top or bottom edge within which a gesture scrolls the page
|
|
25
|
+
export const FASTEST = 720; // px a second the page scrolls at the most
|
|
26
|
+
export const RAMP = 320; // ms over which that speed grows from nothing once the pointer is in the band
|
|
27
|
+
|
|
28
|
+
export const reduced = () => typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
29
|
+
|
|
30
|
+
// The step response of a spring (damping ZETA, settled within 0.2% at t = 1): 0 at 0, 1 at 1, still at both
|
|
31
|
+
// ends, a crest of under 2% past 1 at t ≈ 0.65.
|
|
32
|
+
const ZETA = 0.8;
|
|
33
|
+
const OMEGA = 6.4 / ZETA;
|
|
34
|
+
const DAMPED = OMEGA * Math.sqrt(1 - ZETA * ZETA);
|
|
35
|
+
export function spring(t) {
|
|
36
|
+
if (t <= 0) return 0;
|
|
37
|
+
if (t >= 1) return 1;
|
|
38
|
+
return 1 - Math.exp(-ZETA * OMEGA * t) * (Math.cos(DAMPED * t) + ((ZETA * OMEGA) / DAMPED) * Math.sin(DAMPED * t));
|
|
39
|
+
}
|
|
40
|
+
// The same as an easing a Web Animation takes (CSS linear() of 40 points), else a close cubic Bézier.
|
|
41
|
+
const EASE = (() => {
|
|
42
|
+
const points = Array.from({ length: 41 }, (_, index) => Math.round(spring(index / 40) * 10000) / 10000).join(", ");
|
|
43
|
+
const curve = `linear(${points})`;
|
|
44
|
+
return typeof CSS !== "undefined" && CSS.supports?.("transition-timing-function", curve) ? curve : "cubic-bezier(.2, .9, .25, 1)";
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
const running = new WeakMap(); // a card -> its glide under way
|
|
48
|
+
// Where the kit's board script draws a card now: its cells as the CSS takes them (the board's row skip in).
|
|
49
|
+
const keyOf = (card) => ["x", "y", "w", "h"].map((name) => card.style.getPropertyValue(`--gallery-${name}`)).join();
|
|
50
|
+
|
|
51
|
+
/** Whether a card is gliding now. */
|
|
52
|
+
export const isGliding = (card) => running.has(card);
|
|
53
|
+
|
|
54
|
+
/** A card's glide stopped where it is (a gesture takes it up): it stands where the layout puts it. */
|
|
55
|
+
export function stopGlide(card) {
|
|
56
|
+
running.get(card)?.cancel();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* `change()` run (it lays the board out at once: DocBoard.draft or .layout), and each of `cards` it moves
|
|
61
|
+
* glided from where it was seen to its new place. `still`: a card left out (the one a gesture holds, which
|
|
62
|
+
* follows the pointer); `lift`: a card kept over the others while it glides (data-ge-landing: one let go);
|
|
63
|
+
* `settled()`: run once every glide this started has ended (at once where none started). Gives what change() gave.
|
|
64
|
+
*/
|
|
65
|
+
export function glide(cards, change, { still = null, lift = null, duration = GLIDE, settled = null } = {}) {
|
|
66
|
+
if (reduced()) {
|
|
67
|
+
const result = change();
|
|
68
|
+
settled?.();
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const started = [];
|
|
72
|
+
const before = cards.filter((card) => card !== still).map((card) => ({ card, key: keyOf(card), box: card.getBoundingClientRect() }));
|
|
73
|
+
const result = change();
|
|
74
|
+
for (const { card, key, box } of before) {
|
|
75
|
+
if (!card.isConnected) continue;
|
|
76
|
+
const going = running.get(card);
|
|
77
|
+
if (going && keyOf(card) === key) continue; // on its way to where it still goes
|
|
78
|
+
going?.cancel();
|
|
79
|
+
const now = card.getBoundingClientRect();
|
|
80
|
+
const dx = box.left - now.left;
|
|
81
|
+
const dy = box.top - now.top;
|
|
82
|
+
const sized = Math.abs(box.width - now.width) > 0.5 || Math.abs(box.height - now.height) > 0.5;
|
|
83
|
+
if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5 && !sized) continue;
|
|
84
|
+
const from = { transform: `translate(${dx}px, ${dy}px)` };
|
|
85
|
+
const to = { transform: "translate(0px, 0px)" };
|
|
86
|
+
if (sized) {
|
|
87
|
+
Object.assign(from, { width: `${box.width}px`, height: `${box.height}px` });
|
|
88
|
+
Object.assign(to, { width: `${now.width}px`, height: `${now.height}px` });
|
|
89
|
+
}
|
|
90
|
+
const animation = card.animate([from, to], { duration, easing: EASE });
|
|
91
|
+
running.set(card, animation);
|
|
92
|
+
if (card === lift) card.setAttribute("data-ge-landing", "");
|
|
93
|
+
const end = () => {
|
|
94
|
+
if (running.get(card) !== animation) return;
|
|
95
|
+
running.delete(card);
|
|
96
|
+
card.removeAttribute("data-ge-landing");
|
|
97
|
+
};
|
|
98
|
+
animation.addEventListener("finish", end);
|
|
99
|
+
animation.addEventListener("cancel", end);
|
|
100
|
+
started.push(animation.finished.catch(() => {}));
|
|
101
|
+
}
|
|
102
|
+
if (settled) {
|
|
103
|
+
if (started.length) Promise.all(started).then(settled);
|
|
104
|
+
else settled();
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A held card's place, or a resized card's size, axis by axis (`name`: "x", "y", "w", "h"): at(name, raw,
|
|
111
|
+
* target, time) gives what is drawn for the pointer's `raw` and the `target` a drop gives (the cells' place
|
|
112
|
+
* or size, px): the target within `snap` px of it, else raw; a change of which (or of the target while on
|
|
113
|
+
* it) settles from where the card was carried, in `duration` ms. gliding(): whether any axis still settles.
|
|
114
|
+
*/
|
|
115
|
+
export function createSettle(snap, duration = SETTLE) {
|
|
116
|
+
const axes = new Map();
|
|
117
|
+
return {
|
|
118
|
+
at(name, raw, target, time) {
|
|
119
|
+
const on = Math.abs(raw - target) <= snap;
|
|
120
|
+
const want = on ? target : raw;
|
|
121
|
+
const axis = axes.get(name);
|
|
122
|
+
if (!axis) {
|
|
123
|
+
axes.set(name, { raw, target, on, shown: want, glide: null });
|
|
124
|
+
return want;
|
|
125
|
+
}
|
|
126
|
+
// Where it would be had it gone with the pointer since the last time: what a settling starts from.
|
|
127
|
+
const carried = axis.shown + (raw - axis.raw);
|
|
128
|
+
if (on !== axis.on || (on && target !== axis.target)) axis.glide = reduced() ? null : { offset: carried - want, start: time };
|
|
129
|
+
Object.assign(axis, { raw, target, on });
|
|
130
|
+
let shown = want;
|
|
131
|
+
if (axis.glide) {
|
|
132
|
+
const progress = (time - axis.glide.start) / duration;
|
|
133
|
+
if (progress >= 1) axis.glide = null;
|
|
134
|
+
else shown = want + axis.glide.offset * (1 - spring(progress));
|
|
135
|
+
}
|
|
136
|
+
axis.shown = shown;
|
|
137
|
+
return shown;
|
|
138
|
+
},
|
|
139
|
+
gliding: () => [...axes.values()].some((axis) => axis.glide !== null),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The page scrolled near the window's edge while a gesture lasts: frame(y, time) once a frame (the pointer's
|
|
145
|
+
* y in the window, the frame's time), whether the page moved; stop() as the gesture ends.
|
|
146
|
+
*/
|
|
147
|
+
export function createEdgeScroll({ band = BAND, fastest = FASTEST, ramp = RAMP } = {}) {
|
|
148
|
+
let since = null; // when the pointer came into the band
|
|
149
|
+
let last = null; // the last frame's time
|
|
150
|
+
let carry = 0; // px asked and not yet scrolled (a frame's share is seldom whole)
|
|
151
|
+
return {
|
|
152
|
+
frame(y, time) {
|
|
153
|
+
const step = last === null ? 0 : Math.min(34, Math.max(0, time - last)); // a stalled frame scrolls no more than two
|
|
154
|
+
last = time;
|
|
155
|
+
const into = y < band ? -(band - y) / band : y > innerHeight - band ? (y - (innerHeight - band)) / band : 0;
|
|
156
|
+
if (into === 0) {
|
|
157
|
+
since = null;
|
|
158
|
+
carry = 0;
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
since ??= time;
|
|
162
|
+
const depth = Math.min(1, Math.abs(into));
|
|
163
|
+
carry += Math.sign(into) * fastest * depth * depth * Math.min(1, (time - since) / ramp) * (step / 1000);
|
|
164
|
+
const whole = Math.trunc(carry);
|
|
165
|
+
if (!whole) return false;
|
|
166
|
+
carry -= whole;
|
|
167
|
+
const was = scrollY;
|
|
168
|
+
scrollBy(0, whole);
|
|
169
|
+
return scrollY !== was;
|
|
170
|
+
},
|
|
171
|
+
stop() {
|
|
172
|
+
since = null;
|
|
173
|
+
last = null;
|
|
174
|
+
carry = 0;
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -14,7 +14,8 @@ import { addStyle, button, element, mount } from "../ui.mjs";
|
|
|
14
14
|
// workspace's documents by title (the workbench's outline: GET /__gallery/workspace/outline, its web
|
|
15
15
|
// links no documents), the slug what is kept. A choice from a list (select, document) is a field that
|
|
16
16
|
// opens the editor's own menu card under it (menu.mjs: rows, the chosen one checked, ↑/↓ Enter Escape from
|
|
17
|
-
// the field),
|
|
17
|
+
// the field), a choice of the caller's (type "choice", `choices`: [[value, words]], what a document block adds
|
|
18
|
+
// to its fields: the table a view reads) too, not a native <select>: the page cannot style that one's popup, which a browser draws in
|
|
18
19
|
// the system's colours (a white list with pale words in a dark page, the user's picture of 2026-11-09).
|
|
19
20
|
// While the form stands, the page is not shown again under it (the board's idle).
|
|
20
21
|
|
|
@@ -92,9 +93,9 @@ export async function openSettings({ fields, values, name, anchor, save }) {
|
|
|
92
93
|
continue;
|
|
93
94
|
}
|
|
94
95
|
let control;
|
|
95
|
-
const choice = field.type === "select" || field.type === "document"; // chosen from a list
|
|
96
|
+
const choice = field.type === "select" || field.type === "document" || field.type === "choice"; // chosen from a list
|
|
96
97
|
if (choice) {
|
|
97
|
-
const options = field.type === "select" ? field.options.map((option) => [String(option), String(option)]) : [["", t("(未选择)")], ...listed.map(({ slug, title }) => [slug, title])];
|
|
98
|
+
const options = field.type === "select" ? field.options.map((option) => [String(option), String(option)]) : field.type === "choice" ? field.choices : [["", t("(未选择)")], ...listed.map(({ slug, title }) => [slug, title])];
|
|
98
99
|
if (field.type === "document" && value && !listed.some(({ slug }) => slug === value)) options.push([String(value), String(value)]);
|
|
99
100
|
const text = element("span", { class: "ge-board-pick-text" });
|
|
100
101
|
control = element("button", { type: "button", class: "ge-board-pick", "data-key": field.key, "aria-haspopup": "menu", "aria-expanded": "false" }, text, element("span", { html: CHEVRON }));
|
|
@@ -14,7 +14,8 @@ import { addressOf } from "../../links/address.mjs";
|
|
|
14
14
|
// formatting" (html.mjs containsRichFormatting; WordPress's always): spans, divs and paragraphs
|
|
15
15
|
// alone paste as their text.
|
|
16
16
|
// - Office's HTML (its namespaces) that looks like Word's goes through the Word filter (office.mjs).
|
|
17
|
-
// - A whole $$ formula $$ → a formula block;
|
|
17
|
+
// - A whole $$ formula $$ → a formula block; IDE Markdown → document blocks, other IDE code →
|
|
18
|
+
// code in its language. An unspecified IDE language can be Markdown by its parsed structure.
|
|
18
19
|
// - HTML: several lines of text, or a table → blocks (html.mjs htmlToBlocks); else one rich text.
|
|
19
20
|
// - No HTML: several lines → Markdown (markdown.mjs); one line → inline Markdown (bold, links,
|
|
20
21
|
// addresses, formulas) when it has any, else plain text. A whole line that is a display formula
|
|
@@ -89,6 +90,20 @@ function sameAddress(one, other) {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// VS Code and Cursor copy syntax-colored HTML beside the source text, even for Markdown. Use
|
|
94
|
+
// the same Markdown reader as an ordinary text paste, not the colored HTML. A Markdown language
|
|
95
|
+
// hint is enough; for an unspecified language, require some structure or inline formatting the
|
|
96
|
+
// reader actually recognizes. Unrecognized lines remain text; the whole selection need not be
|
|
97
|
+
// valid Markdown. A declared programming language wins over lookalike comments and operators.
|
|
98
|
+
function editorMarkdown({ text, language }) {
|
|
99
|
+
if (language !== "Markdown" && language !== "Plain Text") return null;
|
|
100
|
+
const specs = fromMarkdown(text);
|
|
101
|
+
if (language === "Markdown") return specs;
|
|
102
|
+
const marked = (blocks) => blocks.some((block) => block.type !== "text"
|
|
103
|
+
|| block.title.some((piece) => piece[1]?.length) || marked(block.children));
|
|
104
|
+
return !addressOf(text) && marked(specs) ? specs : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
92
107
|
/**
|
|
93
108
|
* Another program's content as { kind: "blocks", specs } | { kind: "rich", title } | { kind: "plain",
|
|
94
109
|
* text } (notion-lite readClipboard, after the editor's own formats).
|
|
@@ -98,11 +113,14 @@ export function readExternal(data, options = {}) {
|
|
|
98
113
|
const yuque = readYuque(data, options);
|
|
99
114
|
if (yuque) return yuque;
|
|
100
115
|
const external = externalContent(data, options);
|
|
101
|
-
const { text
|
|
116
|
+
const { text } = external;
|
|
102
117
|
const convert = { colors: external.colors };
|
|
103
118
|
const equation = EQUATION.exec(text)?.[1];
|
|
104
119
|
if (equation?.trim()) return { kind: "blocks", specs: [{ type: "equation", title: [[equation]], props: {}, children: [] }] };
|
|
120
|
+
const markdown = editorMarkdown(external);
|
|
121
|
+
if (markdown?.length && (multiline(text) || markdown.some((block) => block.type !== "text"))) return { kind: "blocks", specs: markdown };
|
|
105
122
|
if (multiline(text) && external.language) return { kind: "blocks", specs: [{ type: "code", title: [[text]], props: { language: external.language }, children: [] }] };
|
|
123
|
+
const html = markdown ? "" : external.html;
|
|
106
124
|
if (html) {
|
|
107
125
|
if (multiline(text) || external.isTable) {
|
|
108
126
|
const specs = mathifySpecs(htmlToBlocks(parseClipboardHTML(html, options), convert));
|
|
@@ -5,7 +5,7 @@ import { formulaMaker } from "../formula/index.mjs";
|
|
|
5
5
|
import { inlineItems, spaceAround } from "../../../shared/inline-spacing.mjs";
|
|
6
6
|
import { caretAt, editable, hostOf, select } from "../caret.mjs";
|
|
7
7
|
import { formatRange, joinMarks, outOfMarks, rangeAt, rangeOffsets } from "../marks.mjs";
|
|
8
|
-
import { BASIC_UNITS, HOSTS, KIND_NAMES, accepts, clipboardOf, takesFormula, texOnly } from "../../components/registry.mjs";
|
|
8
|
+
import { BASIC_UNITS, HOSTS, KIND_NAMES, accepts, clipboardOf, replacedByFile, takesFormula, texOnly } from "../../components/registry.mjs";
|
|
9
9
|
import { listType } from "../../components/list/index.mjs";
|
|
10
10
|
import { nodeAround } from "../../components/flowchart/index.mjs";
|
|
11
11
|
import { cellsData } from "../../components/table/index.mjs";
|
|
@@ -284,7 +284,9 @@ export function createClipboard({ editing, blocks, struct, recordText, say, load
|
|
|
284
284
|
// anchor.parent, after })): right after the block of `at` (unitElement: a new code block's code stands
|
|
285
285
|
// for the block), whole, where a block pasted there goes (placeFor: the first box out that takes it, a
|
|
286
286
|
// list or quote that does not splitting after the block), one undo step (`put`, handed to the
|
|
287
|
-
// component, which makes the block and fills it).
|
|
287
|
+
// component, which makes the block and fills it, the fill part of that step). An empty block of a kind
|
|
288
|
+
// that says `replacedByFile` (an empty paragraph: notion-lite's pasteFiles removes an empty text block)
|
|
289
|
+
// gives its place to the block instead of staying before it. Returns whether one did.
|
|
288
290
|
const fromFiles = (at, files) => {
|
|
289
291
|
const unit = unitElement(at);
|
|
290
292
|
if (!unit?.closest("main")) return false;
|
|
@@ -294,7 +296,12 @@ export function createClipboard({ editing, blocks, struct, recordText, say, load
|
|
|
294
296
|
say(t("这里放不下这种块。"));
|
|
295
297
|
return false;
|
|
296
298
|
}
|
|
297
|
-
|
|
299
|
+
const replaced = replacedByFile(unit.getAttribute("data-ge-unit-kind")) && removableEmpty(unit, unit) && !isFirstLine(unit);
|
|
300
|
+
struct([...new Set([unit.parentElement, ...place.parents])], [], () => {
|
|
301
|
+
const created = placeAfter(place, [block]);
|
|
302
|
+
if (replaced) removalTarget(unit).remove();
|
|
303
|
+
return { touched: [], focus: null, created };
|
|
304
|
+
});
|
|
298
305
|
return true;
|
|
299
306
|
};
|
|
300
307
|
return pageObjects().some((page) => {
|
|
@@ -2,9 +2,8 @@ import { escapeText } from "../../../../shared/escape.mjs";
|
|
|
2
2
|
import { heldIn, newContainer, newHost, pastedLines } from "../../core.mjs";
|
|
3
3
|
import { pageBuild } from "../../../components/builders.mjs";
|
|
4
4
|
import { BLOCK_TYPES, COPIED_AS_ITSELF, HOLDS_ONE, TEXT_TYPES, TOGGLES, UNSAID, accepts, clipboardHooks, clipboardOf, isLead, sizeValue } from "../../../components/registry.mjs";
|
|
5
|
-
import { BLOCK_COLORS } from "../../../components/colors.mjs";
|
|
6
5
|
import { MAX_CELL_HTML } from "../../../components/table/index.mjs";
|
|
7
|
-
import { copied, copyOf } from "./describe.mjs";
|
|
6
|
+
import { BLOCK_LOOKS, copied, copyOf } from "./describe.mjs";
|
|
8
7
|
import { imageSource, kindOfBlock, webPage } from "./rules.mjs";
|
|
9
8
|
|
|
10
9
|
// Described blocks (describe.mjs and selection.mjs, and import/to-blocks.mjs for other programs' content) made (build),
|
|
@@ -42,7 +41,7 @@ export function checkBlocks(blocks) {
|
|
|
42
41
|
const { type } = block;
|
|
43
42
|
const clip = clipboardOf(type);
|
|
44
43
|
if (box && !accepts(box, kindOfBlock(block)) && clip.pastedIn !== box) return false;
|
|
45
|
-
if (block.color !== undefined && (typeof block.color !== "string" || !block.color.split(" ").every((name) =>
|
|
44
|
+
if (block.color !== undefined && (typeof block.color !== "string" || !block.color.split(" ").every((name) => BLOCK_LOOKS.includes(name)))) return false;
|
|
46
45
|
if (TEXT_TYPES.has(type)) return string(block.html);
|
|
47
46
|
if (type === "copy") return COPIES.has(block.kind) && Number.isInteger(block.unit) && [block.report, block.text, block.html].every((value) => typeof value === "string")
|
|
48
47
|
&& (block.data === undefined || (Boolean(clipboardOf(block.data?.type).data) && valid(block.data)));
|
|
@@ -120,7 +119,7 @@ function inline(doc, html, { formula = null, mention = null, picture = null } =
|
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
|
|
123
|
-
// A block's colors (`color`: their classes, one of each family), those its kind takes (TOGGLES).
|
|
122
|
+
// A block's colors (`color`: their classes, one of each family, and a text block's alignment and indent), those its kind takes (TOGGLES).
|
|
124
123
|
function colored(element, block) {
|
|
125
124
|
for (const name of block.color?.split(" ") ?? []) if (TOGGLES[element.getAttribute("data-ge-unit-kind")]?.includes(name)) element.classList.add(name);
|
|
126
125
|
return element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isHost, nestedBlocks, serializeInner, unitsOf } from "../../core.mjs";
|
|
2
2
|
import { BLOCK_TYPES, COPIED_AS_ITSELF, COPY_KEEPS, TEXT_TYPES, askClipboard, clipboardOf, sizeValue, unitClipboard } from "../../../components/registry.mjs";
|
|
3
|
+
import { ALIGN_TOGGLES, TEXT_BLOCKS } from "../../../components/align.mjs";
|
|
3
4
|
import { BLOCK_COLORS } from "../../../components/colors.mjs";
|
|
4
5
|
import { containerType, typeOf } from "../../convert.mjs";
|
|
5
6
|
import { pageOfType, pageOfUnit } from "../../page-objects.mjs";
|
|
@@ -72,8 +73,11 @@ export function copied(doc, block) {
|
|
|
72
73
|
return (typeof block.data?.html === "string" ? copy.outerHTML === block.html : copy.textContent === block.text) ? unit : null;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
/**
|
|
76
|
-
export const
|
|
76
|
+
/** The classes a block's copy carries as its `color`: its colors, and a paragraph's or a heading's alignment and indent. */
|
|
77
|
+
export const BLOCK_LOOKS = [...BLOCK_COLORS, ...ALIGN_TOGGLES];
|
|
78
|
+
|
|
79
|
+
/** A block's colors, and a text block's alignment and indent (their classes, space-separated), or null. */
|
|
80
|
+
export const colorOf = (node) => (node.matches?.(TEXT_BLOCKS) ? BLOCK_LOOKS : BLOCK_COLORS).filter((name) => node.classList.contains(name)).join(" ") || null;
|
|
77
81
|
|
|
78
82
|
/** The type a text block is copied as: its own, the type of the text blocks of what wraps it (a quote's paragraph), or null (code, whose text is no text block's). */
|
|
79
83
|
export function copiedType(host) {
|
|
@@ -1241,8 +1241,8 @@ export function createCommits({ root, map, baselines, units, boxes, getLayout, s
|
|
|
1241
1241
|
else if (element) proposals.set(identityOf(element), id);
|
|
1242
1242
|
}
|
|
1243
1243
|
for (const [key, hash] of Object.entries(receipt.hashes ?? {})) if (map.units[Number(key)]) map.units[Number(key)].hash = hash;
|
|
1244
|
-
// The keys of tables the file gave the blocks the commit made and the tables it keyed (a view
|
|
1245
|
-
//
|
|
1244
|
+
// The keys of tables the file gave the blocks the commit made and the tables it keyed (a view
|
|
1245
|
+
// made from a menu: the key its frame took, an attribute of the unit), other than the page chose
|
|
1246
1246
|
// (source/chart-keys.mjs: one the file had meanwhile): theirs on the page too, so that each card and
|
|
1247
1247
|
// view reads the table the file links it to ({ old: new | null }, null: off a table); `rekeyed` says
|
|
1248
1248
|
// which it changed ([element, name, old, new | null]), for the undo steps that recorded them (history.mjs).
|
|
@@ -10,6 +10,7 @@ import { gridOf, tableChange } from "../components/table/grid.mjs";
|
|
|
10
10
|
import { HELD_TAGS, heldListType, listType } from "../components/list/index.mjs";
|
|
11
11
|
import { isFlowchart, isStep } from "../components/flowchart/index.mjs";
|
|
12
12
|
import { markEmpty } from "../components/placeholder.mjs";
|
|
13
|
+
import { carry } from "../components/align.mjs";
|
|
13
14
|
import { unread } from "./tex.mjs";
|
|
14
15
|
import { MENTION, MENTION_ICON, MENTION_PAGE } from "../../shared/components/mention.mjs";
|
|
15
16
|
import { FLOW_BLOCKS } from "../../shared/components/index.mjs";
|
|
@@ -577,7 +578,8 @@ export function newTail(container, type) {
|
|
|
577
578
|
}
|
|
578
579
|
|
|
579
580
|
// New blocks split off `from` are paragraphs, or list items in a list.
|
|
580
|
-
|
|
581
|
+
// A paragraph split off a paragraph or a heading keeps its alignment and indent (components/align.mjs carry).
|
|
582
|
+
const newBlock = (doc, tag, from) => (tag === "li" ? newHost(doc, "item", from) : carry(from, newHost(doc, "paragraph", from)));
|
|
581
583
|
|
|
582
584
|
/** A new, empty paragraph, as a split makes one outside a list (the page's title's Enter: typing/ enterBody). */
|
|
583
585
|
export const newParagraphHost = (doc) => newBlock(doc, "p", null);
|
|
@@ -10,10 +10,12 @@ import { connectWorkbench } from "./session.mjs";
|
|
|
10
10
|
// slug is "@<slug>", the page's address in the workbench (#@<slug>), which no document's slug can be.
|
|
11
11
|
// It answers the session's own commands (theme, keys, scroll-top: src/editor/page/session.mjs),
|
|
12
12
|
// prepare-close (once the board's ops sent are answered: each is written at once, nothing else waits),
|
|
13
|
-
// add-card (the top bar's 添加自定义块: the library of the page's first board, under the
|
|
14
|
-
// the workbench sends
|
|
15
|
-
//
|
|
16
|
-
//
|
|
13
|
+
// add-card where it has a board (the top bar's 添加自定义块: the library of the page's first board, under the
|
|
14
|
+
// button's place that the workbench sends; a page with none, a blank one or one an Agent wrote with no
|
|
15
|
+
// board, offers no 添加自定义块) and, on a page with the default cards (its page map's `defaults`: its
|
|
16
|
+
// page.json's defaultCards, the default home page's), restore-defaults (恢复默认卡片, its menu's: the board's,
|
|
17
|
+
// one step of its undo; its answer { ok, added, skipped } the workbench says) and undo-restore ({ cards }:
|
|
18
|
+
// that step undone while it is the board's last, the workbench's 撤销),
|
|
17
19
|
// and sends ready, reload (its live script heard that page.html or page.json changed, or a block it
|
|
18
20
|
// holds: the workbench loads it again in a hidden frame and swaps it in once drawn), open (a link to a
|
|
19
21
|
// document or another page, or a block's context.workspace.open: doc-open-request) and key. Opened on its own (not in a frame of the workbench), it has no
|
|
@@ -24,6 +26,13 @@ const IDLE = { editing: false, pending: false, saving: false, offline: false, co
|
|
|
24
26
|
// Nothing stands over a custom page as a modal.
|
|
25
27
|
const NO_MODALS = { open: () => [], listen() {}, closeTop: () => false };
|
|
26
28
|
|
|
29
|
+
// The page map (routes.mjs: its slug, its token, the version it was served at, its blocks): the boards edit.
|
|
30
|
+
let map = null;
|
|
31
|
+
try {
|
|
32
|
+
map = JSON.parse(document.querySelector("script[data-ge-page]")?.textContent ?? "null");
|
|
33
|
+
} catch {}
|
|
34
|
+
const hasBoard = Boolean(document.querySelector(".gallery-board"));
|
|
35
|
+
|
|
27
36
|
let board = null;
|
|
28
37
|
const session = connectWorkbench({
|
|
29
38
|
slug: `@${slug}`,
|
|
@@ -35,8 +44,8 @@ const session = connectWorkbench({
|
|
|
35
44
|
},
|
|
36
45
|
// 添加自定义块 of the workbench's top bar: the library opens in the page, at `anchor` (the button's place in
|
|
37
46
|
// this frame's coordinates; none: in the middle of the window), the first board's.
|
|
38
|
-
"add-card": async ({ anchor } = {}) => (board ? board.addCard({ anchor }) : { ok: false, reason: "board" }),
|
|
39
|
-
...(
|
|
47
|
+
...(hasBoard ? { "add-card": async ({ anchor } = {}) => (board ? board.addCard({ anchor }) : { ok: false, reason: "board" }) } : {}),
|
|
48
|
+
...(map?.defaults ? {
|
|
40
49
|
"restore-defaults": async () => (board ? board.restoreDefaults() : { ok: false, reason: "board" }),
|
|
41
50
|
"undo-restore": async ({ cards }) => (board ? board.undoRestore(cards) : { ok: false, reason: "board" }),
|
|
42
51
|
} : {}),
|
|
@@ -44,11 +53,6 @@ const session = connectWorkbench({
|
|
|
44
53
|
modals: NO_MODALS,
|
|
45
54
|
});
|
|
46
55
|
|
|
47
|
-
// The page map (routes.mjs: its slug, its token, the version it was served at, its blocks): the boards edit.
|
|
48
|
-
let map = null;
|
|
49
|
-
try {
|
|
50
|
-
map = JSON.parse(document.querySelector("script[data-ge-page]")?.textContent ?? "null");
|
|
51
|
-
} catch {}
|
|
52
56
|
board = map?.token ? startBoard({ map, reload: () => (session ? session.reload() : location.reload()), hosted: Boolean(session) }) : null;
|
|
53
57
|
// The live script (routes.mjs) shows the page again through the workbench, which swaps a new page in
|
|
54
58
|
// once drawn: no reload seen; once the board's own writes, a drag, a form or a field typed in are done.
|
|
@@ -15,6 +15,7 @@ import { createCommits, kindOf } from "./commit.mjs";
|
|
|
15
15
|
import { createConflicts, packageChanged, unfit } from "./conflicts.mjs";
|
|
16
16
|
import { checkLeft, createAutosave, keepWith, lostKey, pendingKey, readLost, recover, reloadView, setReloader, takeView, viewNow } from "./autosave.mjs";
|
|
17
17
|
import { SOLO, connectWorkbench } from "./session.mjs";
|
|
18
|
+
import { OUTLINE_OPEN_KEY, onOutlineOpen, outlineOpen, setOutlineOpen } from "../../shared/preferences.mjs";
|
|
18
19
|
import { lookOfElement } from "../../shared/document-look.mjs";
|
|
19
20
|
import { belowTargets, convertBlock, convertGroup, groupParents, groupTargets, noTextToCarry, targetsOf, touchedBy, turnInto, turnParents, turnUnit, typeOf } from "./convert.mjs";
|
|
20
21
|
import { joinCode, rangeAt, rangeOffsets } from "./marks.mjs";
|
|
@@ -24,6 +25,7 @@ import { serveFiles } from "./served.mjs";
|
|
|
24
25
|
import { createFormatting } from "./format-bar/index.mjs";
|
|
25
26
|
import { connectHints, hideHint, readOnlyReason, scheduleHint, trackPointer } from "./hints/index.mjs";
|
|
26
27
|
import { createInsert } from "./insert/index.mjs";
|
|
28
|
+
import { createAfter } from "./after/index.mjs";
|
|
27
29
|
import { connectLinks, dismissLink, drawPasted, followLink, leadsToPage, linkBoxNode, linkHolds, linkNodes, linkOf, linkPending, openLink, pageLinkOf } from "./links/index.mjs";
|
|
28
30
|
import { bookmarkItems, connectViews, linkTitle, offerViews } from "./links/views.mjs";
|
|
29
31
|
import { connectChipFills } from "./chip-fills.mjs";
|
|
@@ -36,6 +38,7 @@ import { createResize } from "./resize/index.mjs";
|
|
|
36
38
|
import { connectPages, fieldOpen, keptByComponent, menuRow, modalOps, pageAt, pageIcon, pageObjects, pageOf, pageOfHost, pageOfType, pageOfUnit, panelOpen, typedAsIs } from "./page-objects.mjs";
|
|
37
39
|
import { preloading } from "./type-grid.mjs";
|
|
38
40
|
import { createColumnGaps } from "./columns/index.mjs";
|
|
41
|
+
import { viewFrames } from "./table/binding.mjs";
|
|
39
42
|
import { createTableEditing } from "./table/index.mjs";
|
|
40
43
|
import { createAtMenu } from "./at-menu/index.mjs";
|
|
41
44
|
import { createSlashMenu } from "./slash-menu/index.mjs";
|
|
@@ -72,6 +75,7 @@ import { language, t } from "../../shared/i18n/index.mjs";
|
|
|
72
75
|
//
|
|
73
76
|
// typing/ Enter, Backspace/Delete at block edges, auto spacing, placeholders
|
|
74
77
|
// insert/ new blocks: below a block (its menu), a click below the last block, a paragraph before or after
|
|
78
|
+
// after/ the caret after a block of no text (a click right of it): Backspace deletes it, Enter or a key a paragraph after it
|
|
75
79
|
// format-bar/ the bar over selected text and its keys (marks.mjs does the marks)
|
|
76
80
|
// links/ the link box (Ctrl+K), following links; hints/: why something is read-only
|
|
77
81
|
// formula/ the formula box: selected text made an inline formula (Ctrl+Shift+E)
|
|
@@ -261,7 +265,9 @@ function showLanded(node) {
|
|
|
261
265
|
modalOps().show(node);
|
|
262
266
|
}
|
|
263
267
|
|
|
264
|
-
|
|
268
|
+
// The caret after a block of no text, where a click right of one puts it (insert/ clickBlank).
|
|
269
|
+
const after = createAfter({ struct, blocks: () => blocks, keepText: (node) => blocks.keepText(node), removeBlocks: (units) => blocks.remove(units), say, undo: () => undo(), redo: () => redo() });
|
|
270
|
+
const insert = createInsert({ struct, blocks: () => blocks, after });
|
|
265
271
|
const { newParagraph } = insert;
|
|
266
272
|
|
|
267
273
|
// What a component does on the page beyond what every block does is its own page module
|
|
@@ -326,6 +332,8 @@ const typing = createTyping({
|
|
|
326
332
|
adding: () => slash?.adding ?? null,
|
|
327
333
|
leave: createLeaving({ struct, touchedBy, convertHost, typeOf, targetsOf }),
|
|
328
334
|
showHidden: (node) => blocks?.folds.showAround(node), // a folded heading's section opened before a join takes in its words (fold/)
|
|
335
|
+
look: lookStep, // a block's alignment and indent taken off by Backspace (align.mjs)
|
|
336
|
+
afterAbove: (host) => after.back(host), // Backspace right after a block of no text: the caret after it (after/)
|
|
329
337
|
});
|
|
330
338
|
// Selecting text across blocks: its listeners go before the editor's own keys and clipboard.
|
|
331
339
|
const crossText = createTextSelection({
|
|
@@ -673,7 +681,7 @@ function convertHost(host, type, typed = null, after = null) {
|
|
|
673
681
|
if (!focus && !typed) return null;
|
|
674
682
|
const more = focus && after ? after(focus) : [];
|
|
675
683
|
return { touched: [focus ?? host], focus: focus ?? host, created: [...created, ...more] };
|
|
676
|
-
});
|
|
684
|
+
}, { attributes: viewFrames(type) });
|
|
677
685
|
if (result) afterConvert(result.focus, type, result.focus !== host ? t("已转换为{0}", menuRow(type).label) : null);
|
|
678
686
|
}
|
|
679
687
|
|
|
@@ -703,10 +711,9 @@ function afterConvert(focus, type, said) {
|
|
|
703
711
|
* heading) and the new block turns into the type; below any other block, an empty paragraph of its
|
|
704
712
|
* box does (the first box out that takes one). One undo step.
|
|
705
713
|
*/
|
|
706
|
-
//
|
|
707
|
-
//
|
|
708
|
-
|
|
709
|
-
function insertBelow(target, type, typed = null, also = null) {
|
|
714
|
+
// A view of a table (a custom block that reads tables) is bound to the table above it as it is made
|
|
715
|
+
// (installed/page.mjs create), the tables' keys recorded in the same step (viewFrames).
|
|
716
|
+
function insertBelow(target, type, typed = null) {
|
|
710
717
|
const splits = isHost(target) && can(target, "split");
|
|
711
718
|
let after = target;
|
|
712
719
|
while (!splits && after && !accepts(after.parentElement?.getAttribute("data-ge-box-kind"), "paragraph")) after = after.parentElement?.closest(OUTER);
|
|
@@ -724,12 +731,10 @@ function insertBelow(target, type, typed = null, also = null) {
|
|
|
724
731
|
after.after(fresh);
|
|
725
732
|
}
|
|
726
733
|
if (!fresh) return null;
|
|
727
|
-
const prior = fresh.previousSibling; // what the new block stands right after, made where the paragraph was
|
|
728
734
|
const turned = type === "paragraph" && fresh.localName === "p" ? { focus: null, created: [] } : convertBlock(fresh, type, { fresh: true });
|
|
729
|
-
also?.done?.(fresh.isConnected ? fresh : prior?.nextElementSibling ?? null);
|
|
730
735
|
const focus = turned.focus ?? fresh;
|
|
731
736
|
return { touched: splits ? [target, focus] : [focus], focus, created: turned.created };
|
|
732
|
-
}, { attributes:
|
|
737
|
+
}, { attributes: viewFrames(type) });
|
|
733
738
|
if (result) afterConvert(result.focus, type, t("已插入{0}", menuRow(type).label));
|
|
734
739
|
}
|
|
735
740
|
|
|
@@ -782,7 +787,7 @@ function convertUnit(unit, type, { loaded = false } = {}) {
|
|
|
782
787
|
const turned = turnUnit(unit, type);
|
|
783
788
|
emptied = Boolean(turned.emptied);
|
|
784
789
|
return turned.focus ? { touched: [turned.focus], focus: turned.focus, created: turned.created, select: emptied ? null : unit } : null;
|
|
785
|
-
});
|
|
790
|
+
}, { attributes: viewFrames(type) });
|
|
786
791
|
if (!result) return;
|
|
787
792
|
if (!made || !onCreated(type, result.focus)) {
|
|
788
793
|
if (emptied && isHost(result.focus)) caretAt(result.focus, "start");
|
|
@@ -823,6 +828,7 @@ function convertAll(group, type, { select = true } = {}) {
|
|
|
823
828
|
function onKeydown(event) {
|
|
824
829
|
compositionOver(event);
|
|
825
830
|
if (!editing) return;
|
|
831
|
+
if (after.key(event)) return; // the caret after a block: its keys are its own (after/)
|
|
826
832
|
// A 批注's card is a field of its own: its keys are its own, but for Ctrl+S, which saves it and the report.
|
|
827
833
|
if (annotate?.holds(event.target)) {
|
|
828
834
|
const mod = event.ctrlKey || event.metaKey;
|
|
@@ -928,7 +934,8 @@ function onKeydown(event) {
|
|
|
928
934
|
moveHostBlock(host, event.key === "ArrowUp" ? -1 : 1);
|
|
929
935
|
} else if (event.key.startsWith("Arrow") && !mod && !event.altKey && !event.shiftKey && !renamed) {
|
|
930
936
|
// ←/→ stop on both sides of inline code's edges (typing/code-edges.mjs); a tab's caret stays in its name.
|
|
931
|
-
|
|
937
|
+
// ← or ↑ at the start of the text right after a block of no text: the caret after that block (after/).
|
|
938
|
+
if ((textFilters(host) && codeArrow(host, event.key)) || after.up(host, event.key) || arrow(host, event.key)) event.preventDefault();
|
|
932
939
|
}
|
|
933
940
|
}
|
|
934
941
|
|
|
@@ -1243,6 +1250,15 @@ const COMMANDS = {
|
|
|
1243
1250
|
if (!editing || !annotate) return { ok: false, reason: "editing" };
|
|
1244
1251
|
return annotate.action(message);
|
|
1245
1252
|
},
|
|
1253
|
+
// The outline (目录) switched by the workbench's 更多: a draft of another workspace is shown from that workspace's own origin,
|
|
1254
|
+
// whose storage the workbench cannot write (the switch is kept per origin: shared/preferences.mjs outlineOpen), so it asks and
|
|
1255
|
+
// this page keeps it, shown as another tab's change is (the storage event the outline listens for). Answers the switch as kept.
|
|
1256
|
+
async outline({ open }) {
|
|
1257
|
+
if (typeof open !== "boolean") return { ok: false, reason: "open" };
|
|
1258
|
+
setOutlineOpen(MAP.slug, open);
|
|
1259
|
+
dispatchEvent(new StorageEvent("storage", { key: OUTLINE_OPEN_KEY }));
|
|
1260
|
+
return { ok: true, outline: outlineOpen(MAP.slug) };
|
|
1261
|
+
},
|
|
1246
1262
|
// Rename the report: its title, selected for typing (saving it updates <title> and report.json).
|
|
1247
1263
|
async "edit-title"() {
|
|
1248
1264
|
const title = document.querySelector('[data-ge-kind="title"]');
|
|
@@ -1473,8 +1489,9 @@ function connect() {
|
|
|
1473
1489
|
// The look the page shows (字体与布局: its <html>'s, src/shared/document-look.mjs), which the workbench's
|
|
1474
1490
|
// 更多 marks as chosen; told again when it changes (the kit's ui.js, doc-look-change).
|
|
1475
1491
|
// `title`: null for a page with no title at all (the workbench names it by its doc.json); "" an untitled one (未命名文档).
|
|
1476
|
-
workbench = connectWorkbench({ slug: MAP.slug, snapshot: () => ({ title: titleNode() ? currentTitle() : null, icon: pageIcon(), look: lookOfElement(document.documentElement), state: sessionState(), annotations: annotate?.count() }), commands: COMMANDS, modals: modalOps() });
|
|
1492
|
+
workbench = connectWorkbench({ slug: MAP.slug, snapshot: () => ({ title: titleNode() ? currentTitle() : null, icon: pageIcon(), look: lookOfElement(document.documentElement), state: sessionState(), annotations: annotate?.count(), outline: outlineOpen(MAP.slug) }), commands: COMMANDS, modals: modalOps() });
|
|
1477
1493
|
addEventListener("doc-look-change", () => workbench?.update());
|
|
1494
|
+
onOutlineOpen(() => workbench?.update()); // the switch changed in another tab of this origin: the workbench's 目录 follows it (a draft's)
|
|
1478
1495
|
// A block's context.workspace.open (the kit's runtime/workspace.js): a document, or a block of one, goes
|
|
1479
1496
|
// where a link to it goes (goTo: in the workbench, its address; on its own, the page once saved); a custom
|
|
1480
1497
|
// page, the workbench's #@<slug>, or /pages/<slug> once saved.
|
|
@@ -1655,8 +1672,8 @@ function boot() {
|
|
|
1655
1672
|
gaps = createColumnGaps({ active, struct, look: lookStep, say, settle });
|
|
1656
1673
|
for (const page of pageObjects()) page.install?.(); // the round "+" at a flowchart node's right, renaming Sheet tabs
|
|
1657
1674
|
tables = createTableEditing({
|
|
1658
|
-
active, struct, look: lookStep, selectBlock: (unit) => blocks.select(unit), resizeTable: (event, unit, axis) => resize.startTable(event, unit, axis), say, cellsOf: (data) => clipboard.cellsOf(data), upload: uploadTo, typedKey,
|
|
1659
|
-
recordStep,
|
|
1675
|
+
active, struct, look: lookStep, selectBlock: (unit) => blocks.select(unit), resizeTable: (event, unit, axis) => resize.startTable(event, unit, axis), say, settle, cellsOf: (data) => clipboard.cellsOf(data), upload: uploadTo, typedKey,
|
|
1676
|
+
recordStep,
|
|
1660
1677
|
});
|
|
1661
1678
|
selectAll = createSelectAll({ blocks, say, cells: { table: () => tables.selectedTable, clear: () => tables.clear() } });
|
|
1662
1679
|
createFrame(() => editing);
|