edytor 0.1.0-next.36 → 0.1.0-next.38
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/README.md +5 -3
- package/dist/block/block.svelte.d.ts +11 -7
- package/dist/block/block.svelte.js +54 -11
- package/dist/block/block.utils.d.ts +69 -0
- package/dist/block/colors.d.ts +15 -0
- package/dist/block/colors.js +32 -0
- package/dist/block/inlineBlock.svelte.d.ts +2 -1
- package/dist/block/inlineBlock.svelte.js +6 -4
- package/dist/clipboard/createClipboardFragment.js +34 -1
- package/dist/clipboard/htmlFlow.d.ts +5 -3
- package/dist/clipboard/htmlFlow.js +32 -7
- package/dist/clipboard/moveText.d.ts +35 -0
- package/dist/clipboard/moveText.js +79 -0
- package/dist/clipboard/serializeClipboardFragment.js +20 -4
- package/dist/cloudflare/DocumentRoom.d.ts +69 -2
- package/dist/cloudflare/DocumentRoom.js +53 -1
- package/dist/cloudflare/index.d.ts +3 -2
- package/dist/cloudflare/index.js +3 -2
- package/dist/cloudflare/room/access.js +13 -2
- package/dist/cloudflare/room/admission.d.ts +7 -0
- package/dist/cloudflare/room/admission.js +29 -0
- package/dist/cloudflare/room/comments.d.ts +58 -0
- package/dist/cloudflare/room/comments.js +293 -0
- package/dist/cloudflare/room/context.d.ts +39 -2
- package/dist/cloudflare/room/context.js +9 -4
- package/dist/cloudflare/room/frames.d.ts +7 -0
- package/dist/cloudflare/room/frames.js +16 -0
- package/dist/cloudflare/room/history.js +8 -8
- package/dist/cloudflare/room/moves.js +6 -3
- package/dist/cloudflare/room/presence.d.ts +33 -3
- package/dist/cloudflare/room/presence.js +113 -23
- package/dist/cloudflare/room/purge.js +11 -2
- package/dist/cloudflare/room/replicas.js +2 -1
- package/dist/cloudflare/room/storage.d.ts +13 -2
- package/dist/cloudflare/room/storage.js +36 -7
- package/dist/cloudflare/room/updates.js +3 -4
- package/dist/cloudflare/room/validation.js +2 -1
- package/dist/cloudflare/routeDocumentSocket.d.ts +20 -0
- package/dist/cloudflare/routeDocumentSocket.js +45 -1
- package/dist/collaboration/comments/client.d.ts +96 -0
- package/dist/collaboration/comments/client.js +133 -0
- package/dist/collaboration/comments/index.d.ts +8 -0
- package/dist/collaboration/comments/index.js +7 -0
- package/dist/collaboration/comments/threads.d.ts +27 -0
- package/dist/collaboration/comments/threads.js +62 -0
- package/dist/collaboration/history/HistoryPanel.svelte +7 -13
- package/dist/collaboration/history/HistoryPanel.svelte.d.ts +1 -1
- package/dist/collaboration/history/panel.d.ts +4 -50
- package/dist/collaboration/history/panel.js +3 -28
- package/dist/components/Block.svelte +2 -1
- package/dist/components/Edytor.svelte +9 -1
- package/dist/components/Edytor.svelte.d.ts +6 -1
- package/dist/crdt/admission.js +4 -2
- package/dist/crdt/attribution/block.js +4 -3
- package/dist/crdt/doc/capability.js +14 -2
- package/dist/crdt/doc/delete.js +11 -1
- package/dist/crdt/doc/history.js +14 -14
- package/dist/crdt/doc/reads.d.ts +18 -0
- package/dist/crdt/doc/reads.js +111 -0
- package/dist/crdt/doc/seed.js +5 -3
- package/dist/crdt/doc/split-merge.js +4 -1
- package/dist/crdt/doc/table.d.ts +21 -0
- package/dist/crdt/doc/table.js +288 -0
- package/dist/crdt/doc/types.d.ts +17 -7
- package/dist/crdt/document.js +15 -11
- package/dist/crdt/edytor-doc.d.ts +49 -3
- package/dist/crdt/edytor-doc.js +44 -3
- package/dist/crdt/engine-api.d.ts +6 -0
- package/dist/crdt/flow.d.ts +6 -0
- package/dist/crdt/flow.js +20 -3
- package/dist/crdt/incarnations.d.ts +16 -3
- package/dist/crdt/incarnations.js +17 -3
- package/dist/crdt/index.d.ts +36 -4
- package/dist/crdt/index.js +5 -3
- package/dist/crdt/migration/generation.js +5 -3
- package/dist/crdt/migration/legacy-schema.js +5 -3
- package/dist/crdt/migration/migrate.js +6 -4
- package/dist/crdt/placement/display.js +10 -2
- package/dist/crdt/placement/model.d.ts +7 -0
- package/dist/crdt/placement/model.js +5 -8
- package/dist/crdt/protocol.d.ts +5 -3
- package/dist/crdt/protocol.js +5 -3
- package/dist/crdt/protocols/comments.d.ts +219 -0
- package/dist/crdt/protocols/comments.js +283 -0
- package/dist/crdt/protocols/sync.js +5 -4
- package/dist/crdt/providers/index.d.ts +1 -0
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/indexeddb.js +15 -3
- package/dist/crdt/providers/room.d.ts +6 -0
- package/dist/crdt/providers/room.js +8 -1
- package/dist/crdt/providers/websocket.d.ts +2 -0
- package/dist/crdt/providers/websocket.js +58 -3
- package/dist/crdt/purge.d.ts +6 -0
- package/dist/crdt/purge.js +34 -4
- package/dist/crdt/rangeDelete.d.ts +9 -0
- package/dist/crdt/rangeDelete.js +40 -0
- package/dist/crdt/semantics.d.ts +63 -6
- package/dist/crdt/semantics.js +45 -9
- package/dist/crdt/structs.d.ts +118 -8
- package/dist/crdt/structs.js +108 -0
- package/dist/crdt/tables.d.ts +44 -0
- package/dist/crdt/tables.js +58 -0
- package/dist/crdt/text/delete-index.d.ts +2 -5
- package/dist/crdt/text/delete-index.js +12 -8
- package/dist/crdt/text/deletes.js +16 -19
- package/dist/crdt/text/index/anchored.js +2 -1
- package/dist/crdt/text/index/fold.d.ts +2 -1
- package/dist/crdt/text/index/fold.js +16 -11
- package/dist/crdt/text/index/layout.d.ts +16 -3
- package/dist/crdt/text/index/layout.js +167 -3
- package/dist/crdt/text/index/placement.js +32 -7
- package/dist/crdt/text/index/records.js +8 -3
- package/dist/crdt/text/index/shared.js +6 -1
- package/dist/crdt/text/index/state.d.ts +11 -1
- package/dist/crdt/text/index/state.js +8 -1
- package/dist/crdt/text/items.js +2 -1
- package/dist/crdt/text/model.d.ts +2 -1
- package/dist/crdt/text/model.js +11 -19
- package/dist/crdt/text/runs.d.ts +6 -0
- package/dist/crdt/vendor/yjs/UPSTREAM.md +38 -13
- package/dist/edytor.svelte.d.ts +13 -5
- package/dist/edytor.svelte.js +43 -14
- package/dist/events/beforeInputCommands.d.ts +0 -2
- package/dist/events/beforeInputCommands.js +26 -15
- package/dist/events/beforeInputDeleteCommands.js +8 -1
- package/dist/events/onDrop.d.ts +11 -0
- package/dist/events/onDrop.js +89 -3
- package/dist/events/onKeyDown.js +6 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -1
- package/dist/kinds.d.ts +2 -0
- package/dist/kinds.js +2 -0
- package/dist/labels.d.ts +149 -1
- package/dist/labels.js +99 -2
- package/dist/plugins/blockHandles/blockHandlesPlugin.js +2 -1
- package/dist/plugins/blockMenu/BlockMenu.svelte +41 -4
- package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +25 -2
- package/dist/plugins/blockMenu/BlockMenuController.svelte.js +63 -2
- package/dist/plugins/blockMenu/blockMenuPlugin.d.ts +4 -3
- package/dist/plugins/blockMenu/blockMenuPlugin.js +7 -4
- package/dist/plugins/comments/CommentsController.svelte.d.ts +124 -0
- package/dist/plugins/comments/CommentsController.svelte.js +351 -0
- package/dist/plugins/comments/CommentsSidebar.svelte +601 -0
- package/dist/plugins/comments/CommentsSidebar.svelte.d.ts +16 -0
- package/dist/plugins/comments/commentsPlugin.d.ts +48 -0
- package/dist/plugins/comments/commentsPlugin.js +60 -0
- package/dist/plugins/equation/EquationEditor.svelte +138 -0
- package/dist/plugins/equation/EquationEditor.svelte.d.ts +8 -0
- package/dist/plugins/equation/EquationPlugin.svelte +328 -0
- package/dist/plugins/equation/EquationPlugin.svelte.d.ts +33 -0
- package/dist/plugins/equation/EquationView.svelte +34 -0
- package/dist/plugins/equation/EquationView.svelte.d.ts +7 -0
- package/dist/plugins/equation/equation.svelte.d.ts +142 -0
- package/dist/plugins/equation/equation.svelte.js +295 -0
- package/dist/plugins/icons.js +6 -0
- package/dist/plugins/index.d.ts +5 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/mention/MentionPlugin.svelte +10 -4
- package/dist/plugins/mention/MentionPlugin.svelte.d.ts +3 -0
- package/dist/plugins/page/PageLink.svelte +17 -5
- package/dist/plugins/page/PagePlugin.svelte +18 -3
- package/dist/plugins/page/PagePlugin.svelte.d.ts +3 -2
- package/dist/plugins/page/page.d.ts +7 -3
- package/dist/plugins/slashMenu/SlashMenuController.svelte.js +4 -1
- package/dist/plugins/table/TableChrome.svelte +153 -0
- package/dist/plugins/table/TableChrome.svelte.d.ts +7 -0
- package/dist/plugins/table/TablePlugin.svelte +450 -0
- package/dist/plugins/table/TablePlugin.svelte.d.ts +43 -0
- package/dist/plugins/table/chrome.svelte.d.ts +153 -0
- package/dist/plugins/table/chrome.svelte.js +420 -0
- package/dist/plugins/table/table.css +202 -0
- package/dist/plugins/table/table.d.ts +94 -0
- package/dist/plugins/table/table.js +224 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +1 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.js +3 -0
- package/dist/plugins/triggers/TriggerController.svelte.js +3 -3
- package/dist/plugins.d.ts +67 -7
- package/dist/selection/selection.svelte.js +7 -2
- package/dist/selection/selection.utils.d.ts +7 -5
- package/dist/selection/selection.utils.js +5 -14
- package/dist/session/attempt.js +1 -1
- package/dist/session/bindings.js +2 -3
- package/dist/session/commands.js +2 -0
- package/dist/session/composition.svelte.js +5 -9
- package/dist/session/editing/text.d.ts +7 -0
- package/dist/session/editing/text.js +17 -0
- package/dist/session/navigation.js +1 -1
- package/dist/session/ports.d.ts +45 -0
- package/dist/session/ports.js +1 -0
- package/dist/surface/overlay.d.ts +15 -3
- package/dist/surface/overlay.js +30 -3
- package/dist/surface/projector.svelte.d.ts +15 -0
- package/dist/surface/projector.svelte.js +24 -1
- package/dist/text/text.svelte.d.ts +4 -2
- package/dist/text/text.svelte.js +9 -7
- package/dist/utils.d.ts +1 -1
- package/package.json +9 -11
- /package/dist/{events → text}/wordBoundary.d.ts +0 -0
- /package/dist/{events → text}/wordBoundary.js +0 -0
package/README.md
CHANGED
|
@@ -10,20 +10,22 @@
|
|
|
10
10
|
<p>
|
|
11
11
|
<a href="https://edytor.dev/docs">Documentation</a> •
|
|
12
12
|
<a href="#quick-start">Quick start</a> •
|
|
13
|
-
<a href="https://edytor.dev/docs/reference/migration">
|
|
13
|
+
<a href="https://edytor.dev/docs/reference/migration">Changelog</a>
|
|
14
14
|
</p>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
Edytor aims to be for Svelte what Slate.js is for React: a heavily customizable editor with an API to build any kind of collaborative rich text editor.
|
|
18
18
|
|
|
19
|
-
> **Work in progress.** Edytor is a pre-release (`0.1.0-next.
|
|
19
|
+
> **Work in progress.** Edytor is a pre-release (`0.1.0-next.38`, `edytor@next` on npm) and not ready for production; the API changes between releases without a compatibility layer. The untagged `edytor@0.0.11` on npm predates the current API. Issues and PRs are welcome: when you report a bug, include the document's JSON value.
|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
23
23
|
- **Notion-style editing out of the box.** `<Edytor />` alone is a rich text editor with images and block handles: headings, toggle headings, lists, to-dos, toggles, callouts, quotes, dividers, ten marks and Notion's hotkeys. Add the Notion theme, a block menu (block colours; drag one block, or every block the selection covers), a slash menu, a selection toolbar, markdown shortcuts (`markdownShortcutsPlugin`) and code blocks.
|
|
24
24
|
- **Pages and a table of contents, opt-in.** `createPagePlugin` links a block to another document (a page per room, opened and named by your app), and `tocPlugin` lists the headings live ([Page](https://edytor.dev/docs/plugins/page), [Table of contents](https://edytor.dev/docs/plugins/table-of-contents)).
|
|
25
25
|
- **Columns, opt-in.** List `columnsPlugin` for Notion's multi-column layouts: drag a block to the edge of another to put them side by side, or pick "2 columns" to "5 columns"; drag the gap to resize. Layouts converge across collaborators and the room ([Columns](https://edytor.dev/docs/plugins/columns)).
|
|
26
|
+
- **Tables, opt-in.** List `tablePlugin` for Notion's simple table: cells of text, <kbd>Tab</kbd> between them, rows and columns added, moved and deleted from the table's edges and grips, resizable columns, header row and column, HTML tables pasted and copied. Concurrent row and column edits converge on every replica ([Table](https://edytor.dev/docs/plugins/table)).
|
|
26
27
|
- **Mentions, page links and your own triggers.** `createMentionPlugin` (`@` people) and `createPageLinkPlugin` (`[[` pages) take your app's search; a plugin's `inputRules` (`:smile:` → 😄) and `triggers` (a menu on a typed character) declare the rest ([Input rules and triggers](https://edytor.dev/docs/plugins/input-rules)).
|
|
28
|
+
- **Comments, opt-in.** `createCommentsPlugin` adds Notion's comment threads: select text, press Comment, and the thread shows in a sidebar beside it with replies, Resolve and Re-open. The room keeps the threads beside the document and sends each change live; the anchor follows the text through edits and is not copied ([Comments](https://edytor.dev/docs/plugins/comments)).
|
|
27
29
|
- **Your markup.** Blocks, marks and inline atoms render through your Svelte snippets; the slash menu, toolbar, block menu and handles take a snippet and keep their behavior.
|
|
28
30
|
- **Your language.** Every word the chrome shows or says, slash keywords included, comes from a labels dictionary each plugin takes; English by default ([Localization](https://edytor.dev/docs/customization/localization)).
|
|
29
31
|
- **Synced properties.** `block.data`, `atom.data` and `edytor.data` read and write like plain objects (`bind:value={block.data.title}`); each property and each array item syncs on its own, so concurrent edits of different properties, or of different items of one array, merge.
|
|
@@ -102,7 +104,7 @@ The [documentation site](https://edytor.dev/docs) is the single source for the A
|
|
|
102
104
|
- [Editor](https://edytor.dev/docs/editor/edytor-component): the component, commands, selection, history, clipboard, readonly
|
|
103
105
|
- [Plugins](https://edytor.dev/docs/plugins) and [customization](https://edytor.dev/docs/customization/blocks): bundled plugins, custom blocks and marks, [hotkeys and editing behavior](https://edytor.dev/docs/customization/hotkeys)
|
|
104
106
|
- [Collaboration](https://edytor.dev/docs/collaboration) and [server](https://edytor.dev/docs/server/quick-start): documents, providers, presence, the Durable Object room and its protocol
|
|
105
|
-
- [Reference](https://edytor.dev/docs/reference/document-api): the document API, [troubleshooting](https://edytor.dev/docs/reference/troubleshooting), [migration from 0.0.11](https://edytor.dev/docs/reference/migration), limitations, [platform support](https://edytor.dev/docs/reference/platform-support)
|
|
107
|
+
- [Reference](https://edytor.dev/docs/reference/document-api): the document API, [troubleshooting](https://edytor.dev/docs/reference/troubleshooting), the [changelog](https://edytor.dev/docs/reference/migration) (with [migration from 0.0.11](https://edytor.dev/docs/reference/migration#upgrading-from-0011)), [importing v13 documents](https://edytor.dev/docs/reference/v13-import), limitations, [platform support](https://edytor.dev/docs/reference/platform-support)
|
|
106
108
|
|
|
107
109
|
## Contributing
|
|
108
110
|
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import { Text } from '../text/text.svelte.js';
|
|
2
|
-
import { Edytor } from '../edytor.svelte.js';
|
|
2
|
+
import type { Edytor } from '../edytor.svelte.js';
|
|
3
3
|
import type { JSONBlock, JSONText, JSONInlineBlock } from '../utils/json.js';
|
|
4
4
|
import { type BlockOperations } from './block.utils.js';
|
|
5
5
|
import type { BlockDefinition } from '../plugins.js';
|
|
6
6
|
import { InlineBlock } from './inlineBlock.svelte.js';
|
|
7
7
|
import type { DocBlock } from '../crdt/index.js';
|
|
8
|
-
/**
|
|
9
|
-
* An id-only block handle (§2.4 "Handles", R4): every getter reads the
|
|
10
|
-
* document index (transaction-aware), every mutator issues a command. The
|
|
11
|
-
* view keeps one per id (`edytor.idToBlock`); `node` is the element the core
|
|
12
|
-
* renders for it (O45).
|
|
13
|
-
*/
|
|
14
8
|
/** Mark an element inside a block's markup as non-editable chrome (`block.void`, a preview's too). */
|
|
15
9
|
export declare const voidChrome: (node: HTMLElement) => void;
|
|
10
|
+
/**
|
|
11
|
+
* An id-only block handle: every getter reads the document index
|
|
12
|
+
* (transaction-aware), every mutator issues a command. The view keeps one per
|
|
13
|
+
* id (`edytor.idToBlock`); `node` is the element the core renders for it.
|
|
14
|
+
*
|
|
15
|
+
* A reactive reader (a template, a `$derived`, an `$effect`) of a getter
|
|
16
|
+
* depends on the cells the answer comes from (this block's, its parent's,
|
|
17
|
+
* the root's list), so it re-runs when a commit changes them, as `data`
|
|
18
|
+
* does; a read anywhere else is a plain read of the document.
|
|
19
|
+
*/
|
|
16
20
|
export declare class Block {
|
|
17
21
|
#private;
|
|
18
22
|
readonly: boolean;
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { Text } from '../text/text.svelte.js';
|
|
2
|
-
import { Edytor } from '../edytor.svelte.js';
|
|
3
2
|
import { batch, removeInlineBlock, addChildBlock, insertBlockAfter, insertBlockBefore, mergeBlockBackward, mergeBlockForward, nestBlock, removeBlock, setBlock, splitBlock, prepareSplit, duplicateBlock, prepareDuplicate, prepareSet, prepareMove, prepareMoves, prepareInsertAfter, prepareInsertBefore, prepareRemove, prepareMergeBackward, prepareMergeForward, prepareUnNest, prepareNest, prepareRemoveInline, preparePatch, patchData, prepareDeleteRange, addChildBlocks, pushContentIntoBlock, unNestBlock, moveBlock, moveBlocks, normalizeContent, addInlineBlock, textAfterAtom, blockOf, acceptSuggestedText, suggestText, deleteContentAtRange, normalizeChildren } from './block.utils.js';
|
|
4
3
|
import { jsonBlockToSpec, jsonContentToItems } from '../utils/json.js';
|
|
5
4
|
import { InlineBlock } from './inlineBlock.svelte.js';
|
|
6
5
|
import { revealed } from '../selection/replaceSelection.js';
|
|
7
6
|
import { propsProxy } from '../session/props.js';
|
|
8
|
-
/**
|
|
9
|
-
* An id-only block handle (§2.4 "Handles", R4): every getter reads the
|
|
10
|
-
* document index (transaction-aware), every mutator issues a command. The
|
|
11
|
-
* view keeps one per id (`edytor.idToBlock`); `node` is the element the core
|
|
12
|
-
* renders for it (O45).
|
|
13
|
-
*/
|
|
14
7
|
/** Mark an element inside a block's markup as non-editable chrome (`block.void`, a preview's too). */
|
|
15
8
|
export const voidChrome = (node) => {
|
|
16
9
|
node.setAttribute('data-edytor-void', `true`);
|
|
17
10
|
node.style.userSelect = 'none';
|
|
18
11
|
node.setAttribute('contenteditable', 'false');
|
|
19
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* An id-only block handle: every getter reads the document index
|
|
15
|
+
* (transaction-aware), every mutator issues a command. The view keeps one per
|
|
16
|
+
* id (`edytor.idToBlock`); `node` is the element the core renders for it.
|
|
17
|
+
*
|
|
18
|
+
* A reactive reader (a template, a `$derived`, an `$effect`) of a getter
|
|
19
|
+
* depends on the cells the answer comes from (this block's, its parent's,
|
|
20
|
+
* the root's list), so it re-runs when a commit changes them, as `data`
|
|
21
|
+
* does; a read anywhere else is a plain read of the document.
|
|
22
|
+
*/
|
|
20
23
|
export class Block {
|
|
21
24
|
readonly = false;
|
|
22
25
|
edytor;
|
|
@@ -29,21 +32,43 @@ export class Block {
|
|
|
29
32
|
get isRoot() {
|
|
30
33
|
return this.id === 'root';
|
|
31
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Under a reactive reader, depend on block `id`'s cell (the root: its
|
|
37
|
+
* child list): the commit that patches it re-runs the reader. A cell
|
|
38
|
+
* that goes away (the block died) re-runs it too.
|
|
39
|
+
*/
|
|
40
|
+
#track(id = this.id) {
|
|
41
|
+
if (!$effect.tracking())
|
|
42
|
+
return;
|
|
43
|
+
const cells = this.edytor.cells;
|
|
44
|
+
if (id === 'root')
|
|
45
|
+
void cells?.rootIds;
|
|
46
|
+
else
|
|
47
|
+
cells?.get(id);
|
|
48
|
+
}
|
|
32
49
|
/** The typed document node — `null` for the root. */
|
|
33
50
|
get model() {
|
|
34
51
|
return this.isRoot ? null : this.edytor.facade.block(this.id);
|
|
35
52
|
}
|
|
36
53
|
/** The display parent (the root for a top-level block); none for the root or a dead block. */
|
|
37
54
|
get parent() {
|
|
38
|
-
|
|
55
|
+
if (this.isRoot)
|
|
56
|
+
return undefined;
|
|
57
|
+
this.#track();
|
|
58
|
+
const at = this.edytor.facade.positionOf(this.id);
|
|
59
|
+
// The parent's child list names this block: a move patches it.
|
|
60
|
+
if (at)
|
|
61
|
+
this.#track(at.parent ?? 'root');
|
|
39
62
|
return at ? this.edytor.idToBlock.block(at.parent ?? 'root') : undefined;
|
|
40
63
|
}
|
|
41
64
|
get children() {
|
|
65
|
+
this.#track();
|
|
42
66
|
const { facade, idToBlock } = this.edytor;
|
|
43
67
|
return facade.childrenIds(this.isRoot ? null : this.id).map(idToBlock.block);
|
|
44
68
|
}
|
|
45
69
|
/** Text segments and inline atoms, alternating, starting and ending with a text. */
|
|
46
70
|
get content() {
|
|
71
|
+
this.#track();
|
|
47
72
|
const handles = this.edytor.idToBlock;
|
|
48
73
|
let ordinal = 0;
|
|
49
74
|
return handles
|
|
@@ -53,7 +78,10 @@ export class Block {
|
|
|
53
78
|
: handles.atom(this.id, part.item.id));
|
|
54
79
|
}
|
|
55
80
|
get type() {
|
|
56
|
-
|
|
81
|
+
if (this.isRoot)
|
|
82
|
+
return 'root';
|
|
83
|
+
this.#track();
|
|
84
|
+
return this.edytor.facade.blockTypeOf(this.id) ?? '';
|
|
57
85
|
}
|
|
58
86
|
/** Retype the block — the `setBlock` command (readonly, hooks, `dispatcher.last`). */
|
|
59
87
|
set type(value) {
|
|
@@ -139,7 +167,13 @@ export class Block {
|
|
|
139
167
|
return undefined;
|
|
140
168
|
}
|
|
141
169
|
get index() {
|
|
142
|
-
|
|
170
|
+
if (this.isRoot)
|
|
171
|
+
return 0;
|
|
172
|
+
const at = this.edytor.facade.positionOf(this.id);
|
|
173
|
+
this.#track();
|
|
174
|
+
if (at)
|
|
175
|
+
this.#track(at.parent ?? 'root');
|
|
176
|
+
return at?.index || 0;
|
|
143
177
|
}
|
|
144
178
|
get depth() {
|
|
145
179
|
return this.parent ? this.parent.depth + 1 : 0;
|
|
@@ -155,7 +189,10 @@ export class Block {
|
|
|
155
189
|
}
|
|
156
190
|
/** The block is visible in the document (a dead handle answers false). */
|
|
157
191
|
get isInTree() {
|
|
158
|
-
|
|
192
|
+
if (this.isRoot)
|
|
193
|
+
return true;
|
|
194
|
+
this.#track();
|
|
195
|
+
return this.edytor.facade.isVisibleBlock(this.id);
|
|
159
196
|
}
|
|
160
197
|
/**
|
|
161
198
|
* @deprecated `edytor.suggestions` with `{ end: block.id }`. The content of
|
|
@@ -211,6 +248,9 @@ export class Block {
|
|
|
211
248
|
}
|
|
212
249
|
/** This block's JSON — the document's one serializer (`facade.blockJSON`). */
|
|
213
250
|
get value() {
|
|
251
|
+
// Its subtree's: every commit re-runs a reactive reader.
|
|
252
|
+
if ($effect.tracking())
|
|
253
|
+
void this.edytor.valueRevision;
|
|
214
254
|
if (!this.isRoot)
|
|
215
255
|
return this.edytor.facade.blockJSON(this.id);
|
|
216
256
|
const { data = {}, children } = this.edytor.facade.toJSON();
|
|
@@ -398,6 +438,8 @@ export class Block {
|
|
|
398
438
|
node.setAttribute(name, value);
|
|
399
439
|
this.node = node;
|
|
400
440
|
const release = this.edytor.surface.register(node, 'block', this.id);
|
|
441
|
+
// A block laid out again with no commit moves the chrome beside it (the overlay measures).
|
|
442
|
+
const unobserve = this.edytor.overlay.observe(node);
|
|
401
443
|
const onDestroy = this.edytor.plugins.flatMap((plugin) => {
|
|
402
444
|
const action = plugin.onBlockAttached?.({ node, block: this });
|
|
403
445
|
return typeof action === 'function' ? [action] : [];
|
|
@@ -408,6 +450,7 @@ export class Block {
|
|
|
408
450
|
if (this.node === node)
|
|
409
451
|
this.node = undefined;
|
|
410
452
|
release();
|
|
453
|
+
unobserve();
|
|
411
454
|
onDestroy.forEach((destroy) => destroy());
|
|
412
455
|
}
|
|
413
456
|
};
|
|
@@ -135,6 +135,19 @@ export type BlockOperations = {
|
|
|
135
135
|
}[];
|
|
136
136
|
replacement: string;
|
|
137
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* A comment thread's anchor (`plugins/comments`): its `comment:<thread>`
|
|
140
|
+
* mark over each run of the text it was made on, one plan, one undo step
|
|
141
|
+
* (its steps: a `formatRange` per run).
|
|
142
|
+
*/
|
|
143
|
+
addComment: {
|
|
144
|
+
thread: string;
|
|
145
|
+
runs: {
|
|
146
|
+
block: string;
|
|
147
|
+
offset: number;
|
|
148
|
+
length: number;
|
|
149
|
+
}[];
|
|
150
|
+
};
|
|
138
151
|
deleteContentAtRange: {
|
|
139
152
|
start: [number, number];
|
|
140
153
|
end: [number, number];
|
|
@@ -146,6 +159,30 @@ export type BlockOperations = {
|
|
|
146
159
|
flow: Flow;
|
|
147
160
|
target: FlowTarget;
|
|
148
161
|
};
|
|
162
|
+
/**
|
|
163
|
+
* A text range dragged inside the editor and dropped at `to` (block
|
|
164
|
+
* offsets): its content placed at `to`, then the range deleted — or,
|
|
165
|
+
* with `copy` (Alt), only placed. Both are prepared before any write:
|
|
166
|
+
* hooks see the placement's planned steps and the deletion's, and a
|
|
167
|
+
* veto or a refusal of either writes nothing.
|
|
168
|
+
*/
|
|
169
|
+
moveText: {
|
|
170
|
+
from: {
|
|
171
|
+
start: {
|
|
172
|
+
block: string;
|
|
173
|
+
offset: number;
|
|
174
|
+
};
|
|
175
|
+
end: {
|
|
176
|
+
block: string;
|
|
177
|
+
offset: number;
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
to: {
|
|
181
|
+
block: string;
|
|
182
|
+
offset: number;
|
|
183
|
+
};
|
|
184
|
+
copy: boolean;
|
|
185
|
+
};
|
|
149
186
|
deleteBlocks: {
|
|
150
187
|
blocks: Block[];
|
|
151
188
|
};
|
|
@@ -157,6 +194,38 @@ export type BlockOperations = {
|
|
|
157
194
|
};
|
|
158
195
|
/** A divider at the caret: its steps are the conversion, insertion or split it plans. */
|
|
159
196
|
insertDivider: {};
|
|
197
|
+
/** A table's row at `index`, one empty cell per column (`table.insert-row`). */
|
|
198
|
+
insertTableRow: {
|
|
199
|
+
index: number;
|
|
200
|
+
};
|
|
201
|
+
/** A table's rows, with their cells (`table.delete-row`). */
|
|
202
|
+
deleteTableRows: {
|
|
203
|
+
rows: Block[];
|
|
204
|
+
};
|
|
205
|
+
/** A table's column at `index`, an empty cell in every row (`table.insert-column`). */
|
|
206
|
+
insertTableColumn: {
|
|
207
|
+
index: number;
|
|
208
|
+
width?: number;
|
|
209
|
+
};
|
|
210
|
+
/** A table's column, by id or position, with its cells (`table.delete-column`). */
|
|
211
|
+
deleteTableColumn: {
|
|
212
|
+
column: string | number;
|
|
213
|
+
};
|
|
214
|
+
/** A table's column moved to position `to` (`table.move-column`). */
|
|
215
|
+
moveTableColumn: {
|
|
216
|
+
column: string | number;
|
|
217
|
+
to: number;
|
|
218
|
+
};
|
|
219
|
+
/** A table's rows moved to position `to` among its rows (`table.move-row`). */
|
|
220
|
+
moveTableRows: {
|
|
221
|
+
rows: Block[];
|
|
222
|
+
to: number;
|
|
223
|
+
};
|
|
224
|
+
/** The cell a row shows none of in `column`, created (`table.pad`). */
|
|
225
|
+
fillTableCell: {
|
|
226
|
+
row: string;
|
|
227
|
+
column: string | number;
|
|
228
|
+
};
|
|
160
229
|
};
|
|
161
230
|
/**
|
|
162
231
|
* Bind an operation onto its wrapper: every call is dispatched as a command
|
package/dist/block/colors.d.ts
CHANGED
|
@@ -45,3 +45,18 @@ export declare const colorable: (block: Block) => boolean;
|
|
|
45
45
|
* that block as it was. Answers whether any block changed.
|
|
46
46
|
*/
|
|
47
47
|
export declare const setBlockColor: (edytor: Edytor, blocks: Iterable<Block>, field: BlockColorField, value: string | null) => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* A block's colours as the classes Notion's HTML export writes
|
|
50
|
+
* (`block-color-red`, `block-color-red_background`), space-separated; empty
|
|
51
|
+
* when it holds none. The copy's `text/html` names them so.
|
|
52
|
+
*/
|
|
53
|
+
export declare const colorClasses: (data: Readonly<Record<string, unknown>> | undefined) => string;
|
|
54
|
+
/**
|
|
55
|
+
* The palette colours `element`'s classes name as Notion's HTML export does
|
|
56
|
+
* (its `teal` read as green): what a paste keeps. A class naming no palette
|
|
57
|
+
* colour adds nothing.
|
|
58
|
+
*/
|
|
59
|
+
export declare const colorsOfClasses: (element: Element) => {
|
|
60
|
+
color?: string;
|
|
61
|
+
background?: string;
|
|
62
|
+
};
|
package/dist/block/colors.js
CHANGED
|
@@ -72,3 +72,35 @@ export const setBlockColor = (edytor, blocks, field, value) => {
|
|
|
72
72
|
});
|
|
73
73
|
return applied.some(Boolean);
|
|
74
74
|
};
|
|
75
|
+
/** The class Notion's HTML export gives a block's colour: `block-color-<name>`, `block-color-<name>_background`. */
|
|
76
|
+
const CLASS = /^block-color-([a-z]+)(_background)?$/;
|
|
77
|
+
/** Notion's export names the palette's green `teal`. */
|
|
78
|
+
const EXPORTED = { teal: 'green' };
|
|
79
|
+
/**
|
|
80
|
+
* A block's colours as the classes Notion's HTML export writes
|
|
81
|
+
* (`block-color-red`, `block-color-red_background`), space-separated; empty
|
|
82
|
+
* when it holds none. The copy's `text/html` names them so.
|
|
83
|
+
*/
|
|
84
|
+
export const colorClasses = (data) => {
|
|
85
|
+
const color = colorName(data?.color);
|
|
86
|
+
const background = colorName(data?.background);
|
|
87
|
+
return [color && `block-color-${color}`, background && `block-color-${background}_background`]
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.join(' ');
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The palette colours `element`'s classes name as Notion's HTML export does
|
|
93
|
+
* (its `teal` read as green): what a paste keeps. A class naming no palette
|
|
94
|
+
* colour adds nothing.
|
|
95
|
+
*/
|
|
96
|
+
export const colorsOfClasses = (element) => {
|
|
97
|
+
const found = {};
|
|
98
|
+
for (const name of element.classList) {
|
|
99
|
+
const [, raw, background] = CLASS.exec(name) ?? [];
|
|
100
|
+
const color = raw && (EXPORTED[raw] ?? raw);
|
|
101
|
+
if (!color || !BLOCK_COLORS.includes(color))
|
|
102
|
+
continue;
|
|
103
|
+
found[background ? 'background' : 'color'] = color;
|
|
104
|
+
}
|
|
105
|
+
return found;
|
|
106
|
+
};
|
|
@@ -5,7 +5,8 @@ import type { Block } from './block.svelte.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* An id-only inline-atom handle: atom `id`, shown in
|
|
7
7
|
* block `blockId`. Getters read the document index; `node` is the element
|
|
8
|
-
* that renders it (a Surface fact).
|
|
8
|
+
* that renders it (a Surface fact). A reactive reader of a getter depends on
|
|
9
|
+
* the block's cell, so it re-runs when a commit changes the block's content.
|
|
9
10
|
*/
|
|
10
11
|
export declare class InlineBlock {
|
|
11
12
|
#private;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { clearDomSelection } from '../selection/domSelection.js';
|
|
2
1
|
import { propsProxy } from '../session/props.js';
|
|
3
2
|
const INLINE_EDGE_CARET_THRESHOLD_PX = 4;
|
|
4
3
|
const isTextPart = (part) => part !== undefined && !(part instanceof InlineBlock);
|
|
5
4
|
/**
|
|
6
5
|
* An id-only inline-atom handle: atom `id`, shown in
|
|
7
6
|
* block `blockId`. Getters read the document index; `node` is the element
|
|
8
|
-
* that renders it (a Surface fact).
|
|
7
|
+
* that renders it (a Surface fact). A reactive reader of a getter depends on
|
|
8
|
+
* the block's cell, so it re-runs when a commit changes the block's content.
|
|
9
9
|
*/
|
|
10
10
|
export class InlineBlock {
|
|
11
11
|
_def = 'inline';
|
|
@@ -25,6 +25,8 @@ export class InlineBlock {
|
|
|
25
25
|
return this.edytor.idToBlock.block(this.blockId);
|
|
26
26
|
}
|
|
27
27
|
get #item() {
|
|
28
|
+
if ($effect.tracking())
|
|
29
|
+
this.edytor.cells?.get(this.blockId);
|
|
28
30
|
const parts = this.edytor.idToBlock.parts(this.blockId);
|
|
29
31
|
const part = parts.find((part) => part.kind === 'inline' && part.item.id === this.id);
|
|
30
32
|
return part?.kind === 'inline' ? part.item : undefined;
|
|
@@ -88,10 +90,10 @@ export class InlineBlock {
|
|
|
88
90
|
if (event.clientX >= rect.right - INLINE_EDGE_CARET_THRESHOLD_PX && isTextPart(after))
|
|
89
91
|
return selection.setAtTextOffset(after, 0);
|
|
90
92
|
selection.selectInlineBlock(this);
|
|
91
|
-
|
|
93
|
+
this.edytor.ports.surface.clear();
|
|
92
94
|
// The atom took the press: the caret the browser still leaves for it
|
|
93
95
|
// (Chromium parks one at the host's start before the release) is its own.
|
|
94
|
-
this.edytor.
|
|
96
|
+
this.edytor.ports.surface.parked();
|
|
95
97
|
};
|
|
96
98
|
node.addEventListener('pointerdown', selectInlineBlock);
|
|
97
99
|
return {
|
|
@@ -67,7 +67,40 @@ const extractBlockRange = (edytor) => {
|
|
|
67
67
|
return value;
|
|
68
68
|
});
|
|
69
69
|
};
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* `fragment` without the marks whose record says `copy: false` (a
|
|
72
|
+
* comment's anchor, `comment:<id>`: Notion copies the text, not the
|
|
73
|
+
* comment): one rule for copy, cut and the internal paste they feed.
|
|
74
|
+
*/
|
|
75
|
+
const withoutUncopied = (edytor, fragment) => {
|
|
76
|
+
if (!fragment)
|
|
77
|
+
return fragment;
|
|
78
|
+
const strip = (content) => {
|
|
79
|
+
for (const part of content ?? []) {
|
|
80
|
+
if (!('text' in part) || !part.marks)
|
|
81
|
+
continue;
|
|
82
|
+
for (const key of Object.keys(part.marks))
|
|
83
|
+
if (edytor.marks.get(key)?.copy === false)
|
|
84
|
+
delete part.marks[key];
|
|
85
|
+
if (Object.keys(part.marks).length === 0)
|
|
86
|
+
delete part.marks;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const walk = (blocks) => {
|
|
90
|
+
for (const block of blocks) {
|
|
91
|
+
strip(block.content);
|
|
92
|
+
if (block.children)
|
|
93
|
+
walk(block.children);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
if (fragment.kind === 'blocks')
|
|
97
|
+
walk(fragment.blocks);
|
|
98
|
+
else
|
|
99
|
+
strip(fragment.content);
|
|
100
|
+
return fragment;
|
|
101
|
+
};
|
|
102
|
+
export const createEdytorClipboardFragment = (edytor) => withoutUncopied(edytor, fragmentOf(edytor));
|
|
103
|
+
const fragmentOf = (edytor) => {
|
|
71
104
|
// A layout the block selection covers whole is copied as the layout (D3).
|
|
72
105
|
const selectedBlocks = selectedMembers(edytor, liftLayouts(edytor.selection.selectedBlocks));
|
|
73
106
|
if (selectedBlocks.length > 0) {
|
|
@@ -2,8 +2,8 @@ import type { Edytor } from '../edytor.svelte.js';
|
|
|
2
2
|
import type { Flow } from '../crdt/flow.js';
|
|
3
3
|
import type { BlockDefinition } from '../plugins.js';
|
|
4
4
|
import { type SerializableContent } from '../utils/json.js';
|
|
5
|
-
/** What HTML import reads: the kind and
|
|
6
|
-
export type ImportKinds = Pick<Edytor, 'blocks' | 'marks'> & {
|
|
5
|
+
/** What HTML import reads: the kind, mark and atom records, and the document's default child. */
|
|
6
|
+
export type ImportKinds = Pick<Edytor, 'blocks' | 'marks'> & Partial<Pick<Edytor, 'inlineBlocks'>> & {
|
|
7
7
|
document: Pick<Edytor['document'], 'defaultChild'>;
|
|
8
8
|
};
|
|
9
9
|
type Values = Record<string, SerializableContent>;
|
|
@@ -17,7 +17,9 @@ export declare const tagOf: (type: string, kind: BlockDefinition, data: Values)
|
|
|
17
17
|
* `tag`, and each record's `parse` hook first (aliases, sanitized values).
|
|
18
18
|
* A void kind with a `parse` hook (an image, an embed, a video) is found
|
|
19
19
|
* by its hook only, and a void takes only its `figcaption`'s text; an
|
|
20
|
-
* `iframe`, `video` or `audio` no hook claims carries nothing.
|
|
20
|
+
* `iframe`, `video` or `audio` no hook claims carries nothing. An atom
|
|
21
|
+
* kind's `parse` hook claims inline elements (an equation's `<math>`): the
|
|
22
|
+
* atom, never its text. Unknown
|
|
21
23
|
* elements degrade to text runs; whitespace collapses as HTML renders it. `null` when the HTML carries nothing (`flow.shape`, F-P10).
|
|
22
24
|
*/
|
|
23
25
|
export declare const flowOfHtml: (kinds: ImportKinds, html: string | undefined) => Flow | null;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { colorsOfClasses } from '../block/colors.js';
|
|
2
|
+
import { isTextPart } from '../block/contentRange.js';
|
|
1
3
|
import { jsonBlockToSpec } from '../utils/json.js';
|
|
2
4
|
/** Never content: their text is code, metadata or form state. */
|
|
3
5
|
const SKIP = /^(script|style|template|noscript|svg|math|iframe|object|embed|canvas|video|audio|img|input|button|select|textarea|head|title|meta|link|base)$/;
|
|
@@ -59,13 +61,16 @@ export const tagOf = (type, kind, data) => {
|
|
|
59
61
|
* `tag`, and each record's `parse` hook first (aliases, sanitized values).
|
|
60
62
|
* A void kind with a `parse` hook (an image, an embed, a video) is found
|
|
61
63
|
* by its hook only, and a void takes only its `figcaption`'s text; an
|
|
62
|
-
* `iframe`, `video` or `audio` no hook claims carries nothing.
|
|
64
|
+
* `iframe`, `video` or `audio` no hook claims carries nothing. An atom
|
|
65
|
+
* kind's `parse` hook claims inline elements (an equation's `<math>`): the
|
|
66
|
+
* atom, never its text. Unknown
|
|
63
67
|
* elements degrade to text runs; whitespace collapses as HTML renders it. `null` when the HTML carries nothing (`flow.shape`, F-P10).
|
|
64
68
|
*/
|
|
65
69
|
export const flowOfHtml = (kinds, html) => {
|
|
66
70
|
if (!html || html.length > 8 * 1024 * 1024 || typeof DOMParser === 'undefined')
|
|
67
71
|
return null;
|
|
68
72
|
const { blocks, marks } = kinds;
|
|
73
|
+
const atoms = kinds.inlineBlocks ?? new Map();
|
|
69
74
|
const byTag = new Map();
|
|
70
75
|
for (const [type, kind] of blocks) {
|
|
71
76
|
// A void found by its hook carries its substance in data (a source): its tag alone is none.
|
|
@@ -92,6 +97,15 @@ export const flowOfHtml = (kinds, html) => {
|
|
|
92
97
|
}
|
|
93
98
|
return byTag.get(element.localName);
|
|
94
99
|
};
|
|
100
|
+
/** An inline atom an atom kind's hook claims `element` as. */
|
|
101
|
+
const atomOf = (element) => {
|
|
102
|
+
for (const [type, atom] of atoms) {
|
|
103
|
+
const data = atom.parse?.(element);
|
|
104
|
+
if (data)
|
|
105
|
+
return { type, data };
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
};
|
|
95
109
|
const marksOf = (element) => {
|
|
96
110
|
const found = {};
|
|
97
111
|
for (const [name, mark] of marks) {
|
|
@@ -102,17 +116,21 @@ export const flowOfHtml = (kinds, html) => {
|
|
|
102
116
|
}
|
|
103
117
|
return found;
|
|
104
118
|
};
|
|
105
|
-
|
|
119
|
+
// A kind's hook may claim an element of another namespace (an equation's MathML `<math>`).
|
|
120
|
+
const isBlock = (node) => (node instanceof HTMLElement && BLOCK.test(node.localName)) ||
|
|
121
|
+
(node instanceof Element && kindOf(node) !== undefined);
|
|
106
122
|
/** A void kind's element (an image): it shows no text, so a line it is met in ends there. */
|
|
107
123
|
const isVoid = (node) => {
|
|
108
|
-
const claim = node instanceof
|
|
124
|
+
const claim = node instanceof Element ? kindOf(node) : undefined;
|
|
109
125
|
return claim !== undefined && blocks.get(claim.type)?.void === true;
|
|
110
126
|
};
|
|
111
127
|
const text = (line, value, marks, pre) => {
|
|
112
|
-
const
|
|
128
|
+
const before = line.content.at(-1);
|
|
129
|
+
// After an atom, text starts a run of its own (a space there is kept).
|
|
130
|
+
const last = before && isTextPart(before) ? before : undefined;
|
|
113
131
|
if (!pre)
|
|
114
132
|
value = value.replace(/[ \t\n\f\r]+/g, ' ');
|
|
115
|
-
if (!pre && (!
|
|
133
|
+
if (!pre && (!before || /[ \n]$/.test(last?.text ?? '')))
|
|
116
134
|
value = value.replace(/^ /, '');
|
|
117
135
|
value = value.replace(/\u00a0/g, ' ');
|
|
118
136
|
if (!value)
|
|
@@ -129,6 +147,9 @@ export const flowOfHtml = (kinds, html) => {
|
|
|
129
147
|
return text(at.line(), node.getAttribute('alt') ?? '', marks, pre);
|
|
130
148
|
if (isVoid(node))
|
|
131
149
|
return at.apart(node);
|
|
150
|
+
const atom = node instanceof Element ? atomOf(node) : undefined;
|
|
151
|
+
if (atom)
|
|
152
|
+
return void at.line().content.push(atom);
|
|
132
153
|
if (!(node instanceof HTMLElement) || SKIP.test(node.localName))
|
|
133
154
|
return;
|
|
134
155
|
if (node.localName === 'br')
|
|
@@ -139,7 +160,8 @@ export const flowOfHtml = (kinds, html) => {
|
|
|
139
160
|
};
|
|
140
161
|
/** A line ends without its trailing space or `<br>`. */
|
|
141
162
|
const end = (line) => {
|
|
142
|
-
const
|
|
163
|
+
const at = line.content.at(-1);
|
|
164
|
+
const last = at && isTextPart(at) ? at : undefined;
|
|
143
165
|
if (last)
|
|
144
166
|
last.text = last.text.replace(/ ?\n?$/, '');
|
|
145
167
|
if (last && !last.text)
|
|
@@ -206,7 +228,10 @@ export const flowOfHtml = (kinds, html) => {
|
|
|
206
228
|
// An element that only wraps blocks (a `div`, a `ul`) is not a line of its own.
|
|
207
229
|
if (!claim && wraps(element))
|
|
208
230
|
return linesOf(element, type);
|
|
209
|
-
|
|
231
|
+
// A text line keeps the colours Notion's export classes name (`block-color-red`).
|
|
232
|
+
const colors = colorsOfClasses(element);
|
|
233
|
+
const data = Object.keys(colors).length ? { ...claim?.data, ...colors } : claim?.data;
|
|
234
|
+
const line = { type: claim?.type ?? type, data, content: [] };
|
|
210
235
|
const childType = claim ? kinds.document.defaultChild(claim.type) : type;
|
|
211
236
|
const pre = element.localName === 'pre';
|
|
212
237
|
// The element's line, then, for each void element met in it, that block
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Edytor } from '../edytor.svelte.js';
|
|
2
|
+
import { type SelectionPoint, type SelectionValue } from '../session/selection.js';
|
|
3
|
+
import type { EdytorClipboardFragment } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* A view's own text drag, from its `dragstart` to its `dragend`: the
|
|
6
|
+
* selected range it moves (anchored: it follows concurrent edits) and its
|
|
7
|
+
* content as a clipboard fragment, also on the drag's data for a drop
|
|
8
|
+
* elsewhere.
|
|
9
|
+
*/
|
|
10
|
+
export type TextDrag = {
|
|
11
|
+
range: SelectionValue & {
|
|
12
|
+
kind: 'text';
|
|
13
|
+
};
|
|
14
|
+
fragment: EdytorClipboardFragment;
|
|
15
|
+
/** The drop's: Alt was held (a copy). */
|
|
16
|
+
copy: boolean;
|
|
17
|
+
/** Set while the view's own drop runs. */
|
|
18
|
+
dropping: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** The view's text drag in progress, if any. */
|
|
21
|
+
export declare const textDragOf: (edytor: Edytor) => TextDrag | undefined;
|
|
22
|
+
export declare const startTextDrag: (edytor: Edytor, drag: Omit<TextDrag, "copy" | "dropping">) => undefined;
|
|
23
|
+
export declare const endTextDrag: (edytor: Edytor) => undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The view's dragged text dropped at `to` (`moveText`, one command and one
|
|
26
|
+
* undo step): its content placed at the drop point as a paste places a flow
|
|
27
|
+
* (`insertFlow`), then, for a move, the range deleted as a replacement
|
|
28
|
+
* deletes it (`replaceRange`); with `copy`, the placement alone. Both halves
|
|
29
|
+
* are prepared before any write, so hooks see the command's effect and
|
|
30
|
+
* every step of both, a veto of any of them keeps everything, and a drop
|
|
31
|
+
* either half refuses writes nothing. A move dropped inside its own range
|
|
32
|
+
* changes nothing. The content is selected after; when nothing was written,
|
|
33
|
+
* the range stays selected. Answers whether it wrote.
|
|
34
|
+
*/
|
|
35
|
+
export declare const dropText: (edytor: Edytor, drag: TextDrag, to: SelectionPoint) => boolean;
|