edytor 0.1.0-next.43 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/clipboard/insertClipboardFragment.d.ts +8 -0
- package/dist/clipboard/insertClipboardFragment.js +23 -0
- package/dist/clipboard/moveText.js +1 -1
- package/dist/clipboard/textBlocks.d.ts +2 -0
- package/dist/clipboard/textBlocks.js +4 -0
- package/dist/cloudflare/DocumentRoom.d.ts +6 -6
- package/dist/cloudflare/room/context.d.ts +128 -128
- package/dist/collaboration/history/HistoryPanel.svelte +2 -2
- package/dist/crdt/document.d.ts +19 -7
- package/dist/crdt/document.js +34 -16
- package/dist/crdt/index.d.ts +1 -0
- package/dist/crdt/index.js +3 -2
- package/dist/crdt/nodes.d.ts +3 -2
- package/dist/crdt/operations.d.ts +161 -0
- package/dist/crdt/operations.js +97 -0
- package/dist/edytor.svelte.js +2 -2
- package/dist/events/onBeforeInput.js +1 -1
- package/dist/events/onFocus.js +7 -0
- package/dist/events/onPaste.js +12 -1
- package/dist/index.d.ts +19 -2
- package/dist/labels.d.ts +4 -0
- package/dist/labels.js +2 -0
- package/dist/plugins/blockHandles/blockHandlesPlugin.d.ts +2 -1
- package/dist/plugins/blockHandles/blockHandlesPlugin.js +70 -24
- package/dist/plugins/image/ImageEmpty.svelte +4 -4
- package/dist/plugins/image/ImageEmpty.svelte.d.ts +3 -3
- package/dist/plugins/image/ImagePlugin.svelte +30 -29
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +4 -2
- package/dist/plugins/media/AudioPlugin.svelte +5 -1
- package/dist/plugins/media/FilePlugin.svelte +5 -1
- package/dist/plugins/media/MediaEmpty.svelte +42 -11
- package/dist/plugins/media/MediaEmpty.svelte.d.ts +2 -1
- package/dist/plugins/media/VideoPlugin.svelte +5 -1
- package/dist/plugins/media/media.d.ts +17 -2
- package/dist/plugins/media/media.js +34 -0
- package/dist/plugins/uploads.svelte.d.ts +92 -0
- package/dist/plugins/uploads.svelte.js +160 -0
- package/dist/selection/selection.svelte.d.ts +5 -8
- package/dist/selection/selection.svelte.js +12 -5
- package/dist/session/commands.d.ts +5 -53
- package/dist/session/commands.js +30 -7
- package/dist/session/history.js +1 -1
- package/dist/session/selection.d.ts +1 -1
- package/dist/surface/projector.svelte.js +1 -1
- package/package.json +1 -1
- package/dist/plugins/image/uploads.svelte.d.ts +0 -50
- package/dist/plugins/image/uploads.svelte.js +0 -111
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
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.
|
|
19
19
|
|
|
20
|
-
> **
|
|
20
|
+
> **Release candidate.** Edytor `1.0.0-rc.1` is the 1.0 release candidate (`edytor` on npm): its API is frozen, and a change to it before 1.0 is a listed breaking change. It has no production track record yet: read the [limitations](https://edytor.dev/docs/reference/limitations) before you build on it. Issues and PRs are welcome: when you report a bug, include the document's JSON value.
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
@@ -42,7 +42,7 @@ Edytor aims to be for Svelte what Slate.js is for React: a heavily customizable
|
|
|
42
42
|
The pre-release is on npm under the `next` tag. Name the tag: a bare `edytor` is the old, incompatible `0.0.11`.
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
-
pnpm add edytor
|
|
45
|
+
pnpm add edytor
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
`svelte@^5` is a peer dependency. Do not install `yjs`: the v14 engine is vendored (`edytor/crdt`). See [installation](https://edytor.dev/docs/getting-started) and [platform support](https://edytor.dev/docs/reference/platform-support) (Chrome, Edge, Firefox and Safari; real phones are not tested yet).
|
|
@@ -4,6 +4,14 @@ import { type SelectionInsertionTarget, type SelectionReplacementState } from '.
|
|
|
4
4
|
import { type JSONText } from '../utils/json.js';
|
|
5
5
|
import type { EdytorClipboardFragment } from './types.js';
|
|
6
6
|
export declare const flowOfText: (text: string, marks?: JSONText["marks"]) => Flow;
|
|
7
|
+
/**
|
|
8
|
+
* A pasted `text/plain` as markdown (`paste.markdown`): `null` unless a line
|
|
9
|
+
* holds block markdown (`hasBlockMarkdown`), else the blocks it reads as
|
|
10
|
+
* (`textToBlocks`). A kind the view does not register is a paragraph of its
|
|
11
|
+
* text, its children after it (a divider is dropped); without a paragraph
|
|
12
|
+
* kind, nothing converts.
|
|
13
|
+
*/
|
|
14
|
+
export declare const flowOfMarkdown: (edytor: Pick<Edytor, "blocks">, text: string) => Flow | null;
|
|
7
15
|
export declare const flowOfFragment: (fragment: EdytorClipboardFragment) => Flow;
|
|
8
16
|
/**
|
|
9
17
|
* Place `flow` (`flow.*`) at `at`, over the selected blocks (unless
|
|
@@ -4,6 +4,7 @@ import { cloneJsonSafe, jsonBlockToSpec, jsonContentToItems } from '../utils/jso
|
|
|
4
4
|
import { id } from '../utils.js';
|
|
5
5
|
import { caretAt } from '../session/attempt.js';
|
|
6
6
|
import { isValidEdytorClipboardFragment } from './fragmentData.js';
|
|
7
|
+
import { hasBlockMarkdown, textToBlocks } from './textBlocks.js';
|
|
7
8
|
// Admission (`flow.shape`): every id is minted fresh here, once.
|
|
8
9
|
const run = (content) => ({
|
|
9
10
|
id: id('b'),
|
|
@@ -12,6 +13,28 @@ const run = (content) => ({
|
|
|
12
13
|
export const flowOfText = (text, marks) => ({
|
|
13
14
|
lines: text.split(/\r\n|\r|\n/).map((line) => run(line ? [{ text: line, marks }] : []))
|
|
14
15
|
});
|
|
16
|
+
/**
|
|
17
|
+
* A pasted `text/plain` as markdown (`paste.markdown`): `null` unless a line
|
|
18
|
+
* holds block markdown (`hasBlockMarkdown`), else the blocks it reads as
|
|
19
|
+
* (`textToBlocks`). A kind the view does not register is a paragraph of its
|
|
20
|
+
* text, its children after it (a divider is dropped); without a paragraph
|
|
21
|
+
* kind, nothing converts.
|
|
22
|
+
*/
|
|
23
|
+
export const flowOfMarkdown = (edytor, text) => {
|
|
24
|
+
if (!edytor.blocks.has('paragraph') || !hasBlockMarkdown(text))
|
|
25
|
+
return null;
|
|
26
|
+
const known = (block) => {
|
|
27
|
+
const children = (block.children ?? []).flatMap(known);
|
|
28
|
+
if (edytor.blocks.has(block.type))
|
|
29
|
+
return [{ ...block, ...(block.children ? { children } : {}) }];
|
|
30
|
+
const own = block.type === 'divider' || (!block.content?.length && block.children?.length)
|
|
31
|
+
? []
|
|
32
|
+
: [{ type: 'paragraph', content: block.content ?? [] }];
|
|
33
|
+
return [...own, ...children];
|
|
34
|
+
};
|
|
35
|
+
const blocks = textToBlocks(text).flatMap(known);
|
|
36
|
+
return blocks.length ? { lines: blocks.map((block) => jsonBlockToSpec(block, true)) } : null;
|
|
37
|
+
};
|
|
15
38
|
export const flowOfFragment = (fragment) => fragment.kind === 'content'
|
|
16
39
|
? { lines: [run(fragment.content)] }
|
|
17
40
|
: {
|
|
@@ -28,7 +28,7 @@ export const dropText = (edytor, drag, to) => {
|
|
|
28
28
|
const { copy } = drag;
|
|
29
29
|
/** Nothing written: the range stays selected (the drop's point is not adopted). */
|
|
30
30
|
const keep = () => {
|
|
31
|
-
edytor.selection.select(drag.range
|
|
31
|
+
edytor.selection.select(drag.range);
|
|
32
32
|
return false;
|
|
33
33
|
};
|
|
34
34
|
// A move onto itself: nothing to write.
|
|
@@ -19,5 +19,7 @@ export type TextToBlocksOptions = {
|
|
|
19
19
|
/** The kind of a paragraph (default `paragraph`). */
|
|
20
20
|
type?: string;
|
|
21
21
|
};
|
|
22
|
+
/** Whether `text` holds block markdown (one line of it is enough), as a paste reads it. */
|
|
23
|
+
export declare const hasBlockMarkdown: (text: string) => boolean;
|
|
22
24
|
/** `text` as blocks (see the module comment). */
|
|
23
25
|
export declare const textToBlocks: (text: string, { markdown, type }?: TextToBlocksOptions) => JSONBlock[];
|
|
@@ -21,6 +21,10 @@ const runs = (text) => {
|
|
|
21
21
|
out.push({ text: text.slice(at) });
|
|
22
22
|
return out;
|
|
23
23
|
};
|
|
24
|
+
/** A line that opens block markdown: a heading, a list or to-do item, a quote, a fence, a divider. */
|
|
25
|
+
const BLOCK_LINE = /^\s*(?:#{1,6}\s|[-*+]\s|\d+[.)]\s|>|```|(?:-{3,}|\*{3,}|_{3,})\s*$)/;
|
|
26
|
+
/** Whether `text` holds block markdown (one line of it is enough), as a paste reads it. */
|
|
27
|
+
export const hasBlockMarkdown = (text) => text.split(/\r?\n/).some((line) => BLOCK_LINE.test(line));
|
|
24
28
|
/** One line's kind, data and text, or `null` for a paragraph line. */
|
|
25
29
|
const lineKind = (line) => {
|
|
26
30
|
let m = /^(#{1,6})\s+(.*)$/.exec(line);
|
|
@@ -110,7 +110,7 @@ import { type HistoryEntry, type HistoryOptions, type KVLike, type R2BucketLike
|
|
|
110
110
|
import type { LockOptions } from './locks.js';
|
|
111
111
|
import type { CommitResult, ExportedBlocks, ImportReceipt, ImportRequest, LateEditBatch, MoveNamespace } from './move.js';
|
|
112
112
|
import type { AwarenessEntry } from '../crdt/protocol.js';
|
|
113
|
-
import type { DocumentSemanticsConfig,
|
|
113
|
+
import type { DocumentSemanticsConfig, DocumentOperations, JSONBlock, JSONDoc, YDoc } from '../crdt/index.js';
|
|
114
114
|
import type { CommentActor, CommentChange, CommentOutcome, CommentRequest, CommentSnapshot } from '../crdt/protocols/comments.js';
|
|
115
115
|
/** Under SQLite-backed Durable Objects' 2 MB row cap, with room for the other columns. */
|
|
116
116
|
export declare const DEFAULT_MAX_ROW_BYTES: number;
|
|
@@ -481,7 +481,7 @@ export type FrameValidation = {
|
|
|
481
481
|
/** A block as it is now (`null`: it does not show). */
|
|
482
482
|
after: (id: string) => ValidatedBlock | null;
|
|
483
483
|
/** The live document. Read it; never write from `validate` (defer a `transact` instead). */
|
|
484
|
-
facade:
|
|
484
|
+
facade: DocumentOperations;
|
|
485
485
|
};
|
|
486
486
|
/** The room's comment threads (`comments` of {@link AttachRoomOptions}). */
|
|
487
487
|
export type CommentOptions = {
|
|
@@ -678,7 +678,7 @@ export declare class AttachedDocument {
|
|
|
678
678
|
* first drops a direct write whose append failed (`heal`), so the next
|
|
679
679
|
* write through it is stored.
|
|
680
680
|
*/
|
|
681
|
-
get facade():
|
|
681
|
+
get facade(): DocumentOperations;
|
|
682
682
|
/**
|
|
683
683
|
* Manipulate: run `fn` on the facade in one transaction. The edit is
|
|
684
684
|
* stored, then broadcast to every socket. It is not a rollback, like
|
|
@@ -695,7 +695,7 @@ export declare class AttachedDocument {
|
|
|
695
695
|
* without writing: the engine would queue the write until that one
|
|
696
696
|
* ends, so it could not be stored before returning.
|
|
697
697
|
*/
|
|
698
|
-
transact<T>(fn: (facade:
|
|
698
|
+
transact<T>(fn: (facade: DocumentOperations) => T): T;
|
|
699
699
|
/** The document as JSON. */
|
|
700
700
|
read(): JSONDoc;
|
|
701
701
|
/**
|
|
@@ -1044,10 +1044,10 @@ export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<E
|
|
|
1044
1044
|
/** Generation cutover, also over RPC — see {@link AttachedDocument.reset}. */
|
|
1045
1045
|
reset(): Promise<void>;
|
|
1046
1046
|
/** Server-side edit — see {@link AttachedDocument.transact}. */
|
|
1047
|
-
transact<T>(fn: (facade:
|
|
1047
|
+
transact<T>(fn: (facade: DocumentOperations) => T): T;
|
|
1048
1048
|
/** The document as JSON. */
|
|
1049
1049
|
read(): JSONDoc;
|
|
1050
|
-
get facade():
|
|
1050
|
+
get facade(): DocumentOperations;
|
|
1051
1051
|
get doc(): YDoc | null;
|
|
1052
1052
|
get failure(): Error | null;
|
|
1053
1053
|
get refusals(): Refusal[];
|