@aicayzer/inkkit 0.0.8 → 0.0.10

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.
@@ -0,0 +1,40 @@
1
+ import type { DocumentContext, FileAdapter, FileContext, FilePresentation, FileReference } from './types.js';
2
+ import type { EditorLabels } from './labels.js';
3
+ export interface FileViewRuntime {
4
+ adapter: FileAdapter;
5
+ capture(): {
6
+ context: DocumentContext;
7
+ isCurrent(): boolean;
8
+ };
9
+ active(): boolean;
10
+ labels: Readonly<EditorLabels>;
11
+ error?(error: Error): void;
12
+ }
13
+ export declare function withFileSignal<T>(operation: Promise<T>, signal: AbortSignal): Promise<T>;
14
+ export declare function resolveFile(adapter: FileAdapter, reference: FileReference, context: FileContext): Promise<FilePresentation>;
15
+ export type FileResolutionState = {
16
+ status: 'idle';
17
+ } | {
18
+ status: 'loading';
19
+ } | {
20
+ status: 'resolved';
21
+ presentation: FilePresentation;
22
+ } | {
23
+ status: 'failed';
24
+ error: Error;
25
+ };
26
+ export declare class FileResolution {
27
+ private readonly runtime;
28
+ private readonly changed;
29
+ state: FileResolutionState;
30
+ private reference?;
31
+ private key;
32
+ private request?;
33
+ private dead;
34
+ constructor(runtime: FileViewRuntime, changed: () => void);
35
+ update(reference: FileReference): void;
36
+ retry(): void;
37
+ private current;
38
+ cancel(): void;
39
+ destroy(): void;
40
+ }
package/dist/files.js ADDED
@@ -0,0 +1,133 @@
1
+ function abort(signal) {
2
+ if (signal.aborted)
3
+ throw new DOMException('File resolution cancelled', 'AbortError');
4
+ }
5
+ export function withFileSignal(operation, signal) {
6
+ return new Promise((resolve, reject) => {
7
+ const cleanup = () => signal.removeEventListener('abort', cancelled);
8
+ const cancelled = () => {
9
+ cleanup();
10
+ reject(new DOMException('File operation cancelled', 'AbortError'));
11
+ };
12
+ if (signal.aborted)
13
+ cancelled();
14
+ else
15
+ signal.addEventListener('abort', cancelled, { once: true });
16
+ operation.then((result) => {
17
+ cleanup();
18
+ if (signal.aborted)
19
+ cancelled();
20
+ else
21
+ resolve(result);
22
+ }, (error) => {
23
+ cleanup();
24
+ reject(error);
25
+ });
26
+ });
27
+ }
28
+ export async function resolveFile(adapter, reference, context) {
29
+ abort(context.signal);
30
+ const presentation = await withFileSignal(adapter.resolve(Object.freeze({ ...reference }), context), context.signal);
31
+ abort(context.signal);
32
+ if (!presentation ||
33
+ typeof presentation !== 'object' ||
34
+ !['image', 'audio', 'video', 'pdf', 'file', 'missing', 'error'].includes(presentation.kind))
35
+ throw new TypeError('File adapter returned an unsupported presentation');
36
+ if (presentation.label !== undefined &&
37
+ typeof presentation.label !== 'string')
38
+ throw new TypeError('File presentation label must be text');
39
+ if (presentation.kind === 'error' && typeof presentation.message !== 'string')
40
+ throw new TypeError('File presentation error must include a message');
41
+ if ('url' in presentation) {
42
+ if (typeof presentation.url !== 'string' || !presentation.url.trim())
43
+ throw new TypeError('File presentation needs a resource URL');
44
+ const url = new URL(presentation.url, document.baseURI);
45
+ if (['javascript:', 'vbscript:'].includes(url.protocol))
46
+ throw new TypeError('Executable file presentation URLs are not supported');
47
+ const prefix = presentation.kind === 'pdf' ? 'application/pdf' : `${presentation.kind}/`;
48
+ const mime = presentation.mimeType?.toLowerCase().split(';')[0]?.trim();
49
+ if (mime &&
50
+ (presentation.kind === 'pdf' ? mime !== prefix : !mime.startsWith(prefix)))
51
+ throw new TypeError('File presentation MIME type does not match its kind');
52
+ const dataMime = url.pathname.toLowerCase().split(/[;,]/)[0] ?? '';
53
+ if (url.protocol === 'data:' &&
54
+ (presentation.kind === 'pdf'
55
+ ? dataMime !== prefix
56
+ : !dataMime.startsWith(prefix)))
57
+ throw new TypeError('File presentation data URL does not match its kind');
58
+ }
59
+ else if (['image', 'audio', 'video', 'pdf'].includes(presentation.kind)) {
60
+ throw new TypeError('File presentation needs a resource URL');
61
+ }
62
+ return Object.freeze({ ...presentation });
63
+ }
64
+ export class FileResolution {
65
+ runtime;
66
+ changed;
67
+ state = { status: 'idle' };
68
+ reference;
69
+ key = '';
70
+ request;
71
+ dead = false;
72
+ constructor(runtime, changed) {
73
+ this.runtime = runtime;
74
+ this.changed = changed;
75
+ }
76
+ update(reference) {
77
+ const key = JSON.stringify([
78
+ reference.reference,
79
+ reference.kind,
80
+ reference.fragment,
81
+ ]);
82
+ this.reference = Object.freeze({ ...reference });
83
+ if (this.key === key && this.request?.isCurrent())
84
+ return;
85
+ this.key = key;
86
+ this.retry();
87
+ }
88
+ retry() {
89
+ this.cancel();
90
+ if (this.dead || !this.reference || !this.runtime.active())
91
+ return;
92
+ const captured = this.runtime.capture();
93
+ const request = {
94
+ controller: new AbortController(),
95
+ isCurrent: captured.isCurrent,
96
+ };
97
+ this.request = request;
98
+ this.state = { status: 'loading' };
99
+ this.changed();
100
+ void resolveFile(this.runtime.adapter, this.reference, {
101
+ ...captured.context,
102
+ signal: request.controller.signal,
103
+ }).then((presentation) => {
104
+ if (!this.current(request))
105
+ return;
106
+ this.state = { status: 'resolved', presentation };
107
+ this.changed();
108
+ }, (failure) => {
109
+ if (!this.current(request))
110
+ return;
111
+ const error = failure instanceof Error ? failure : new Error(String(failure));
112
+ this.state = { status: 'failed', error };
113
+ this.changed();
114
+ this.runtime.error?.(error);
115
+ });
116
+ }
117
+ current(request) {
118
+ return (!this.dead &&
119
+ this.request === request &&
120
+ !request.controller.signal.aborted &&
121
+ this.runtime.active() &&
122
+ request.isCurrent());
123
+ }
124
+ cancel() {
125
+ this.request?.controller.abort();
126
+ this.request = undefined;
127
+ this.state = { status: 'idle' };
128
+ }
129
+ destroy() {
130
+ this.dead = true;
131
+ this.cancel();
132
+ }
133
+ }
package/dist/images.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Node as ProseNode } from '@milkdown/kit/prose/model';
2
2
  import type { EditorView, NodeView } from '@milkdown/kit/prose/view';
3
3
  import type { ImageAdapter } from './types.js';
4
+ import { type FileViewRuntime } from './files.js';
4
5
  /** A width rides in the alt text, as `![a picture|400](images/….png)`. Other Markdown readers
5
6
  * show the picture and keep the number without requiring a private extension. */
6
7
  export declare function splitAlt(alt: string): {
@@ -8,26 +9,41 @@ export declare function splitAlt(alt: string): {
8
9
  width: number | null;
9
10
  };
10
11
  export declare function joinAlt(alt: string, width: number | null): string;
11
- /** Draws the image and the handle that sizes it. The width is kept in the alt text, so the schema is the
12
- * one commonmark gives us; a picture is never wider than its editor, whatever number the alt text holds. */
13
12
  declare class ImageView implements NodeView {
14
13
  private node;
15
14
  private readonly view;
16
15
  private readonly getPos;
17
16
  private readonly adapter;
17
+ private readonly runtime?;
18
+ private readonly canMutate;
18
19
  dom: HTMLElement;
19
20
  private image?;
21
+ private media?;
22
+ private pdf?;
20
23
  private width;
21
24
  private stopResize?;
22
- constructor(node: ProseNode, view: EditorView, getPos: () => number | undefined, adapter: ImageAdapter | undefined);
25
+ private resolution?;
26
+ private previewFailed;
27
+ constructor(node: ProseNode, view: EditorView, getPos: () => number | undefined, adapter: ImageAdapter | undefined, runtime?: FileViewRuntime | undefined, canMutate?: () => boolean);
28
+ private reference;
29
+ private active;
23
30
  update(node: ProseNode): boolean;
24
31
  stopEvent(event: Event): boolean;
25
32
  ignoreMutation(): boolean;
33
+ private mediaPolicy;
26
34
  private cancelResize;
35
+ private releaseMedia;
27
36
  destroy(): void;
37
+ private button;
38
+ private activate;
39
+ private report;
40
+ private card;
41
+ private failPreview;
28
42
  private render;
43
+ private renderImage;
29
44
  private startResize;
30
45
  private commit;
46
+ private canResize;
31
47
  }
32
- export declare function imageView(adapter?: ImageAdapter): import("@milkdown/kit/utils").$View<import("@milkdown/kit/utils").$Node, (node: ProseNode, view: EditorView, getPos: () => number | undefined) => ImageView>;
48
+ export declare function imageView(adapter?: ImageAdapter, runtime?: FileViewRuntime, canMutate?: () => boolean): import("@milkdown/kit/utils").$View<import("@milkdown/kit/utils").$Node, (node: ProseNode, view: EditorView, getPos: () => number | undefined) => ImageView>;
33
49
  export {};
package/dist/images.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { imageSchema } from '@milkdown/kit/preset/commonmark';
2
+ import { closeHistory } from '@milkdown/kit/prose/history';
2
3
  import { $view } from '@milkdown/kit/utils';
4
+ import { FileResolution } from './files.js';
5
+ import { fileReference } from './linked-syntax.js';
3
6
  /** A width rides in the alt text, as `![a picture|400](images/….png)`. Other Markdown readers
4
7
  * show the picture and keep the number without requiring a private extension. */
5
8
  export function splitAlt(alt) {
@@ -12,75 +15,274 @@ export function joinAlt(alt, width) {
12
15
  return width == null ? alt : `${alt}|${width}`;
13
16
  }
14
17
  const minimumWidth = 48;
15
- /** Draws the image and the handle that sizes it. The width is kept in the alt text, so the schema is the
16
- * one commonmark gives us; a picture is never wider than its editor, whatever number the alt text holds. */
17
18
  class ImageView {
18
19
  node;
19
20
  view;
20
21
  getPos;
21
22
  adapter;
23
+ runtime;
24
+ canMutate;
22
25
  dom;
23
26
  image;
27
+ media;
28
+ pdf;
24
29
  width = null;
25
30
  stopResize;
26
- constructor(node, view, getPos, adapter) {
31
+ resolution;
32
+ previewFailed = false;
33
+ constructor(node, view, getPos, adapter, runtime, canMutate = () => true) {
27
34
  this.node = node;
28
35
  this.view = view;
29
36
  this.getPos = getPos;
30
37
  this.adapter = adapter;
38
+ this.runtime = runtime;
39
+ this.canMutate = canMutate;
31
40
  this.dom = document.createElement('span');
32
41
  this.dom.className = 'image';
33
42
  this.view.dom.addEventListener('inkkit-cancel-resize', this.cancelResize);
43
+ this.view.dom.addEventListener('inkkit-media-policy', this.mediaPolicy);
44
+ if (runtime) {
45
+ this.resolution = new FileResolution({ ...runtime, active: () => this.active() }, () => {
46
+ this.previewFailed = false;
47
+ this.render();
48
+ });
49
+ this.resolution.update(this.reference());
50
+ }
34
51
  this.render();
35
52
  }
53
+ reference() {
54
+ return fileReference(this.node);
55
+ }
56
+ active() {
57
+ return (!!this.runtime?.active() &&
58
+ !this.dom.closest('[data-inkkit-folded="true"], [hidden]'));
59
+ }
36
60
  update(node) {
37
61
  if (node.type !== this.node.type)
38
62
  return false;
63
+ if (node.sameMarkup(this.node)) {
64
+ this.node = node;
65
+ return true;
66
+ }
39
67
  this.node = node;
68
+ this.previewFailed = false;
69
+ this.resolution?.update(this.reference());
40
70
  this.render();
41
71
  return true;
42
72
  }
43
- /// The handle's own events are the view's business, not the document's.
44
73
  stopEvent(event) {
45
- return (event.target instanceof HTMLElement &&
46
- event.target.classList.contains('image-handle'));
74
+ return (event.target instanceof Element &&
75
+ !!event.target.closest('.image-handle, .inkkit-file-control, audio, video, iframe'));
47
76
  }
48
77
  ignoreMutation() {
49
78
  return true;
50
79
  }
80
+ mediaPolicy = () => {
81
+ if (!this.runtime)
82
+ return;
83
+ if (!this.active()) {
84
+ this.stopResize?.();
85
+ this.stopResize = undefined;
86
+ this.resolution?.cancel();
87
+ this.releaseMedia();
88
+ this.render();
89
+ }
90
+ else
91
+ this.resolution?.update(this.reference());
92
+ };
51
93
  cancelResize = () => {
94
+ if (!this.stopResize)
95
+ return;
52
96
  this.stopResize?.();
53
97
  this.stopResize = undefined;
54
98
  this.render();
55
99
  };
100
+ releaseMedia() {
101
+ const media = this.media;
102
+ this.media = undefined;
103
+ if (media) {
104
+ media.pause();
105
+ media.removeAttribute('src');
106
+ media.load();
107
+ }
108
+ this.pdf?.removeAttribute('src');
109
+ this.pdf = undefined;
110
+ }
56
111
  destroy() {
57
112
  this.view.dom.removeEventListener('inkkit-cancel-resize', this.cancelResize);
113
+ this.view.dom.removeEventListener('inkkit-media-policy', this.mediaPolicy);
58
114
  this.stopResize?.();
59
115
  this.stopResize = undefined;
116
+ this.resolution?.destroy();
117
+ this.releaseMedia();
118
+ }
119
+ button(label, action) {
120
+ const button = document.createElement('button');
121
+ button.type = 'button';
122
+ button.className = 'inkkit-file-control';
123
+ button.textContent = label;
124
+ button.addEventListener('click', (event) => {
125
+ event.preventDefault();
126
+ action();
127
+ });
128
+ return button;
129
+ }
130
+ activate = () => {
131
+ if (!this.runtime || !this.active())
132
+ return;
133
+ const captured = this.runtime.capture();
134
+ try {
135
+ if (captured.isCurrent())
136
+ this.runtime.adapter.open?.(this.reference(), captured.context);
137
+ }
138
+ catch (error) {
139
+ this.report(error);
140
+ }
141
+ };
142
+ report(error) {
143
+ this.runtime?.error?.(error instanceof Error ? error : new Error(String(error)));
144
+ }
145
+ card(label, status, retry = false) {
146
+ const text = document.createElement('span');
147
+ text.className = 'inkkit-file-label';
148
+ text.textContent = status ? `${label}: ${status}` : label;
149
+ if (status)
150
+ text.setAttribute('role', 'status');
151
+ this.dom.append(text);
152
+ if (retry && this.runtime)
153
+ this.dom.append(this.button(this.runtime.labels.fileRetry, () => this.resolution?.retry()));
154
+ if (this.runtime?.adapter.open) {
155
+ const open = this.button(this.runtime.labels.fileOpen, this.activate);
156
+ open.setAttribute('aria-label', `${this.runtime.labels.fileOpen} ${label}`);
157
+ this.dom.append(open);
158
+ }
159
+ }
160
+ failPreview() {
161
+ this.previewFailed = true;
162
+ this.runtime?.error?.(new Error(this.runtime.labels.filePreviewUnavailable));
163
+ this.render();
60
164
  }
61
165
  render() {
62
166
  this.stopResize?.();
63
167
  this.stopResize = undefined;
64
- const src = String(this.node.attrs.src ?? '');
65
- const { alt, width } = splitAlt(String(this.node.attrs.alt ?? ''));
168
+ this.releaseMedia();
169
+ const reference = this.reference();
170
+ const { alt, width } = this.node.attrs.inkkitFileRaw
171
+ ? {
172
+ alt: reference.label ?? reference.reference,
173
+ width: reference.width ?? null,
174
+ }
175
+ : splitAlt(String(this.node.attrs.alt ?? ''));
66
176
  this.width = width;
67
177
  this.dom.textContent = '';
68
178
  this.image = undefined;
69
- // Only the host adapter decides which authored references can be presented.
70
- const presentation = this.adapter?.presentation(src);
179
+ if (this.runtime) {
180
+ this.dom.classList.add('inkkit-file');
181
+ this.dom.oncontextmenu = (event) => {
182
+ if (!this.runtime?.adapter.contextMenu || !this.active())
183
+ return;
184
+ const captured = this.runtime.capture();
185
+ if (!captured.isCurrent())
186
+ return;
187
+ event.preventDefault();
188
+ try {
189
+ this.runtime.adapter.contextMenu(this.reference(), { clientX: event.clientX, clientY: event.clientY }, captured.context);
190
+ }
191
+ catch (error) {
192
+ this.report(error);
193
+ }
194
+ };
195
+ const state = this.resolution.state;
196
+ const label = state.status === 'resolved'
197
+ ? state.presentation.label || alt || reference.reference
198
+ : alt || reference.reference;
199
+ this.dom.setAttribute('aria-label', label);
200
+ this.dom.dataset.inkkitFileState = this.previewFailed
201
+ ? 'error'
202
+ : state.status;
203
+ this.dom.dataset.inkkitFileKind =
204
+ state.status === 'resolved' ? state.presentation.kind : 'file';
205
+ if (this.previewFailed) {
206
+ this.card(label, this.runtime.labels.filePreviewUnavailable, true);
207
+ return;
208
+ }
209
+ if (state.status === 'loading' || state.status === 'idle') {
210
+ this.card(label, this.runtime.labels.fileLoading, true);
211
+ return;
212
+ }
213
+ if (state.status === 'failed') {
214
+ this.card(label, this.runtime.labels.fileError, true);
215
+ return;
216
+ }
217
+ const presentation = state.presentation;
218
+ if (presentation.kind === 'missing' || presentation.kind === 'error') {
219
+ this.card(label, presentation.kind === 'missing'
220
+ ? this.runtime.labels.fileMissing
221
+ : this.runtime.labels.fileError, true);
222
+ }
223
+ else if (presentation.kind === 'image') {
224
+ this.renderImage(presentation.url, label, width);
225
+ this.card(label);
226
+ }
227
+ else if (presentation.kind === 'audio' ||
228
+ presentation.kind === 'video') {
229
+ const media = document.createElement(presentation.kind);
230
+ media.controls = true;
231
+ media.preload = 'metadata';
232
+ if (width != null)
233
+ media.style.width = `${width}px`;
234
+ media.setAttribute('aria-label', label);
235
+ media.addEventListener('error', () => {
236
+ if (this.media === media)
237
+ this.failPreview();
238
+ });
239
+ media.src = presentation.url;
240
+ this.media = media;
241
+ this.dom.append(media);
242
+ this.card(label);
243
+ }
244
+ else if (presentation.kind === 'pdf') {
245
+ const pdf = document.createElement('iframe');
246
+ pdf.title = label;
247
+ pdf.setAttribute('sandbox', '');
248
+ pdf.referrerPolicy = 'no-referrer';
249
+ if (width != null)
250
+ pdf.style.width = `${width}px`;
251
+ pdf.addEventListener('error', () => {
252
+ if (this.pdf === pdf)
253
+ this.failPreview();
254
+ });
255
+ pdf.src = presentation.url;
256
+ this.pdf = pdf;
257
+ this.dom.append(pdf);
258
+ this.card(label, this.runtime.labels.filePDFPreview);
259
+ }
260
+ else
261
+ this.card(label);
262
+ return;
263
+ }
264
+ const presentation = this.adapter?.presentation(String(this.node.attrs.src ?? ''));
71
265
  if (!presentation) {
72
266
  this.dom.classList.add('image-absent');
73
- this.dom.textContent = alt || src;
267
+ this.dom.textContent = alt || String(this.node.attrs.src ?? '');
74
268
  return;
75
269
  }
76
270
  this.dom.classList.remove('image-absent');
271
+ this.renderImage(presentation.url, alt, width);
272
+ }
273
+ renderImage(url, alt, width) {
77
274
  const image = document.createElement('img');
78
- image.src = presentation.url;
79
275
  image.alt = alt;
80
276
  if (this.node.attrs.title)
81
277
  image.title = String(this.node.attrs.title);
82
278
  if (width != null)
83
279
  image.style.width = `${width}px`;
280
+ if (this.runtime)
281
+ image.addEventListener('error', () => {
282
+ if (this.image === image)
283
+ this.failPreview();
284
+ });
285
+ image.src = url;
84
286
  this.image = image;
85
287
  const handle = document.createElement('span');
86
288
  handle.className = 'image-handle';
@@ -88,53 +290,68 @@ class ImageView {
88
290
  this.dom.append(image, handle);
89
291
  }
90
292
  startResize = (event) => {
293
+ event.preventDefault();
91
294
  const image = this.image;
92
- if (!image || !this.view.editable)
295
+ if (!image || !this.canResize())
93
296
  return;
94
- event.preventDefault();
95
297
  const handle = event.currentTarget;
96
298
  const startX = event.clientX;
97
299
  const startWidth = image.getBoundingClientRect().width;
98
300
  const limit = this.view.dom.clientWidth;
99
301
  handle.setPointerCapture?.(event.pointerId);
100
302
  const move = (moved) => {
101
- if (!this.view.editable) {
303
+ if (!this.canResize()) {
102
304
  this.cancelResize();
103
305
  return;
104
306
  }
105
- const next = Math.round(Math.min(limit, Math.max(minimumWidth, startWidth + (moved.clientX - startX))));
106
- this.width = next;
107
- image.style.width = `${next}px`;
307
+ this.width = Math.round(Math.min(limit, Math.max(minimumWidth, startWidth + moved.clientX - startX)));
308
+ image.style.width = `${this.width}px`;
108
309
  };
109
310
  const cleanup = () => {
110
311
  handle.removeEventListener('pointermove', move);
111
312
  handle.removeEventListener('pointerup', done);
112
- handle.removeEventListener('pointercancel', done);
313
+ handle.removeEventListener('pointercancel', cancel);
113
314
  };
114
315
  const done = () => {
115
316
  cleanup();
116
317
  this.stopResize = undefined;
117
318
  this.commit();
118
319
  };
320
+ const cancel = () => {
321
+ cleanup();
322
+ this.stopResize = undefined;
323
+ this.render();
324
+ };
119
325
  this.stopResize = cleanup;
120
326
  handle.addEventListener('pointermove', move);
121
327
  handle.addEventListener('pointerup', done);
122
- handle.addEventListener('pointercancel', done);
328
+ handle.addEventListener('pointercancel', cancel);
123
329
  };
124
330
  commit() {
125
- if (!this.view.editable)
331
+ if (!this.canResize()) {
332
+ this.render();
126
333
  return;
334
+ }
127
335
  const pos = this.getPos();
128
336
  if (pos == null)
129
337
  return;
130
- const { alt } = splitAlt(String(this.node.attrs.alt ?? ''));
338
+ const alt = this.node.attrs.inkkitFileRaw
339
+ ? (this.reference().label ?? this.reference().reference)
340
+ : splitAlt(String(this.node.attrs.alt ?? '')).alt;
131
341
  const next = joinAlt(alt, this.width);
132
342
  if (next === this.node.attrs.alt)
133
343
  return;
134
344
  const { state } = this.view;
135
- this.view.dispatch(state.tr.setNodeMarkup(pos, undefined, { ...this.node.attrs, alt: next }));
345
+ this.view.dispatch(closeHistory(state.tr.setNodeMarkup(pos, undefined, {
346
+ ...this.node.attrs,
347
+ alt: next,
348
+ })));
349
+ this.view.dispatch(closeHistory(this.view.state.tr));
350
+ }
351
+ canResize() {
352
+ return this.view.editable && !this.view.composing && this.canMutate();
136
353
  }
137
354
  }
138
- export function imageView(adapter) {
139
- return $view(imageSchema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter));
355
+ export function imageView(adapter, runtime, canMutate = () => true) {
356
+ return $view(imageSchema.node, () => (node, view, getPos) => new ImageView(node, view, getPos, adapter, runtime, canMutate));
140
357
  }
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export type { EditorEvents, EditorOptions, TextInputPreferences, CommandState, E
3
3
  export type { TableCommand, TableOptions } from './tables.js';
4
4
  export type { Heading } from './outline.js';
5
5
  export { InkKitError } from './types.js';
6
- export type { DocumentFormat, DocumentContext, DocumentInput, DocumentSnapshot, EditorErrorCode, ImageAdapter, CapturedImage, PortableImage, ClipboardInput, ClipboardOutput, ClipboardImage, ClipboardDiagram, PrintableDocument, PrintableWarning, } from './types.js';
6
+ export type { DocumentFormat, DocumentContext, DocumentInput, DocumentSnapshot, EditorErrorCode, ImageAdapter, WikiLinkReference, WikiLinkAdapter, FileReference, FileContext, FilePresentation, FileAdapter, CapturedImage, PortableImage, ClipboardInput, ClipboardOutput, ClipboardImage, ClipboardDiagram, PrintableDocument, PrintableWarning, TextRange, ReadableTextSnapshot, TextRect, ViewportInsets, ViewportOptions, ViewportSnapshot, } from './types.js';
7
7
  export type { EditorLabels } from './labels.js';
package/dist/labels.d.ts CHANGED
@@ -22,6 +22,14 @@ export interface EditorLabels {
22
22
  tableAlignRight: string;
23
23
  tableMoveColumn: string;
24
24
  tableMoveRow: string;
25
+ fileLoading: string;
26
+ fileMissing: string;
27
+ fileError: string;
28
+ fileRetry: string;
29
+ fileOpen: string;
30
+ filePreviewUnavailable: string;
31
+ filePDFPreview: string;
32
+ wikiMissing: string;
25
33
  }
26
34
  export declare const defaultLabels: Readonly<EditorLabels>;
27
35
  export declare const labelsCtx: import("@milkdown/kit/utils").$Ctx<Readonly<EditorLabels>, "inkkitLabels">;
package/dist/labels.js CHANGED
@@ -19,6 +19,14 @@ export const defaultLabels = {
19
19
  tableAlignRight: 'Align right',
20
20
  tableMoveColumn: 'Move column',
21
21
  tableMoveRow: 'Move row',
22
+ fileLoading: 'Loading file…',
23
+ fileMissing: 'File unavailable',
24
+ fileError: 'File could not be loaded',
25
+ fileRetry: 'Retry',
26
+ fileOpen: 'Open',
27
+ filePreviewUnavailable: 'Preview unavailable',
28
+ filePDFPreview: 'Open to view this PDF',
29
+ wikiMissing: 'Link unavailable',
22
30
  };
23
31
  export const labelsCtx = $ctx(defaultLabels, 'inkkitLabels');
24
32
  export function editorLabels(ctx) {