@aseity/idocs-preview 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +39 -3
- package/dist/core/display.d.ts +67 -0
- package/dist/core/editor.d.ts +172 -0
- package/dist/core/editor.js +527 -0
- package/dist/core/engine.js +132 -38
- package/dist/core/index.d.ts +6 -19
- package/dist/core/index.js +13 -17
- package/dist/core/protocol.d.ts +87 -0
- package/dist/core/protocol.js +13 -1
- package/dist/view/edit-selection.d.ts +80 -0
- package/dist/view/edit-selection.js +841 -0
- package/dist/view/editor.d.ts +77 -0
- package/dist/view/editor.js +1477 -0
- package/dist/view/find.d.ts +2 -1
- package/dist/view/find.js +7 -0
- package/dist/view/ime.d.ts +63 -0
- package/dist/view/ime.js +352 -0
- package/dist/view/index.d.ts +4 -0
- package/dist/view/index.js +3 -0
- package/dist/view/selection-layer.d.ts +1 -0
- package/dist/view/selection-layer.js +4 -4
- package/dist/view/workspace-styles.d.ts +1 -0
- package/dist/view/workspace-styles.js +879 -0
- package/dist/view/workspace.d.ts +70 -0
- package/dist/view/workspace.js +1028 -0
- package/package.json +2 -2
- package/wasm/idocs_wasm.d.ts +30 -0
- package/wasm/idocs_wasm.js +147 -0
- package/wasm/idocs_wasm_bg.wasm +0 -0
- package/wasm/idocs_wasm_bg.wasm.d.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @aseity/idocs-preview
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add `openEditor` for stateful editing of iDocs source documents, with source snapshots, undo and redo, revision tracking, and DOCX export. Add `mountEditor` for Canvas text editing with Chinese IME input and `mountEditorWorkspace` for page thumbnails, an outline, formatting controls, and paragraph, table, and image edits. Existing preview APIs remain available. Hosts own source persistence and editor session disposal.
|
|
8
|
+
|
|
3
9
|
## 0.3.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# iDocs preview
|
|
1
|
+
# iDocs preview and editor
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
(`.idoc`). A Web Worker measures browser-local fonts with Canvas and lays out
|
|
3
|
+
Browser preview for Word documents (`.docx`) and iDocs source documents
|
|
4
|
+
(`.idoc`), with an interactive editor for iDocs source documents. A Web Worker measures browser-local fonts with Canvas and lays out
|
|
5
5
|
the document in WASM. Canvas draws text using the same font settings. No text
|
|
6
6
|
font downloads or Local Font Access permissions are required. Different installed
|
|
7
7
|
fonts can change line breaks and page counts.
|
|
@@ -55,6 +55,42 @@ during server rendering.
|
|
|
55
55
|
owns a session and a view; `mountPreview(container, doc)` shows a document you
|
|
56
56
|
opened yourself. Both re-export the core types.
|
|
57
57
|
|
|
58
|
+
## Editing iDocs source documents
|
|
59
|
+
|
|
60
|
+
Open an editor session and mount the workspace in a container with a height:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const { openEditor } = await import("@aseity/idocs-preview/core");
|
|
64
|
+
const { mountEditorWorkspace } = await import("@aseity/idocs-preview/view");
|
|
65
|
+
|
|
66
|
+
const session = await openEditor({ source, name: "report.idoc" });
|
|
67
|
+
const workspace = mountEditorWorkspace(container, session, { railOpen: true });
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`source` is an iDocs YAML string or a `Uint8Array`. Use `readResource` in
|
|
71
|
+
`openEditor` to resolve referenced images. The workspace includes page thumbnails,
|
|
72
|
+
an outline, formatting controls, and an insert dock. It supports text selection,
|
|
73
|
+
Chinese IME input, undo and redo, paragraph styles, tables, and images.
|
|
74
|
+
Use `mountEditor(container, session)` for the page editor without workspace controls.
|
|
75
|
+
|
|
76
|
+
Save the source snapshot through your host, then mark that revision as saved:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const snapshot = await session.snapshot();
|
|
80
|
+
await saveSource(snapshot.source);
|
|
81
|
+
await session.markSaved({ revision: snapshot.revision });
|
|
82
|
+
const docx = await session.exportDocx();
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`saveSource` is your application's persistence function. Subscribe to
|
|
86
|
+
`session.subscribe(listener)` to observe revisions, unsaved changes, and layout
|
|
87
|
+
state. Supply `onPickImage` to the workspace to enable image insertion.
|
|
88
|
+
`onQuoteSelection` receives selected text for a host assistant integration.
|
|
89
|
+
|
|
90
|
+
When closing the editor, call `workspace.destroy()` and `session.dispose()`.
|
|
91
|
+
The workspace does not own the session. Existing preview APIs remain available.
|
|
92
|
+
The development page enables the workspace with `?edit=1`.
|
|
93
|
+
|
|
58
94
|
## Opening documents
|
|
59
95
|
|
|
60
96
|
`open(bytes, { name?, kind?, readResource? })` takes a `Uint8Array` or
|
package/dist/core/display.d.ts
CHANGED
|
@@ -150,6 +150,71 @@ export interface LineGlyph {
|
|
|
150
150
|
/** UTF-16 offset within the line's text. */
|
|
151
151
|
offset: number;
|
|
152
152
|
}
|
|
153
|
+
export type Affinity = 'upstream' | 'downstream';
|
|
154
|
+
export type GeneratedRole = 'listMarker' | 'headingNumber' | 'captionLabel' | 'equationNumber' | 'noteNumber';
|
|
155
|
+
export type ReadOnlyReason = 'aliasShared' | 'generated' | 'repeatedHeader' | 'unsupportedStory' | 'unsupportedStructure' | 'protectedInline';
|
|
156
|
+
export type Revision = string & {
|
|
157
|
+
readonly __brand: 'Revision';
|
|
158
|
+
};
|
|
159
|
+
export type SessionId = string & {
|
|
160
|
+
readonly __brand: 'SessionId';
|
|
161
|
+
};
|
|
162
|
+
export type FlowId = string & {
|
|
163
|
+
readonly __brand: 'FlowId';
|
|
164
|
+
};
|
|
165
|
+
export type BlockId = string & {
|
|
166
|
+
readonly __brand: 'BlockId';
|
|
167
|
+
};
|
|
168
|
+
export type DisplayUtf16 = number & {
|
|
169
|
+
readonly __brand: 'DisplayUtf16';
|
|
170
|
+
};
|
|
171
|
+
export type ScalarUtf16 = number & {
|
|
172
|
+
readonly __brand: 'ScalarUtf16';
|
|
173
|
+
};
|
|
174
|
+
export type FlowOffset = number & {
|
|
175
|
+
readonly __brand: 'FlowOffset';
|
|
176
|
+
};
|
|
177
|
+
export interface Anchor {
|
|
178
|
+
readonly session: SessionId;
|
|
179
|
+
readonly flow: FlowId;
|
|
180
|
+
readonly offset: FlowOffset;
|
|
181
|
+
readonly affinity: Affinity;
|
|
182
|
+
}
|
|
183
|
+
export interface CaretStop {
|
|
184
|
+
readonly displayOffset: DisplayUtf16;
|
|
185
|
+
readonly anchor: Anchor;
|
|
186
|
+
}
|
|
187
|
+
export type BlockStyle = 'body' | 'heading1' | 'heading2' | 'heading3';
|
|
188
|
+
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
189
|
+
export type LineSource = {
|
|
190
|
+
readonly kind: 'editable';
|
|
191
|
+
readonly path: string;
|
|
192
|
+
readonly sourceStart: ScalarUtf16;
|
|
193
|
+
readonly sourceEnd: ScalarUtf16;
|
|
194
|
+
readonly stops: readonly CaretStop[];
|
|
195
|
+
readonly blockStyle?: BlockStyle;
|
|
196
|
+
readonly align?: TextAlign;
|
|
197
|
+
} | {
|
|
198
|
+
readonly kind: 'atomic';
|
|
199
|
+
readonly path?: string;
|
|
200
|
+
readonly reason: ReadOnlyReason;
|
|
201
|
+
readonly role?: GeneratedRole;
|
|
202
|
+
readonly before?: Anchor;
|
|
203
|
+
readonly after?: Anchor;
|
|
204
|
+
};
|
|
205
|
+
export interface LineSourceSegment {
|
|
206
|
+
readonly start: DisplayUtf16;
|
|
207
|
+
readonly end: DisplayUtf16;
|
|
208
|
+
readonly source: LineSource;
|
|
209
|
+
}
|
|
210
|
+
export type LineOccurrence = {
|
|
211
|
+
readonly kind: 'original';
|
|
212
|
+
} | {
|
|
213
|
+
readonly kind: 'repeatedHeader';
|
|
214
|
+
readonly canonicalPath: string;
|
|
215
|
+
} | {
|
|
216
|
+
readonly kind: 'readOnlyStory';
|
|
217
|
+
};
|
|
153
218
|
/** One laid-out line's glyph table, for hit testing. */
|
|
154
219
|
export interface TextLine {
|
|
155
220
|
story: Story;
|
|
@@ -169,6 +234,8 @@ export interface TextLine {
|
|
|
169
234
|
/** Read-only in opened previews; indexed glyph objects are snapshots. */
|
|
170
235
|
glyphs: readonly LineGlyph[];
|
|
171
236
|
turn?: [number, number];
|
|
237
|
+
sourceSegments?: readonly LineSourceSegment[];
|
|
238
|
+
occurrence?: LineOccurrence;
|
|
172
239
|
}
|
|
173
240
|
export interface DisplayPage {
|
|
174
241
|
number: number;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { type Affinity, type Anchor, type BlockId, type BlockStyle, type CaretStop, type DisplayUtf16, type FlowId, type FlowOffset, type GeneratedRole, type LineOccurrence, type LineSource, type LineSourceSegment, type ReadOnlyReason, type Revision, type ScalarUtf16, type SessionId, type TextAlign, type TextLine } from './display.js';
|
|
2
|
+
import { type Endpoint, type PreviewDiagnostic, type PreviewDocument, type Read, type SessionOptions } from './protocol.js';
|
|
3
|
+
export type { Affinity, Anchor, BlockId, CaretStop, DisplayUtf16, FlowId, FlowOffset, GeneratedRole, LineOccurrence, LineSource, LineSourceSegment, ReadOnlyReason, Revision, ScalarUtf16, SessionId, };
|
|
4
|
+
export interface EditRange {
|
|
5
|
+
readonly revision: Revision;
|
|
6
|
+
readonly start: Anchor;
|
|
7
|
+
readonly end: Anchor;
|
|
8
|
+
}
|
|
9
|
+
export interface Selection {
|
|
10
|
+
readonly revision: Revision;
|
|
11
|
+
readonly range: EditRange;
|
|
12
|
+
readonly reversed: boolean;
|
|
13
|
+
}
|
|
14
|
+
export type Mark = 'bold' | 'italic' | 'underline' | 'strike';
|
|
15
|
+
export type InsertBlock = {
|
|
16
|
+
kind: 'paragraph';
|
|
17
|
+
text?: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'heading';
|
|
20
|
+
level: 1 | 2 | 3;
|
|
21
|
+
text?: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'callout';
|
|
24
|
+
text?: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'table';
|
|
27
|
+
rows: number;
|
|
28
|
+
cols: number;
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'image';
|
|
31
|
+
src: string;
|
|
32
|
+
alt?: string;
|
|
33
|
+
widthRatio?: number;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'pageBreak';
|
|
36
|
+
};
|
|
37
|
+
export type TableAction = 'insertRowAbove' | 'insertRowBelow' | 'insertColumnLeft' | 'insertColumnRight' | 'deleteRow' | 'deleteColumn' | 'toggleHeaderRow';
|
|
38
|
+
export type ImageAlign = 'left' | 'center' | 'right';
|
|
39
|
+
export type Edit = {
|
|
40
|
+
kind: 'replaceText';
|
|
41
|
+
range: EditRange;
|
|
42
|
+
text: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'setMark';
|
|
45
|
+
range: EditRange;
|
|
46
|
+
mark: Mark;
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'splitBlock';
|
|
50
|
+
at: Anchor;
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'mergeBackward';
|
|
53
|
+
at: Anchor;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'setBlockStyle';
|
|
56
|
+
at: Anchor;
|
|
57
|
+
style: BlockStyle;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'setAlignment';
|
|
60
|
+
at: Anchor;
|
|
61
|
+
align: TextAlign;
|
|
62
|
+
} | {
|
|
63
|
+
kind: 'insertBlock';
|
|
64
|
+
after?: Anchor;
|
|
65
|
+
block: InsertBlock;
|
|
66
|
+
} | {
|
|
67
|
+
kind: 'editTable';
|
|
68
|
+
at: Anchor;
|
|
69
|
+
action: TableAction;
|
|
70
|
+
} | {
|
|
71
|
+
kind: 'editImage';
|
|
72
|
+
path: string;
|
|
73
|
+
widthRatio?: number;
|
|
74
|
+
align?: ImageAlign;
|
|
75
|
+
src?: string;
|
|
76
|
+
alt?: string;
|
|
77
|
+
};
|
|
78
|
+
export interface SourceSnapshot {
|
|
79
|
+
readonly revision: Revision;
|
|
80
|
+
readonly source: string;
|
|
81
|
+
}
|
|
82
|
+
export interface RenderStats {
|
|
83
|
+
readonly localResolve: boolean;
|
|
84
|
+
readonly paragraphsLaid: number;
|
|
85
|
+
readonly paragraphsReused: number;
|
|
86
|
+
readonly passes: number;
|
|
87
|
+
}
|
|
88
|
+
export interface Commit {
|
|
89
|
+
readonly revision: Revision;
|
|
90
|
+
readonly selection: Selection | null;
|
|
91
|
+
readonly changedBlocks: readonly BlockId[];
|
|
92
|
+
readonly localResolve: boolean;
|
|
93
|
+
readonly diagnostics: readonly PreviewDiagnostic[];
|
|
94
|
+
}
|
|
95
|
+
export interface EditorFrame {
|
|
96
|
+
readonly revision: Revision;
|
|
97
|
+
readonly document: PreviewDocument;
|
|
98
|
+
readonly stats: RenderStats;
|
|
99
|
+
}
|
|
100
|
+
export type LayoutState = {
|
|
101
|
+
kind: 'pending';
|
|
102
|
+
previous: EditorFrame | null;
|
|
103
|
+
} | {
|
|
104
|
+
kind: 'ready';
|
|
105
|
+
frame: EditorFrame;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'failed';
|
|
108
|
+
revision: Revision;
|
|
109
|
+
message: string;
|
|
110
|
+
diagnostics: readonly PreviewDiagnostic[];
|
|
111
|
+
previous: EditorFrame | null;
|
|
112
|
+
};
|
|
113
|
+
export interface EditorState {
|
|
114
|
+
readonly revision: Revision;
|
|
115
|
+
readonly dirty: boolean;
|
|
116
|
+
readonly selection: Selection | null;
|
|
117
|
+
readonly canUndo: boolean;
|
|
118
|
+
readonly canRedo: boolean;
|
|
119
|
+
readonly layout: LayoutState;
|
|
120
|
+
}
|
|
121
|
+
export interface OpenEditorOptions {
|
|
122
|
+
source: string | Uint8Array;
|
|
123
|
+
name?: string;
|
|
124
|
+
readResource?: Read;
|
|
125
|
+
runtime?: SessionOptions;
|
|
126
|
+
}
|
|
127
|
+
export interface EditorSession {
|
|
128
|
+
state(): EditorState;
|
|
129
|
+
subscribe(listener: (state: EditorState) => void): () => void;
|
|
130
|
+
range(input: {
|
|
131
|
+
revision: Revision;
|
|
132
|
+
start: Anchor;
|
|
133
|
+
end: Anchor;
|
|
134
|
+
}): Promise<EditRange>;
|
|
135
|
+
apply(input: {
|
|
136
|
+
base: Revision;
|
|
137
|
+
edit: Edit;
|
|
138
|
+
}): Promise<Commit>;
|
|
139
|
+
undo(input: {
|
|
140
|
+
base: Revision;
|
|
141
|
+
}): Promise<Commit>;
|
|
142
|
+
redo(input: {
|
|
143
|
+
base: Revision;
|
|
144
|
+
}): Promise<Commit>;
|
|
145
|
+
snapshot(): Promise<SourceSnapshot>;
|
|
146
|
+
markSaved(input: {
|
|
147
|
+
revision: Revision;
|
|
148
|
+
}): Promise<void>;
|
|
149
|
+
refresh(input: {
|
|
150
|
+
base: Revision;
|
|
151
|
+
source: string | Uint8Array;
|
|
152
|
+
}): Promise<Commit>;
|
|
153
|
+
exportDocx(): Promise<Uint8Array>;
|
|
154
|
+
dispose(): void;
|
|
155
|
+
}
|
|
156
|
+
export type LineHit = {
|
|
157
|
+
kind: 'editable';
|
|
158
|
+
anchor: Anchor;
|
|
159
|
+
displayOffset: DisplayUtf16;
|
|
160
|
+
x: number;
|
|
161
|
+
} | {
|
|
162
|
+
kind: 'atomic';
|
|
163
|
+
reason: ReadOnlyReason;
|
|
164
|
+
role?: GeneratedRole;
|
|
165
|
+
before?: Anchor;
|
|
166
|
+
after?: Anchor;
|
|
167
|
+
} | {
|
|
168
|
+
kind: 'unmapped';
|
|
169
|
+
};
|
|
170
|
+
export declare function locateLineSource(line: TextLine, xPt: number): LineHit;
|
|
171
|
+
export declare function lineCaretX(line: TextLine, anchor: Anchor): number | null;
|
|
172
|
+
export declare function openEditorWith(worker: Endpoint, options: OpenEditorOptions): Promise<EditorSession>;
|