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.
Files changed (36) hide show
  1. package/README.md +28 -0
  2. package/dist/host/index.d.ts +14 -2
  3. package/dist/i18n/catalog.en.js +8 -0
  4. package/dist/i18n/catalog.ja.js +9 -1
  5. package/dist/i18n/catalog.ko.js +8 -0
  6. package/dist/i18n/catalog.zh-cn.js +8 -1
  7. package/dist/parsers/pptx/index.d.ts +1 -0
  8. package/dist/parsers/pptx/index.js +1 -1
  9. package/dist/parsers/yaml/index.js +14 -2
  10. package/dist/parsers/yaml/self-loading.js +26 -14
  11. package/dist/registry/index.js +1 -1
  12. package/dist/styles/archive.css +1 -1
  13. package/dist/styles/pdf.css +26 -0
  14. package/dist/styles/toml.css +1 -1
  15. package/dist/styles/yaml.css +1 -1
  16. package/dist/viewers/archive/index.js +60 -11
  17. package/dist/viewers/archive/styles.d.ts +1 -1
  18. package/dist/viewers/archive/styles.js +1 -1
  19. package/dist/viewers/json/controller.js +13 -24
  20. package/dist/viewers/json/index.js +5 -1
  21. package/dist/viewers/markdown/index.d.ts +3 -2
  22. package/dist/viewers/markdown/index.js +29 -14
  23. package/dist/viewers/pdf/controller.d.ts +14 -2
  24. package/dist/viewers/pdf/controller.js +28 -10
  25. package/dist/viewers/pdf/index.d.ts +79 -8
  26. package/dist/viewers/pdf/index.js +363 -81
  27. package/dist/viewers/pdf/self-loading.d.ts +3 -3
  28. package/dist/viewers/pdf/styles.d.ts +1 -1
  29. package/dist/viewers/pdf/styles.js +26 -0
  30. package/dist/viewers/ppt/index.js +3 -3
  31. package/dist/viewers/structured-styles.d.ts +1 -1
  32. package/dist/viewers/structured-styles.js +1 -1
  33. package/dist/viewers/structured.d.ts +1 -0
  34. package/dist/viewers/structured.js +5 -3
  35. package/dist/viewers/yaml/controller.js +48 -5
  36. package/package.json +2 -1
@@ -1,6 +1,6 @@
1
- import type { ViewerInput, MountOptions } from '../types.js';
2
- import { type PdfViewerContext, type PdfViewerDeps, type PdfViewerHandle } from './index.js';
1
+ import type { ViewerInput } from '../types.js';
2
+ import { type PdfMountOptions, type PdfViewerContext, type PdfViewerDeps, type PdfViewerHandle } from './index.js';
3
3
  export * from './index.js';
4
4
  export declare const selfLoadingPdfDeps: PdfViewerDeps;
5
5
  /** mountPdfViewer with the core's own dynamic-import loaders. */
6
- export declare function mountSelfLoadingPdfViewer(input: ViewerInput, container: HTMLElement, ctx: PdfViewerContext, options?: MountOptions): Promise<PdfViewerHandle>;
6
+ export declare function mountSelfLoadingPdfViewer(input: ViewerInput, container: HTMLElement, ctx: PdfViewerContext, options?: PdfMountOptions): Promise<PdfViewerHandle>;
@@ -1 +1 @@
1
- export declare const pdfViewerCss = "\n/* Reverse-contamination guard for 'scoped' mode \u2014 see csv/styles.ts. */\n.omni-viewer--pdf {\n all: initial;\n}\n.omni-viewer--pdf :where(button, input, canvas, img, span, div) {\n all: revert;\n}\n\n:host, .omni-viewer--pdf {\n display: block;\n height: 100%;\n box-sizing: border-box;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-bg, #1e1e1e);\n font-family: var(--omni-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-size: var(--omni-font-size, 13px);\n}\n.omni-pdf, .omni-pdf * { box-sizing: border-box; }\n.omni-pdf {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n.omni-pdf__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px;\n background: var(--omni-bg-secondary, #252526);\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__title {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 40%;\n}\n.omni-pdf__toolbar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n.omni-pdf__page-info {\n color: var(--omni-fg-muted, #9d9d9d);\n margin-right: 8px;\n}\n.omni-pdf__page-input {\n width: 52px;\n padding: 3px 5px;\n margin-right: 5px;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-input-bg, #3c3c3c);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n font: inherit;\n text-align: center;\n}\n.omni-pdf__page-input:focus {\n outline: 1px solid var(--omni-accent, #0e639c);\n border-color: var(--omni-accent, #0e639c);\n}\n.omni-pdf button {\n font: inherit;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-button-bg, #3a3d41);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n cursor: pointer;\n}\n.omni-pdf button:hover:not(:disabled) {\n background: var(--omni-button-hover-bg, #45494e);\n}\n.omni-pdf button:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.omni-pdf button[aria-pressed=\"true\"] {\n background: var(--omni-accent, #0e639c);\n border-color: var(--omni-accent, #0e639c);\n color: var(--omni-accent-fg, #ffffff);\n}\n.omni-pdf__tool { padding: 5px 10px; }\n.omni-pdf__markup-wrap {\n position: relative;\n display: inline-flex;\n}\n.omni-pdf__markup-wrap .omni-pdf__tool {\n min-width: 34px;\n padding: 4px 8px;\n font-size: 21px;\n line-height: 1;\n border-radius: 4px 0 0 4px;\n}\n.omni-pdf__markup-menu-btn {\n min-width: 28px;\n padding: 0 6px;\n border-radius: 0 4px 4px 0;\n}\n.omni-pdf__markup-menu {\n position: absolute;\n z-index: 20;\n top: calc(100% + 4px);\n left: 0;\n display: none;\n min-width: 170px;\n padding: 7px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 6px;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);\n}\n.omni-pdf__markup-menu.is-open { display: block; }\n.omni-pdf__markup-choice {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n box-sizing: border-box;\n min-height: 32px;\n padding: 2px 0;\n}\n.omni-pdf__markup-menu button {\n flex: 1;\n min-height: 32px;\n padding: 5px 8px;\n color: inherit;\n font: inherit;\n}\n.omni-pdf__markup-menu button {\n text-align: left;\n background: transparent;\n border-color: transparent;\n}\n.omni-pdf__markup-menu button:hover,\n.omni-pdf__markup-menu button[aria-checked=\"true\"] { background: var(--omni-button-hover-bg, #45494e); }\n.omni-pdf__markup-menu button[aria-checked=\"true\"]::before { content: \"\u2713 \"; }\n.omni-pdf__highlight-color {\n width: 30px;\n height: 28px;\n padding: 2px;\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n background: var(--omni-button-bg, #3a3d41);\n cursor: pointer;\n}\n.omni-pdf__highlight-color:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.omni-pdf__zoom {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px;\n background: var(--omni-bg-secondary, #252526);\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__zoom-btn {\n width: 28px;\n height: 28px;\n padding: 0;\n font-size: 18px;\n line-height: 1;\n}\n.omni-pdf__zoom-level {\n min-width: 56px;\n text-align: center;\n}\n.omni-pdf__view-menu-wrap { position: relative; }\n.omni-pdf__view-menu-btn {\n width: 30px;\n height: 28px;\n padding: 0;\n font-size: 22px;\n line-height: 1;\n}\n.omni-pdf__view-menu {\n position: absolute;\n z-index: 20;\n top: calc(100% + 4px);\n right: 0;\n display: none;\n min-width: 210px;\n padding: 6px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 6px;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);\n}\n.omni-pdf__view-menu.is-open { display: block; }\n.omni-pdf__view-menu button {\n display: block;\n width: 100%;\n padding: 7px 10px;\n text-align: left;\n background: transparent;\n border-color: transparent;\n}\n.omni-pdf__view-menu button:hover,\n.omni-pdf__view-menu button[aria-checked=\"true\"] {\n background: var(--omni-button-hover-bg, #45494e);\n}\n.omni-pdf__view-menu button[aria-checked=\"true\"]::before {\n content: \"\u2713 \";\n}\n.omni-pdf__view-menu hr {\n height: 1px;\n margin: 5px 2px;\n border: 0;\n background: var(--omni-border, #3c3c3c);\n}\n.omni-pdf__body {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: 160px minmax(0, 1fr);\n overflow: hidden;\n}\n.omni-pdf__thumbs {\n overflow: auto;\n min-height: 0;\n background: var(--omni-bg-secondary, #252526);\n border-right: 1px solid var(--omni-border, #3c3c3c);\n padding: 8px;\n}\n.omni-pdf__thumb {\n position: relative;\n display: flex;\n width: 100%;\n min-height: 130px;\n margin: 0 0 8px;\n padding: 6px;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 4px;\n background: transparent;\n}\n.omni-pdf__thumb:hover,\n.omni-pdf__thumb[aria-current] {\n background: var(--omni-button-hover-bg, #45494e);\n border-color: var(--omni-accent, #0e639c);\n}\n.omni-pdf__thumb.is-dragging { opacity: 0.5; }\n.omni-pdf__thumb.drop-before::before,\n.omni-pdf__thumb.drop-after::after {\n content: \"\";\n position: absolute;\n left: -4px;\n right: -4px;\n height: 3px;\n background: var(--omni-accent, #0e639c);\n border-radius: 2px;\n z-index: 4;\n}\n.omni-pdf__thumb.drop-before::before { top: -6px; }\n.omni-pdf__thumb.drop-after::after { bottom: -6px; }\n.omni-pdf__thumb canvas {\n max-width: 100%;\n background: #ffffff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n}\n.omni-pdf__thumb-label {\n font-family: var(--omni-font-mono, Monaco, Menlo, monospace);\n font-size: 10px;\n color: var(--omni-fg-muted, #9d9d9d);\n}\n.omni-pdf__thumb-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n}\n.omni-pdf__pages {\n overflow: auto;\n position: relative;\n padding: 20px;\n min-width: 0;\n min-height: 0;\n}\n.omni-pdf__pages--spread {\n display: grid;\n grid-template-columns: repeat(2, max-content);\n align-content: start;\n justify-content: center;\n column-gap: 16px;\n}\n.omni-pdf__spread-blank {\n min-width: 1px;\n}\n.omni-pdf__pages--theme .omni-pdf__page {\n background: var(--omni-bg-secondary, #252526);\n}\n.omni-pdf__pages--theme .omni-pdf__page canvas {\n filter: invert(0.92) hue-rotate(180deg);\n}\n.omni-pdf__page {\n position: relative;\n margin: 0 auto 16px;\n background: #ffffff; /* paper */\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);\n}\n.omni-pdf__page canvas {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.omni-pdf__placeholder {\n display: flex;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n color: var(--omni-fg-muted, #9d9d9d);\n}\n.omni-pdf__text-layer {\n position: absolute;\n inset: 0;\n z-index: 1;\n overflow: clip;\n opacity: 1;\n line-height: 1;\n text-align: initial;\n text-size-adjust: none;\n forced-color-adjust: none;\n transform-origin: 0 0;\n caret-color: CanvasText;\n pointer-events: auto;\n user-select: text;\n -webkit-user-select: text;\n}\n.omni-pdf__text-layer span,\n.omni-pdf__text-layer br {\n position: absolute;\n white-space: pre;\n color: transparent;\n cursor: text;\n transform-origin: 0 0;\n user-select: text;\n -webkit-user-select: text;\n}\n.omni-pdf__text-layer ::selection {\n background: rgba(14, 99, 156, 0.35);\n}\n/* While placing text/signature, let clicks fall through to the canvas. */\n.omni-pdf__pages--placing .omni-pdf__text-layer {\n pointer-events: none;\n user-select: none;\n}\n.omni-pdf__annotation {\n position: absolute;\n z-index: 2;\n padding: 0;\n color: #111111; /* ink on paper */\n cursor: move;\n touch-action: none;\n}\n.omni-pdf button.omni-pdf__annotation {\n background: transparent;\n border: 0;\n font: inherit;\n}\n.omni-pdf__annotation img {\n display: block;\n pointer-events: none;\n}\n.omni-pdf__annotation.is-selected,\n.omni-pdf__annotation:hover,\n.omni-pdf__annotation.is-hovered,\n.omni-pdf__annotation:focus-visible,\n.omni-pdf__annotation:focus-within {\n outline: 1px solid var(--omni-accent, #0e639c);\n outline-offset: 2px;\n}\n.omni-pdf__annotation-delete {\n position: absolute;\n top: -10px;\n right: -10px;\n z-index: 1;\n display: none;\n width: 18px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 50%;\n background: var(--omni-bg-secondary, #252526);\n color: var(--omni-fg, #e8e8e8);\n font-size: 15px;\n line-height: 14px;\n cursor: pointer;\n}\n.omni-pdf__annotation.is-selected .omni-pdf__annotation-delete,\n.omni-pdf__annotation:hover .omni-pdf__annotation-delete,\n.omni-pdf__annotation:focus-visible .omni-pdf__annotation-delete,\n.omni-pdf__annotation:focus-within .omni-pdf__annotation-delete {\n display: block;\n}\n.omni-pdf__highlight {\n background: var(--omni-hl-color, #ffeb3b);\n mix-blend-mode: multiply;\n cursor: pointer;\n}\n.omni-pdf__strikeout {\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__strikeout::after {\n position: absolute;\n top: calc(50% - 1px);\n left: 0;\n width: 100%;\n height: 2px;\n content: '';\n background: var(--omni-hl-color, #ffeb3b);\n}\n.omni-pdf__underline {\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__underline::after {\n position: absolute;\n bottom: 8%;\n left: 0;\n width: 100%;\n height: 2px;\n content: '';\n background: var(--omni-hl-color, #ffeb3b);\n}\n.omni-pdf__text-editor {\n position: absolute;\n z-index: 3;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 3px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-accent, #0e639c);\n border-radius: 3px;\n}\n.omni-pdf__text-input {\n min-width: 160px;\n font: inherit;\n color: #111111;\n background: #ffffff;\n border: 0;\n border-radius: 2px;\n padding: 2px 4px;\n}\n.omni-pdf__text-size-input {\n width: 52px;\n font: inherit;\n}\n.omni-pdf__color-input {\n width: 28px;\n height: 24px;\n padding: 0;\n border: 0;\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__status {\n padding: 8px 12px;\n color: var(--omni-fg-muted, #9d9d9d);\n border-top: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__status--error {\n color: var(--omni-error-fg, #f48771);\n font-weight: 600;\n}\n.omni-pdf__modal {\n /* A dialog must be positioned against the visible viewport. Anchoring it\n to the flex viewer made it drift when the host or page list scrolled. */\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: none;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.55);\n}\n.omni-pdf__modal.is-open { display: flex; }\n.omni-pdf__panel {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 8px;\n}\n.omni-pdf__panel input {\n font: inherit;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-input-bg, #3c3c3c);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n padding: 6px 8px;\n}\n.omni-pdf__field {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.omni-pdf__field input[type=\"color\"] {\n width: 40px;\n height: 26px;\n padding: 0;\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__panel button { padding: 5px 12px; }\n.omni-pdf__panel-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.omni-pdf__error {\n color: var(--omni-error-fg, #f48771);\n}\n.omni-pdf__signature-pad {\n display: block;\n width: min(480px, 80vw);\n height: auto;\n background: #ffffff;\n touch-action: none;\n}\n@media (max-width: 720px) {\n .omni-pdf__body {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(120px, 30vh) minmax(0, 1fr);\n }\n .omni-pdf__thumbs {\n border-right: 0;\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n }\n .omni-pdf__pages { grid-row: 2; }\n}\n";
1
+ export declare const pdfViewerCss = "\n/* Reverse-contamination guard for 'scoped' mode \u2014 see csv/styles.ts. */\n.omni-viewer--pdf {\n all: initial;\n}\n.omni-viewer--pdf :where(button, input, canvas, img, span, div) {\n all: revert;\n}\n\n:host, .omni-viewer--pdf {\n display: block;\n height: 100%;\n box-sizing: border-box;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-bg, #1e1e1e);\n font-family: var(--omni-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-size: var(--omni-font-size, 13px);\n}\n.omni-pdf, .omni-pdf * { box-sizing: border-box; }\n.omni-pdf {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n.omni-pdf__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n padding: 8px 12px;\n background: var(--omni-bg-secondary, #252526);\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__title {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 40%;\n}\n.omni-pdf__toolbar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n.omni-pdf__page-info {\n color: var(--omni-fg-muted, #9d9d9d);\n margin-right: 8px;\n}\n.omni-pdf__save-mode {\n padding: 2px 6px;\n color: var(--omni-fg-muted, #9d9d9d);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 999px;\n font-size: 11px;\n white-space: nowrap;\n}\n.omni-pdf__page-input {\n width: 52px;\n padding: 3px 5px;\n margin-right: 5px;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-input-bg, #3c3c3c);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n font: inherit;\n text-align: center;\n}\n.omni-pdf__page-input:focus {\n outline: 1px solid var(--omni-accent, #0e639c);\n border-color: var(--omni-accent, #0e639c);\n}\n.omni-pdf button {\n font: inherit;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-button-bg, #3a3d41);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n cursor: pointer;\n}\n.omni-pdf button:hover:not(:disabled) {\n background: var(--omni-button-hover-bg, #45494e);\n}\n.omni-pdf button:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.omni-pdf button[aria-pressed=\"true\"] {\n background: var(--omni-accent, #0e639c);\n border-color: var(--omni-accent, #0e639c);\n color: var(--omni-accent-fg, #ffffff);\n}\n.omni-pdf__tool { padding: 5px 10px; }\n.omni-pdf__markup-wrap {\n position: relative;\n display: inline-flex;\n}\n.omni-pdf__markup-wrap .omni-pdf__tool {\n min-width: 34px;\n padding: 4px 8px;\n font-size: 21px;\n line-height: 1;\n border-radius: 4px 0 0 4px;\n}\n.omni-pdf__markup-menu-btn {\n min-width: 28px;\n padding: 0 6px;\n border-radius: 0 4px 4px 0;\n}\n.omni-pdf__markup-menu {\n position: absolute;\n z-index: 20;\n top: calc(100% + 4px);\n left: 0;\n display: none;\n min-width: 170px;\n padding: 7px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 6px;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);\n}\n.omni-pdf__markup-menu.is-open { display: block; }\n.omni-pdf__markup-choice {\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n box-sizing: border-box;\n min-height: 32px;\n padding: 2px 0;\n}\n.omni-pdf__markup-menu button {\n flex: 1;\n min-height: 32px;\n padding: 5px 8px;\n color: inherit;\n font: inherit;\n}\n.omni-pdf__markup-menu button {\n text-align: left;\n background: transparent;\n border-color: transparent;\n}\n.omni-pdf__markup-menu button:hover,\n.omni-pdf__markup-menu button[aria-checked=\"true\"] { background: var(--omni-button-hover-bg, #45494e); }\n.omni-pdf__markup-menu button[aria-checked=\"true\"]::before { content: \"\u2713 \"; }\n.omni-pdf__highlight-color {\n width: 30px;\n height: 28px;\n padding: 2px;\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n background: var(--omni-button-bg, #3a3d41);\n cursor: pointer;\n}\n.omni-pdf__highlight-color:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.omni-pdf__zoom {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 6px;\n background: var(--omni-bg-secondary, #252526);\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__zoom-btn {\n width: 28px;\n height: 28px;\n padding: 0;\n font-size: 18px;\n line-height: 1;\n}\n.omni-pdf__zoom-level {\n min-width: 56px;\n text-align: center;\n}\n.omni-pdf__view-menu-wrap { position: relative; }\n.omni-pdf__view-menu-btn {\n width: 30px;\n height: 28px;\n padding: 0;\n font-size: 22px;\n line-height: 1;\n}\n.omni-pdf__view-menu {\n position: absolute;\n z-index: 20;\n top: calc(100% + 4px);\n right: 0;\n display: none;\n min-width: 210px;\n padding: 6px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 6px;\n box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);\n}\n.omni-pdf__view-menu.is-open { display: block; }\n.omni-pdf__view-menu button {\n display: block;\n width: 100%;\n padding: 7px 10px;\n text-align: left;\n background: transparent;\n border-color: transparent;\n}\n.omni-pdf__view-menu button:hover,\n.omni-pdf__view-menu button[aria-checked=\"true\"] {\n background: var(--omni-button-hover-bg, #45494e);\n}\n.omni-pdf__view-menu button[aria-checked=\"true\"]::before {\n content: \"\u2713 \";\n}\n.omni-pdf__view-menu hr {\n height: 1px;\n margin: 5px 2px;\n border: 0;\n background: var(--omni-border, #3c3c3c);\n}\n.omni-pdf__body {\n flex: 1;\n min-height: 0;\n display: grid;\n grid-template-columns: 160px minmax(0, 1fr);\n overflow: hidden;\n}\n.omni-pdf__thumbs {\n overflow: auto;\n min-height: 0;\n background: var(--omni-bg-secondary, #252526);\n border-right: 1px solid var(--omni-border, #3c3c3c);\n padding: 8px;\n}\n.omni-pdf__thumb {\n position: relative;\n display: flex;\n width: 100%;\n min-height: 130px;\n margin: 0 0 8px;\n padding: 6px;\n align-items: center;\n justify-content: center;\n flex-direction: column;\n gap: 4px;\n background: transparent;\n}\n.omni-pdf__thumb:hover,\n.omni-pdf__thumb[aria-current] {\n background: var(--omni-button-hover-bg, #45494e);\n border-color: var(--omni-accent, #0e639c);\n}\n.omni-pdf__thumb.is-dragging { opacity: 0.5; }\n.omni-pdf__thumb.drop-before::before,\n.omni-pdf__thumb.drop-after::after {\n content: \"\";\n position: absolute;\n left: -4px;\n right: -4px;\n height: 3px;\n background: var(--omni-accent, #0e639c);\n border-radius: 2px;\n z-index: 4;\n}\n.omni-pdf__thumb.drop-before::before { top: -6px; }\n.omni-pdf__thumb.drop-after::after { bottom: -6px; }\n.omni-pdf__thumb canvas {\n display: block;\n max-width: 100%;\n background: #ffffff;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);\n}\n.omni-pdf__thumb-page {\n position: relative;\n max-width: 100%;\n}\n.omni-pdf__thumb-annotation {\n position: absolute;\n z-index: 1;\n overflow: hidden;\n pointer-events: none;\n color: #111111; /* ink on paper */\n white-space: nowrap;\n}\n.omni-pdf__thumb-annotation img { display: block; }\n.omni-pdf__thumb-annotation.omni-pdf__underline::after,\n.omni-pdf__thumb-annotation.omni-pdf__strikeout::after {\n height: 1px;\n}\n.omni-pdf__thumb-label {\n font-family: var(--omni-font-mono, Monaco, Menlo, monospace);\n font-size: 10px;\n color: var(--omni-fg-muted, #9d9d9d);\n}\n.omni-pdf__thumb-remove {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 22px;\n height: 22px;\n padding: 0;\n border-radius: 50%;\n}\n.omni-pdf__pages {\n overflow: auto;\n position: relative;\n padding: 20px;\n min-width: 0;\n min-height: 0;\n}\n.omni-pdf__pages--spread {\n display: grid;\n grid-template-columns: repeat(2, max-content);\n align-content: start;\n justify-content: center;\n column-gap: 16px;\n}\n.omni-pdf__spread-blank {\n min-width: 1px;\n}\n.omni-pdf__pages--theme .omni-pdf__page {\n background: var(--omni-bg-secondary, #252526);\n}\n.omni-pdf__pages--theme .omni-pdf__page canvas {\n filter: invert(0.92) hue-rotate(180deg);\n}\n.omni-pdf__page {\n position: relative;\n margin: 0 auto 16px;\n background: #ffffff; /* paper */\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);\n}\n.omni-pdf__page canvas {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.omni-pdf__placeholder {\n display: flex;\n width: 100%;\n height: 100%;\n align-items: center;\n justify-content: center;\n color: var(--omni-fg-muted, #9d9d9d);\n}\n.omni-pdf__text-layer {\n position: absolute;\n inset: 0;\n z-index: 1;\n overflow: clip;\n opacity: 1;\n line-height: 1;\n text-align: initial;\n text-size-adjust: none;\n forced-color-adjust: none;\n transform-origin: 0 0;\n caret-color: CanvasText;\n pointer-events: auto;\n user-select: text;\n -webkit-user-select: text;\n}\n.omni-pdf__text-layer span,\n.omni-pdf__text-layer br {\n position: absolute;\n white-space: pre;\n color: transparent;\n cursor: text;\n transform-origin: 0 0;\n user-select: text;\n -webkit-user-select: text;\n}\n.omni-pdf__text-layer ::selection {\n background: rgba(14, 99, 156, 0.35);\n}\n/* While placing text/signature, let clicks fall through to the canvas. */\n.omni-pdf__pages--placing .omni-pdf__text-layer {\n pointer-events: none;\n user-select: none;\n}\n.omni-pdf__annotation {\n position: absolute;\n z-index: 2;\n padding: 0;\n color: #111111; /* ink on paper */\n cursor: move;\n touch-action: none;\n}\n.omni-pdf button.omni-pdf__annotation {\n background: transparent;\n border: 0;\n font: inherit;\n}\n.omni-pdf__annotation img {\n display: block;\n pointer-events: none;\n}\n.omni-pdf__annotation.is-selected,\n.omni-pdf__annotation:hover,\n.omni-pdf__annotation.is-hovered,\n.omni-pdf__annotation:focus-visible,\n.omni-pdf__annotation:focus-within {\n outline: 1px solid var(--omni-accent, #0e639c);\n outline-offset: 2px;\n}\n.omni-pdf__annotation-delete {\n position: absolute;\n top: -10px;\n right: -10px;\n z-index: 1;\n display: none;\n width: 18px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 50%;\n background: var(--omni-bg-secondary, #252526);\n color: var(--omni-fg, #e8e8e8);\n font-size: 15px;\n line-height: 14px;\n cursor: pointer;\n}\n.omni-pdf__annotation.is-selected .omni-pdf__annotation-delete,\n.omni-pdf__annotation:hover .omni-pdf__annotation-delete,\n.omni-pdf__annotation:focus-visible .omni-pdf__annotation-delete,\n.omni-pdf__annotation:focus-within .omni-pdf__annotation-delete {\n display: block;\n}\n.omni-pdf__highlight {\n background: var(--omni-hl-color, #ffeb3b);\n mix-blend-mode: multiply;\n cursor: pointer;\n}\n.omni-pdf__strikeout {\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__strikeout::after {\n position: absolute;\n top: calc(50% - 1px);\n left: 0;\n width: 100%;\n height: 2px;\n content: '';\n background: var(--omni-hl-color, #ffeb3b);\n}\n.omni-pdf__underline {\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__underline::after {\n position: absolute;\n bottom: 8%;\n left: 0;\n width: 100%;\n height: 2px;\n content: '';\n background: var(--omni-hl-color, #ffeb3b);\n}\n.omni-pdf__text-editor {\n position: absolute;\n z-index: 3;\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 3px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-accent, #0e639c);\n border-radius: 3px;\n}\n.omni-pdf__text-input {\n min-width: 160px;\n font: inherit;\n color: #111111;\n background: #ffffff;\n border: 0;\n border-radius: 2px;\n padding: 2px 4px;\n}\n.omni-pdf__text-size-input {\n width: 52px;\n font: inherit;\n}\n.omni-pdf__color-input {\n width: 28px;\n height: 24px;\n padding: 0;\n border: 0;\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__status {\n padding: 8px 12px;\n color: var(--omni-fg-muted, #9d9d9d);\n border-top: 1px solid var(--omni-border, #3c3c3c);\n flex: none;\n}\n.omni-pdf__status--error {\n color: var(--omni-error-fg, #f48771);\n font-weight: 600;\n}\n.omni-pdf__modal {\n /* A dialog must be positioned against the visible viewport. Anchoring it\n to the flex viewer made it drift when the host or page list scrolled. */\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: none;\n align-items: center;\n justify-content: center;\n background: rgba(0, 0, 0, 0.55);\n}\n.omni-pdf__modal.is-open { display: flex; }\n.omni-pdf__panel {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 18px;\n background: var(--omni-bg-secondary, #252526);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 8px;\n}\n.omni-pdf__panel input {\n font: inherit;\n color: var(--omni-fg, #e8e8e8);\n background: var(--omni-input-bg, #3c3c3c);\n border: 1px solid var(--omni-border, #3c3c3c);\n border-radius: 4px;\n padding: 6px 8px;\n}\n.omni-pdf__field {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n.omni-pdf__field input[type=\"color\"] {\n width: 40px;\n height: 26px;\n padding: 0;\n background: transparent;\n cursor: pointer;\n}\n.omni-pdf__panel button { padding: 5px 12px; }\n.omni-pdf__panel-actions {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n}\n.omni-pdf__error {\n color: var(--omni-error-fg, #f48771);\n}\n.omni-pdf__signature-pad {\n display: block;\n width: min(480px, 80vw);\n height: auto;\n background: #ffffff;\n touch-action: none;\n}\n@media (max-width: 720px) {\n .omni-pdf__body {\n grid-template-columns: minmax(0, 1fr);\n grid-template-rows: minmax(120px, 30vh) minmax(0, 1fr);\n }\n .omni-pdf__thumbs {\n border-right: 0;\n border-bottom: 1px solid var(--omni-border, #3c3c3c);\n }\n .omni-pdf__pages { grid-row: 2; }\n}\n";
@@ -54,6 +54,14 @@ export const pdfViewerCss = `
54
54
  color: var(--omni-fg-muted, #9d9d9d);
55
55
  margin-right: 8px;
56
56
  }
57
+ .omni-pdf__save-mode {
58
+ padding: 2px 6px;
59
+ color: var(--omni-fg-muted, #9d9d9d);
60
+ border: 1px solid var(--omni-border, #3c3c3c);
61
+ border-radius: 999px;
62
+ font-size: 11px;
63
+ white-space: nowrap;
64
+ }
57
65
  .omni-pdf__page-input {
58
66
  width: 52px;
59
67
  padding: 3px 5px;
@@ -268,10 +276,28 @@ export const pdfViewerCss = `
268
276
  .omni-pdf__thumb.drop-before::before { top: -6px; }
269
277
  .omni-pdf__thumb.drop-after::after { bottom: -6px; }
270
278
  .omni-pdf__thumb canvas {
279
+ display: block;
271
280
  max-width: 100%;
272
281
  background: #ffffff;
273
282
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
274
283
  }
284
+ .omni-pdf__thumb-page {
285
+ position: relative;
286
+ max-width: 100%;
287
+ }
288
+ .omni-pdf__thumb-annotation {
289
+ position: absolute;
290
+ z-index: 1;
291
+ overflow: hidden;
292
+ pointer-events: none;
293
+ color: #111111; /* ink on paper */
294
+ white-space: nowrap;
295
+ }
296
+ .omni-pdf__thumb-annotation img { display: block; }
297
+ .omni-pdf__thumb-annotation.omni-pdf__underline::after,
298
+ .omni-pdf__thumb-annotation.omni-pdf__strikeout::after {
299
+ height: 1px;
300
+ }
275
301
  .omni-pdf__thumb-label {
276
302
  font-family: var(--omni-font-mono, Monaco, Menlo, monospace);
277
303
  font-size: 10px;
@@ -1,5 +1,5 @@
1
1
  import { parsePptBinaryVscode } from '../../parsers/ppt-binary/index.js';
2
- import { openPptxZip, parsePptx, parsePptxVscode } from '../../parsers/pptx/index.js';
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) ?? parsePptx(input.data, { openZip: openPptxZip }, 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, plus, reset, mode);
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);
@@ -1 +1 @@
1
- export declare const structuredViewerCss = ":host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}";
1
+ export declare const structuredViewerCss = ":host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__preview-body{position:relative;flex:1;min-height:200px}.omni-structured__preview-body>.omni-structured__tree,.omni-structured__preview-body>.omni-structured__json-preview,.omni-structured__preview-body>.omni-structured__raw-preview{position:absolute;inset:0}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}";
@@ -1 +1 @@
1
- export const structuredViewerCss = `:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}`;
1
+ export const structuredViewerCss = `:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__preview-body{position:relative;flex:1;min-height:200px}.omni-structured__preview-body>.omni-structured__tree,.omni-structured__preview-body>.omni-structured__json-preview,.omni-structured__preview-body>.omni-structured__raw-preview{position:absolute;inset:0}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}`;
@@ -15,6 +15,7 @@ export interface StructuredState {
15
15
  expanded: ReadonlySet<string>;
16
16
  diagnostics: readonly {
17
17
  messageKey: string;
18
+ args?: Record<string, string | number>;
18
19
  location?: string;
19
20
  }[];
20
21
  }
@@ -77,7 +77,9 @@ export function mountStructured(input, container, ctx, options, className, css,
77
77
  const flatBody = el('div', 'omni-structured__tree omni-structured__flat');
78
78
  const jsonPreview = el('pre', 'omni-structured__json-preview');
79
79
  const rawPreview = el('pre', 'omni-structured__raw-preview');
80
- previewPane.append(previewHeader, treeBody, flatBody, jsonPreview, rawPreview);
80
+ const previewBody = el('div', 'omni-structured__preview-body');
81
+ previewBody.append(treeBody, flatBody, jsonPreview, rawPreview);
82
+ previewPane.append(previewHeader, previewBody);
81
83
  workspace.append(sourcePane, previewPane);
82
84
  frame.append(toolbar, diagnostics, workspace);
83
85
  const copy = (value) => { if (value !== undefined)
@@ -115,12 +117,12 @@ export function mountStructured(input, container, ctx, options, className, css,
115
117
  wrap.append(rendered);
116
118
  } return wrap; };
117
119
  const render = (state) => { if (editor.value !== state.text && document.activeElement !== editor)
118
- editor.value = state.text; if (sourceHighlight && sourceHighlighter)
120
+ editor.value = state.text; editor.rows = Math.min(state.text.split('\n').length + 1, 200); if (sourceHighlight && sourceHighlighter)
119
121
  sourceHighlighter(sourceHighlight, state.text); if (sourceHighlighter)
120
122
  sourceHighlighter(rawPreview, state.text);
121
123
  else
122
124
  rawPreview.textContent = state.text; search.value = state.search; treeMode.setAttribute('aria-pressed', String(state.mode === 'tree')); flatMode.setAttribute('aria-pressed', String(state.mode === 'flat')); jsonMode.setAttribute('aria-pressed', String(state.mode === 'json')); rawMode.setAttribute('aria-pressed', String(state.mode === 'raw')); treeBody.style.display = state.mode === 'tree' ? '' : 'none'; flatBody.style.display = state.mode === 'flat' ? '' : 'none'; jsonPreview.style.display = state.mode === 'json' ? '' : 'none'; rawPreview.style.display = state.mode === 'raw' ? '' : 'none'; jsonPreview.textContent = state.mode === 'json' ? controller.documentJson?.() ?? '' : ''; expand.disabled = collapse.disabled = state.mode !== 'tree'; save.disabled = !ctx.writeback; save.title = ctx.writeback ? '' : ctx.i18n.t('common.noWriteback'); saveAs.disabled = !ctx.save; saveAs.title = ctx.save ? '' : ctx.i18n.t('common.noFileSave'); diagnostics.replaceChildren(); diagnostics.style.display = state.diagnostics.length ? '' : 'none'; for (const diagnostic of state.diagnostics)
123
- diagnostics.append(el('div', undefined, `${ctx.i18n.t(diagnostic.messageKey)}${diagnostic.location ? ` (${diagnostic.location})` : ''}`)); const shaped = state; const docs = shaped.documents; documentSelect.replaceChildren(); documentSelect.style.display = docs && docs.length > 1 ? '' : 'none'; if (docs && docs.length > 1) {
125
+ diagnostics.append(el('div', undefined, `${ctx.i18n.t(diagnostic.messageKey, diagnostic.args)}${diagnostic.location ? ` (${diagnostic.location})` : ''}`)); const shaped = state; const docs = shaped.documents; documentSelect.replaceChildren(); documentSelect.style.display = docs && docs.length > 1 ? '' : 'none'; if (docs && docs.length > 1) {
124
126
  docs.forEach((_, index) => { const option = el('option', undefined, String(index + 1)); option.value = String(index); option.selected = index === shaped.selectedDocument; documentSelect.append(option); });
125
127
  documentSelect.onchange = () => controller.dispatch({ type: 'select-document', index: Number(documentSelect.value) });
126
128
  } treeBody.replaceChildren(); flatBody.replaceChildren(); const roots = shaped.root ? [shaped.root] : docs ? [docs[shaped.selectedDocument ?? 0]].filter((node) => !!node) : []; roots.forEach(node => { const rendered = renderNode(node, 0, state); if (rendered)
@@ -1,15 +1,58 @@
1
1
  import { asciiLower } from '../../parsers/csv/index.js';
2
2
  import { parseYaml } from '../../parsers/yaml/index.js';
3
3
  export function createYamlController(input, deps) {
4
- const listeners = new Set(), nodes = new Map();
4
+ const listeners = new Set(), nodes = new Map(), anchors = new Map(), order = new Map();
5
5
  const state = { status: 'ok', documents: [], selectedDocument: 0, mode: 'tree', search: '', expanded: new Set(), diagnostics: [], text: typeof input === 'string' ? input : new TextDecoder().decode(input) };
6
- const index = () => { const result = parseYaml(state.text, deps ? { deps } : {}).result; state.status = result.status; state.diagnostics = result.diagnostics; state.documents = result.status === 'failed' ? [] : result.document.documents; state.selectedDocument = Math.min(state.selectedDocument, Math.max(0, state.documents.length - 1)); nodes.clear(); const walk = (node) => { nodes.set(node.path, node); for (const child of node.children ?? [])
7
- walk(child); }; state.documents.forEach(walk); };
8
- const plain = (node) => node.children ? node.kind === 'seq' ? node.children.map(plain) : Object.fromEntries(node.children.map(child => [child.key, plain(child)])) : node.value;
6
+ const index = () => { const result = parseYaml(state.text, deps ? { deps } : {}).result; state.status = result.status; state.diagnostics = result.diagnostics; state.documents = result.status === 'failed' ? [] : result.document.documents; state.selectedDocument = Math.min(state.selectedDocument, Math.max(0, state.documents.length - 1)); nodes.clear(); anchors.clear(); order.clear(); let ordinal = 0; const walk = (node, doc) => { nodes.set(node.path, node); order.set(node, ordinal++); if (node.anchorId) {
7
+ const defs = anchors.get(`${doc}|${node.anchorId}`) ?? [];
8
+ defs.push(node);
9
+ anchors.set(`${doc}|${node.anchorId}`, defs);
10
+ } for (const child of node.children ?? [])
11
+ walk(child, doc); }; state.documents.forEach(document => walk(document, document.path)); };
12
+ const docOf = (path) => path.slice(0, path.indexOf(']') + 1);
13
+ const resolveAlias = (node, doc) => { const defs = node.aliasOf === undefined ? undefined : anchors.get(`${doc}|${node.aliasOf}`); if (!defs?.length)
14
+ return undefined; const at = order.get(node) ?? Number.MAX_SAFE_INTEGER; let target; for (const def of defs) {
15
+ if ((order.get(def) ?? Number.MAX_SAFE_INTEGER) < at)
16
+ target = def;
17
+ else
18
+ break;
19
+ } return target ?? defs[defs.length - 1]; };
20
+ const plain = (node, doc, visiting = new Set()) => {
21
+ if (node.kind === 'alias') {
22
+ if (node.value !== undefined)
23
+ return node.value;
24
+ const target = resolveAlias(node, doc);
25
+ if (!target || visiting.has(target))
26
+ return null;
27
+ visiting.add(target);
28
+ const resolved = plain(target, doc, visiting);
29
+ visiting.delete(target);
30
+ return resolved;
31
+ }
32
+ if (!node.children)
33
+ return node.value;
34
+ if (node.kind === 'seq')
35
+ return node.children.map(child => plain(child, doc, visiting));
36
+ const out = {};
37
+ const merges = [];
38
+ for (const child of node.children)
39
+ child.key === '<<' ? merges.push(child) : out[child.key] = plain(child, doc, visiting);
40
+ for (const merge of merges) {
41
+ const merged = plain(merge, doc, visiting);
42
+ for (const source of Array.isArray(merged) ? merged : [merged]) {
43
+ if (source && typeof source === 'object')
44
+ for (const [key, value] of Object.entries(source)) {
45
+ if (!(key in out))
46
+ out[key] = value;
47
+ }
48
+ }
49
+ }
50
+ return out;
51
+ };
9
52
  const notify = () => listeners.forEach(listener => listener(state));
10
53
  index();
11
54
  state.expanded = new Set(state.documents.flatMap(node => [node.path, ...(node.children ?? []).filter(child => !!child.children?.length).map(child => child.path)]));
12
- return { state, subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }, nodePath: id => nodes.get(id)?.path ?? '', nodeValue: id => nodes.get(id)?.raw ?? String(nodes.get(id)?.value ?? ''), nodeJson: id => JSON.stringify(plain(nodes.get(id) ?? state.documents[state.selectedDocument]), null, 2), documentJson() { const document = state.documents[state.selectedDocument]; return document ? JSON.stringify(plain(document), null, 2) : ''; }, dispatch(action) { if (action.type === 'edit-source') {
55
+ return { state, subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); }, nodePath: id => nodes.get(id)?.path ?? '', nodeValue: id => nodes.get(id)?.raw ?? String(nodes.get(id)?.value ?? ''), nodeJson: id => { const node = nodes.get(id) ?? state.documents[state.selectedDocument]; return JSON.stringify(plain(node, docOf(node.path)), null, 2); }, documentJson() { const document = state.documents[state.selectedDocument]; return document ? JSON.stringify(plain(document, document.path), null, 2) : ''; }, dispatch(action) { if (action.type === 'edit-source') {
13
56
  state.text = action.text;
14
57
  index();
15
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-viewer-core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Shared parsing + rendering core for Omni Viewer (vscode / chrome / obsidian / web)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -283,6 +283,7 @@
283
283
  "build": "tsc -p tsconfig.build.json && node scripts/build-styles.mjs",
284
284
  "test": "vitest run",
285
285
  "typecheck": "tsc -p tsconfig.json --noEmit",
286
+ "verify:pdf-package": "node scripts/verify-pdf-package.mjs",
286
287
  "prepublishOnly": "npm run build && npm test"
287
288
  },
288
289
  "devDependencies": {