@heycys/gallery 0.1.2 → 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/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/icons.mjs +3 -0
- 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/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/emoji/line-icons.mjs +37 -0
- package/browser/shared/http.mjs +9 -0
- package/browser/shared/i18n/en.mjs +40 -17
- package/browser/shared/outline.mjs +30 -2
- 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/dist/chunk-2OHANJ6Q.mjs +2 -0
- package/dist/{chunk-QOUEFUCH.mjs → chunk-3MBRL2PM.mjs} +1 -1
- package/dist/{chunk-NWR2LT5B.mjs → chunk-4OKWZY5E.mjs} +1 -1
- package/dist/{chunk-RMECZA2K.mjs → chunk-5K44HLTZ.mjs} +1 -1
- package/dist/{chunk-NFMCP2HI.mjs → chunk-5XA2ZBAE.mjs} +2 -2
- package/dist/{chunk-OUUP3WKT.mjs → chunk-67N2QJYT.mjs} +1 -1
- package/dist/chunk-6KBYRA2C.mjs +2 -0
- package/dist/{chunk-SX4LJYT6.mjs → chunk-6N3PX4XX.mjs} +1 -1
- package/dist/{chunk-RFDZXFJF.mjs → chunk-7JDX4E65.mjs} +1 -1
- package/dist/chunk-7M2BQQGZ.mjs +2 -0
- package/dist/{chunk-ZUNVAXLH.mjs → chunk-7OG26PGF.mjs} +1 -1
- package/dist/{chunk-B4P2CM2J.mjs → chunk-AHN3NFKJ.mjs} +1 -1
- package/dist/chunk-ALZ6ZENZ.mjs +3 -0
- package/dist/{chunk-UGWYKHBL.mjs → chunk-AR5D57ZD.mjs} +2 -2
- package/dist/{chunk-W3BGBJQW.mjs → chunk-BFJGHSQN.mjs} +1 -1
- package/dist/{chunk-HLYV7RIH.mjs → chunk-CMA72MAG.mjs} +1 -1
- package/dist/{chunk-D4CKRD3V.mjs → chunk-DAZL73VK.mjs} +2 -2
- package/dist/{chunk-6VTU6KEO.mjs → chunk-DJISHPUL.mjs} +1 -1
- package/dist/{chunk-BPL64HGA.mjs → chunk-EAIFITAV.mjs} +1 -1
- package/dist/chunk-EP25HDAJ.mjs +2 -0
- package/dist/{chunk-6EZ7FNIK.mjs → chunk-F4ZA6JRZ.mjs} +1 -1
- package/dist/{chunk-3KJHLHUO.mjs → chunk-F6HVDZR4.mjs} +1 -1
- package/dist/{chunk-3NJACR4W.mjs → chunk-FDMAXMQA.mjs} +1 -1
- package/dist/chunk-FETSREUN.mjs +5 -0
- package/dist/{chunk-RKPWLGZ3.mjs → chunk-GHOYKMQ3.mjs} +1 -1
- package/dist/chunk-HDWTZ763.mjs +12 -0
- package/dist/{chunk-VTKNEYT3.mjs → chunk-HPOI4ZX7.mjs} +1 -1
- package/dist/{chunk-OW5NWPOJ.mjs → chunk-HXG7EOPJ.mjs} +1 -1
- package/dist/{chunk-V6RIYQZZ.mjs → chunk-ITOH5EGO.mjs} +1 -1
- package/dist/{chunk-ZWPMCK7P.mjs → chunk-IV64FFI4.mjs} +1 -1
- package/dist/chunk-J43Z6LSS.mjs +2 -0
- package/dist/chunk-J5PUIV3Y.mjs +4 -0
- package/dist/{chunk-KTKHRC6H.mjs → chunk-JA7G53PC.mjs} +1 -1
- package/dist/{chunk-NWFBE2YA.mjs → chunk-JLTZF4LA.mjs} +1 -1
- package/dist/{chunk-GY3FAUVM.mjs → chunk-JZT7I2OJ.mjs} +1 -1
- package/dist/{chunk-QKXMPUXC.mjs → chunk-K5XATEOO.mjs} +1 -1
- package/dist/chunk-L2SUIS5S.mjs +2 -0
- package/dist/{chunk-TEZAWDSQ.mjs → chunk-LM4XFQHZ.mjs} +1 -1
- package/dist/{chunk-5ZJA7CXT.mjs → chunk-MN7BZL4R.mjs} +1 -1
- package/dist/chunk-MTYYHM2G.mjs +15 -0
- package/dist/{chunk-HMYTM3GT.mjs → chunk-MXUBKDRO.mjs} +1 -1
- package/dist/{chunk-MHW7ZKOU.mjs → chunk-OEXWRJME.mjs} +1 -1
- package/dist/{chunk-CRJEG7MT.mjs → chunk-OFHXGWOX.mjs} +1 -1
- package/dist/chunk-ORID3AE7.mjs +166 -0
- package/dist/{chunk-32ZUEFZ7.mjs → chunk-PGDE24N5.mjs} +1 -1
- package/dist/{chunk-UNOYVIYR.mjs → chunk-POTA2FV4.mjs} +1 -1
- package/dist/{chunk-N7AZZLGT.mjs → chunk-QE5RG76S.mjs} +18 -17
- package/dist/{chunk-N76HWBA3.mjs → chunk-R2ZDLJYT.mjs} +1 -1
- package/dist/chunk-SWUTTZCN.mjs +2 -0
- package/dist/{chunk-POQSE4C2.mjs → chunk-TR7TWGHB.mjs} +1 -1
- package/dist/chunk-U5XL7D6A.mjs +3 -0
- package/dist/{chunk-PVER7LSC.mjs → chunk-U7NZI6N4.mjs} +2 -2
- package/dist/{chunk-Q47PLKIJ.mjs → chunk-UGWMMEJ5.mjs} +1 -1
- package/dist/chunk-UJQJKNGK.mjs +2 -0
- package/dist/chunk-UYVCQK3H.mjs +22 -0
- package/dist/{chunk-FJYN7RTH.mjs → chunk-VFX2KNHP.mjs} +1 -1
- package/dist/{chunk-UBBRTJ2R.mjs → chunk-VS7JIGJW.mjs} +1 -1
- package/dist/{chunk-NVRNLK25.mjs → chunk-W7665ZEC.mjs} +2 -2
- package/dist/{chunk-NG375ILQ.mjs → chunk-WQDQWCX5.mjs} +1 -1
- package/dist/chunk-YLPSMDIS.mjs +5 -0
- package/dist/{chunk-VXACTLO3.mjs → chunk-YNAWBAYN.mjs} +1 -1
- package/dist/{chunk-ZB4H7ZN4.mjs → chunk-YROX2O22.mjs} +1 -1
- package/dist/{chunk-YQGT4AB2.mjs → chunk-ZDLPUOAG.mjs} +1 -1
- package/dist/chunk-ZQS6H473.mjs +16 -0
- package/dist/{chunk-OJKMINIU.mjs → chunk-ZWWC4KLF.mjs} +15 -15
- 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/report.css +120 -41
- 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 +9 -9
- package/skill/SKILL.md +12 -12
- package/skill/references/building-blocks.md +4 -4
- package/skill/references/pages-and-data.md +13 -9
- package/skill/references/writing-html.md +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-2NRBY6AJ.mjs +0 -2
- package/dist/chunk-35NQ4PLQ.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-JVHHHGEA.mjs +0 -3
- package/dist/chunk-PZDLJJTJ.mjs +0 -3
- package/dist/chunk-SXHBYZST.mjs +0 -16
- 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
|
@@ -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);
|
|
@@ -69,7 +69,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
69
69
|
typing.time = at;
|
|
70
70
|
} else {
|
|
71
71
|
closeTyping();
|
|
72
|
-
typing = { type: "text", element: host, before, after, time: at };
|
|
72
|
+
typing = { type: "text", element: host, before, after, time: at, began: at };
|
|
73
73
|
}
|
|
74
74
|
changed(now);
|
|
75
75
|
}
|
|
@@ -83,8 +83,14 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
83
83
|
changed(true);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
// A run of typing closed: a step of its own, unless it went into the block the last step made for
|
|
87
|
+
// what was typed (struct's `typedInto`: a paragraph after a block, after/), begun within a second of it:
|
|
88
|
+
// then it is part of that step, which undo takes away whole, redo putting the words back with it.
|
|
86
89
|
function closeTyping() {
|
|
87
|
-
|
|
90
|
+
const last = undoStack.at(-1);
|
|
91
|
+
if (typingChanged() && last?.typedInto === typing.element && typing.began - last.madeAt < 1000) last.htmlAfter.set(typing.element, typing.after);
|
|
92
|
+
else if (typingChanged()) push(typing);
|
|
93
|
+
if (typing && last?.typedInto) last.typedInto = null;
|
|
88
94
|
typing = null;
|
|
89
95
|
}
|
|
90
96
|
|
|
@@ -151,7 +157,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
151
157
|
// An attribute the file gave another value than the page had (`changes`: [element, name, from, to],
|
|
152
158
|
// `to` null for none; a key of a table a commit gave a pair anew: page/commit.mjs apply), the page's
|
|
153
159
|
// element set so: the steps that recorded `from` of it record `to` instead, so that an undo or a redo
|
|
154
|
-
// puts back what the file has, never the value it changed (a
|
|
160
|
+
// puts back what the file has, never the value it changed (a view made from "/" redone with the key its view
|
|
155
161
|
// no longer carries).
|
|
156
162
|
function rekeyed(changes) {
|
|
157
163
|
if (changes.length === 0) return;
|
|
@@ -162,6 +168,14 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
162
168
|
if (values?.some(([each, value]) => each === name && value === from)) recorded.set(element, values.map(([each, value]) => [each, each === name && value === from ? to : value]));
|
|
163
169
|
}
|
|
164
170
|
}
|
|
171
|
+
// A view's table chosen in its 设置 is a step of look (installed/page.mjs readTable): its attributes by element.
|
|
172
|
+
if (step.type === "look") {
|
|
173
|
+
for (const recorded of [step.before, step.after]) {
|
|
174
|
+
step.elements.forEach((each, index) => {
|
|
175
|
+
for (const [element, name, from, to] of changes) if (each === element && recorded[index]?.attributes?.[name] === from) recorded[index].attributes[name] = to;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
|
|
@@ -194,7 +208,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
194
208
|
// spans and widths, which its operations change: components/table/page.mjs). `caret`: where the caret
|
|
195
209
|
// stood in the first text block it touched is kept too, which undo puts back (notion-lite's Enter in the
|
|
196
210
|
// page's title, enter.js: "撤销后光标回到回车前的位置"; typing/ enterBody).
|
|
197
|
-
function struct(parents, touched, mutate, { attributes = [], caret = false } = {}) {
|
|
211
|
+
function struct(parents, touched, mutate, { attributes = [], caret = false, typedInto = null } = {}) {
|
|
198
212
|
closeTyping();
|
|
199
213
|
// What the change may leave untidy is tidied after it by the components that know (rows of
|
|
200
214
|
// columns with a column emptied, lists set side by side: tidy), so what they name is recorded
|
|
@@ -246,6 +260,7 @@ export function createHistory({ changed, beforeUndo, blocks, afterText, refuse =
|
|
|
246
260
|
selectAfter: result.selectAfter ?? null, // what redo selects instead
|
|
247
261
|
caret: result.caret ?? null, // where redo puts the caret when it selects nothing (tidyEmptied)
|
|
248
262
|
settle: result.settle ?? null,
|
|
263
|
+
typedInto, madeAt: Date.now(), // the block made for what is typed, whose first run of typing is part of this step (closeTyping)
|
|
249
264
|
});
|
|
250
265
|
result.touched.forEach((node) => lastKnown.set(node, ownHTML(node)));
|
|
251
266
|
note("history", "change");
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { OUTER, UNIT, blocksIn, boxKind, heldIn, newHost, newTail, placeBefore, unitElement, withLeads } from "../core.mjs";
|
|
2
2
|
import { containerType } from "../convert.mjs";
|
|
3
3
|
import { begin, done, made } from "../made.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { caretBeside } from "../beside.mjs";
|
|
5
5
|
import { caretAt, nearestOnLine, placeCaret, selectAt } from "../caret.mjs";
|
|
6
6
|
import { sectionOps } from "../page-objects.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { endOf, hoverAt, innerBox, measure } from "../geometry.mjs";
|
|
8
8
|
import { BOXED, HOLDS_ONE, accepts } from "../../components/registry.mjs";
|
|
9
9
|
import { DIALOG } from "../../components/kit.mjs";
|
|
10
10
|
|
|
@@ -18,9 +18,9 @@ import { DIALOG } from "../../components/kit.mjs";
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* struct(parents, touched, mutate): the editor's undoable structural change (history.mjs). blocks():
|
|
21
|
-
* the block tools (blocks.mjs).
|
|
21
|
+
* the block tools (blocks.mjs). after: the caret after a block (after/).
|
|
22
22
|
*/
|
|
23
|
-
export function createInsert({ struct, blocks }) {
|
|
23
|
+
export function createInsert({ struct, blocks, after }) {
|
|
24
24
|
// An empty paragraph right before the block of a text block, the caret staying where it is.
|
|
25
25
|
// Returns whether one could go there.
|
|
26
26
|
function paragraphBefore(host) {
|
|
@@ -54,10 +54,10 @@ export function createInsert({ struct, blocks }) {
|
|
|
54
54
|
// (geometry.mjs endOf). Under the last block of a box whose end is its own padding (endIsPadding: a
|
|
55
55
|
// highlight block, a callout, a disclosure's content; the user's, 2026-11-06, as notion-lite's click-blank.js
|
|
56
56
|
// makes a block only below the page and in a column) it is a gap like any other, below. Right of
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// paragraph after it;
|
|
60
|
-
//
|
|
57
|
+
// a boxed block (a code block, a callout, a file…: not text) or a row of columns, at its height
|
|
58
|
+
// (boxedBeside), the caret after it, at its bottom right (after/: Backspace deletes it, Enter or a
|
|
59
|
+
// key makes a paragraph after it; the user's, 2026-11-09: a click there selected it before).
|
|
60
|
+
// Elsewhere beside text, the caret goes into the text block on that line (the nearest of those
|
|
61
61
|
// side by side), where the line is nearest the click (Notion's way); in the gap between two
|
|
62
62
|
// blocks, or beside a block of no text, into the nearer of the blocks around it that is text, in the
|
|
63
63
|
// innermost box at that height (boxBeside: the press may be on the page's margin, the block in a
|
|
@@ -82,7 +82,7 @@ export function createInsert({ struct, blocks }) {
|
|
|
82
82
|
if (end) return newParagraph(end.box, end.after);
|
|
83
83
|
const top = y + scrollY;
|
|
84
84
|
const beside = boxedBeside(main, x, y, target);
|
|
85
|
-
if (beside) return
|
|
85
|
+
if (beside) return after.place(beside);
|
|
86
86
|
const host = hoverAt(measure(main, { top, bottom: top }), x + scrollX, top, { reach: Infinity, skip: (unit) => !unit.text });
|
|
87
87
|
if (host) return caretNear(host, x, y);
|
|
88
88
|
for (let around = boxBeside(box, x, y); around; around = around === box ? null : around.parentElement.closest("[data-ge-box]")) {
|
|
@@ -157,26 +157,24 @@ export function createInsert({ struct, blocks }) {
|
|
|
157
157
|
const texts = [unit, ...unit.querySelectorAll("[data-ge-block], [data-ge-new]")].filter((node) => node.isContentEditable && node.getClientRects().length > 0 && !node.closest("details:not([open]) > :not(summary)"));
|
|
158
158
|
return (last ? texts.at(-1) : texts[0]) ?? null;
|
|
159
159
|
}
|
|
160
|
-
// A boxed block or a row of columns at height `y` with (x, y) right of
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// the gap's (columns/: it drags, a click alone changes nothing), or past the row, which is selected.
|
|
160
|
+
// A boxed block or a row of columns at height `y` with (x, y) right of it: between its right edge
|
|
161
|
+
// and the text area's it stands in (a table, a picture narrower than its column: the user's,
|
|
162
|
+
// 2026-11-09, the caret after it where a click there once selected it, 2026-11-07, and later went to
|
|
163
|
+
// the nearest text), in the padding of the box it stands in (a highlight block's, a callout's) or in the
|
|
164
|
+
// page's margin; or null. Never in another text area (the blank box `target` the press was on holds the
|
|
165
|
+
// block: a column further right is no place beside one in the column left of it). The innermost,
|
|
166
|
+
// but never a block inside a row of columns the click is to the right of too (the caret goes after
|
|
167
|
+
// the row): past a column's edge is the gap to the next column, whose press is the gap's (columns/:
|
|
168
|
+
// it drags, a click alone changes nothing), or past the row.
|
|
170
169
|
function boxedBeside(main, x, y, target) {
|
|
171
170
|
let found = null;
|
|
172
171
|
const right = (unit) => {
|
|
173
172
|
const rect = unit.getBoundingClientRect();
|
|
174
173
|
if (y < rect.top || y > rect.bottom || (target?.contains && !target.contains(unit))) return false;
|
|
175
|
-
|
|
176
|
-
return x > Math.max(rect.right, contentOf(box.getBoundingClientRect(), getComputedStyle(box)).right);
|
|
174
|
+
return x > rect.right;
|
|
177
175
|
};
|
|
178
176
|
for (const unit of main.querySelectorAll(`[data-ge-unit-kind]`)) {
|
|
179
|
-
if (!
|
|
177
|
+
if (!caretBeside(unit) || !unit.parentElement?.hasAttribute("data-ge-box") || unit.parentElement.closest("details:not([open])")) continue;
|
|
180
178
|
if (!right(unit)) continue;
|
|
181
179
|
const row = unit.parentElement.closest('[data-ge-unit-kind="columns"]');
|
|
182
180
|
if (row && main.contains(row) && right(row)) continue;
|
|
@@ -86,8 +86,8 @@ const STYLE = `
|
|
|
86
86
|
/* A note wraps at the card's width and never widens it (no width of its own: contain). */
|
|
87
87
|
.ge-pop-note { contain: inline-size; padding: 0 12px 6px 40px; color: var(--ge-tx3); font-size: 12px; line-height: 1.5; }
|
|
88
88
|
.ge-pop-empty { padding: 8px 12px; color: var(--ge-tx3); }
|
|
89
|
-
/* What a row makes, shown beside it under the pointer (a component package's preview.svg). */
|
|
90
|
-
.ge-pop-preview { display: block; width: 240px; max-height: 180px; border-radius: 4px; object-fit: contain; background: var(--ge-bg); }
|
|
89
|
+
/* What a row makes, shown beside it under the pointer (a component package's preview.svg), in the middle of the bubble: its description under it wraps wider than the picture. */
|
|
90
|
+
.ge-pop-preview { display: block; width: 240px; max-width: 100%; max-height: 180px; margin: 0 auto; border-radius: 4px; object-fit: contain; object-position: center; background: var(--ge-bg); }
|
|
91
91
|
.ge-pop-search { position: sticky; top: 0; z-index: 1; padding: 8px 8px 6px; background: var(--ge-bg); }
|
|
92
92
|
.ge-pop-search input { box-sizing: border-box; width: 100%; height: 28px; margin: 0; padding: 0 8px; border: 0; border-radius: 6px; outline: none; background: var(--ge-hover); color: var(--ge-tx); font: inherit; box-shadow: inset 0 0 0 1px var(--ge-line); }
|
|
93
93
|
.ge-pop-search input:focus { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ge-tx3) 60%, transparent); }
|
|
@@ -145,7 +145,9 @@ export const menuOpen = () => opened.size > 0;
|
|
|
145
145
|
* as its rows (FIT; SEARCHING with a search field). around: the rect of the block a handle
|
|
146
146
|
* (the anchor) stands for; the menu keeps clear of that block: left of it beside the handle, else
|
|
147
147
|
* below or above it (scrolling inside when only part of it fits), and over it only when the block
|
|
148
|
-
* leaves no room at all
|
|
148
|
+
* leaves no room at all; a tall block (more than TALL high: a table, a formula, a highlight, an image)
|
|
149
|
+
* has it beside the handle, left of it or, with no room there,
|
|
150
|
+
* right of it over the block, centered on the handle, never over the handle. side: "right": beside the anchor, right of it (left of it when there is no
|
|
149
151
|
* room), level with it (a section's bar). Without either the menu stands below the anchor (the
|
|
150
152
|
* caret), or above it when there is no room below; align: "end", its right edge at the anchor's.
|
|
151
153
|
* icons: false, rows of words alone (no column for icons: a code block's languages; a row that opens a
|
|
@@ -276,6 +278,7 @@ export function createMenu() {
|
|
|
276
278
|
}
|
|
277
279
|
|
|
278
280
|
const clamp = (value, low, high) => Math.max(low, Math.min(high, value));
|
|
281
|
+
const TALL = 34; // px: a block taller than this (a line of text is 26px) is a tall one: its menu may stand over it
|
|
279
282
|
|
|
280
283
|
function place() {
|
|
281
284
|
if (options.inside) return;
|
|
@@ -324,11 +327,16 @@ export function createMenu() {
|
|
|
324
327
|
const below = innerHeight - margin - (block.bottom + gap);
|
|
325
328
|
const above = block.top - gap - margin;
|
|
326
329
|
const beside = anchor.left - gap - width;
|
|
327
|
-
|
|
328
|
-
|
|
330
|
+
// A block of more than a line (a table, a formula, a highlight, an image: taller than TALL) may
|
|
331
|
+
// have the menu over it: beside its handle, right of it where there is no room left of it,
|
|
332
|
+
// rather than below the block, away from the handle.
|
|
333
|
+
const middle = (anchor.top + anchor.bottom) / 2;
|
|
334
|
+
const onRight = block.bottom - block.top > TALL && anchor.right + gap + width <= innerWidth - margin;
|
|
335
|
+
if (beside >= margin || onRight) {
|
|
336
|
+
// Beside the handle (never over it), left first, centered on it.
|
|
329
337
|
fit(room);
|
|
330
|
-
left = beside;
|
|
331
|
-
top = clamp(
|
|
338
|
+
left = beside >= margin ? beside : anchor.right + gap;
|
|
339
|
+
top = clamp(middle - height / 2, margin, innerHeight - margin - height);
|
|
332
340
|
} else if (Math.max(below, above) >= Math.min(height, 180)) {
|
|
333
341
|
// Below the block, else above it; in the larger of the two when it fits in neither.
|
|
334
342
|
left = clamp(anchor.left, margin, innerWidth - width - margin);
|
|
@@ -36,14 +36,10 @@ const variants = () => Object.keys(TYPE_COMPONENT).flatMap((type) => Object.entr
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* The keys of the variants the menus of blocks offer now ("/", 在下方新建, the handle's +, 转换成): every
|
|
39
|
-
* one
|
|
40
|
-
*
|
|
41
|
-
* only say it has no table to read.
|
|
39
|
+
* one, a view of a table (`readsTables`: a custom block whose needs hold "tables") too, which binds
|
|
40
|
+
* itself to the table above it as it is made (components/installed/page.mjs; page/table/binding.mjs).
|
|
42
41
|
*/
|
|
43
|
-
export const variantKeys = () => variants().
|
|
44
|
-
|
|
45
|
-
/** The keys of the variants that are views of a table, for a table's 视图 › (table/views.mjs). */
|
|
46
|
-
export const viewKeys = () => variants().filter(([, row]) => row.readsTables).map(([key]) => key);
|
|
42
|
+
export const variantKeys = () => variants().map(([key]) => key);
|
|
47
43
|
|
|
48
44
|
/** The page objects, in the order they are made. */
|
|
49
45
|
export const pageObjects = () => Object.values(pages);
|
|
@@ -2,7 +2,7 @@ import { t } from "../../shared/i18n/index.mjs";
|
|
|
2
2
|
import { addressOn, elsewhere, fromFile, isPageAddress, mayBePage, pageAddress, workbenchHash } from "../../shared/page-address.mjs";
|
|
3
3
|
import { inTrash, linkedPages, pageLinks } from "../../shared/page-links.mjs";
|
|
4
4
|
import { entryOfKey } from "../../shared/extension-sidebar.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { SHELL, tell } from "./present-shell.mjs";
|
|
6
6
|
|
|
7
7
|
// The links of a page shown in 演示视图 (?present: src/server/routes.mjs), which has no editor and whose
|
|
8
8
|
// frame the presentation holds (src/workbench/app/features/present/). A link to a document or a block
|
|
@@ -16,7 +16,8 @@ import { PROTOCOL, VERSION } from "../../shared/session.mjs";
|
|
|
16
16
|
// a click stays theirs: an anchor, a link to save, a link of a target of its own, any click with a key
|
|
17
17
|
// held. A workbench's address that names a web link of the sidebar, or an entry of an extension's column
|
|
18
18
|
// (#~<id>/<entry id>, one with an address), is a web address to the presentation: its page goes to a new
|
|
19
|
-
// tab; an entry the list does not hold, or one with no address, is said gone as a document is.
|
|
19
|
+
// tab; an entry the list does not hold, or one with no address, is said gone as a document is. (In a draft's presentation, a frame
|
|
20
|
+
// of its workspace's own server, the word goes to the workbench of another origin and a link keeps it around the page: present-shell.mjs.)
|
|
20
21
|
|
|
21
22
|
const own = pageAddress(location.href)?.slug ?? null;
|
|
22
23
|
const openTab = (address) => window.open(address, "_blank", "noopener");
|
|
@@ -24,13 +25,13 @@ const read = (route) => fetch(`/__gallery/workspace/${route}`).then((answer) =>
|
|
|
24
25
|
|
|
25
26
|
function go({ slug, block }) {
|
|
26
27
|
const fragment = block ? `#${encodeURIComponent(block)}` : "";
|
|
27
|
-
if (slug !== own) location.assign(`/generated/${slug}.html?present${fragment}`);
|
|
28
|
+
if (slug !== own) location.assign(`/generated/${slug}.html?present${SHELL ? `&shell=${encodeURIComponent(SHELL)}` : ""}${fragment}`); // a draft's presentation keeps the workbench around it (present-shell.mjs)
|
|
28
29
|
else if (block) dispatchEvent(new CustomEvent("doc-jump-request", { detail: { hash: fragment } })); // the kit's jump, a step of the history as a link #<block id> is
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function sayGone(name, { trash = [] } = {}) {
|
|
32
33
|
const text = inTrash(trash, name) ? t("这个页面已删除。") : t("没有找到这个页面。");
|
|
33
|
-
|
|
34
|
+
tell("notice", { text });
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
async function follow(address) {
|