xiaodao-editor 0.1.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 +306 -0
- package/dist/block-editor.js +23254 -0
- package/dist/block-editor.umd.cjs +26 -0
- package/dist/core/Editor.d.ts +83 -0
- package/dist/core/command/Command.d.ts +32 -0
- package/dist/core/command/InputRule.d.ts +21 -0
- package/dist/core/command/Keymap.d.ts +42 -0
- package/dist/core/command/SlashCommand.d.ts +24 -0
- package/dist/core/command/primitiveCommands.d.ts +97 -0
- package/dist/core/extension/Extension.d.ts +52 -0
- package/dist/core/extension/Registry.d.ts +46 -0
- package/dist/core/history/HistoryManager.d.ts +37 -0
- package/dist/core/ids.d.ts +5 -0
- package/dist/core/index.d.ts +35 -0
- package/dist/core/plugin/Plugin.d.ts +26 -0
- package/dist/core/schema/BlockSchema.d.ts +70 -0
- package/dist/core/schema/SchemaRegistry.d.ts +22 -0
- package/dist/core/selection/Selection.d.ts +25 -0
- package/dist/core/serialize/Serializer.d.ts +31 -0
- package/dist/core/state/EditorState.d.ts +26 -0
- package/dist/core/state/Step.d.ts +39 -0
- package/dist/core/state/Transaction.d.ts +52 -0
- package/dist/core/state/invert.d.ts +19 -0
- package/dist/core/state/store.d.ts +78 -0
- package/dist/core/types.d.ts +105 -0
- package/dist/extensions/BulletList.d.ts +2 -0
- package/dist/extensions/CodeBlock.d.ts +2 -0
- package/dist/extensions/Divider.d.ts +2 -0
- package/dist/extensions/Heading.d.ts +2 -0
- package/dist/extensions/History.d.ts +2 -0
- package/dist/extensions/Image.d.ts +34 -0
- package/dist/extensions/Keymap.d.ts +2 -0
- package/dist/extensions/OrderedList.d.ts +18 -0
- package/dist/extensions/Paragraph.d.ts +5 -0
- package/dist/extensions/Quote.d.ts +2 -0
- package/dist/extensions/Table.d.ts +74 -0
- package/dist/extensions/TableOfContents.d.ts +18 -0
- package/dist/extensions/TodoList.d.ts +2 -0
- package/dist/extensions/_commonAttrs.d.ts +84 -0
- package/dist/extensions/builtin.d.ts +15 -0
- package/dist/extensions/tableModel.d.ts +199 -0
- package/dist/i18n.d.ts +31 -0
- package/dist/index.d.ts +30 -0
- package/dist/style.css +1 -0
- package/dist/test-img.png +0 -0
- package/dist/view/BlockContent.vue.d.ts +21 -0
- package/dist/view/BlockEditor.vue.d.ts +67 -0
- package/dist/view/BlockHost.vue.d.ts +51 -0
- package/dist/view/BlockList.vue.d.ts +64 -0
- package/dist/view/clipboard.d.ts +11 -0
- package/dist/view/context.d.ts +116 -0
- package/dist/view/domSelection.d.ts +65 -0
- package/dist/view/imageUpload.d.ts +130 -0
- package/dist/view/inlineDom.d.ts +23 -0
- package/dist/view/keymapHandler.d.ts +7 -0
- package/dist/view/ui/BlockHandle.vue.d.ts +33 -0
- package/dist/view/ui/BlockSettingsMenu.vue.d.ts +32 -0
- package/dist/view/ui/CodeLangPicker.vue.d.ts +20 -0
- package/dist/view/ui/HoverToolbar.vue.d.ts +81 -0
- package/dist/view/ui/LinkPopover.vue.d.ts +62 -0
- package/dist/view/ui/MobileToolbar.vue.d.ts +29 -0
- package/dist/view/ui/NumberPicker.vue.d.ts +20 -0
- package/dist/view/ui/OrderedListMenu.vue.d.ts +37 -0
- package/dist/view/ui/PlusMenu.vue.d.ts +29 -0
- package/dist/view/ui/SafeHtml.vue.d.ts +8 -0
- package/dist/view/ui/icons.d.ts +51 -0
- package/dist/view/ui/inputRulesEngine.d.ts +33 -0
- package/dist/view/ui/popup.d.ts +75 -0
- package/dist/view/ui/useMenuDismiss.d.ts +9 -0
- package/dist/view/ui/useMenuScroll.d.ts +2 -0
- package/dist/view/urlUtils.d.ts +39 -0
- package/package.json +73 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { InjectionKey, Ref } from 'vue';
|
|
2
|
+
import { Editor } from '../core/Editor';
|
|
3
|
+
import { Block, BlockId } from '../core/types';
|
|
4
|
+
/**
|
|
5
|
+
* Injection key for the reactive `isMobile` flag. Provided by BlockEditor so
|
|
6
|
+
* child components (TableBlock, MobileToolbar, …) can adapt their rendering
|
|
7
|
+
* for touch devices. Uses `(pointer: coarse)` matchMedia — matches iOS /
|
|
8
|
+
* iPadOS / Android browsers.
|
|
9
|
+
*/
|
|
10
|
+
export declare const mobileKey: InjectionKey<Ref<boolean>>;
|
|
11
|
+
/**
|
|
12
|
+
* A bag of props + event handlers that fully describes what the
|
|
13
|
+
* MobileToolbar should render inside its embedded HoverToolbar. Produced by
|
|
14
|
+
* whichever context currently owns the selection (text-block selection in
|
|
15
|
+
* BlockEditor, or table cell / cell-edit selection in TableBlock) and
|
|
16
|
+
* consumed by MobileToolbar which spreads it onto `<HoverToolbar mobile>`.
|
|
17
|
+
*
|
|
18
|
+
* This is the reuse mechanism: instead of duplicating HoverToolbar's buttons
|
|
19
|
+
* and logic, the source context builds the exact same prop/handler bag it
|
|
20
|
+
* would normally pass to a floating HoverToolbar, and hands it to the
|
|
21
|
+
* MobileToolbar via the bridge so a SINGLE HoverToolbar instance (in mobile
|
|
22
|
+
* mode) renders the buttons.
|
|
23
|
+
*/
|
|
24
|
+
export interface MobileToolbarDescriptor {
|
|
25
|
+
visible: boolean;
|
|
26
|
+
selectionRect: DOMRect | null;
|
|
27
|
+
blockId: BlockId | null;
|
|
28
|
+
blockType: string | null;
|
|
29
|
+
blockAttrs: Readonly<Record<string, unknown>>;
|
|
30
|
+
rootEl: HTMLElement | null;
|
|
31
|
+
tableMode?: boolean;
|
|
32
|
+
cellEditMode?: boolean;
|
|
33
|
+
tableActiveMarks?: Set<string>;
|
|
34
|
+
tableActiveColor?: string;
|
|
35
|
+
tableActiveBgColor?: string;
|
|
36
|
+
tableActiveVerticalAlign?: string;
|
|
37
|
+
showDelete?: boolean;
|
|
38
|
+
deleteLabel?: string;
|
|
39
|
+
deleteIcon?: string;
|
|
40
|
+
showMerge?: boolean;
|
|
41
|
+
showSplit?: boolean;
|
|
42
|
+
showHeaderRow?: boolean;
|
|
43
|
+
headerRowActive?: boolean;
|
|
44
|
+
onClose?: () => void;
|
|
45
|
+
onLinkClick?: (blockId: BlockId, from: number, to: number) => void;
|
|
46
|
+
onDelete?: () => void;
|
|
47
|
+
onMerge?: () => void;
|
|
48
|
+
onSplit?: () => void;
|
|
49
|
+
onTableHeaderRow?: () => void;
|
|
50
|
+
onTableType?: (cellType: string) => void;
|
|
51
|
+
onTableAlign?: (align: string) => void;
|
|
52
|
+
onTableVerticalAlign?: (verticalAlign: string) => void;
|
|
53
|
+
onTableMark?: (markType: string) => void;
|
|
54
|
+
onTableTextColor?: (color: string | null) => void;
|
|
55
|
+
onTableBgColor?: (color: string | null) => void;
|
|
56
|
+
onTableCopy?: () => void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Injection key for the mobile-toolbar bridge — a reactive ref holding the
|
|
60
|
+
* **table-sourced** descriptor (or null). BlockEditor provides it; TableBlock
|
|
61
|
+
* injects it and publishes its toolbar state when running on a mobile device.
|
|
62
|
+
* The text-block descriptor is computed directly inside MobileToolbar from
|
|
63
|
+
* BlockEditor's hoverToolbar state (no bridge needed for that path).
|
|
64
|
+
*/
|
|
65
|
+
export declare const mobileToolbarBridgeKey: InjectionKey<Ref<MobileToolbarDescriptor | null>>;
|
|
66
|
+
export declare const editorKey: InjectionKey<Editor>;
|
|
67
|
+
/**
|
|
68
|
+
* Injection key for the reactive `editable` flag. Provided by BlockEditor
|
|
69
|
+
* so child components (BlockContent, TableBlock, …) can reactively bind
|
|
70
|
+
* `contenteditable` and gate editing actions. Unlike `editorKey` (which is
|
|
71
|
+
* non-reactive), this is a Vue `Ref<boolean>` so changes propagate
|
|
72
|
+
* immediately through the component tree.
|
|
73
|
+
*/
|
|
74
|
+
export declare const editableKey: InjectionKey<Ref<boolean>>;
|
|
75
|
+
/**
|
|
76
|
+
* Injection key for image upload helper function. Provided by BlockEditor
|
|
77
|
+
* so child components (slash menu, image block, block-list drop handler)
|
|
78
|
+
* can initiate an image upload against the correct upload target block,
|
|
79
|
+
* with proper transient state management and document updating.
|
|
80
|
+
*
|
|
81
|
+
* The callback returns the BlockId of the NEW (or existing) image block so
|
|
82
|
+
* the caller can set focus / selection if needed.
|
|
83
|
+
*/
|
|
84
|
+
export type BeginImageUploadFn = (fileOrSrc: File | string, opts?: {
|
|
85
|
+
/** If provided, insert image block relative to this block id. */
|
|
86
|
+
relativeToBlockId?: BlockId | null;
|
|
87
|
+
/** 'after' (default) | 'before' | 'replace' the target block. */
|
|
88
|
+
position?: 'after' | 'before' | 'replace';
|
|
89
|
+
/** If provided and the target block is empty, convert it instead of inserting. */
|
|
90
|
+
convertIfEmpty?: boolean;
|
|
91
|
+
}) => Promise<BlockId | null>;
|
|
92
|
+
export declare const imageUploadKey: InjectionKey<BeginImageUploadFn>;
|
|
93
|
+
/**
|
|
94
|
+
* A flat render item: the block id plus the block snapshot. The id is
|
|
95
|
+
* redundant with `block.id` but is carried separately so `BlockList` can
|
|
96
|
+
* use it as a `:key` without reaching into the block object.
|
|
97
|
+
*/
|
|
98
|
+
export interface BlockRenderItem {
|
|
99
|
+
readonly id: BlockId;
|
|
100
|
+
readonly block: Block;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Access the editor instance. Must be called within a component tree that
|
|
104
|
+
* contains a `<BlockEditor>`.
|
|
105
|
+
*/
|
|
106
|
+
export declare function useEditor(): Editor;
|
|
107
|
+
/**
|
|
108
|
+
* Access the image upload starter helper. Returns `null` if not provided.
|
|
109
|
+
*/
|
|
110
|
+
export declare function useBeginImageUpload(): BeginImageUploadFn | null;
|
|
111
|
+
/**
|
|
112
|
+
* Access the reactive `editable` flag. Returns a ref that is `true` when
|
|
113
|
+
* the editor is in editing mode, `false` when read-only. Must be called
|
|
114
|
+
* within a component tree that contains a `<BlockEditor>`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function useEditable(): Ref<boolean>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { BlockId, Selection, DocState } from '../core/types';
|
|
2
|
+
/**
|
|
3
|
+
* Find the contenteditable element for a block id within the editor root.
|
|
4
|
+
*
|
|
5
|
+
* The `data-block-id` attr appears BOTH on the outer `.block-host` wrapper AND
|
|
6
|
+
* on the inner contenteditable (BlockContent), so a plain querySelector returns
|
|
7
|
+
* the wrapper first. We must prefer the contenteditable editing element:
|
|
8
|
+
* focusing/placing the caret in the wrapper (a non-editable div) silences the
|
|
9
|
+
* blinking caret for EMPTY blocks (no text node to walk to). Non-text blocks
|
|
10
|
+
* (image/table/divider/TOC) have no inner contenteditable, so we fall back to
|
|
11
|
+
* the host wrapper for hit-testing / scroll-into-view.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findBlockEl(root: HTMLElement, id: BlockId): HTMLElement | null;
|
|
14
|
+
/**
|
|
15
|
+
* Read the native selection and convert it to an editor `Selection`.
|
|
16
|
+
* Returns `null` if the selection is not within a block element inside `root`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readDomSelection(root: HTMLElement, doc: DocState): Selection | null;
|
|
19
|
+
/** Walk up from a node to find the nearest element with `data-block-id`. */
|
|
20
|
+
export declare function findBlockAncestor(node: Node, root: HTMLElement): HTMLElement | null;
|
|
21
|
+
/**
|
|
22
|
+
* Create a DOM Range covering [startOffset, endOffset) within a block's
|
|
23
|
+
* contenteditable element. Used for hit-testing and overlay rendering.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rangeFromOffsets(el: HTMLElement, startOffset: number, endOffset: number): Range | null;
|
|
26
|
+
export interface HitTestResult {
|
|
27
|
+
blockEl: HTMLElement;
|
|
28
|
+
blockId: BlockId;
|
|
29
|
+
offset: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Hit-test a viewport coordinate against the editor's block content elements.
|
|
33
|
+
* Returns the block id + character offset under the point, or null if the
|
|
34
|
+
* point is not over a block's contenteditable inside `root`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function positionFromPoint(x: number, y: number, root: HTMLElement, doc: DocState): HitTestResult | null;
|
|
37
|
+
/**
|
|
38
|
+
* Apply an editor selection to the DOM. Focuses the target block's
|
|
39
|
+
* contenteditable and places the caret. For Phase 1, only caret and
|
|
40
|
+
* single-block text selections are handled.
|
|
41
|
+
*
|
|
42
|
+
* Cross-block text selections are NOT applied to the native Selection (the
|
|
43
|
+
* browser cannot represent a selection spanning multiple contenteditable
|
|
44
|
+
* elements). The view layer renders them via an overlay instead.
|
|
45
|
+
*/
|
|
46
|
+
export declare function applySelectionToDom(root: HTMLElement, selection: Selection): void;
|
|
47
|
+
/**
|
|
48
|
+
* Whether a text selection spans more than one block (and thus requires the
|
|
49
|
+
* overlay renderer instead of the native Selection API).
|
|
50
|
+
*/
|
|
51
|
+
export declare function isCrossBlockText(sel: Selection): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Compute the viewport-space rectangles covering the visible characters in a
|
|
54
|
+
* cross-block text selection. Used by the overlay renderer.
|
|
55
|
+
*
|
|
56
|
+
* The selection is split into three regions:
|
|
57
|
+
* - start block: [start.offset, endOfBlock)
|
|
58
|
+
* - fully-covered middle blocks: [0, endOfBlock)
|
|
59
|
+
* - end block: [0, end.offset)
|
|
60
|
+
* When start === end block (single-block text selection handled by the native
|
|
61
|
+
* Selection API), this returns an empty array.
|
|
62
|
+
*/
|
|
63
|
+
export declare function crossBlockSelectionRects(root: HTMLElement, doc: DocState, sel: Extract<Selection, {
|
|
64
|
+
kind: 'text';
|
|
65
|
+
}>): DOMRect[];
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { BlockId } from '../core/types';
|
|
2
|
+
export type UploadStatus = 'pending' | 'success' | 'error';
|
|
3
|
+
export interface UploadState {
|
|
4
|
+
readonly status: UploadStatus;
|
|
5
|
+
/** 0–1 (0%–100%), meaningful only when status==='pending'. */
|
|
6
|
+
readonly progress: number;
|
|
7
|
+
/** Error message, meaningful only when status==='error'. */
|
|
8
|
+
readonly error?: string;
|
|
9
|
+
/** Original file so retry can re-upload it. */
|
|
10
|
+
readonly file?: File;
|
|
11
|
+
/** Temporary preview URL (object URL for local preview only).
|
|
12
|
+
* NOT saved to the document. */
|
|
13
|
+
readonly tempPreviewUrl?: string;
|
|
14
|
+
/** Block attributes to apply on success (width/height detected from file). */
|
|
15
|
+
readonly pendingAttrs?: {
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
};
|
|
19
|
+
/** AbortController for the in-flight upload. Aborted when the block is
|
|
20
|
+
* removed or the editor is unmounted. */
|
|
21
|
+
readonly controller?: AbortController;
|
|
22
|
+
}
|
|
23
|
+
/** UploadResult: the final outcome handed back from the upload handler (either
|
|
24
|
+
* external uploadImage prop or internal mockUpload). */
|
|
25
|
+
export interface UploadResult {
|
|
26
|
+
readonly url: string;
|
|
27
|
+
readonly width?: number;
|
|
28
|
+
readonly height?: number;
|
|
29
|
+
readonly alt?: string;
|
|
30
|
+
readonly title?: string;
|
|
31
|
+
/** Optional server-managed file identifier (integer). When provided,
|
|
32
|
+
* the editor tracks references and emits `cleanup:image-file` when
|
|
33
|
+
* the last block referencing this fileId is removed. */
|
|
34
|
+
readonly fileId?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The public-facing result type returned by the `uploadImage` prop function.
|
|
38
|
+
* Identical to UploadResult but exported under a separate name so consumers
|
|
39
|
+
* have a clean public API type.
|
|
40
|
+
*/
|
|
41
|
+
export interface ImageUploadResult {
|
|
42
|
+
readonly url: string;
|
|
43
|
+
readonly width?: number;
|
|
44
|
+
readonly height?: number;
|
|
45
|
+
readonly alt?: string;
|
|
46
|
+
readonly title?: string;
|
|
47
|
+
/** Optional server-managed file identifier (integer). When provided,
|
|
48
|
+
* the editor tracks references and emits `cleanup:image-file` when
|
|
49
|
+
* the last block referencing this fileId is removed. */
|
|
50
|
+
readonly fileId?: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The `uploadImage` prop function signature. When provided, the editor delegates
|
|
54
|
+
* image uploads to this function instead of using the built-in mock upload.
|
|
55
|
+
*
|
|
56
|
+
* The function receives:
|
|
57
|
+
* - name: the original file name (file.name)
|
|
58
|
+
* - file: the File object to upload
|
|
59
|
+
* - controller: AbortController — the editor will call .abort() if the
|
|
60
|
+
* image block is removed or the editor is unmounted
|
|
61
|
+
* - onProgress: callback to report progress as 0–100
|
|
62
|
+
*
|
|
63
|
+
* The function must return a Promise that resolves with the final uploaded
|
|
64
|
+
* URL (and optional dimensions/metadata) or rejects with an error.
|
|
65
|
+
*/
|
|
66
|
+
export type UploadImageHandler = (name: string, file: File, controller: AbortController, onProgress: (percent: number) => void) => Promise<ImageUploadResult>;
|
|
67
|
+
/** Convenience alias: UploadProgress = pct 0-1 callback. */
|
|
68
|
+
export type UploadProgress = (progress01: number) => void;
|
|
69
|
+
type StateChangeListener = (blockId: BlockId, state: UploadState | null) => void;
|
|
70
|
+
export declare function getUploadState(blockId: BlockId): UploadState | null;
|
|
71
|
+
export declare function setUploadState(blockId: BlockId, state: UploadState | null): void;
|
|
72
|
+
export declare function subscribeUploadState(fn: StateChangeListener): () => void;
|
|
73
|
+
/** Alias for subscribeUploadState (used by BlockEditor). */
|
|
74
|
+
export declare const subscribeToUploadChanges: typeof subscribeUploadState;
|
|
75
|
+
/** Release any object URLs and wipe the transient map.
|
|
76
|
+
* Called by BlockEditor on destroy / document replacement. */
|
|
77
|
+
export declare function resetAllUploadStates(): void;
|
|
78
|
+
/** Alias for resetAllUploadStates. */
|
|
79
|
+
export declare const clearAllUploadStates: typeof resetAllUploadStates;
|
|
80
|
+
export declare function revokeAllTempUrls(): void;
|
|
81
|
+
/** Wrap URL.createObjectURL and track in tempUrls so revokeAllTempUrls can
|
|
82
|
+
* release everything in one shot. */
|
|
83
|
+
export declare function createTempObjectUrl(file: Blob): string;
|
|
84
|
+
/** Clean up upload state for a set of removed blocks.
|
|
85
|
+
* Called by BlockEditor's document-change subscription. */
|
|
86
|
+
export declare function cleanupRemovedBlockIds(removed: Iterable<BlockId>): void;
|
|
87
|
+
/** Alias for cleanupRemovedBlockIds. */
|
|
88
|
+
export declare function cleanupUploadState(removed: Iterable<BlockId>): void;
|
|
89
|
+
export type UploadProgressFn = (progress01: number) => void;
|
|
90
|
+
export interface UploadCallbacks {
|
|
91
|
+
readonly onProgress: UploadProgressFn;
|
|
92
|
+
readonly onSuccess: (result: UploadResult) => void;
|
|
93
|
+
readonly onError: (message: string) => void;
|
|
94
|
+
}
|
|
95
|
+
export type UploadRequestHandler = (name: string, file: File, controller: AbortController, callbacks: UploadCallbacks) => void;
|
|
96
|
+
export declare function registerUploadRequestHandler(handler: UploadRequestHandler | null): void;
|
|
97
|
+
/** Alias used by BlockEditor.vue (shorter name). */
|
|
98
|
+
export declare const registerUploadHandler: typeof registerUploadRequestHandler;
|
|
99
|
+
export declare function hasUploadRequestHandler(): boolean;
|
|
100
|
+
export declare function dispatchUploadRequest(name: string, file: File, controller: AbortController, callbacks: UploadCallbacks): void;
|
|
101
|
+
type Result<T, E> = {
|
|
102
|
+
ok: true;
|
|
103
|
+
value: T;
|
|
104
|
+
} | {
|
|
105
|
+
ok: false;
|
|
106
|
+
error: E;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Simulate an upload for demo / dev environments where no real server is
|
|
110
|
+
* configured. Produces a blob: object URL in ~600 ms with fake progress.
|
|
111
|
+
*
|
|
112
|
+
* IMPORTANT: blob: object URLs are NOT serialisable across page reloads.
|
|
113
|
+
* Consumers that intend to persist and reload documents MUST provide an
|
|
114
|
+
* `uploadImage` prop. This mock exists purely so the editor still works
|
|
115
|
+
* out of the box in demos and tests.
|
|
116
|
+
*/
|
|
117
|
+
export declare function mockUpload(file: File, onProgress?: (progress01: number) => void): Promise<UploadResult>;
|
|
118
|
+
export declare function beginUpload(blockId: BlockId, file: File): Promise<Result<UploadResult, string>>;
|
|
119
|
+
/**
|
|
120
|
+
* Convert an image src string (from an <img> tag in pasted HTML) to a File
|
|
121
|
+
* object suitable for the upload pipeline.
|
|
122
|
+
*
|
|
123
|
+
* - `data:image/...;base64,...` → decode base64 → Blob → File
|
|
124
|
+
* - `blob:...` → fetch blob → File
|
|
125
|
+
* - `http://` / `https://` → fetch (may fail due to CORS) → File
|
|
126
|
+
*
|
|
127
|
+
* Returns null if the conversion fails (e.g. CORS, invalid data, network).
|
|
128
|
+
*/
|
|
129
|
+
export declare function srcToFile(src: string, fallbackName?: string): Promise<File | null>;
|
|
130
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { InlineSeq } from '../core/types';
|
|
2
|
+
/** CSS selector that matches any mark-carrying element. */
|
|
3
|
+
export declare const MARK_SELECTOR = "b,strong,i,em,u,s,strike,del,code,a[href],span.be-color-gray,span.be-color-brown,span.be-color-orange,span.be-color-yellow,span.be-color-green,span.be-color-blue,span.be-color-purple,span.be-color-pink,span.be-color-red,span.be-bg-gray,span.be-bg-brown,span.be-bg-orange,span.be-bg-yellow,span.be-bg-green,span.be-bg-blue,span.be-bg-purple,span.be-bg-pink,span.be-bg-red";
|
|
4
|
+
/** True if the sequence contains at least one text run with marks. */
|
|
5
|
+
export declare function hasMarks(seq: InlineSeq): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Convert an InlineSeq to an HTML string suitable for `innerHTML`.
|
|
8
|
+
* Marks are wrapped in nested tags; the order is deterministic so that
|
|
9
|
+
* repeated renders produce identical HTML (important for diffing).
|
|
10
|
+
*/
|
|
11
|
+
export declare function inlineToHtml(seq: InlineSeq): string;
|
|
12
|
+
/**
|
|
13
|
+
* Extract an InlineSeq from the live DOM of a contenteditable element.
|
|
14
|
+
* Walks the tree, accumulating text and collecting marks from ancestor tags.
|
|
15
|
+
* Adjacent runs with identical marks are merged.
|
|
16
|
+
*/
|
|
17
|
+
export declare function inlineFromDom(el: HTMLElement): InlineSeq;
|
|
18
|
+
/**
|
|
19
|
+
* Compute a deterministic signature string for an InlineSeq that captures
|
|
20
|
+
* both text and marks (including mark attrs). Used to decide whether a DOM
|
|
21
|
+
* re-render is needed (plain `textContent` comparison misses mark-only changes).
|
|
22
|
+
*/
|
|
23
|
+
export declare function contentSignature(seq: InlineSeq): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Editor } from '../core/Editor';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a keyboard event to a command and dispatch it.
|
|
4
|
+
* Returns `true` if a binding matched and the command returned `true`
|
|
5
|
+
* (handled). The caller should `preventDefault()` in that case.
|
|
6
|
+
*/
|
|
7
|
+
export declare function dispatchKeymap(editor: Editor, event: KeyboardEvent): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
/** Called by the parent (BlockEditor) when the drag actually starts (past
|
|
3
|
+
* the threshold). We mark `dragging = true` so the .handle-grip shows the
|
|
4
|
+
* "grabbing" cursor and the block handle keeps pointer-events auto even
|
|
5
|
+
* if visible gets toggled. */
|
|
6
|
+
declare function handleSetLocalDragging(active: boolean): void;
|
|
7
|
+
declare const _default: import('vue').DefineComponent<{
|
|
8
|
+
blockId: BlockId;
|
|
9
|
+
visible: boolean;
|
|
10
|
+
}, {
|
|
11
|
+
isOpen: import('vue').Ref<boolean, boolean>;
|
|
12
|
+
insertBtn: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
13
|
+
gripBtn: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
14
|
+
setLocalDragging: typeof handleSetLocalDragging;
|
|
15
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
16
|
+
openPlusMenu: (anchor: HTMLElement) => any;
|
|
17
|
+
openSettingsMenu: (anchor: HTMLElement) => any;
|
|
18
|
+
gripPointerDown: (blockId: BlockId, startX: number, startY: number, options: {
|
|
19
|
+
thresholdPx: number;
|
|
20
|
+
}) => any;
|
|
21
|
+
gripPointerUp: (blockId: BlockId) => any;
|
|
22
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
23
|
+
blockId: BlockId;
|
|
24
|
+
visible: boolean;
|
|
25
|
+
}> & Readonly<{
|
|
26
|
+
onOpenPlusMenu?: ((anchor: HTMLElement) => any) | undefined;
|
|
27
|
+
onOpenSettingsMenu?: ((anchor: HTMLElement) => any) | undefined;
|
|
28
|
+
onGripPointerDown?: ((blockId: BlockId, startX: number, startY: number, options: {
|
|
29
|
+
thresholdPx: number;
|
|
30
|
+
}) => any) | undefined;
|
|
31
|
+
onGripPointerUp?: ((blockId: BlockId) => any) | undefined;
|
|
32
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
33
|
+
export default _default;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
export interface SettingsItem {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly icon?: string;
|
|
5
|
+
readonly iconHtml?: string;
|
|
6
|
+
readonly title?: string;
|
|
7
|
+
readonly shortcut?: string;
|
|
8
|
+
readonly shortcutHtml?: string;
|
|
9
|
+
readonly danger?: boolean;
|
|
10
|
+
readonly run: () => void;
|
|
11
|
+
readonly value?: string;
|
|
12
|
+
readonly kind?: 'turnInto' | 'align' | 'color' | 'action';
|
|
13
|
+
}
|
|
14
|
+
declare function onKeyDown(event: KeyboardEvent): boolean;
|
|
15
|
+
declare const _default: import('vue').DefineComponent<{
|
|
16
|
+
visible: boolean;
|
|
17
|
+
anchorEl: HTMLElement | null;
|
|
18
|
+
blockId: BlockId | null;
|
|
19
|
+
rootEl: HTMLElement | null;
|
|
20
|
+
}, {
|
|
21
|
+
onKeyDown: typeof onKeyDown;
|
|
22
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
23
|
+
close: () => any;
|
|
24
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
25
|
+
visible: boolean;
|
|
26
|
+
anchorEl: HTMLElement | null;
|
|
27
|
+
blockId: BlockId | null;
|
|
28
|
+
rootEl: HTMLElement | null;
|
|
29
|
+
}> & Readonly<{
|
|
30
|
+
onClose?: (() => any) | undefined;
|
|
31
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
32
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{
|
|
2
|
+
visible: boolean;
|
|
3
|
+
initialValue: string;
|
|
4
|
+
/** Anchor element (e.g. the code-block language label). */
|
|
5
|
+
anchor: HTMLElement | null;
|
|
6
|
+
rootEl: HTMLElement | null;
|
|
7
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
8
|
+
close: () => any;
|
|
9
|
+
confirm: (value: string) => any;
|
|
10
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
11
|
+
visible: boolean;
|
|
12
|
+
initialValue: string;
|
|
13
|
+
/** Anchor element (e.g. the code-block language label). */
|
|
14
|
+
anchor: HTMLElement | null;
|
|
15
|
+
rootEl: HTMLElement | null;
|
|
16
|
+
}> & Readonly<{
|
|
17
|
+
onClose?: (() => any) | undefined;
|
|
18
|
+
onConfirm?: ((value: string) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
export interface ToolbarButton {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly icon: string;
|
|
5
|
+
readonly title: string;
|
|
6
|
+
readonly active?: boolean;
|
|
7
|
+
readonly convertType?: string;
|
|
8
|
+
readonly convertAttrs?: Readonly<Record<string, unknown>>;
|
|
9
|
+
}
|
|
10
|
+
declare const _default: import('vue').DefineComponent<{
|
|
11
|
+
visible: boolean;
|
|
12
|
+
selectionRect: DOMRect | null;
|
|
13
|
+
blockId: BlockId | null;
|
|
14
|
+
blockType: string | null;
|
|
15
|
+
blockAttrs: Readonly<Record<string, unknown>>;
|
|
16
|
+
rootEl: HTMLElement | null;
|
|
17
|
+
tableMode?: boolean;
|
|
18
|
+
showDelete?: boolean;
|
|
19
|
+
deleteLabel?: string;
|
|
20
|
+
deleteIcon?: string;
|
|
21
|
+
showMerge?: boolean;
|
|
22
|
+
showSplit?: boolean;
|
|
23
|
+
showHeaderRow?: boolean;
|
|
24
|
+
headerRowActive?: boolean;
|
|
25
|
+
cellEditMode?: boolean;
|
|
26
|
+
tableActiveMarks?: Set<string>;
|
|
27
|
+
tableActiveColor?: string;
|
|
28
|
+
tableActiveBgColor?: string;
|
|
29
|
+
tableActiveVerticalAlign?: string;
|
|
30
|
+
mobile?: boolean;
|
|
31
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
32
|
+
delete: () => any;
|
|
33
|
+
close: () => any;
|
|
34
|
+
linkClick: (blockId: BlockId, from: number, to: number) => any;
|
|
35
|
+
split: () => any;
|
|
36
|
+
merge: () => any;
|
|
37
|
+
tableHeaderRow: () => any;
|
|
38
|
+
tableType: (cellType: string) => any;
|
|
39
|
+
tableAlign: (align: string) => any;
|
|
40
|
+
tableVerticalAlign: (verticalAlign: string) => any;
|
|
41
|
+
tableMark: (markType: string) => any;
|
|
42
|
+
tableTextColor: (color: string | null) => any;
|
|
43
|
+
tableBgColor: (color: string | null) => any;
|
|
44
|
+
tableCopy: () => any;
|
|
45
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
46
|
+
visible: boolean;
|
|
47
|
+
selectionRect: DOMRect | null;
|
|
48
|
+
blockId: BlockId | null;
|
|
49
|
+
blockType: string | null;
|
|
50
|
+
blockAttrs: Readonly<Record<string, unknown>>;
|
|
51
|
+
rootEl: HTMLElement | null;
|
|
52
|
+
tableMode?: boolean;
|
|
53
|
+
showDelete?: boolean;
|
|
54
|
+
deleteLabel?: string;
|
|
55
|
+
deleteIcon?: string;
|
|
56
|
+
showMerge?: boolean;
|
|
57
|
+
showSplit?: boolean;
|
|
58
|
+
showHeaderRow?: boolean;
|
|
59
|
+
headerRowActive?: boolean;
|
|
60
|
+
cellEditMode?: boolean;
|
|
61
|
+
tableActiveMarks?: Set<string>;
|
|
62
|
+
tableActiveColor?: string;
|
|
63
|
+
tableActiveBgColor?: string;
|
|
64
|
+
tableActiveVerticalAlign?: string;
|
|
65
|
+
mobile?: boolean;
|
|
66
|
+
}> & Readonly<{
|
|
67
|
+
onDelete?: (() => any) | undefined;
|
|
68
|
+
onClose?: (() => any) | undefined;
|
|
69
|
+
onLinkClick?: ((blockId: BlockId, from: number, to: number) => any) | undefined;
|
|
70
|
+
onSplit?: (() => any) | undefined;
|
|
71
|
+
onMerge?: (() => any) | undefined;
|
|
72
|
+
onTableHeaderRow?: (() => any) | undefined;
|
|
73
|
+
onTableType?: ((cellType: string) => any) | undefined;
|
|
74
|
+
onTableAlign?: ((align: string) => any) | undefined;
|
|
75
|
+
onTableVerticalAlign?: ((verticalAlign: string) => any) | undefined;
|
|
76
|
+
onTableMark?: ((markType: string) => any) | undefined;
|
|
77
|
+
onTableTextColor?: ((color: string | null) => any) | undefined;
|
|
78
|
+
onTableBgColor?: ((color: string | null) => any) | undefined;
|
|
79
|
+
onTableCopy?: (() => any) | undefined;
|
|
80
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
81
|
+
export default _default;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
type PopoverMode = 'view' | 'edit';
|
|
3
|
+
declare function onSave(): void;
|
|
4
|
+
declare function onCancelEdit(): void;
|
|
5
|
+
declare const _default: import('vue').DefineComponent<{
|
|
6
|
+
visible: boolean;
|
|
7
|
+
/** Viewport-relative rect of the link text (for positioning). */
|
|
8
|
+
anchorRect: DOMRect | null;
|
|
9
|
+
/** The block containing the link. */
|
|
10
|
+
blockId: BlockId | null;
|
|
11
|
+
/** Character offset where the link starts. */
|
|
12
|
+
from: number;
|
|
13
|
+
/** Character offset where the link ends. */
|
|
14
|
+
to: number;
|
|
15
|
+
/** Current href (empty if creating a new link). */
|
|
16
|
+
href: string;
|
|
17
|
+
/** Current link text (empty if creating a new link). */
|
|
18
|
+
text: string;
|
|
19
|
+
/** Whether to open in edit mode immediately (e.g. when creating a new link). */
|
|
20
|
+
initialMode?: PopoverMode;
|
|
21
|
+
/** Whether to show the text input field (hide when editing from Ctrl+K on selection). */
|
|
22
|
+
showTextInput?: boolean;
|
|
23
|
+
/** When provided, overrides the default editor.commands.setLink for cell edit mode. */
|
|
24
|
+
onSaveLink?: (url: string, text: string | undefined) => void;
|
|
25
|
+
/** When provided, overrides the default editor.commands.unsetLink for cell edit mode. */
|
|
26
|
+
onRemoveLinkMark?: () => void;
|
|
27
|
+
/** Read-only mode: hides the edit/delete buttons and forbids entering edit mode. */
|
|
28
|
+
readonly?: boolean;
|
|
29
|
+
}, {
|
|
30
|
+
isEditing: () => boolean;
|
|
31
|
+
save: typeof onSave;
|
|
32
|
+
cancel: typeof onCancelEdit;
|
|
33
|
+
}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
34
|
+
close: () => any;
|
|
35
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
36
|
+
visible: boolean;
|
|
37
|
+
/** Viewport-relative rect of the link text (for positioning). */
|
|
38
|
+
anchorRect: DOMRect | null;
|
|
39
|
+
/** The block containing the link. */
|
|
40
|
+
blockId: BlockId | null;
|
|
41
|
+
/** Character offset where the link starts. */
|
|
42
|
+
from: number;
|
|
43
|
+
/** Character offset where the link ends. */
|
|
44
|
+
to: number;
|
|
45
|
+
/** Current href (empty if creating a new link). */
|
|
46
|
+
href: string;
|
|
47
|
+
/** Current link text (empty if creating a new link). */
|
|
48
|
+
text: string;
|
|
49
|
+
/** Whether to open in edit mode immediately (e.g. when creating a new link). */
|
|
50
|
+
initialMode?: PopoverMode;
|
|
51
|
+
/** Whether to show the text input field (hide when editing from Ctrl+K on selection). */
|
|
52
|
+
showTextInput?: boolean;
|
|
53
|
+
/** When provided, overrides the default editor.commands.setLink for cell edit mode. */
|
|
54
|
+
onSaveLink?: (url: string, text: string | undefined) => void;
|
|
55
|
+
/** When provided, overrides the default editor.commands.unsetLink for cell edit mode. */
|
|
56
|
+
onRemoveLinkMark?: () => void;
|
|
57
|
+
/** Read-only mode: hides the edit/delete buttons and forbids entering edit mode. */
|
|
58
|
+
readonly?: boolean;
|
|
59
|
+
}> & Readonly<{
|
|
60
|
+
onClose?: (() => any) | undefined;
|
|
61
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
62
|
+
export default _default;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
declare const _default: import('vue').DefineComponent<{
|
|
3
|
+
rootEl: HTMLElement | null;
|
|
4
|
+
focusBlockId: BlockId | null;
|
|
5
|
+
hoverVisible: boolean;
|
|
6
|
+
hoverSelectionRect: DOMRect | null;
|
|
7
|
+
hoverBlockId: BlockId | null;
|
|
8
|
+
hoverBlockType: string | null;
|
|
9
|
+
hoverBlockAttrs: Readonly<Record<string, unknown>>;
|
|
10
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
11
|
+
linkClick: (blockId: BlockId, from: number, to: number) => any;
|
|
12
|
+
openPlusMenu: (blockId: BlockId, anchor: HTMLElement) => any;
|
|
13
|
+
openSettingsMenu: (blockId: BlockId, anchor: HTMLElement) => any;
|
|
14
|
+
hoverClose: () => any;
|
|
15
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
16
|
+
rootEl: HTMLElement | null;
|
|
17
|
+
focusBlockId: BlockId | null;
|
|
18
|
+
hoverVisible: boolean;
|
|
19
|
+
hoverSelectionRect: DOMRect | null;
|
|
20
|
+
hoverBlockId: BlockId | null;
|
|
21
|
+
hoverBlockType: string | null;
|
|
22
|
+
hoverBlockAttrs: Readonly<Record<string, unknown>>;
|
|
23
|
+
}> & Readonly<{
|
|
24
|
+
onLinkClick?: ((blockId: BlockId, from: number, to: number) => any) | undefined;
|
|
25
|
+
onOpenPlusMenu?: ((blockId: BlockId, anchor: HTMLElement) => any) | undefined;
|
|
26
|
+
onOpenSettingsMenu?: ((blockId: BlockId, anchor: HTMLElement) => any) | undefined;
|
|
27
|
+
onHoverClose?: (() => any) | undefined;
|
|
28
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
29
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare const _default: import('vue').DefineComponent<{
|
|
2
|
+
visible: boolean;
|
|
3
|
+
initialValue: number;
|
|
4
|
+
/** Anchor element (e.g. the OrderedListMenu item). */
|
|
5
|
+
anchor: HTMLElement | null;
|
|
6
|
+
rootEl: HTMLElement | null;
|
|
7
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
8
|
+
close: () => any;
|
|
9
|
+
confirm: (value: number) => any;
|
|
10
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
11
|
+
visible: boolean;
|
|
12
|
+
initialValue: number;
|
|
13
|
+
/** Anchor element (e.g. the OrderedListMenu item). */
|
|
14
|
+
anchor: HTMLElement | null;
|
|
15
|
+
rootEl: HTMLElement | null;
|
|
16
|
+
}> & Readonly<{
|
|
17
|
+
onClose?: (() => any) | undefined;
|
|
18
|
+
onConfirm?: ((value: number) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BlockId } from '../../core/types';
|
|
2
|
+
declare const _default: import('vue').DefineComponent<{
|
|
3
|
+
visible: boolean;
|
|
4
|
+
blockId: BlockId | null;
|
|
5
|
+
/** The marker element that was clicked (serves as anchor). */
|
|
6
|
+
anchor: HTMLElement | null;
|
|
7
|
+
rootEl: HTMLElement | null;
|
|
8
|
+
/** True if the previous block in document order is an ordered-list item. */
|
|
9
|
+
canContinue: boolean;
|
|
10
|
+
/** True if "Start new list" is actionable (current ordinal is not 1). */
|
|
11
|
+
canStartNew: boolean;
|
|
12
|
+
/** The currently displayed ordinal of this block (for the picker default). */
|
|
13
|
+
currentNumber: number;
|
|
14
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
15
|
+
close: () => any;
|
|
16
|
+
continue: () => any;
|
|
17
|
+
startNew: () => any;
|
|
18
|
+
modify: (initialValue: number) => any;
|
|
19
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
20
|
+
visible: boolean;
|
|
21
|
+
blockId: BlockId | null;
|
|
22
|
+
/** The marker element that was clicked (serves as anchor). */
|
|
23
|
+
anchor: HTMLElement | null;
|
|
24
|
+
rootEl: HTMLElement | null;
|
|
25
|
+
/** True if the previous block in document order is an ordered-list item. */
|
|
26
|
+
canContinue: boolean;
|
|
27
|
+
/** True if "Start new list" is actionable (current ordinal is not 1). */
|
|
28
|
+
canStartNew: boolean;
|
|
29
|
+
/** The currently displayed ordinal of this block (for the picker default). */
|
|
30
|
+
currentNumber: number;
|
|
31
|
+
}> & Readonly<{
|
|
32
|
+
onClose?: (() => any) | undefined;
|
|
33
|
+
onContinue?: (() => any) | undefined;
|
|
34
|
+
onStartNew?: (() => any) | undefined;
|
|
35
|
+
onModify?: ((initialValue: number) => any) | undefined;
|
|
36
|
+
}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
37
|
+
export default _default;
|