omni-viewer-core 0.2.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/dist/parsers/pptx/index.d.ts +3 -1
- package/dist/parsers/pptx/index.js +5 -2
- package/dist/styles/archive.css +1 -1
- package/dist/styles/ppt.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/pdf/editing.d.ts +6 -4
- package/dist/viewers/pdf/editing.js +47 -13
- package/dist/viewers/pdf/index.js +11 -10
- package/dist/viewers/ppt/chart.js +22 -3
- package/dist/viewers/ppt/index.js +3 -3
- package/dist/viewers/ppt/render.js +23 -5
- package/dist/viewers/ppt/styles.d.ts +1 -1
- package/dist/viewers/ppt/styles.js +1 -1
- package/package.json +1 -1
|
@@ -11,5 +11,7 @@ export interface PptxParserDeps {
|
|
|
11
11
|
openZip(data: Uint8Array, options?: ParseOptions): Promise<PptxZipReader>;
|
|
12
12
|
}
|
|
13
13
|
export { openPptxZip } from './zip-reader.js';
|
|
14
|
-
|
|
14
|
+
import { parsePptxVscode } from './vscode.js';
|
|
15
|
+
export { parsePptxVscode };
|
|
16
|
+
export declare function parsePptxLegacy(input: Uint8Array, deps: PptxParserDeps, options?: ParseOptions): Promise<ParseOutcome<SlideDeck>>;
|
|
15
17
|
export declare function parsePptx(input: Uint8Array, deps: PptxParserDeps, options?: ParseOptions): Promise<ParseOutcome<SlideDeck>>;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { DEFAULT_LIMITS, LimitTracker } from '../types.js';
|
|
3
3
|
export * from '../slide-model.js';
|
|
4
4
|
export { openPptxZip } from './zip-reader.js';
|
|
5
|
-
|
|
5
|
+
import { parsePptxVscode } from './vscode.js';
|
|
6
|
+
export { parsePptxVscode };
|
|
6
7
|
const EMU_PER_PX = 9525;
|
|
7
8
|
const DEFAULT_SIZE = { widthPx: 1280, heightPx: 720 };
|
|
8
9
|
const attr = (tag, name) => new RegExp(`(?:^|\\s)${name}="([^"]*)"`).exec(tag)?.[1];
|
|
@@ -54,7 +55,7 @@ async function parseSlide(zip, path, n, size) { const xml = await zip.text(path)
|
|
|
54
55
|
else
|
|
55
56
|
elements.push({ ...common, type: 'shape', presetGeom: geom, fillColor: color(block, 'transparent'), borderColor: color(/<a:ln\b[\s\S]*?<\/a:ln>/.exec(block)?.[0] ?? '') });
|
|
56
57
|
} return { slideNumber: n, ...size, backgroundColor: color(/<p:bg\b[\s\S]*?<\/p:bg>/.exec(xml)?.[0] ?? '', '#ffffff') ?? '#ffffff', elements }; }
|
|
57
|
-
export async function
|
|
58
|
+
export async function parsePptxLegacy(input, deps, options = {}) { const started = Date.now(), diagnostics = []; if (input.byteLength > (options.limits?.maxInputBytes ?? 50 * 1024 * 1024))
|
|
58
59
|
return { result: { status: 'failed', failure: { code: 'limit-exceeded', retryable: false, messageKey: 'diag.limit-exceeded.input' }, diagnostics }, execution: { workerUsed: false, hardLimitEnforced: false, elapsedMillis: Date.now() - started } }; let zip; try {
|
|
59
60
|
zip = await deps.openZip(input, options);
|
|
60
61
|
const presentation = await zip.text('ppt/presentation.xml');
|
|
@@ -86,3 +87,5 @@ catch {
|
|
|
86
87
|
finally {
|
|
87
88
|
await zip?.close?.();
|
|
88
89
|
} }
|
|
90
|
+
export async function parsePptx(input, deps, options = {}) { const full = await parsePptxVscode(input, options); if (full.result.status !== 'failed' || full.result.failure.code !== 'invalid-format')
|
|
91
|
+
return full; return parsePptxLegacy(input, deps, options); }
|
package/dist/styles/archive.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}
|
|
5
5
|
.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}
|
|
6
6
|
.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}
|
|
7
|
-
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
7
|
+
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
8
8
|
.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}
|
|
9
9
|
.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}
|
|
10
10
|
.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}
|
package/dist/styles/ppt.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset="cover-title"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset="cover-subtitle"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table{border-collapse:collapse;width:100%;
|
|
1
|
+
:host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;border-radius:6px;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{padding:2px;white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset="cover-title"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset="cover-subtitle"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table-frame{background:#fff;border:1px solid #bdbdbd;overflow:auto}.omni-ppt__table{border-collapse:collapse;width:100%;font-size:12px;color:#111}.omni-ppt__table th,.omni-ppt__table td{border:1px solid #d3d3d3;padding:4px 6px;vertical-align:top}.omni-ppt__chart{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;background:#f7f7f7;border:1px dashed #9a9a9a;color:#333}.omni-ppt__chart--rendered{background:transparent;border:0}.omni-ppt__chart-title{font-size:14px;font-weight:700;margin-bottom:4px}.omni-ppt__chart-subtitle{font-size:12px;opacity:.8}.omni-ppt__shape{width:100%;height:100%}.omni-ppt__empty{padding:32px;text-align:center}
|
|
@@ -294,20 +294,57 @@ export async function mountArchiveViewer(inputOrSource, container, ctx, depsOrOp
|
|
|
294
294
|
const ROW_HEIGHT = 43;
|
|
295
295
|
const OVERSCAN = 8;
|
|
296
296
|
const FALLBACK_VIEWPORT_HEIGHT = 600;
|
|
297
|
-
|
|
297
|
+
let lastStart = -1;
|
|
298
|
+
let lastEnd = -1;
|
|
299
|
+
let lastLength = -1;
|
|
300
|
+
let lastQuery;
|
|
301
|
+
let lastExpanded;
|
|
302
|
+
/** `force` covers state changes (search/select/expand) that must repaint the
|
|
303
|
+
* rows; scroll events pass `false` so an unchanged window is a no-op. */
|
|
304
|
+
const render = (force = true) => {
|
|
298
305
|
const visible = controller.visibleEntries();
|
|
306
|
+
// Measure before mutating: emptying the tbody first would collapse the
|
|
307
|
+
// scroll height, so the layout flush from reading clientHeight clamps
|
|
308
|
+
// scrollTop to 0 and every scroll snaps back to the top of the list.
|
|
309
|
+
const viewportHeight = tableWrap.clientHeight || FALLBACK_VIEWPORT_HEIGHT;
|
|
310
|
+
const scrollTop = tableWrap.scrollTop;
|
|
311
|
+
const start = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
|
|
312
|
+
const count = Math.ceil(viewportHeight / ROW_HEIGHT) + OVERSCAN * 2;
|
|
313
|
+
const end = Math.min(visible.length, start + count);
|
|
314
|
+
// Most scroll ticks land inside the overscan margin. Rebuilding the rows
|
|
315
|
+
// anyway would destroy the focused row on every tick, and the resulting
|
|
316
|
+
// focus restore plus scroll anchoring drags the viewport around.
|
|
317
|
+
// The rendered window is fully determined by these; the controller hands
|
|
318
|
+
// out a fresh `expanded` set per toggle, so identity comparison is enough.
|
|
319
|
+
const sameWindow = start === lastStart && end === lastEnd && visible.length === lastLength
|
|
320
|
+
&& controller.state.query === lastQuery && controller.state.expanded === lastExpanded;
|
|
321
|
+
// Most scroll ticks land inside the overscan margin. Rebuilding the rows
|
|
322
|
+
// anyway would destroy the focused row on every tick, and the resulting
|
|
323
|
+
// focus restore plus scroll anchoring drags the viewport around.
|
|
324
|
+
if (!force && sameWindow)
|
|
325
|
+
return;
|
|
326
|
+
// Selecting an entry only flips a class. Rebuilding every row for that is
|
|
327
|
+
// what threw the viewport back to the top the moment a row was clicked,
|
|
328
|
+
// so patch the existing rows in place and leave the scroll state alone.
|
|
329
|
+
if (sameWindow) {
|
|
330
|
+
for (const row of tbody.querySelectorAll('.omni-archive__entry'))
|
|
331
|
+
row.classList.toggle('is-selected', Number(row.dataset.entryId) === controller.state.selectedId);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
lastStart = start;
|
|
335
|
+
lastEnd = end;
|
|
336
|
+
lastLength = visible.length;
|
|
337
|
+
lastQuery = controller.state.query;
|
|
338
|
+
lastExpanded = controller.state.expanded;
|
|
299
339
|
visibleStat[1] = visible.length;
|
|
300
340
|
stats.querySelectorAll('.omni-archive__stat-value')[3].textContent = visible.length.toLocaleString();
|
|
301
|
-
tbody.replaceChildren();
|
|
302
341
|
empty.hidden = visible.length !== 0;
|
|
303
342
|
table.hidden = visible.length === 0;
|
|
304
|
-
const viewportHeight = tableWrap.clientHeight || FALLBACK_VIEWPORT_HEIGHT;
|
|
305
|
-
const start = Math.max(0, Math.floor(tableWrap.scrollTop / ROW_HEIGHT) - OVERSCAN);
|
|
306
|
-
const count = Math.ceil(viewportHeight / ROW_HEIGHT) + OVERSCAN * 2;
|
|
307
|
-
const end = Math.min(visible.length, start + count);
|
|
308
343
|
const spacer = (height) => { const row = element('tr', 'omni-archive__spacer'); const cell = element('td'); cell.colSpan = 5; cell.style.height = `${height}px`; row.append(cell); return row; };
|
|
344
|
+
// Build off-DOM and swap once, so the tbody is never laid out empty.
|
|
345
|
+
const rows = [];
|
|
309
346
|
if (start > 0)
|
|
310
|
-
|
|
347
|
+
rows.push(spacer(start * ROW_HEIGHT));
|
|
311
348
|
for (const entry of visible.slice(start, end)) {
|
|
312
349
|
const row = element('tr', `omni-archive__entry${controller.state.selectedId === entry.entryId ? ' is-selected' : ''}`);
|
|
313
350
|
row.tabIndex = 0;
|
|
@@ -320,14 +357,26 @@ export async function mountArchiveViewer(inputOrSource, container, ctx, depsOrOp
|
|
|
320
357
|
event.preventDefault();
|
|
321
358
|
void select(entry);
|
|
322
359
|
} });
|
|
323
|
-
|
|
360
|
+
rows.push(row);
|
|
324
361
|
}
|
|
325
362
|
if (end < visible.length)
|
|
326
|
-
|
|
363
|
+
rows.push(spacer((visible.length - end) * ROW_HEIGHT));
|
|
364
|
+
// Recycling rows destroys the focused one; hand focus back to its
|
|
365
|
+
// replacement so keyboard navigation survives a scroll.
|
|
366
|
+
const active = (typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.activeElement : document.activeElement);
|
|
367
|
+
const focusedId = active instanceof HTMLElement && tbody.contains(active) ? active.dataset.entryId : undefined;
|
|
368
|
+
tbody.replaceChildren(...rows);
|
|
369
|
+
// Swapping the rows can still make the browser adjust the offset (focus
|
|
370
|
+
// fixup, anchoring, a transient height change). The list is virtualized,
|
|
371
|
+
// so the pre-swap offset is authoritative — put it back.
|
|
372
|
+
if (tableWrap.scrollTop !== scrollTop)
|
|
373
|
+
tableWrap.scrollTop = scrollTop;
|
|
374
|
+
if (focusedId !== undefined)
|
|
375
|
+
rows.find(row => row.dataset.entryId === focusedId)?.focus({ preventScroll: true });
|
|
327
376
|
};
|
|
328
|
-
tableWrap.addEventListener('scroll', render, { passive: true });
|
|
377
|
+
tableWrap.addEventListener('scroll', () => render(false), { passive: true });
|
|
329
378
|
search.addEventListener('input', () => { tableWrap.scrollTop = 0; controller.dispatch({ type: 'set-search', query: search.value }); });
|
|
330
|
-
const unsubscribe = controller.subscribe(render);
|
|
379
|
+
const unsubscribe = controller.subscribe(() => render(true));
|
|
331
380
|
render();
|
|
332
381
|
return { dispose() { ticket++; extraction?.abort(); saveExtraction?.abort(); clearPreviewMedia(); unsubscribe(); wrap.remove(); void handle?.close(); handle = undefined; } };
|
|
333
382
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const archiveViewerCss = "\n.omni-viewer--archive{all:initial}.omni-viewer--archive :where(header,section,aside,div,h1,h2,p,span,label,input,table,thead,tbody,tr,th,td,pre){all:revert}\n:host,.omni-viewer--archive{display:block;min-height:100%;box-sizing:border-box;color:var(--omni-fg,#d4d4d4);background:radial-gradient(circle at top left,color-mix(in srgb,var(--omni-accent,#d97706) 14%,transparent),transparent 28%),var(--omni-bg,#1e1e1e);font:13px var(--omni-font,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif)}\n.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}\n.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}\n.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}\n.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}\n.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}\n.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}\n.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}\n@media(max-width:720px){.omni-viewer--archive{padding:16px 10px 24px}.omni-archive__hero{flex-direction:column}.omni-archive__pills{justify-content:flex-start}.omni-archive__table th:nth-child(n+3),.omni-archive__table td:nth-child(n+3){display:none}.omni-archive__workspace{grid-template-columns:1fr}.omni-archive__preview-panel{min-height:280px}}\n";
|
|
1
|
+
export declare const archiveViewerCss = "\n.omni-viewer--archive{all:initial}.omni-viewer--archive :where(header,section,aside,div,h1,h2,p,span,label,input,table,thead,tbody,tr,th,td,pre){all:revert}\n:host,.omni-viewer--archive{display:block;min-height:100%;box-sizing:border-box;color:var(--omni-fg,#d4d4d4);background:radial-gradient(circle at top left,color-mix(in srgb,var(--omni-accent,#d97706) 14%,transparent),transparent 28%),var(--omni-bg,#1e1e1e);font:13px var(--omni-font,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif)}\n.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}\n.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}\n.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}\n.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}\n.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}\n.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}\n.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}\n@media(max-width:720px){.omni-viewer--archive{padding:16px 10px 24px}.omni-archive__hero{flex-direction:column}.omni-archive__pills{justify-content:flex-start}.omni-archive__table th:nth-child(n+3),.omni-archive__table td:nth-child(n+3){display:none}.omni-archive__workspace{grid-template-columns:1fr}.omni-archive__preview-panel{min-height:280px}}\n";
|
|
@@ -4,7 +4,7 @@ export const archiveViewerCss = `
|
|
|
4
4
|
.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}
|
|
5
5
|
.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}
|
|
6
6
|
.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}
|
|
7
|
-
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
7
|
+
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
8
8
|
.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}
|
|
9
9
|
.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}
|
|
10
10
|
.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}
|
|
@@ -2,7 +2,7 @@ import { type PdfAnnotation, type PdfViewState } from './controller.js';
|
|
|
2
2
|
/** Type-only reference — erased at compile time. */
|
|
3
3
|
export type PdfLibModule = typeof import('pdf-lib');
|
|
4
4
|
export declare const SIDECAR_LAYER_NAME = "omni-viewer-layer.json";
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const SIDECAR_BASE_NAME = "omni-viewer-base.pdf";
|
|
6
6
|
/** Result of parsing a sidecar's layer JSON. */
|
|
7
7
|
export interface ParsedLayer {
|
|
8
8
|
pageOrder: number[];
|
|
@@ -14,9 +14,11 @@ export declare function buildEditedPdf(pdfLib: PdfLibModule, source: Uint8Array,
|
|
|
14
14
|
/**
|
|
15
15
|
* Like {@link buildEditedPdf}, but also embeds the hybrid sidecar so the
|
|
16
16
|
* omni viewer can rehydrate a removable overlay layer on reopen:
|
|
17
|
-
* -
|
|
18
|
-
* - the layer JSON `{ version, pageOrder, annotations }
|
|
19
|
-
*
|
|
17
|
+
* - a re-editable base (kept pages only, signatures flattened in), and
|
|
18
|
+
* - the layer JSON `{ version, pageOrder, annotations }` for text/markup,
|
|
19
|
+
* with page references remapped onto the kept-pages base.
|
|
20
|
+
* Deleted pages and signatures are unrecoverable; external readers only ever
|
|
21
|
+
* see the flattened pages.
|
|
20
22
|
*/
|
|
21
23
|
export declare function buildSavedPdf(pdfLib: PdfLibModule, pristine: Uint8Array, state: PdfViewState): Promise<Uint8Array>;
|
|
22
24
|
/** Parse and validate a sidecar layer JSON string; null if unusable. */
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
// imports). Callers inject the loaded module; `viewers/pdf/self-loading`
|
|
6
6
|
// provides the dynamic-import loader for platforms whose bundler resolves it.
|
|
7
7
|
import { mergeHighlightRects } from './controller.js';
|
|
8
|
-
// Hybrid sidecar: saved PDFs keep flattened (portable) pages
|
|
9
|
-
//
|
|
10
|
-
// removable overlay layer on reopen.
|
|
8
|
+
// Hybrid sidecar: saved PDFs keep flattened (portable) pages for external
|
|
9
|
+
// readers while embedding a re-editable base + a layer JSON, so the omni
|
|
10
|
+
// viewer can restore a removable overlay layer on reopen. The embedded base
|
|
11
|
+
// holds only the kept pages (deleted pages are gone for good) with signatures
|
|
12
|
+
// permanently flattened in; text/markup stay out so they remain editable.
|
|
13
|
+
// External readers only ever see the flat pages.
|
|
11
14
|
export const SIDECAR_LAYER_NAME = 'omni-viewer-layer.json';
|
|
12
|
-
export const
|
|
15
|
+
export const SIDECAR_BASE_NAME = 'omni-viewer-base.pdf';
|
|
13
16
|
/** Bumped when the sidecar shape changes; older/newer versions are ignored. */
|
|
14
|
-
const SIDECAR_VERSION =
|
|
17
|
+
const SIDECAR_VERSION = 2;
|
|
15
18
|
/** Defensive ceilings — the sidecar is untrusted input on reopen. */
|
|
16
19
|
const MAX_ANNOTATIONS = 2000;
|
|
17
20
|
const MAX_TEXT_LENGTH = 10_000;
|
|
@@ -104,16 +107,39 @@ async function stamp(pdfLib, page, annotation) {
|
|
|
104
107
|
height: annotation.height
|
|
105
108
|
});
|
|
106
109
|
}
|
|
107
|
-
/**
|
|
108
|
-
async function
|
|
110
|
+
/** Copy the kept pages (in display order) from `source` into a fresh doc. */
|
|
111
|
+
async function copyKeptPages(pdfLib, source, pageOrder) {
|
|
109
112
|
const original = await pdfLib.PDFDocument.load(source);
|
|
110
113
|
const output = await pdfLib.PDFDocument.create();
|
|
111
|
-
const indices =
|
|
114
|
+
const indices = pageOrder
|
|
112
115
|
.map((page) => page - 1)
|
|
113
116
|
.filter((page) => page >= 0 && page < original.getPageCount());
|
|
114
117
|
const copied = await output.copyPages(original, indices);
|
|
115
118
|
copied.forEach((page) => output.addPage(page));
|
|
119
|
+
return output;
|
|
120
|
+
}
|
|
121
|
+
/** Reorder/delete pages and stamp the overlays into a fresh (unsaved) doc. */
|
|
122
|
+
async function flattenInto(pdfLib, source, state) {
|
|
123
|
+
const output = await copyKeptPages(pdfLib, source, state.pageOrder);
|
|
124
|
+
for (const annotation of state.annotations) {
|
|
125
|
+
const outputIndex = state.pageOrder.indexOf(annotation.page);
|
|
126
|
+
const page = output.getPages()[outputIndex];
|
|
127
|
+
if (page)
|
|
128
|
+
await stamp(pdfLib, page, annotation);
|
|
129
|
+
}
|
|
130
|
+
return output;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The re-editable base embedded in a saved PDF: the kept pages (unflattened)
|
|
134
|
+
* with ONLY signatures permanently stamped in. Text/markup are deliberately
|
|
135
|
+
* left out so the omni viewer can restore them as a removable layer, while
|
|
136
|
+
* signatures — and any pages the user deleted — cannot be recovered.
|
|
137
|
+
*/
|
|
138
|
+
async function buildEditableBase(pdfLib, pristine, state) {
|
|
139
|
+
const output = await copyKeptPages(pdfLib, pristine, state.pageOrder);
|
|
116
140
|
for (const annotation of state.annotations) {
|
|
141
|
+
if (annotation.kind !== 'signature')
|
|
142
|
+
continue;
|
|
117
143
|
const outputIndex = state.pageOrder.indexOf(annotation.page);
|
|
118
144
|
const page = output.getPages()[outputIndex];
|
|
119
145
|
if (page)
|
|
@@ -130,21 +156,29 @@ export async function buildEditedPdf(pdfLib, source, state) {
|
|
|
130
156
|
/**
|
|
131
157
|
* Like {@link buildEditedPdf}, but also embeds the hybrid sidecar so the
|
|
132
158
|
* omni viewer can rehydrate a removable overlay layer on reopen:
|
|
133
|
-
* -
|
|
134
|
-
* - the layer JSON `{ version, pageOrder, annotations }
|
|
135
|
-
*
|
|
159
|
+
* - a re-editable base (kept pages only, signatures flattened in), and
|
|
160
|
+
* - the layer JSON `{ version, pageOrder, annotations }` for text/markup,
|
|
161
|
+
* with page references remapped onto the kept-pages base.
|
|
162
|
+
* Deleted pages and signatures are unrecoverable; external readers only ever
|
|
163
|
+
* see the flattened pages.
|
|
136
164
|
*/
|
|
137
165
|
export async function buildSavedPdf(pdfLib, pristine, state) {
|
|
138
166
|
const output = await flattenInto(pdfLib, pristine, state);
|
|
167
|
+
const base = await buildEditableBase(pdfLib, pristine, state);
|
|
168
|
+
// The base is already in display order, so its pages number 1..keptCount.
|
|
169
|
+
// Remap each overlay's page reference (an original page number) onto that.
|
|
170
|
+
const remap = new Map(state.pageOrder.map((page, index) => [page, index + 1]));
|
|
139
171
|
const layer = JSON.stringify({
|
|
140
172
|
version: SIDECAR_VERSION,
|
|
141
|
-
pageOrder:
|
|
173
|
+
pageOrder: base.getPages().map((_, index) => index + 1),
|
|
142
174
|
annotations: state.annotations
|
|
175
|
+
.filter((annotation) => annotation.kind !== 'signature')
|
|
176
|
+
.map((annotation) => ({ ...annotation, page: remap.get(annotation.page) ?? annotation.page }))
|
|
143
177
|
});
|
|
144
178
|
await output.attach(new TextEncoder().encode(layer), SIDECAR_LAYER_NAME, {
|
|
145
179
|
mimeType: 'application/json'
|
|
146
180
|
});
|
|
147
|
-
await output.attach(
|
|
181
|
+
await output.attach(await base.save(), SIDECAR_BASE_NAME, {
|
|
148
182
|
mimeType: 'application/pdf'
|
|
149
183
|
});
|
|
150
184
|
return output.save();
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// annotations use an inline input overlay instead.
|
|
12
12
|
import { MountAbortedError, VIEWER_ROOT_CLASS } from '../types.js';
|
|
13
13
|
import { createPdfController, mergeHighlightRects, PDF_ZOOM_LEVELS } from './controller.js';
|
|
14
|
-
import { buildSavedPdf, mergePdfBytes, parseLayer, savedPdfName, SIDECAR_LAYER_NAME,
|
|
14
|
+
import { buildSavedPdf, mergePdfBytes, parseLayer, savedPdfName, SIDECAR_LAYER_NAME, SIDECAR_BASE_NAME } from './editing.js';
|
|
15
15
|
import { pdfViewerCss } from './styles.js';
|
|
16
16
|
export { createPdfController, PDF_ZOOM_LEVELS } from './controller.js';
|
|
17
17
|
export { pdfViewerCss } from './styles.js';
|
|
@@ -176,7 +176,7 @@ export async function mountPdfViewer(input, container, ctx, deps, options = {})
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
|
-
* Read the hybrid sidecar (
|
|
179
|
+
* Read the hybrid sidecar (re-editable base + layer JSON) from a document's
|
|
180
180
|
* attachments. Returns null unless BOTH parts are present and the layer
|
|
181
181
|
* parses — a lone layer over already-flattened pages would double up.
|
|
182
182
|
*/
|
|
@@ -185,19 +185,19 @@ export async function mountPdfViewer(input, container, ctx, deps, options = {})
|
|
|
185
185
|
const attachments = await d.getAttachments?.();
|
|
186
186
|
if (!attachments)
|
|
187
187
|
return null;
|
|
188
|
-
let
|
|
188
|
+
let base;
|
|
189
189
|
let layerText;
|
|
190
190
|
for (const entry of Object.values(attachments)) {
|
|
191
|
-
if (entry.filename ===
|
|
192
|
-
|
|
191
|
+
if (entry.filename === SIDECAR_BASE_NAME)
|
|
192
|
+
base = entry.content;
|
|
193
193
|
else if (entry.filename === SIDECAR_LAYER_NAME) {
|
|
194
194
|
layerText = new TextDecoder().decode(entry.content);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
-
if (!
|
|
197
|
+
if (!base || layerText === undefined)
|
|
198
198
|
return null;
|
|
199
199
|
const layer = parseLayer(layerText);
|
|
200
|
-
return layer ? {
|
|
200
|
+
return layer ? { base, layer } : null;
|
|
201
201
|
}
|
|
202
202
|
catch {
|
|
203
203
|
return null;
|
|
@@ -1374,12 +1374,13 @@ export async function mountPdfViewer(input, container, ctx, deps, options = {})
|
|
|
1374
1374
|
}
|
|
1375
1375
|
throwIfAborted();
|
|
1376
1376
|
// Hybrid rehydration: if this file was saved by us, its visible pages are
|
|
1377
|
-
// flattened but it carries
|
|
1378
|
-
// from
|
|
1377
|
+
// flattened but it carries a re-editable base (kept pages, signatures baked
|
|
1378
|
+
// in) + layer JSON. Render/edit from that base and restore the text/markup
|
|
1379
|
+
// overlays as a removable layer.
|
|
1379
1380
|
const sidecar = await readSidecar(doc);
|
|
1380
1381
|
if (sidecar) {
|
|
1381
1382
|
await doc.destroy();
|
|
1382
|
-
sourceBytes = sidecar.
|
|
1383
|
+
sourceBytes = sidecar.base;
|
|
1383
1384
|
doc = await loadDocument(sourceBytes, password);
|
|
1384
1385
|
throwIfAborted();
|
|
1385
1386
|
adoptController(createPdfController(doc.numPages, sidecar.layer));
|
|
@@ -9,7 +9,7 @@ export function computeChartBounds(data) { const explicitMin = data.valueAxis?.m
|
|
|
9
9
|
const positive = data.categories.map((_, i) => data.series.reduce((sum, s) => sum + Math.max(0, s.values[i] ?? 0), 0)), negative = data.categories.map((_, i) => data.series.reduce((sum, s) => sum + Math.min(0, s.values[i] ?? 0), 0));
|
|
10
10
|
const min = finite(explicitMin) ? explicitMin : Math.min(0, ...negative), max = finite(explicitMax) ? explicitMax : Math.max(1, ...positive);
|
|
11
11
|
return min < max ? { min, max } : { min, max: min + 1 };
|
|
12
|
-
} const values = data.series.flatMap(s => s.values).filter(finite); let min = finite(explicitMin) ? explicitMin : Math.min(0, ...values), max = finite(explicitMax) ? explicitMax : Math.max(
|
|
12
|
+
} const values = data.series.flatMap(s => s.values).filter(finite); let min = finite(explicitMin) ? explicitMin : Math.min(0, ...values), max = finite(explicitMax) ? explicitMax : values.length ? Math.max(...values) : 1; if (min >= max) {
|
|
13
13
|
const pad = Math.max(1, Math.abs(max || 1) * .1);
|
|
14
14
|
min -= pad;
|
|
15
15
|
max += pad;
|
|
@@ -26,6 +26,12 @@ function legend(ctx, w, h, data, line) { const style = data.legend ?? {}, font =
|
|
|
26
26
|
ctx.moveTo(x, h - 11);
|
|
27
27
|
ctx.lineTo(x + swatch, h - 11);
|
|
28
28
|
ctx.stroke();
|
|
29
|
+
ctx.fillStyle = '#fff';
|
|
30
|
+
ctx.beginPath();
|
|
31
|
+
ctx.arc(x + swatch / 2, h - 11, 3.5, 0, Math.PI * 2);
|
|
32
|
+
ctx.fill();
|
|
33
|
+
ctx.strokeStyle = item.color;
|
|
34
|
+
ctx.stroke();
|
|
29
35
|
}
|
|
30
36
|
else {
|
|
31
37
|
ctx.fillStyle = item.color;
|
|
@@ -36,7 +42,11 @@ function legend(ctx, w, h, data, line) { const style = data.legend ?? {}, font =
|
|
|
36
42
|
ctx.fillText(item.label, x, h - 7);
|
|
37
43
|
x += item.label.length * font * .55 + gap;
|
|
38
44
|
} }
|
|
39
|
-
function axes(ctx, w, h, data, bounds) { const m = { left: 74, right: 20, top: 20, bottom: 88 }, pw = w - m.left - m.right, ph = h - m.top - m.bottom, axis = data.valueAxis ?? {}, format = axis.numFmt ?? (data.kind === 'stackedColumn' ? '$#,##0.0' : '0.0'), step = finite(axis.majorUnit) && axis.majorUnit > 0 ? axis.majorUnit : niceStep(bounds.max - bounds.min), y = (value) => m.top + (bounds.max - value) / (bounds.max - bounds.min) * ph;
|
|
45
|
+
function axes(ctx, w, h, data, bounds) { const m = { left: 74, right: 20, top: 20, bottom: 88 }, pw = w - m.left - m.right, ph = h - m.top - m.bottom, axis = data.valueAxis ?? {}, format = axis.numFmt ?? (data.kind === 'stackedColumn' ? '$#,##0.0' : '0.0'), step = finite(axis.majorUnit) && axis.majorUnit > 0 ? axis.majorUnit : niceStep(bounds.max - bounds.min), y = (value) => m.top + (bounds.max - value) / (bounds.max - bounds.min) * ph; if (data.kind === 'stackedColumn') {
|
|
46
|
+
ctx.strokeStyle = axis.lineColor ?? '#4f81bd';
|
|
47
|
+
ctx.lineWidth = 1;
|
|
48
|
+
ctx.strokeRect(m.left, m.top, pw, ph);
|
|
49
|
+
} ctx.strokeStyle = axis.gridColor ?? (data.kind === 'line' ? 'rgba(0,0,0,0.18)' : 'rgba(0,0,0,0.22)'); ctx.lineWidth = 1; for (let tick = Math.floor(bounds.min / step) * step, count = 0; tick <= bounds.max + step * .5 && count++ < 250; tick += step) {
|
|
40
50
|
const py = y(tick);
|
|
41
51
|
ctx.beginPath();
|
|
42
52
|
ctx.moveTo(m.left, py);
|
|
@@ -47,7 +57,13 @@ function axes(ctx, w, h, data, bounds) { const m = { left: 74, right: 20, top: 2
|
|
|
47
57
|
ctx.textAlign = 'right';
|
|
48
58
|
ctx.textBaseline = 'middle';
|
|
49
59
|
ctx.fillText(formatChartValue(tick, format), m.left - 12, py);
|
|
50
|
-
} const cross = finite(axis.crossesAt) ? axis.crossesAt : 0; ctx.strokeStyle = axis.lineColor ?? '#111111'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(m.left, y(cross)); ctx.lineTo(m.left + pw, y(cross)); ctx.stroke();
|
|
60
|
+
} const cross = finite(axis.crossesAt) ? axis.crossesAt : 0; ctx.strokeStyle = data.kind === 'stackedColumn' ? '#111111' : axis.lineColor ?? '#111111'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(m.left, y(cross)); ctx.lineTo(m.left + pw, y(cross)); ctx.stroke(); if (data.kind === 'line') {
|
|
61
|
+
ctx.beginPath();
|
|
62
|
+
ctx.moveTo(m.left, m.top);
|
|
63
|
+
ctx.lineTo(m.left, m.top + ph);
|
|
64
|
+
ctx.lineTo(m.left + pw, m.top + ph);
|
|
65
|
+
ctx.stroke();
|
|
66
|
+
} return { ...m, pw, ph, y, format }; }
|
|
51
67
|
function label(ctx, series, value, x, y, height, axisFormat) { const style = series.dataLabel ?? {}; if (style.showValue === false || Math.abs(value) < .1 || height <= 12)
|
|
52
68
|
return; ctx.fillStyle = style.color ?? '#ffffff'; ctx.font = `400 ${Math.max(9, style.fontSizePx ?? 11)}px Arial, sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(formatChartValue(value, style.numFmt ?? series.valueFormat ?? axisFormat), x, y); }
|
|
53
69
|
function stacked(ctx, w, h, data, bounds) { const a = axes(ctx, w, h, data, bounds), group = a.pw / data.categories.length, gap = finite(data.gapWidth) ? Math.max(.2, Math.min(.9, 1 - data.gapWidth / 300)) : .58, bar = Math.max(8, group * gap), category = data.categoryAxis ?? {}; data.categories.forEach((name, i) => { const cx = a.left + group * i + group / 2; let pos = 0, neg = 0; for (const series of data.series) {
|
|
@@ -72,6 +88,9 @@ function line(ctx, w, h, data, bounds) { const a = axes(ctx, w, h, data, bounds)
|
|
|
72
88
|
ctx.arc(px, py, 4.5, 0, Math.PI * 2);
|
|
73
89
|
ctx.fill();
|
|
74
90
|
ctx.strokeStyle = series.color || '#4472c4';
|
|
91
|
+
ctx.lineWidth = 2;
|
|
92
|
+
ctx.beginPath();
|
|
93
|
+
ctx.arc(px, py, 4.5, 0, Math.PI * 2);
|
|
75
94
|
ctx.stroke();
|
|
76
95
|
const style = series.dataLabel ?? {};
|
|
77
96
|
if (style.showValue !== false) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parsePptBinaryVscode } from '../../parsers/ppt-binary/index.js';
|
|
2
|
-
import { openPptxZip,
|
|
2
|
+
import { openPptxZip, parsePptxLegacy, parsePptxVscode } from '../../parsers/pptx/index.js';
|
|
3
3
|
import { mountPdfViewer } from '../pdf/index.js';
|
|
4
4
|
import { MountAbortedError, VIEWER_ROOT_CLASS } from '../types.js';
|
|
5
5
|
import { createPptController } from './controller.js';
|
|
@@ -41,7 +41,7 @@ export async function mountPptViewer(input, container, ctx, deps = {}, options =
|
|
|
41
41
|
return mountPdfFallback(input, container, ctx, deps, options, 'diag.ppt.empty');
|
|
42
42
|
if (!deck.slides.some(slide => slide.elements.length > 0)) {
|
|
43
43
|
if (isPptx) {
|
|
44
|
-
const legacy = await (deps.parseLegacyPptx?.(input.data, options) ??
|
|
44
|
+
const legacy = await (deps.parseLegacyPptx?.(input.data, options) ?? parsePptxLegacy(input.data, { openZip: deps.openZip ?? openPptxZip }, options));
|
|
45
45
|
if (legacy.result.status !== 'failed' && legacy.result.document.slides.some(slide => slide.elements.length > 0))
|
|
46
46
|
deck = legacy.result.document;
|
|
47
47
|
else
|
|
@@ -64,7 +64,7 @@ export async function mountPptViewer(input, container, ctx, deps = {}, options =
|
|
|
64
64
|
next.setAttribute('aria-label', ctx.i18n.t('ppt.next'));
|
|
65
65
|
jump.setAttribute('aria-label', ctx.i18n.t('ppt.jump'));
|
|
66
66
|
deck.slides.forEach(slide => { const option = el('option'); option.value = String(slide.slideNumber); const title = slide.elements.find(element => element.isTitle)?.paragraphs?.map(paragraph => paragraph.text).join(' '); option.textContent = `Slide ${slide.slideNumber}${title ? `: ${title}` : ''}`; jump.append(option); });
|
|
67
|
-
toolbar.append(prev, next, jump, count, minus,
|
|
67
|
+
toolbar.append(prev, next, jump, count, minus, reset, plus, mode);
|
|
68
68
|
const viewport = el('div', 'omni-ppt__viewport'), slides = el('div', 'omni-ppt__slides');
|
|
69
69
|
viewport.append(slides);
|
|
70
70
|
frame.append(toolbar, viewport);
|
|
@@ -79,8 +79,9 @@ function imageElement(e) { const box = frame(e, 'image'), img = node('img', 'omn
|
|
|
79
79
|
img.src = recolored;
|
|
80
80
|
}
|
|
81
81
|
catch { /* keep the original raster when recoloring fails */ } }, { once: true }); box.append(img); return box; }
|
|
82
|
-
function tableElement(e) { const box = frame(e, 'table'), table = node('table', 'omni-ppt__table'); (e.tableRows ?? []).forEach((row, rowIndex) => { const tr = node('tr'); row.forEach(value => { const cell = node(rowIndex === 0 ? 'th' : 'td'); cell.textContent = value; tr.append(cell); }); table.append(tr); }); box.append(table); return box; }
|
|
82
|
+
function tableElement(e) { const box = frame(e, 'table-frame'), table = node('table', 'omni-ppt__table'); (e.tableRows ?? []).forEach((row, rowIndex) => { const tr = node('tr'); row.forEach(value => { const cell = node(rowIndex === 0 ? 'th' : 'td'); cell.textContent = value; tr.append(cell); }); table.append(tr); }); box.append(table); return box; }
|
|
83
83
|
function chartElement(e) { const box = frame(e, 'chart'); if (e.chartData) {
|
|
84
|
+
box.classList.add('omni-ppt__chart--rendered');
|
|
84
85
|
const canvas = node('canvas');
|
|
85
86
|
canvas.width = Math.max(300, Math.floor(e.width));
|
|
86
87
|
canvas.height = Math.max(180, Math.floor(e.height));
|
|
@@ -90,7 +91,10 @@ function chartElement(e) { const box = frame(e, 'chart'); if (e.chartData) {
|
|
|
90
91
|
queueMicrotask(() => drawChart(canvas, e.chartData));
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
|
-
|
|
94
|
+
const title = node('div', 'omni-ppt__chart-title'), subtitle = node('div', 'omni-ppt__chart-subtitle');
|
|
95
|
+
title.textContent = e.chartTitle ?? (e.chartKind === 'smartart' ? 'SmartArt' : 'Chart');
|
|
96
|
+
subtitle.textContent = e.chartKind === 'smartart' ? 'SmartArt placeholder' : 'Chart placeholder';
|
|
97
|
+
box.append(title, subtitle);
|
|
94
98
|
} return box; }
|
|
95
99
|
export function presetGeomToSvgPath(preset, w, h) { if (!preset || w <= 0 || h <= 0)
|
|
96
100
|
return; switch (preset) {
|
|
@@ -120,11 +124,25 @@ export function presetGeomToSvgPath(preset, w, h) { if (!preset || w <= 0 || h <
|
|
|
120
124
|
case 'rect': return `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
121
125
|
default: return;
|
|
122
126
|
} }
|
|
123
|
-
function marker(defs, id, type, color) { const value = document.createElementNS(SVG_NS, 'marker'); value.id = id; value.setAttribute('markerWidth', '8'); value.setAttribute('markerHeight', '
|
|
124
|
-
|
|
127
|
+
function marker(defs, id, type, color) { const value = document.createElementNS(SVG_NS, 'marker'), circle = type === 'circle'; value.id = id; value.setAttribute('markerWidth', circle ? '6' : '8'); value.setAttribute('markerHeight', '6'); value.setAttribute('refX', circle ? '3' : '8'); value.setAttribute('refY', '3'); value.setAttribute('orient', 'auto'); value.setAttribute('markerUnits', 'strokeWidth'); value.setAttribute('overflow', 'visible'); if (circle) {
|
|
128
|
+
const dot = document.createElementNS(SVG_NS, 'circle');
|
|
129
|
+
dot.setAttribute('cx', '3');
|
|
130
|
+
dot.setAttribute('cy', '3');
|
|
131
|
+
dot.setAttribute('r', '2.5');
|
|
132
|
+
dot.setAttribute('fill', color);
|
|
133
|
+
value.append(dot);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const path = document.createElementNS(SVG_NS, 'path');
|
|
137
|
+
path.setAttribute('d', 'M 0 0 L 8 3 L 0 6 Z');
|
|
138
|
+
path.setAttribute('fill', color);
|
|
139
|
+
value.append(path);
|
|
140
|
+
} defs.append(value); }
|
|
141
|
+
function shapeElement(e) { const box = frame(e, 'shape'), w = Math.max(1, Math.abs(e.width)), h = Math.max(1, Math.abs(e.height)), svg = document.createElementNS(SVG_NS, 'svg'); svg.setAttribute('width', String(w)); svg.setAttribute('height', String(h)); svg.setAttribute('viewBox', `0 0 ${w} ${h}`); Object.assign(svg.style, { display: 'block', overflow: 'visible' }); const stroke = e.borderColor ?? '#000', strokeWidth = String(Math.max(1, e.borderWidthPx ?? 1)); if (e.presetGeom === 'line' || e.presetGeom === 'straightConnector1') {
|
|
125
142
|
box.style.transform = e.rotateDeg ? `rotate(${e.rotateDeg}deg)` : '';
|
|
143
|
+
Object.assign(svg.style, { position: 'absolute', left: '0', top: '0' });
|
|
126
144
|
const defs = document.createElementNS(SVG_NS, 'defs'), suffix = `${e.zIndex}-${Math.round(e.x)}-${Math.round(e.y)}`;
|
|
127
|
-
marker(defs, `arrow-${suffix}`, '
|
|
145
|
+
marker(defs, `arrow-${suffix}`, 'arrow', stroke);
|
|
128
146
|
marker(defs, `circle-${suffix}`, 'circle', stroke);
|
|
129
147
|
svg.append(defs);
|
|
130
148
|
const line = document.createElementNS(SVG_NS, 'line');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const pptViewerCss = ":host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset=\"cover-title\"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset=\"cover-subtitle\"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table{border-collapse:collapse;width:100%;
|
|
1
|
+
export declare const pptViewerCss = ":host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;border-radius:6px;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{padding:2px;white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset=\"cover-title\"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset=\"cover-subtitle\"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table-frame{background:#fff;border:1px solid #bdbdbd;overflow:auto}.omni-ppt__table{border-collapse:collapse;width:100%;font-size:12px;color:#111}.omni-ppt__table th,.omni-ppt__table td{border:1px solid #d3d3d3;padding:4px 6px;vertical-align:top}.omni-ppt__chart{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;background:#f7f7f7;border:1px dashed #9a9a9a;color:#333}.omni-ppt__chart--rendered{background:transparent;border:0}.omni-ppt__chart-title{font-size:14px;font-weight:700;margin-bottom:4px}.omni-ppt__chart-subtitle{font-size:12px;opacity:.8}.omni-ppt__shape{width:100%;height:100%}.omni-ppt__empty{padding:32px;text-align:center}";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const pptViewerCss = `:host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset="cover-title"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset="cover-subtitle"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table{border-collapse:collapse;width:100%;
|
|
1
|
+
export const pptViewerCss = `:host,.omni-viewer--ppt{color:var(--omni-fg,#ddd);font:13px system-ui}.omni-ppt{height:100%;display:flex;flex-direction:column;background:var(--omni-bg,#181818)}.omni-ppt__toolbar{display:flex;gap:6px;align-items:center;padding:8px;border-bottom:1px solid var(--omni-border,#444)}.omni-ppt__toolbar button,.omni-ppt__toolbar select{background:var(--omni-control-bg,#292929);color:inherit;border:1px solid var(--omni-border,#555);border-radius:4px;padding:4px 8px}.omni-ppt__viewport{overflow:auto;flex:1;padding:20px}.omni-ppt__slides{display:flex;flex-direction:column;align-items:center;gap:20px}.omni-ppt__slide{position:relative;overflow:hidden;border-radius:6px;box-shadow:0 2px 12px #0008;transform-origin:top center;flex:none}.omni-ppt__element{position:absolute;box-sizing:border-box;overflow:hidden}.omni-ppt__text{padding:2px;white-space:pre-wrap;word-break:break-word}.omni-ppt__text p{margin:0 0 4px}.omni-ppt__text[data-text-preset="cover-title"] .omni-ppt__text-line,.omni-ppt__text[data-text-preset="cover-subtitle"] .omni-ppt__text-line{margin-bottom:0}.omni-ppt__image{width:100%;height:100%;object-fit:cover;object-position:center}.omni-ppt__table-frame{background:#fff;border:1px solid #bdbdbd;overflow:auto}.omni-ppt__table{border-collapse:collapse;width:100%;font-size:12px;color:#111}.omni-ppt__table th,.omni-ppt__table td{border:1px solid #d3d3d3;padding:4px 6px;vertical-align:top}.omni-ppt__chart{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;background:#f7f7f7;border:1px dashed #9a9a9a;color:#333}.omni-ppt__chart--rendered{background:transparent;border:0}.omni-ppt__chart-title{font-size:14px;font-weight:700;margin-bottom:4px}.omni-ppt__chart-subtitle{font-size:12px;opacity:.8}.omni-ppt__shape{width:100%;height:100%}.omni-ppt__empty{padding:32px;text-align:center}`;
|