omni-viewer-core 0.3.0 → 0.5.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/README.md +28 -0
- package/dist/host/index.d.ts +14 -2
- package/dist/i18n/catalog.en.js +8 -0
- package/dist/i18n/catalog.ja.js +9 -1
- package/dist/i18n/catalog.ko.js +8 -0
- package/dist/i18n/catalog.zh-cn.js +8 -1
- package/dist/parsers/pptx/index.d.ts +1 -0
- package/dist/parsers/pptx/index.js +1 -1
- package/dist/parsers/yaml/index.js +14 -2
- package/dist/parsers/yaml/self-loading.js +26 -14
- package/dist/registry/index.js +1 -1
- package/dist/styles/archive.css +1 -1
- package/dist/styles/pdf.css +26 -0
- package/dist/styles/toml.css +1 -1
- package/dist/styles/yaml.css +1 -1
- package/dist/viewers/archive/index.js +60 -11
- package/dist/viewers/archive/styles.d.ts +1 -1
- package/dist/viewers/archive/styles.js +1 -1
- package/dist/viewers/json/controller.js +13 -24
- package/dist/viewers/json/index.js +5 -1
- package/dist/viewers/markdown/index.d.ts +3 -2
- package/dist/viewers/markdown/index.js +29 -14
- package/dist/viewers/pdf/controller.d.ts +14 -2
- package/dist/viewers/pdf/controller.js +28 -10
- package/dist/viewers/pdf/index.d.ts +79 -8
- package/dist/viewers/pdf/index.js +363 -81
- package/dist/viewers/pdf/self-loading.d.ts +3 -3
- package/dist/viewers/pdf/styles.d.ts +1 -1
- package/dist/viewers/pdf/styles.js +26 -0
- package/dist/viewers/ppt/index.js +3 -3
- package/dist/viewers/structured-styles.d.ts +1 -1
- package/dist/viewers/structured-styles.js +1 -1
- package/dist/viewers/structured.d.ts +1 -0
- package/dist/viewers/structured.js +5 -3
- package/dist/viewers/yaml/controller.js +48 -5
- package/package.json +2 -1
|
@@ -246,10 +246,13 @@ export async function mountJsonViewer(input, container, ctx, options = {}) {
|
|
|
246
246
|
copyResultBtn.disabled = true;
|
|
247
247
|
copyResultBtn.title = t('common.noClipboard');
|
|
248
248
|
}
|
|
249
|
+
const replaceResultBtn = el('button', undefined, t('json.result.replace'));
|
|
250
|
+
replaceResultBtn.type = 'button';
|
|
251
|
+
on(replaceResultBtn, 'click', () => controller.dispatch({ type: 'apply-result-to-editor' }));
|
|
249
252
|
const closeResultBtn = el('button', undefined, t('json.result.close'));
|
|
250
253
|
closeResultBtn.type = 'button';
|
|
251
254
|
on(closeResultBtn, 'click', () => controller.dispatch({ type: 'dismiss-result' }));
|
|
252
|
-
resultActions.append(copyResultBtn, closeResultBtn);
|
|
255
|
+
resultActions.append(copyResultBtn, replaceResultBtn, closeResultBtn);
|
|
253
256
|
resultPanel.append(resultTitle, resultOutput, resultMarkup, resultTableWrap, resultActions);
|
|
254
257
|
frame.append(toolbar, statusBar, diagnosticsBar, resultPanel, body);
|
|
255
258
|
root.appendChild(frame);
|
|
@@ -475,6 +478,7 @@ export async function mountJsonViewer(input, container, ctx, options = {}) {
|
|
|
475
478
|
if (isMarkup)
|
|
476
479
|
renderMarkupResult(state.toolResult.action, state.toolResult.output);
|
|
477
480
|
copyResultBtn.disabled = !clipboard || !!state.toolResult.error;
|
|
481
|
+
replaceResultBtn.disabled = !!state.toolResult.error;
|
|
478
482
|
}
|
|
479
483
|
else {
|
|
480
484
|
resultPanel.style.display = 'none';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClipboardService, DocumentAssetsService, FileWritebackService, HostContext, NavigationService } from '../../host/index.js';
|
|
1
|
+
import type { ClipboardService, DocumentAssetsService, FileSaveService, FileWritebackService, HostContext, NavigationService } from '../../host/index.js';
|
|
2
2
|
import { type MarkdownParseOptions } from '../../parsers/markdown/index.js';
|
|
3
3
|
import type { ResourceLimits } from '../../parsers/types.js';
|
|
4
4
|
import { type MountOptions, type ViewerHandle, type ViewerInput } from '../types.js';
|
|
@@ -12,7 +12,7 @@ export declare const MARKDOWN_VIEWER_META: {
|
|
|
12
12
|
extensions: string[];
|
|
13
13
|
priority: number;
|
|
14
14
|
requiredServices: readonly [];
|
|
15
|
-
optionalServices: readonly ["clipboard", "navigation", "documentAssets", "writeback"];
|
|
15
|
+
optionalServices: readonly ["clipboard", "navigation", "documentAssets", "writeback", "save"];
|
|
16
16
|
inputOwnership: "borrows";
|
|
17
17
|
};
|
|
18
18
|
export interface MarkdownRenderer {
|
|
@@ -56,6 +56,7 @@ export type MarkdownViewerContext = HostContext & {
|
|
|
56
56
|
navigation?: NavigationService;
|
|
57
57
|
documentAssets?: DocumentAssetsService;
|
|
58
58
|
writeback?: FileWritebackService;
|
|
59
|
+
save?: FileSaveService;
|
|
59
60
|
};
|
|
60
61
|
export interface MarkdownMountOptions extends MountOptions {
|
|
61
62
|
limits?: ResourceLimits;
|
|
@@ -12,7 +12,7 @@ export const MARKDOWN_VIEWER_META = {
|
|
|
12
12
|
id: 'markdown', displayNameKey: 'markdown.title',
|
|
13
13
|
extensions: ['md', 'markdown', 'mdown', 'mkdn', 'mkd'], priority: 10,
|
|
14
14
|
requiredServices: [],
|
|
15
|
-
optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback'],
|
|
15
|
+
optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback', 'save'],
|
|
16
16
|
inputOwnership: 'borrows'
|
|
17
17
|
};
|
|
18
18
|
const SANITIZE = {
|
|
@@ -323,22 +323,37 @@ export async function mountMarkdownViewer(input, container, ctx, deps, options =
|
|
|
323
323
|
}
|
|
324
324
|
};
|
|
325
325
|
const saveSource = async () => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
326
|
+
const bytes = new TextEncoder().encode(controller.state.source);
|
|
327
|
+
if (ctx.writeback) {
|
|
328
|
+
try {
|
|
329
|
+
await ctx.writeback.write(bytes);
|
|
330
|
+
controller.dispatch({ type: 'mark-saved' });
|
|
331
|
+
setStatus('common.savedToOriginal', 'valid');
|
|
332
|
+
sourceCaption.textContent = t('common.savedToOriginal');
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
ctx.logger.log('error', `markdown save failed: ${String(error)}`);
|
|
336
|
+
setStatus('common.saveFailed', 'invalid');
|
|
337
|
+
showMessage(errorText(error, t('common.saveFailed')));
|
|
338
|
+
}
|
|
329
339
|
return;
|
|
330
340
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
if (ctx.save) {
|
|
342
|
+
// Download fallback saves a copy, not the original — the dirty
|
|
343
|
+
// state is intentionally kept (no mark-saved).
|
|
344
|
+
try {
|
|
345
|
+
await ctx.save.saveFile(input.fileName, bytes, 'text/markdown');
|
|
346
|
+
setStatus('common.savedToOriginal', 'valid');
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
ctx.logger.log('error', `markdown save failed: ${String(error)}`);
|
|
350
|
+
setStatus('common.saveFailed', 'invalid');
|
|
351
|
+
showMessage(errorText(error, t('common.saveFailed')));
|
|
352
|
+
}
|
|
353
|
+
return;
|
|
341
354
|
}
|
|
355
|
+
showMessage(t('common.noWriteback'));
|
|
356
|
+
setStatus('common.saveFailed', 'invalid');
|
|
342
357
|
};
|
|
343
358
|
const copy = async (value, successKey) => {
|
|
344
359
|
if (!ctx.clipboard)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
export declare const PDF_ZOOM_LEVELS: readonly [50, 75, 100, 125, 150, 200, 300];
|
|
1
|
+
export declare const PDF_ZOOM_LEVELS: readonly [50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300];
|
|
2
|
+
export declare const PDF_MIN_ZOOM = 25;
|
|
3
|
+
export declare const PDF_MAX_ZOOM = 400;
|
|
2
4
|
export type PdfZoom = number;
|
|
3
5
|
export interface PdfTextAnnotation {
|
|
4
6
|
id: string;
|
|
@@ -109,12 +111,22 @@ export type PdfAction = {
|
|
|
109
111
|
};
|
|
110
112
|
export interface PdfController {
|
|
111
113
|
readonly state: PdfViewState;
|
|
114
|
+
/** Ordered button steps used by zoom-in/zoom-out. */
|
|
115
|
+
readonly zoomLevels: readonly number[];
|
|
116
|
+
readonly minZoom: number;
|
|
117
|
+
readonly maxZoom: number;
|
|
112
118
|
dispatch(action: PdfAction): void;
|
|
113
119
|
subscribe(listener: (state: PdfViewState) => void): () => void;
|
|
114
120
|
}
|
|
121
|
+
export interface PdfControllerOptions {
|
|
122
|
+
/** Custom button steps. Fit-to-width/height may still set an intermediate value. */
|
|
123
|
+
zoomLevels?: readonly number[];
|
|
124
|
+
minZoom?: number;
|
|
125
|
+
maxZoom?: number;
|
|
126
|
+
}
|
|
115
127
|
/** Persisted layer used to restore an editing session (sidecar rehydration). */
|
|
116
128
|
export interface PdfControllerSeed {
|
|
117
129
|
pageOrder?: readonly number[];
|
|
118
130
|
annotations?: readonly PdfAnnotation[];
|
|
119
131
|
}
|
|
120
|
-
export declare function createPdfController(pageCount: number, seed?: PdfControllerSeed): PdfController;
|
|
132
|
+
export declare function createPdfController(pageCount: number, seed?: PdfControllerSeed, options?: PdfControllerOptions): PdfController;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export const PDF_ZOOM_LEVELS = [
|
|
1
|
+
export const PDF_ZOOM_LEVELS = [
|
|
2
|
+
50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300
|
|
3
|
+
];
|
|
4
|
+
export const PDF_MIN_ZOOM = 25;
|
|
5
|
+
export const PDF_MAX_ZOOM = 400;
|
|
2
6
|
/**
|
|
3
7
|
* PDF.js commonly returns one client rect per text span, even for a single
|
|
4
8
|
* continuous selection. Join neighbouring spans on the same text line so
|
|
@@ -41,8 +45,18 @@ export function mergeHighlightRects(rects) {
|
|
|
41
45
|
return merged;
|
|
42
46
|
});
|
|
43
47
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
48
|
+
function normalizedZoomOptions(options) {
|
|
49
|
+
const requestedMin = Number.isFinite(options.minZoom) ? Math.round(options.minZoom) : PDF_MIN_ZOOM;
|
|
50
|
+
const requestedMax = Number.isFinite(options.maxZoom) ? Math.round(options.maxZoom) : PDF_MAX_ZOOM;
|
|
51
|
+
const min = Math.max(1, Math.min(requestedMin, requestedMax));
|
|
52
|
+
const max = Math.max(min, Math.max(requestedMin, requestedMax));
|
|
53
|
+
const source = options.zoomLevels ?? PDF_ZOOM_LEVELS;
|
|
54
|
+
const levels = [...new Set(source
|
|
55
|
+
.filter(Number.isFinite)
|
|
56
|
+
.map((level) => Math.round(level))
|
|
57
|
+
.filter((level) => level >= min && level <= max))]
|
|
58
|
+
.sort((a, b) => a - b);
|
|
59
|
+
return { levels: levels.length > 0 ? levels : [min, max], min, max };
|
|
46
60
|
}
|
|
47
61
|
/** Highest `pdf-a<n>` suffix among seeded ids, so new ids never collide. */
|
|
48
62
|
function maxSequence(annotations) {
|
|
@@ -59,10 +73,11 @@ function copyAnnotations(annotations) {
|
|
|
59
73
|
? { ...annotation, rects: annotation.rects.map((rect) => ({ ...rect })) }
|
|
60
74
|
: { ...annotation });
|
|
61
75
|
}
|
|
62
|
-
export function createPdfController(pageCount, seed) {
|
|
76
|
+
export function createPdfController(pageCount, seed, options = {}) {
|
|
63
77
|
const original = Array.from({ length: Math.max(0, Math.floor(pageCount)) }, (_, i) => i + 1);
|
|
64
78
|
const listeners = new Set();
|
|
65
|
-
|
|
79
|
+
const zoomOptions = normalizedZoomOptions(options);
|
|
80
|
+
let zoom = Math.max(zoomOptions.min, Math.min(zoomOptions.max, 100));
|
|
66
81
|
// A seeded order may only reference pages that still exist; an empty result
|
|
67
82
|
// falls back to the natural order so the document is never left blank.
|
|
68
83
|
const seededOrder = seed?.pageOrder?.filter((page) => page >= 1 && page <= original.length) ?? [];
|
|
@@ -95,6 +110,9 @@ export function createPdfController(pageCount, seed) {
|
|
|
95
110
|
savedSignature = signature();
|
|
96
111
|
return {
|
|
97
112
|
get state() { return state(); },
|
|
113
|
+
zoomLevels: zoomOptions.levels,
|
|
114
|
+
minZoom: zoomOptions.min,
|
|
115
|
+
maxZoom: zoomOptions.max,
|
|
98
116
|
subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
|
|
99
117
|
dispatch(action) {
|
|
100
118
|
if (action.type === 'undo') {
|
|
@@ -125,19 +143,19 @@ export function createPdfController(pageCount, seed) {
|
|
|
125
143
|
const beforeSignature = tracksHistory ? signature() : undefined;
|
|
126
144
|
switch (action.type) {
|
|
127
145
|
case 'zoom-in': {
|
|
128
|
-
zoom =
|
|
129
|
-
??
|
|
146
|
+
zoom = zoomOptions.levels.find((level) => level > zoom)
|
|
147
|
+
?? zoomOptions.max;
|
|
130
148
|
break;
|
|
131
149
|
}
|
|
132
150
|
case 'zoom-out': {
|
|
133
|
-
zoom = [...
|
|
134
|
-
??
|
|
151
|
+
zoom = [...zoomOptions.levels].reverse().find((level) => level < zoom)
|
|
152
|
+
?? zoomOptions.min;
|
|
135
153
|
break;
|
|
136
154
|
}
|
|
137
155
|
case 'set-zoom': {
|
|
138
156
|
if (!Number.isFinite(action.zoom))
|
|
139
157
|
return;
|
|
140
|
-
zoom = Math.max(
|
|
158
|
+
zoom = Math.max(zoomOptions.min, Math.min(zoomOptions.max, Math.round(action.zoom)));
|
|
141
159
|
break;
|
|
142
160
|
}
|
|
143
161
|
case 'reorder-pages': {
|
|
@@ -1,12 +1,73 @@
|
|
|
1
|
-
import type { FilePickService, FileSaveService, FileWritebackService, HostContext } from '../../host/index.js';
|
|
1
|
+
import type { FilePickService, FileSaveResult, FileSaveService, FileWritebackService, HostContext } from '../../host/index.js';
|
|
2
2
|
import { type MountOptions, type ViewerHandle, type ViewerInput } from '../types.js';
|
|
3
|
-
import { type PdfController } from './controller.js';
|
|
3
|
+
import { type PdfController, type PdfControllerOptions, type PdfViewState } from './controller.js';
|
|
4
4
|
import { type PdfLibModule } from './editing.js';
|
|
5
|
-
export { createPdfController, PDF_ZOOM_LEVELS } from './controller.js';
|
|
6
|
-
export type { PdfController, PdfAction, PdfViewState, PdfAnnotation } from './controller.js';
|
|
5
|
+
export { createPdfController, PDF_MAX_ZOOM, PDF_MIN_ZOOM, PDF_ZOOM_LEVELS } from './controller.js';
|
|
6
|
+
export type { PdfController, PdfControllerOptions, PdfAction, PdfViewState, PdfAnnotation } from './controller.js';
|
|
7
7
|
export { pdfViewerCss } from './styles.js';
|
|
8
|
-
export { buildEditedPdf, mergePdfBytes, savedPdfName } from './editing.js';
|
|
9
|
-
export type { PdfLibModule } from './editing.js';
|
|
8
|
+
export { buildEditedPdf, buildSavedPdf, mergePdfBytes, parseLayer, savedPdfName, SIDECAR_BASE_NAME, SIDECAR_LAYER_NAME } from './editing.js';
|
|
9
|
+
export type { ParsedLayer, PdfLibModule } from './editing.js';
|
|
10
|
+
/** Asset key passed to `HostContext.assets.resolveAssetUrl` by default. */
|
|
11
|
+
export declare const PDF_WORKER_ASSET_KEY = "assets/pdfjs/pdf.worker.min.mjs";
|
|
12
|
+
export type PdfSaveMode = 'hybrid' | 'flattened';
|
|
13
|
+
export type PdfProcessingMode = 'auto' | 'host' | 'browser';
|
|
14
|
+
export interface PdfToolbarAction {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
ariaLabel?: string;
|
|
19
|
+
disabled?: boolean | (() => boolean);
|
|
20
|
+
onClick(): void | Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
export interface PdfProcessingControl {
|
|
23
|
+
signal: AbortSignal;
|
|
24
|
+
onProgress(progress: number | undefined): void;
|
|
25
|
+
}
|
|
26
|
+
/** Optional host/Worker implementation for byte-heavy PDF operations. */
|
|
27
|
+
export interface PdfProcessingService {
|
|
28
|
+
/** Request byte arrays are borrowed: implementations must not mutate or
|
|
29
|
+
* detach their buffers. A Worker adapter may copy/transfer internally. */
|
|
30
|
+
buildPdf?(request: {
|
|
31
|
+
source: Uint8Array;
|
|
32
|
+
state: PdfViewState;
|
|
33
|
+
mode: PdfSaveMode;
|
|
34
|
+
}, control: PdfProcessingControl): Promise<Uint8Array>;
|
|
35
|
+
mergePdfs?(request: {
|
|
36
|
+
first: Uint8Array;
|
|
37
|
+
second: Uint8Array;
|
|
38
|
+
}, control: PdfProcessingControl): Promise<Uint8Array>;
|
|
39
|
+
}
|
|
40
|
+
export type PdfOperationState = {
|
|
41
|
+
status: 'idle';
|
|
42
|
+
} | {
|
|
43
|
+
status: 'running';
|
|
44
|
+
kind: 'save' | 'save-as' | 'merge';
|
|
45
|
+
progress?: number;
|
|
46
|
+
} | {
|
|
47
|
+
status: 'succeeded';
|
|
48
|
+
kind: 'save' | 'save-as' | 'merge';
|
|
49
|
+
} | {
|
|
50
|
+
status: 'failed';
|
|
51
|
+
kind: 'save' | 'save-as' | 'merge';
|
|
52
|
+
error: string;
|
|
53
|
+
} | {
|
|
54
|
+
status: 'cancelled';
|
|
55
|
+
kind: 'save' | 'save-as' | 'merge';
|
|
56
|
+
};
|
|
57
|
+
export interface PdfMountOptions extends MountOptions, PdfControllerOptions {
|
|
58
|
+
/** Defaults to `hybrid` for v0.4 compatibility. */
|
|
59
|
+
saveMode?: PdfSaveMode;
|
|
60
|
+
/** `auto` delegates when a service method exists, `browser` always uses pdf-lib. */
|
|
61
|
+
processingMode?: PdfProcessingMode;
|
|
62
|
+
/** Maximum merge input requested from FilePickService. */
|
|
63
|
+
maxMergeBytes?: number;
|
|
64
|
+
toolbarActions?: readonly PdfToolbarAction[];
|
|
65
|
+
/** Safe default for VS Code CSP is false. */
|
|
66
|
+
isEvalSupported?: boolean;
|
|
67
|
+
/** Host-provided worker URL. Omit to resolve {@link PDF_WORKER_ASSET_KEY}. */
|
|
68
|
+
workerSrc?: string;
|
|
69
|
+
onSaveAsComplete?(result: FileSaveResult | void): void | Promise<void>;
|
|
70
|
+
}
|
|
10
71
|
/** Viewer metadata — single source for the registry codegen (DESIGN.md §7). */
|
|
11
72
|
export declare const PDF_VIEWER_META: {
|
|
12
73
|
id: string;
|
|
@@ -58,12 +119,13 @@ export interface PdfJsDocument {
|
|
|
58
119
|
}
|
|
59
120
|
export interface PdfJsLoadingTask {
|
|
60
121
|
promise: Promise<PdfJsDocument>;
|
|
61
|
-
destroy(): void
|
|
122
|
+
destroy(): void | Promise<void>;
|
|
62
123
|
}
|
|
63
124
|
export interface PdfJsModule {
|
|
64
125
|
getDocument(options: {
|
|
65
126
|
data: Uint8Array;
|
|
66
127
|
password?: string;
|
|
128
|
+
isEvalSupported?: boolean;
|
|
67
129
|
}): PdfJsLoadingTask;
|
|
68
130
|
GlobalWorkerOptions: {
|
|
69
131
|
workerSrc: string;
|
|
@@ -80,9 +142,18 @@ export interface PdfViewerDeps {
|
|
|
80
142
|
/** Editing dependency (save / save-as / merge / annotation stamping).
|
|
81
143
|
* Absent -> editing controls disabled with a reason. */
|
|
82
144
|
loadPdfLib?(): Promise<PdfLibModule>;
|
|
145
|
+
/** Host/extension-host/Web Worker implementation for large byte operations. */
|
|
146
|
+
processing?: PdfProcessingService;
|
|
83
147
|
}
|
|
84
148
|
export interface PdfViewerHandle extends ViewerHandle {
|
|
85
149
|
readonly controller: PdfController;
|
|
150
|
+
readonly operation: PdfOperationState;
|
|
86
151
|
isDirty(): boolean;
|
|
152
|
+
cancelOperation(): void;
|
|
153
|
+
refreshToolbarActions(): void;
|
|
87
154
|
}
|
|
88
|
-
export declare function
|
|
155
|
+
export declare function containPdfSignatureSize(width: number, height: number, maxWidth?: number, maxHeight?: number): {
|
|
156
|
+
width: number;
|
|
157
|
+
height: number;
|
|
158
|
+
};
|
|
159
|
+
export declare function mountPdfViewer(input: ViewerInput, container: HTMLElement, ctx: PdfViewerContext, deps: PdfViewerDeps, options?: PdfMountOptions): Promise<PdfViewerHandle>;
|