@formicoidea/labre-core 0.58.0 → 0.59.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/architecture/code.c4 +94 -65
- package/architecture/domain.c4 +11 -5
- package/dist/_pkgs/affine-block-attachment/edgeless-clipboard-config.d.ts +3 -2
- package/dist/_pkgs/affine-block-attachment/edgeless-clipboard-config.js +18 -13
- package/dist/_pkgs/affine-block-callout/adapters/markdown.js +4 -1
- package/dist/_pkgs/affine-block-code/adapters/html.js +1 -1
- package/dist/_pkgs/affine-block-code/adapters/markdown/preprocessor.js +13 -1
- package/dist/_pkgs/affine-block-code/adapters/notion-html.js +1 -1
- package/dist/_pkgs/affine-block-code/code-block.d.ts +17 -2
- package/dist/_pkgs/affine-block-code/code-block.js +28 -23
- package/dist/_pkgs/affine-block-code/code-toolbar/components/lang-button.js +17 -4
- package/dist/_pkgs/affine-block-code/code-toolbar/components/preview-button.js +1 -1
- package/dist/_pkgs/affine-block-data-view/data-source.js +12 -5
- package/dist/_pkgs/affine-block-edgeless-text/edgeless-clipboard-config.d.ts +3 -2
- package/dist/_pkgs/affine-block-edgeless-text/edgeless-clipboard-config.js +8 -5
- package/dist/_pkgs/affine-block-image/asset-picker.d.ts +23 -2
- package/dist/_pkgs/affine-block-image/asset-picker.js +52 -12
- package/dist/_pkgs/affine-block-image/configs/replace-action.d.ts +8 -5
- package/dist/_pkgs/affine-block-image/configs/replace-action.js +8 -8
- package/dist/_pkgs/affine-block-image/configs/toolbar.js +2 -1
- package/dist/_pkgs/affine-block-image/crop/crop-controller.d.ts +15 -0
- package/dist/_pkgs/affine-block-image/crop/crop-controller.js +40 -2
- package/dist/_pkgs/affine-block-image/crop/crop-math.d.ts +22 -0
- package/dist/_pkgs/affine-block-image/crop/crop-math.js +55 -2
- package/dist/_pkgs/affine-block-image/crop/crop-overlay.d.ts +17 -3
- package/dist/_pkgs/affine-block-image/crop/crop-overlay.js +64 -6
- package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.d.ts +3 -2
- package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.js +20 -14
- package/dist/_pkgs/affine-block-image/image-block.js +1 -0
- package/dist/_pkgs/affine-block-image/image-edgeless-block.js +3 -0
- package/dist/_pkgs/affine-block-image/index.d.ts +2 -0
- package/dist/_pkgs/affine-block-image/index.js +2 -0
- package/dist/_pkgs/affine-block-image/replace/commands.d.ts +68 -0
- package/dist/_pkgs/affine-block-image/replace/commands.js +200 -0
- package/dist/_pkgs/affine-block-image/replace/replace-model.d.ts +44 -0
- package/dist/_pkgs/affine-block-image/replace/replace-model.js +168 -0
- package/dist/_pkgs/affine-block-image/replace/toolbar.d.ts +21 -0
- package/dist/_pkgs/affine-block-image/replace/toolbar.js +159 -0
- package/dist/_pkgs/affine-block-image/translations.d.ts +15 -2
- package/dist/_pkgs/affine-block-image/translations.js +35 -2
- package/dist/_pkgs/affine-block-image/utils.d.ts +7 -0
- package/dist/_pkgs/affine-block-image/utils.js +13 -0
- package/dist/_pkgs/affine-block-image/view.js +7 -0
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.d.ts +14 -5
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +18 -11
- package/dist/_pkgs/affine-block-note/config.d.ts +9 -2
- package/dist/_pkgs/affine-block-note/edgeless-clipboard-config.d.ts +3 -2
- package/dist/_pkgs/affine-block-note/edgeless-clipboard-config.js +8 -5
- package/dist/_pkgs/affine-block-paragraph/adapters/markdown.js +11 -0
- package/dist/_pkgs/affine-block-root/commands/invisible-characters-commands.d.ts +17 -0
- package/dist/_pkgs/affine-block-root/commands/invisible-characters-commands.js +123 -0
- package/dist/_pkgs/affine-block-root/edgeless/clipboard/command.js +82 -65
- package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +2 -0
- package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-root/index.js +1 -0
- package/dist/_pkgs/affine-block-root/translations.d.ts +6 -0
- package/dist/_pkgs/affine-block-root/translations.js +10 -0
- package/dist/_pkgs/affine-block-root/view.js +6 -1
- package/dist/_pkgs/affine-block-surface/commands/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-surface/commands/index.js +1 -1
- package/dist/_pkgs/affine-block-surface/commands/reassociate-connectors.d.ts +9 -0
- package/dist/_pkgs/affine-block-surface/commands/reassociate-connectors.js +21 -12
- package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.d.ts +33 -0
- package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.js +53 -0
- package/dist/_pkgs/affine-block-surface/framework-background/background-view.js +2 -1
- package/dist/_pkgs/affine-block-surface/framework-background/band-widths.d.ts +104 -0
- package/dist/_pkgs/affine-block-surface/framework-background/band-widths.js +156 -0
- package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +87 -4
- package/dist/_pkgs/affine-block-surface/framework-background/def.js +26 -7
- package/dist/_pkgs/affine-block-surface/framework-background/ellipsis.d.ts +58 -0
- package/dist/_pkgs/affine-block-surface/framework-background/ellipsis.js +110 -0
- package/dist/_pkgs/affine-block-surface/framework-background/index.d.ts +3 -0
- package/dist/_pkgs/affine-block-surface/framework-background/index.js +3 -0
- package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +12 -2
- package/dist/_pkgs/affine-block-surface/framework-background/labels.js +29 -5
- package/dist/_pkgs/affine-block-surface/framework-background/measure-text.d.ts +14 -0
- package/dist/_pkgs/affine-block-surface/framework-background/measure-text.js +31 -0
- package/dist/_pkgs/affine-block-surface/framework-background/renderer.d.ts +14 -1
- package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +84 -31
- package/dist/_pkgs/affine-block-table/adapters/html.js +26 -14
- package/dist/_pkgs/affine-block-table/adapters/utils.d.ts +1 -0
- package/dist/_pkgs/affine-block-table/adapters/utils.js +23 -5
- package/dist/_pkgs/affine-block-table/header-menu.d.ts +13 -0
- package/dist/_pkgs/affine-block-table/header-menu.js +25 -0
- package/dist/_pkgs/affine-block-table/selection-controller.d.ts +15 -0
- package/dist/_pkgs/affine-block-table/selection-controller.js +62 -0
- package/dist/_pkgs/affine-block-table/table-cell-css.d.ts +7 -0
- package/dist/_pkgs/affine-block-table/table-cell-css.js +10 -0
- package/dist/_pkgs/affine-block-table/table-cell.d.ts +8 -0
- package/dist/_pkgs/affine-block-table/table-cell.js +35 -3
- package/dist/_pkgs/affine-block-table/table-data-manager.d.ts +14 -0
- package/dist/_pkgs/affine-block-table/table-data-manager.js +44 -3
- package/dist/_pkgs/affine-block-table/translations.d.ts +14 -0
- package/dist/_pkgs/affine-block-table/translations.js +22 -0
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.d.ts +2 -2
- package/dist/_pkgs/affine-gfx-template/builtin/other.d.ts +4 -0
- package/dist/_pkgs/affine-gfx-template/builtin/other.js +1 -1
- package/dist/_pkgs/affine-gfx-template/builtin/slides/build.d.ts +81 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/build.js +276 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/consts.d.ts +126 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/consts.js +101 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/index.d.ts +8 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/index.js +27 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/masks.d.ts +83 -0
- package/dist/_pkgs/affine-gfx-template/builtin/slides/masks.js +674 -0
- package/dist/_pkgs/affine-gfx-template/contribute.js +12 -17
- package/dist/_pkgs/affine-gfx-template/make-snapshot.d.ts +14 -2
- package/dist/_pkgs/affine-gfx-template/make-snapshot.js +6 -2
- package/dist/_pkgs/affine-gfx-template/services/template-middlewares.d.ts +12 -0
- package/dist/_pkgs/affine-gfx-template/services/template-middlewares.js +48 -0
- package/dist/_pkgs/affine-gfx-template/services/template.js +2 -1
- package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +15 -1
- package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +33 -37
- package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +6 -0
- package/dist/_pkgs/affine-gfx-template/translations.d.ts +94 -0
- package/dist/_pkgs/affine-gfx-template/translations.js +441 -2
- package/dist/_pkgs/affine-model/blocks/table/table-model.d.ts +4 -0
- package/dist/_pkgs/affine-model/blocks/table/table-model.js +4 -0
- package/dist/_pkgs/affine-model/elements/bpmn/node.d.ts +46 -3
- package/dist/_pkgs/affine-model/elements/bpmn/node.js +47 -4
- package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +20 -0
- package/dist/_pkgs/affine-model/elements/bpmn/pool.js +33 -4
- package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +3 -16
- package/dist/_pkgs/affine-shared/services/edit-props-store.js +16 -16
- package/dist/_pkgs/affine-shared/services/editor-setting-service.d.ts +3 -0
- package/dist/_pkgs/affine-shared/services/editor-setting-service.js +4 -0
- package/dist/_pkgs/affine-shared/services/index.d.ts +1 -0
- package/dist/_pkgs/affine-shared/services/index.js +1 -0
- package/dist/_pkgs/affine-shared/services/invisible-characters.d.ts +33 -0
- package/dist/_pkgs/affine-shared/services/invisible-characters.js +63 -0
- package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-cache.d.ts +0 -9
- package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-cache.js +3 -9
- package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +26 -0
- package/dist/_pkgs/affine-shared/services/telemetry-service/telemetry-service.d.ts +4 -3
- package/dist/_pkgs/affine-shared/services/telemetry-service/types.d.ts +11 -0
- package/dist/_pkgs/affine-shared/utils/index.d.ts +1 -0
- package/dist/_pkgs/affine-shared/utils/index.js +1 -0
- package/dist/_pkgs/affine-shared/utils/invisible-characters.d.ts +42 -0
- package/dist/_pkgs/affine-shared/utils/invisible-characters.js +81 -0
- package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/zoom-toolbar.js +13 -2
- package/dist/_pkgs/data-view/view-presets/table/pc/header/column-header.js +6 -0
- package/dist/_pkgs/data-view/view-presets/table/pc/table-view-ui-logic.js +5 -2
- package/dist/_pkgs/std/clipboard/clipboard.d.ts +15 -1
- package/dist/_pkgs/std/clipboard/clipboard.js +18 -0
- package/dist/_pkgs/std/inline/components/embed-gap.js +1 -1
- package/dist/_pkgs/std/inline/components/v-line.js +1 -1
- package/dist/_pkgs/std/inline/components/v-text.d.ts +15 -2
- package/dist/_pkgs/std/inline/components/v-text.js +147 -15
- package/dist/_pkgs/std/inline/consts.d.ts +5 -0
- package/dist/_pkgs/std/inline/consts.js +5 -0
- package/dist/_pkgs/std/inline/utils/index.d.ts +1 -0
- package/dist/_pkgs/std/inline/utils/index.js +1 -0
- package/dist/_pkgs/std/inline/utils/invisible-characters.d.ts +57 -0
- package/dist/_pkgs/std/inline/utils/invisible-characters.js +162 -0
- package/dist/_pkgs/std/inline/utils/point-conversion.js +3 -3
- package/dist/_pkgs/std/inline/utils/text.d.ts +8 -0
- package/dist/_pkgs/std/inline/utils/text.js +11 -2
- package/dist/_pkgs/store/model/store/crud.d.ts +28 -0
- package/dist/_pkgs/store/model/store/crud.js +97 -30
- package/dist/_pkgs/store/model/store/query.js +3 -2
- package/dist/_pkgs/store/model/store/store.d.ts +21 -0
- package/dist/_pkgs/store/model/store/store.js +88 -21
- package/dist/_pkgs/store/transformer/transformer.d.ts +46 -0
- package/dist/_pkgs/store/transformer/transformer.js +72 -0
- package/dist/commands.js +6 -1
- package/dist/translations.js +2 -0
- package/package.json +3 -4
|
@@ -154,10 +154,13 @@ export class TableViewUI extends DataViewUIBase {
|
|
|
154
154
|
dataViewLogic: this.logic,
|
|
155
155
|
})
|
|
156
156
|
: ''}
|
|
157
|
-
<div
|
|
157
|
+
<div
|
|
158
|
+
class="${tableWrapperStyle} dv-table-wrapper"
|
|
159
|
+
style="${wrapperStyle}"
|
|
160
|
+
>
|
|
158
161
|
<div
|
|
159
162
|
${ref(this.logic.scrollContainer$)}
|
|
160
|
-
class="${tableScrollContainerStyle}"
|
|
163
|
+
class="${tableScrollContainerStyle} dv-table-scroll-container"
|
|
161
164
|
@wheel="${this.logic.onWheel}"
|
|
162
165
|
>
|
|
163
166
|
<div
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import type { BlockSnapshot, Slice, Store, TransformerMiddleware } from '../../store/index.js';
|
|
1
|
+
import type { BlockSnapshot, InsertedBlockSnapshot, Slice, Store, TransformerMiddleware } from '../../store/index.js';
|
|
2
2
|
import { LifeCycleWatcher } from '../extension/index.js';
|
|
3
|
+
/** What `Clipboard.prepareBlockSnapshot` resolves to. */
|
|
4
|
+
export interface PreparedBlockSnapshotPaste {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
insert(doc: Store): InsertedBlockSnapshot | undefined;
|
|
7
|
+
}
|
|
3
8
|
export declare class Clipboard extends LifeCycleWatcher {
|
|
4
9
|
static key: string;
|
|
5
10
|
protected get _adapters(): import("./clipboard-adapter.js").ClipboardAdapterConfig[];
|
|
@@ -11,6 +16,15 @@ export declare class Clipboard extends LifeCycleWatcher {
|
|
|
11
16
|
duplicateSlice: (slice: Slice, doc: Store, parent?: string, index?: number, type?: string) => Promise<void>;
|
|
12
17
|
paste: (event: ClipboardEvent, doc: Store, parent?: string, index?: number) => Promise<Slice | null | undefined>;
|
|
13
18
|
pasteBlockSnapshot: (snapshot: BlockSnapshot, doc: Store, parent?: string, index?: number) => Promise<import("../../store/index.js").BlockModel<object> | undefined>;
|
|
19
|
+
/**
|
|
20
|
+
* `pasteBlockSnapshot` in two halves (ADR 0042): the returned promise does
|
|
21
|
+
* the async work and writes nothing; `insert(doc)` then writes
|
|
22
|
+
* synchronously, so a caller can run it inside its own transaction, and
|
|
23
|
+
* `flushAfterImport()` runs the import middlewares once that transaction
|
|
24
|
+
* has ended. Both halves use one transformer, whose middlewares keep their
|
|
25
|
+
* state between them.
|
|
26
|
+
*/
|
|
27
|
+
prepareBlockSnapshot: (snapshot: BlockSnapshot, parent?: string, index?: number) => Promise<PreparedBlockSnapshotPaste | undefined>;
|
|
14
28
|
unuse: (middleware: TransformerMiddleware) => void;
|
|
15
29
|
use: (middleware: TransformerMiddleware) => void;
|
|
16
30
|
get configs(): Map<string, string>;
|
|
@@ -130,6 +130,24 @@ export class Clipboard extends LifeCycleWatcher {
|
|
|
130
130
|
this.pasteBlockSnapshot = async (snapshot, doc, parent, index) => {
|
|
131
131
|
return this._getJob().snapshotToBlock(snapshot, doc, parent, index);
|
|
132
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* `pasteBlockSnapshot` in two halves (ADR 0042): the returned promise does
|
|
135
|
+
* the async work and writes nothing; `insert(doc)` then writes
|
|
136
|
+
* synchronously, so a caller can run it inside its own transaction, and
|
|
137
|
+
* `flushAfterImport()` runs the import middlewares once that transaction
|
|
138
|
+
* has ended. Both halves use one transformer, whose middlewares keep their
|
|
139
|
+
* state between them.
|
|
140
|
+
*/
|
|
141
|
+
this.prepareBlockSnapshot = async (snapshot, parent, index) => {
|
|
142
|
+
const job = this._getJob();
|
|
143
|
+
const prepared = await job.prepareSnapshotToBlock(snapshot, parent, index);
|
|
144
|
+
if (!prepared)
|
|
145
|
+
return;
|
|
146
|
+
return {
|
|
147
|
+
id: prepared.id,
|
|
148
|
+
insert: (doc) => job.insertPrepared(prepared, doc),
|
|
149
|
+
};
|
|
150
|
+
};
|
|
133
151
|
this.unuse = (middleware) => {
|
|
134
152
|
this._jobMiddlewares = this._jobMiddlewares.filter(m => m !== middleware);
|
|
135
153
|
};
|
|
@@ -126,7 +126,7 @@ let VLine = (() => {
|
|
|
126
126
|
if (this.elements.length === 0) {
|
|
127
127
|
// don't use v-element because it not correspond to the actual delta
|
|
128
128
|
return html `
|
|
129
|
-
<div><v-text .str=${ZERO_WIDTH_FOR_EMPTY_LINE}></v-text></div>
|
|
129
|
+
<div><v-text empty .str=${ZERO_WIDTH_FOR_EMPTY_LINE}></v-text></div>
|
|
130
130
|
`;
|
|
131
131
|
}
|
|
132
132
|
const inlineEditor = this.inlineEditor;
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
|
-
|
|
2
|
+
declare const VText_base: typeof LitElement;
|
|
3
|
+
export declare class VText extends VText_base {
|
|
4
|
+
private readonly _std;
|
|
5
|
+
private _showInvisibles$;
|
|
3
6
|
createRenderRoot(): this;
|
|
4
|
-
|
|
7
|
+
connectedCallback(): void;
|
|
8
|
+
private _segments;
|
|
9
|
+
render(): import("lit-html").TemplateResult<1> | import("lit-html").TemplateResult<1>[];
|
|
10
|
+
/**
|
|
11
|
+
* A placeholder, not text of the model: the zero-width character an empty
|
|
12
|
+
* line or an embed gap renders so the caret has somewhere to go. Counted as
|
|
13
|
+
* length 0 by the offsets (`calculateTextLength`), whatever its content —
|
|
14
|
+
* a zero-width space the USER stored is text and keeps its length.
|
|
15
|
+
*/
|
|
16
|
+
accessor empty: boolean;
|
|
5
17
|
accessor str: string;
|
|
6
18
|
}
|
|
7
19
|
declare global {
|
|
@@ -9,3 +21,4 @@ declare global {
|
|
|
9
21
|
'v-text': VText;
|
|
10
22
|
}
|
|
11
23
|
}
|
|
24
|
+
export {};
|
|
@@ -32,43 +32,175 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import {
|
|
35
|
+
import { SignalWatcher } from '../../../global/lit/index.js';
|
|
36
|
+
import { ContextConsumer } from '@lit/context';
|
|
37
|
+
import { css, html, LitElement, nothing, unsafeCSS } from 'lit';
|
|
36
38
|
import { property } from 'lit/decorators.js';
|
|
37
39
|
import { styleMap } from 'lit/directives/style-map.js';
|
|
38
|
-
import {
|
|
40
|
+
import { stdContext } from '../../view/element/lit-host.js';
|
|
41
|
+
import { INLINE_ROOT_ATTR, SHOW_INVISIBLES_ATTR, ZERO_WIDTH_FOR_EMPTY_LINE, } from '../consts.js';
|
|
42
|
+
import { InvisibleCharactersDisplayIdentifier, segmentInvisibleCharacters, } from '../utils/invisible-characters.js';
|
|
43
|
+
const textStyle = styleMap({
|
|
44
|
+
'word-break': 'break-word',
|
|
45
|
+
'text-wrap': 'wrap',
|
|
46
|
+
'white-space-collapse': 'break-spaces',
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* The marks of "show special characters". Each mark is drawn by an
|
|
50
|
+
* absolutely positioned `::before` over a span that still holds the original
|
|
51
|
+
* character: the layout, the offsets, a copy and the IME see the text as it is.
|
|
52
|
+
* Line ends are drawn per `v-line` from the attribute the editor sets on its
|
|
53
|
+
* host, so they need no element of their own. Colours are theme tokens.
|
|
54
|
+
*/
|
|
55
|
+
const invisiblesStyles = css `
|
|
56
|
+
[data-v-invisible] {
|
|
57
|
+
position: relative;
|
|
58
|
+
}
|
|
59
|
+
[data-v-invisible]::before {
|
|
60
|
+
position: absolute;
|
|
61
|
+
left: 0;
|
|
62
|
+
right: 0;
|
|
63
|
+
text-align: center;
|
|
64
|
+
pointer-events: none;
|
|
65
|
+
user-select: none;
|
|
66
|
+
color: var(--affine-text-disable-color, currentColor);
|
|
67
|
+
font-weight: normal;
|
|
68
|
+
font-style: normal;
|
|
69
|
+
}
|
|
70
|
+
[data-v-invisible='space']::before {
|
|
71
|
+
content: '·';
|
|
72
|
+
}
|
|
73
|
+
[data-v-invisible='tab']::before {
|
|
74
|
+
content: '→';
|
|
75
|
+
}
|
|
76
|
+
[data-v-invisible='nbsp']::before {
|
|
77
|
+
content: '°';
|
|
78
|
+
}
|
|
79
|
+
[data-v-invisible='invisible']::before {
|
|
80
|
+
content: '';
|
|
81
|
+
left: -1px;
|
|
82
|
+
right: auto;
|
|
83
|
+
top: 10%;
|
|
84
|
+
bottom: 10%;
|
|
85
|
+
border-left: 2px solid var(--affine-error-color, currentColor);
|
|
86
|
+
}
|
|
87
|
+
[${unsafeCSS(SHOW_INVISIBLES_ATTR)}] v-line::after {
|
|
88
|
+
position: absolute;
|
|
89
|
+
pointer-events: none;
|
|
90
|
+
user-select: none;
|
|
91
|
+
color: var(--affine-text-disable-color, currentColor);
|
|
92
|
+
}
|
|
93
|
+
[${unsafeCSS(SHOW_INVISIBLES_ATTR)}] v-line:not(:last-of-type)::after {
|
|
94
|
+
content: '↵';
|
|
95
|
+
}
|
|
96
|
+
[${unsafeCSS(SHOW_INVISIBLES_ATTR)}] v-line:last-of-type::after {
|
|
97
|
+
content: '¶';
|
|
98
|
+
}
|
|
99
|
+
`;
|
|
100
|
+
/**
|
|
101
|
+
* One `[data-v-text]` span holding one text node. We need to avoid
|
|
102
|
+
|
|
103
|
+
* appearing before and after the span element, which will cause the sync
|
|
104
|
+
* problem about the cursor position.
|
|
105
|
+
*/
|
|
106
|
+
function renderRun(text, kind, empty = false) {
|
|
107
|
+
return html `<span
|
|
108
|
+
style=${textStyle}
|
|
109
|
+
data-v-text="true"
|
|
110
|
+
data-v-empty=${empty ? 'true' : nothing}
|
|
111
|
+
data-v-invisible=${kind ?? nothing}
|
|
112
|
+
>${text}</span
|
|
113
|
+
>`;
|
|
114
|
+
}
|
|
115
|
+
const styledRoots = new WeakSet();
|
|
116
|
+
/**
|
|
117
|
+
* Adopt the marks' stylesheet once per document or shadow root, the first
|
|
118
|
+
* time a mark is rendered there: `v-text` renders in light DOM, so a static
|
|
119
|
+
* `styles` would never apply.
|
|
120
|
+
*/
|
|
121
|
+
function adoptInvisiblesStyles(node) {
|
|
122
|
+
const root = node.getRootNode();
|
|
123
|
+
if (!(root instanceof Document || root instanceof ShadowRoot))
|
|
124
|
+
return;
|
|
125
|
+
if (styledRoots.has(root))
|
|
126
|
+
return;
|
|
127
|
+
const sheet = invisiblesStyles.styleSheet;
|
|
128
|
+
if (!sheet)
|
|
129
|
+
return;
|
|
130
|
+
styledRoots.add(root);
|
|
131
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
|
|
132
|
+
}
|
|
39
133
|
let VText = (() => {
|
|
40
|
-
let _classSuper = LitElement;
|
|
134
|
+
let _classSuper = SignalWatcher(LitElement);
|
|
135
|
+
let _empty_decorators;
|
|
136
|
+
let _empty_initializers = [];
|
|
137
|
+
let _empty_extraInitializers = [];
|
|
41
138
|
let _str_decorators;
|
|
42
139
|
let _str_initializers = [];
|
|
43
140
|
let _str_extraInitializers = [];
|
|
44
141
|
return class VText extends _classSuper {
|
|
45
142
|
static {
|
|
46
143
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
144
|
+
_empty_decorators = [property({ type: Boolean })];
|
|
47
145
|
_str_decorators = [property({ attribute: false })];
|
|
146
|
+
__esDecorate(this, null, _empty_decorators, { kind: "accessor", name: "empty", static: false, private: false, access: { has: obj => "empty" in obj, get: obj => obj.empty, set: (obj, value) => { obj.empty = value; } }, metadata: _metadata }, _empty_initializers, _empty_extraInitializers);
|
|
48
147
|
__esDecorate(this, null, _str_decorators, { kind: "accessor", name: "str", static: false, private: false, access: { has: obj => "str" in obj, get: obj => obj.str, set: (obj, value) => { obj.str = value; } }, metadata: _metadata }, _str_initializers, _str_extraInitializers);
|
|
49
148
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
50
149
|
}
|
|
51
150
|
createRenderRoot() {
|
|
52
151
|
return this;
|
|
53
152
|
}
|
|
153
|
+
connectedCallback() {
|
|
154
|
+
super.connectedCallback();
|
|
155
|
+
this._showInvisibles$ =
|
|
156
|
+
this._std.value?.getOptional(InvisibleCharactersDisplayIdentifier)
|
|
157
|
+
?.showInvisibles$ ?? null;
|
|
158
|
+
}
|
|
159
|
+
_segments() {
|
|
160
|
+
if (!this._showInvisibles$?.value || this.empty)
|
|
161
|
+
return null;
|
|
162
|
+
// An embed's placeholder is one character of the model drawn as
|
|
163
|
+
// something else: never a mark on it.
|
|
164
|
+
if (this.closest('[data-v-embed="true"]'))
|
|
165
|
+
return null;
|
|
166
|
+
// Re-cutting the text under an IME composition would tear the
|
|
167
|
+
// composition apart: the plain run stays until the next render.
|
|
168
|
+
const root = this.closest(`[${INLINE_ROOT_ATTR}]`);
|
|
169
|
+
if (root?.inlineEditor?.isComposing)
|
|
170
|
+
return null;
|
|
171
|
+
return segmentInvisibleCharacters(this.str);
|
|
172
|
+
}
|
|
54
173
|
render() {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
data-v-text="true"
|
|
64
|
-
>${this.str}</span
|
|
65
|
-
>`;
|
|
174
|
+
const segments = this._segments();
|
|
175
|
+
if (!segments)
|
|
176
|
+
return renderRun(this.str, undefined, this.empty);
|
|
177
|
+
adoptInvisiblesStyles(this);
|
|
178
|
+
// Several `[data-v-text]` spans, one text node each: offsets add the
|
|
179
|
+
// text nodes of every span (`getTextNodesFromElement`), so the caret and
|
|
180
|
+
// the selection map exactly as for one span.
|
|
181
|
+
return segments.map(segment => renderRun(segment.text, segment.kind));
|
|
66
182
|
}
|
|
67
|
-
#
|
|
183
|
+
#empty_accessor_storage;
|
|
184
|
+
/**
|
|
185
|
+
* A placeholder, not text of the model: the zero-width character an empty
|
|
186
|
+
* line or an embed gap renders so the caret has somewhere to go. Counted as
|
|
187
|
+
* length 0 by the offsets (`calculateTextLength`), whatever its content —
|
|
188
|
+
* a zero-width space the USER stored is text and keeps its length.
|
|
189
|
+
*/
|
|
190
|
+
get empty() { return this.#empty_accessor_storage; }
|
|
191
|
+
set empty(value) { this.#empty_accessor_storage = value; }
|
|
192
|
+
#str_accessor_storage;
|
|
68
193
|
get str() { return this.#str_accessor_storage; }
|
|
69
194
|
set str(value) { this.#str_accessor_storage = value; }
|
|
70
195
|
constructor() {
|
|
71
196
|
super(...arguments);
|
|
197
|
+
// Resolved once, on connect: the editor host provides `std`, and `std` the
|
|
198
|
+
// viewer's choice. Outside a host (the document title, a standalone inline
|
|
199
|
+
// editor) there is none, and nothing is ever marked.
|
|
200
|
+
this._std = new ContextConsumer(this, { context: stdContext });
|
|
201
|
+
this._showInvisibles$ = null;
|
|
202
|
+
this.#empty_accessor_storage = __runInitializers(this, _empty_initializers, false);
|
|
203
|
+
this.#str_accessor_storage = (__runInitializers(this, _empty_extraInitializers), __runInitializers(this, _str_initializers, ZERO_WIDTH_FOR_EMPTY_LINE));
|
|
72
204
|
__runInitializers(this, _str_extraInitializers);
|
|
73
205
|
}
|
|
74
206
|
};
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export declare const ZERO_WIDTH_FOR_EMPTY_LINE: string;
|
|
2
2
|
export declare const ZERO_WIDTH_FOR_EMBED_NODE: string;
|
|
3
3
|
export declare const INLINE_ROOT_ATTR = "data-v-root";
|
|
4
|
+
/**
|
|
5
|
+
* Set on the editor host while its viewer shows the special characters; the
|
|
6
|
+
* line ends are drawn from it (`v-text.ts`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const SHOW_INVISIBLES_ATTR = "data-show-invisibles";
|
|
@@ -2,3 +2,8 @@ import { IS_SAFARI } from '../../global/env/index.js';
|
|
|
2
2
|
export const ZERO_WIDTH_FOR_EMPTY_LINE = IS_SAFARI ? '\u200C' : '\u200B';
|
|
3
3
|
export const ZERO_WIDTH_FOR_EMBED_NODE = IS_SAFARI ? '\u200B' : '\u200C';
|
|
4
4
|
export const INLINE_ROOT_ATTR = 'data-v-root';
|
|
5
|
+
/**
|
|
6
|
+
* Set on the editor host while its viewer shows the special characters; the
|
|
7
|
+
* line ends are drawn from it (`v-text.ts`).
|
|
8
|
+
*/
|
|
9
|
+
export const SHOW_INVISIBLES_ATTR = 'data-show-invisibles';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
/**
|
|
3
|
+
* The characters a reader cannot see and a writer rarely typed: the ones an
|
|
4
|
+
* AI text generator, a copy from a web page or a "watermarking" tool leaves in
|
|
5
|
+
* a paragraph. `text.invisibles.remove` strips them and the "show special
|
|
6
|
+
* characters" display marks them.
|
|
7
|
+
*
|
|
8
|
+
* - U+200B zero width space, U+2060 word joiner, U+FEFF zero width no-break
|
|
9
|
+
* space — a BOM inside a text is a watermark, never a byte order mark;
|
|
10
|
+
* - U+00AD soft hyphen, U+180E Mongolian vowel separator;
|
|
11
|
+
* - U+2061–U+2064 the invisible math operators;
|
|
12
|
+
* - U+E0000–U+E007F the tag characters ("ASCII smuggling");
|
|
13
|
+
* - U+200D zero width joiner — see {@link findInvisibleCharacters} for the
|
|
14
|
+
* two contexts where it and the tags are kept.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT here: U+200C zero width non-joiner, which Persian and the
|
|
17
|
+
* Indic scripts need, and the no-break spaces U+00A0 and U+202F, which French
|
|
18
|
+
* typography needs before `: ; ! ?` and inside quotation marks.
|
|
19
|
+
*/
|
|
20
|
+
export declare const INVISIBLE_CHARACTERS: ReadonlySet<number>;
|
|
21
|
+
/** A character to strip or mark, as a UTF-16 slice of the string. */
|
|
22
|
+
export interface InvisibleCharacterRun {
|
|
23
|
+
index: number;
|
|
24
|
+
length: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Every character of {@link INVISIBLE_CHARACTERS} in `str`, one run per code
|
|
28
|
+
* point, in order. Kept, so never reported: a ZWJ joining two pictographs, and
|
|
29
|
+
* the tag sequence of a subdivision flag — stripping either would break an
|
|
30
|
+
* emoji the writer meant.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findInvisibleCharacters(str: string): InvisibleCharacterRun[];
|
|
33
|
+
/** What the "show special characters" display draws over a character. */
|
|
34
|
+
export type InvisibleCharacterKind = 'space' | 'tab' | 'nbsp' | 'invisible';
|
|
35
|
+
export interface InvisibleCharacterSegment {
|
|
36
|
+
text: string;
|
|
37
|
+
/** Absent: ordinary text, rendered as it is. */
|
|
38
|
+
kind?: InvisibleCharacterKind;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* `str` cut so that every space, tab, no-break space and invisible character
|
|
42
|
+
* stands alone in its own segment, the rest kept in runs. `null` when there is
|
|
43
|
+
* nothing to mark, so the common case allocates nothing.
|
|
44
|
+
*
|
|
45
|
+
* The segments concatenate back to `str` exactly: the display keeps every
|
|
46
|
+
* character as it is and only draws over it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function segmentInvisibleCharacters(str: string): InvisibleCharacterSegment[] | null;
|
|
49
|
+
/**
|
|
50
|
+
* Whether THIS viewer asked to see the special characters. Provided by the
|
|
51
|
+
* editor (`InvisibleCharacters` in `@labre/affine-shared/services`), read by
|
|
52
|
+
* every `v-text` under the editor host; absent, nothing is marked.
|
|
53
|
+
*/
|
|
54
|
+
export interface InvisibleCharactersDisplay {
|
|
55
|
+
readonly showInvisibles$: ReadonlySignal<boolean>;
|
|
56
|
+
}
|
|
57
|
+
export declare const InvisibleCharactersDisplayIdentifier: import("../../../global/di/types.js").ServiceIdentifier<InvisibleCharactersDisplay> & (<U extends InvisibleCharactersDisplay = InvisibleCharactersDisplay>(variant: import("../../../global/di/types.js").ServiceVariant) => import("../../../global/di/types.js").ServiceIdentifier<U>);
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createIdentifier } from '../../../global/di/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The characters a reader cannot see and a writer rarely typed: the ones an
|
|
4
|
+
* AI text generator, a copy from a web page or a "watermarking" tool leaves in
|
|
5
|
+
* a paragraph. `text.invisibles.remove` strips them and the "show special
|
|
6
|
+
* characters" display marks them.
|
|
7
|
+
*
|
|
8
|
+
* - U+200B zero width space, U+2060 word joiner, U+FEFF zero width no-break
|
|
9
|
+
* space — a BOM inside a text is a watermark, never a byte order mark;
|
|
10
|
+
* - U+00AD soft hyphen, U+180E Mongolian vowel separator;
|
|
11
|
+
* - U+2061–U+2064 the invisible math operators;
|
|
12
|
+
* - U+E0000–U+E007F the tag characters ("ASCII smuggling");
|
|
13
|
+
* - U+200D zero width joiner — see {@link findInvisibleCharacters} for the
|
|
14
|
+
* two contexts where it and the tags are kept.
|
|
15
|
+
*
|
|
16
|
+
* Deliberately NOT here: U+200C zero width non-joiner, which Persian and the
|
|
17
|
+
* Indic scripts need, and the no-break spaces U+00A0 and U+202F, which French
|
|
18
|
+
* typography needs before `: ; ! ?` and inside quotation marks.
|
|
19
|
+
*/
|
|
20
|
+
export const INVISIBLE_CHARACTERS = new Set([
|
|
21
|
+
0x200b,
|
|
22
|
+
0x200d,
|
|
23
|
+
0x2060,
|
|
24
|
+
0xfeff,
|
|
25
|
+
0x00ad,
|
|
26
|
+
0x180e,
|
|
27
|
+
0x2061,
|
|
28
|
+
0x2062,
|
|
29
|
+
0x2063,
|
|
30
|
+
0x2064,
|
|
31
|
+
...Array.from({ length: 0x80 }, (_, i) => 0xe0000 + i),
|
|
32
|
+
]);
|
|
33
|
+
const ZWJ = 0x200d;
|
|
34
|
+
const VARIATION_SELECTOR_16 = 0xfe0f;
|
|
35
|
+
const BLACK_FLAG = 0x1f3f4;
|
|
36
|
+
const CANCEL_TAG = 0xe007f;
|
|
37
|
+
const EXTENDED_PICTOGRAPHIC = /\p{Extended_Pictographic}/u;
|
|
38
|
+
function isPictographic(codePoint) {
|
|
39
|
+
return (codePoint !== undefined &&
|
|
40
|
+
EXTENDED_PICTOGRAPHIC.test(String.fromCodePoint(codePoint)));
|
|
41
|
+
}
|
|
42
|
+
function isSkinTone(codePoint) {
|
|
43
|
+
return codePoint >= 0x1f3fb && codePoint <= 0x1f3ff;
|
|
44
|
+
}
|
|
45
|
+
function codePointBefore(str, index) {
|
|
46
|
+
if (index <= 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
const low = str.charCodeAt(index - 1);
|
|
49
|
+
if (low >= 0xdc00 && low <= 0xdfff && index >= 2) {
|
|
50
|
+
const high = str.charCodeAt(index - 2);
|
|
51
|
+
if (high >= 0xd800 && high <= 0xdbff)
|
|
52
|
+
return str.codePointAt(index - 2);
|
|
53
|
+
}
|
|
54
|
+
return low;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A ZWJ between two pictographs is the glue of an emoji sequence (woman
|
|
58
|
+
* technologist, heart on fire, family): the emoji before it may carry a
|
|
59
|
+
* variation selector or a skin tone.
|
|
60
|
+
*/
|
|
61
|
+
function joinsEmoji(str, index) {
|
|
62
|
+
let before = index;
|
|
63
|
+
let previous = codePointBefore(str, before);
|
|
64
|
+
while (previous !== undefined &&
|
|
65
|
+
(previous === VARIATION_SELECTOR_16 || isSkinTone(previous))) {
|
|
66
|
+
before -= previous > 0xffff ? 2 : 1;
|
|
67
|
+
previous = codePointBefore(str, before);
|
|
68
|
+
}
|
|
69
|
+
return isPictographic(previous) && isPictographic(str.codePointAt(index + 1));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The tags after a black flag, closed by CANCEL TAG, spell a subdivision
|
|
73
|
+
* flag (Scotland, Wales, England): answers the index just past the sequence, or `-1`.
|
|
74
|
+
*/
|
|
75
|
+
function endOfFlagTags(str, start) {
|
|
76
|
+
let index = start;
|
|
77
|
+
while (index < str.length) {
|
|
78
|
+
const codePoint = str.codePointAt(index);
|
|
79
|
+
if (codePoint < 0xe0020 || codePoint > 0xe007e)
|
|
80
|
+
break;
|
|
81
|
+
index += 2;
|
|
82
|
+
}
|
|
83
|
+
if (index === start || str.codePointAt(index) !== CANCEL_TAG)
|
|
84
|
+
return -1;
|
|
85
|
+
return index + 2;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Every character of {@link INVISIBLE_CHARACTERS} in `str`, one run per code
|
|
89
|
+
* point, in order. Kept, so never reported: a ZWJ joining two pictographs, and
|
|
90
|
+
* the tag sequence of a subdivision flag — stripping either would break an
|
|
91
|
+
* emoji the writer meant.
|
|
92
|
+
*/
|
|
93
|
+
export function findInvisibleCharacters(str) {
|
|
94
|
+
const runs = [];
|
|
95
|
+
let index = 0;
|
|
96
|
+
while (index < str.length) {
|
|
97
|
+
const codePoint = str.codePointAt(index);
|
|
98
|
+
const size = codePoint > 0xffff ? 2 : 1;
|
|
99
|
+
if (codePoint === BLACK_FLAG) {
|
|
100
|
+
const end = endOfFlagTags(str, index + size);
|
|
101
|
+
index = end === -1 ? index + size : end;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (INVISIBLE_CHARACTERS.has(codePoint) &&
|
|
105
|
+
!(codePoint === ZWJ && joinsEmoji(str, index))) {
|
|
106
|
+
runs.push({ index, length: size });
|
|
107
|
+
}
|
|
108
|
+
index += size;
|
|
109
|
+
}
|
|
110
|
+
return runs;
|
|
111
|
+
}
|
|
112
|
+
const MAYBE_SPECIAL = /[ \t\u00A0\u202F\u00AD\u180E\u200B\u200D\u2060-\u2064\uFEFF]|\udb40[\udc00-\udc7f]/;
|
|
113
|
+
function whitespaceKind(char) {
|
|
114
|
+
if (char === ' ')
|
|
115
|
+
return 'space';
|
|
116
|
+
if (char === '\t')
|
|
117
|
+
return 'tab';
|
|
118
|
+
if (char === '\u00A0' || char === '\u202F')
|
|
119
|
+
return 'nbsp';
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* `str` cut so that every space, tab, no-break space and invisible character
|
|
124
|
+
* stands alone in its own segment, the rest kept in runs. `null` when there is
|
|
125
|
+
* nothing to mark, so the common case allocates nothing.
|
|
126
|
+
*
|
|
127
|
+
* The segments concatenate back to `str` exactly: the display keeps every
|
|
128
|
+
* character as it is and only draws over it.
|
|
129
|
+
*/
|
|
130
|
+
export function segmentInvisibleCharacters(str) {
|
|
131
|
+
if (!MAYBE_SPECIAL.test(str))
|
|
132
|
+
return null;
|
|
133
|
+
const invisible = new Map();
|
|
134
|
+
for (const run of findInvisibleCharacters(str)) {
|
|
135
|
+
invisible.set(run.index, run.length);
|
|
136
|
+
}
|
|
137
|
+
const segments = [];
|
|
138
|
+
let plainStart = 0;
|
|
139
|
+
const flush = (end) => {
|
|
140
|
+
if (end > plainStart)
|
|
141
|
+
segments.push({ text: str.slice(plainStart, end) });
|
|
142
|
+
};
|
|
143
|
+
let index = 0;
|
|
144
|
+
while (index < str.length) {
|
|
145
|
+
const length = invisible.get(index);
|
|
146
|
+
const kind = length ? 'invisible' : whitespaceKind(str[index]);
|
|
147
|
+
if (!kind) {
|
|
148
|
+
index += 1;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const size = length ?? 1;
|
|
152
|
+
flush(index);
|
|
153
|
+
segments.push({ text: str.slice(index, index + size), kind });
|
|
154
|
+
index += size;
|
|
155
|
+
plainStart = index;
|
|
156
|
+
}
|
|
157
|
+
if (segments.length === 0)
|
|
158
|
+
return null;
|
|
159
|
+
flush(str.length);
|
|
160
|
+
return segments;
|
|
161
|
+
}
|
|
162
|
+
export const InvisibleCharactersDisplayIdentifier = createIdentifier('InvisibleCharactersDisplay');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BlockSuiteError, ErrorCode } from '../../../global/exceptions/index.js';
|
|
2
|
-
import { INLINE_ROOT_ATTR
|
|
2
|
+
import { INLINE_ROOT_ATTR } from '../consts.js';
|
|
3
3
|
import { isInlineRoot, isNativeTextInVText, isVElement, isVLine, } from './guard.js';
|
|
4
|
-
import { calculateTextLength, getTextNodesFromElement } from './text.js';
|
|
4
|
+
import { calculateTextLength, getTextNodesFromElement, isPlaceholderText, } from './text.js';
|
|
5
5
|
export function nativePointToTextPoint(node, offset) {
|
|
6
6
|
if (isNativeTextInVText(node)) {
|
|
7
7
|
return [node, offset];
|
|
@@ -44,7 +44,7 @@ export function textPointToDomPoint(text, offset, rootElement) {
|
|
|
44
44
|
for (const text of texts.slice(0, goalIndex)) {
|
|
45
45
|
index += calculateTextLength(text);
|
|
46
46
|
}
|
|
47
|
-
if (text
|
|
47
|
+
if (!isPlaceholderText(text)) {
|
|
48
48
|
index += offset;
|
|
49
49
|
}
|
|
50
50
|
const textParentElement = text.parentElement;
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether `text` is a placeholder rather than text of the model: the
|
|
3
|
+
* zero-width character an empty line or an embed gap renders (`v-text`'s
|
|
4
|
+
* `empty`, rendered as `data-v-empty`). Decided by the attribute, never by
|
|
5
|
+
* the content: a zero-width space the user stored, alone in its run (or in
|
|
6
|
+
* its own span when special characters are shown), is one character long.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isPlaceholderText(text: Text): boolean;
|
|
1
9
|
export declare function calculateTextLength(text: Text): number;
|
|
2
10
|
export declare function getTextNodesFromElement(element: Element): Text[];
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Whether `text` is a placeholder rather than text of the model: the
|
|
3
|
+
* zero-width character an empty line or an embed gap renders (`v-text`'s
|
|
4
|
+
* `empty`, rendered as `data-v-empty`). Decided by the attribute, never by
|
|
5
|
+
* the content: a zero-width space the user stored, alone in its run (or in
|
|
6
|
+
* its own span when special characters are shown), is one character long.
|
|
7
|
+
*/
|
|
8
|
+
export function isPlaceholderText(text) {
|
|
9
|
+
return text.parentElement?.dataset.vEmpty === 'true';
|
|
10
|
+
}
|
|
2
11
|
export function calculateTextLength(text) {
|
|
3
|
-
if (text
|
|
12
|
+
if (isPlaceholderText(text)) {
|
|
4
13
|
return 0;
|
|
5
14
|
}
|
|
6
15
|
else {
|
|
@@ -5,7 +5,35 @@ export declare class DocCRUD {
|
|
|
5
5
|
private readonly _yBlocks;
|
|
6
6
|
private readonly _schema;
|
|
7
7
|
get root(): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* In-memory index of the tree: `childId -> parentId` plus the root id,
|
|
10
|
+
* rebuilt lazily after any structural change. Nothing here is written to Yjs
|
|
11
|
+
* (ADR 0041).
|
|
12
|
+
*
|
|
13
|
+
* Why: `root` scanned every block and `getParent` walked the tree from the
|
|
14
|
+
* root, so a load path calling `getParent` once per block was quadratic
|
|
15
|
+
* (#507: 11 s for 4000 blocks under a query with `viewType: 'hidden'`, whose
|
|
16
|
+
* `setAncestorsToDisplayIfHidden` climbs the ancestors of every block). #508
|
|
17
|
+
* only skipped that walk for queries that cannot hide blocks; the index
|
|
18
|
+
* makes the walk itself linear.
|
|
19
|
+
*
|
|
20
|
+
* Invalidation contract:
|
|
21
|
+
* - every transaction, local, remote (`Y.applyUpdate`) or undo, reaches
|
|
22
|
+
* `beforeObserverCalls` before any observer runs, so observers and
|
|
23
|
+
* everything after them read a fresh index;
|
|
24
|
+
* - inside a transaction, before that event, only the mutators below write
|
|
25
|
+
* the tree (`sys:children`, the blocks map): each drops the index before
|
|
26
|
+
* its first write, so a throw half-way leaves no stale index, and again
|
|
27
|
+
* after its last one, so a read in between cannot cache a half-written
|
|
28
|
+
* tree. A new writer of `sys:children` must do the same.
|
|
29
|
+
*/
|
|
30
|
+
private _index;
|
|
31
|
+
/** Once deaf to remote changes, every read rebuilds (the pre-#507 cost). */
|
|
32
|
+
private _live;
|
|
33
|
+
private readonly _onBeforeObserverCalls;
|
|
8
34
|
constructor(_yBlocks: Y.Map<YBlock>, _schema: Schema);
|
|
35
|
+
dispose(): void;
|
|
36
|
+
private _ensureIndex;
|
|
9
37
|
private _getSiblings;
|
|
10
38
|
addBlock(id: string, flavour: string, initialProps?: Record<string, unknown>, parent?: string | null, parentIndex?: number): void;
|
|
11
39
|
deleteBlock(id: string, options?: {
|