omni-viewer-core 0.1.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/LICENSE +21 -0
- package/README.md +80 -0
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/dist/assets/audio-engine/audio_engine.mjs +2 -0
- package/dist/assets/audio-engine/audio_engine.wasm +0 -0
- package/dist/assets/pdfjs/pdf.worker.min.mjs +21 -0
- package/dist/host/index.d.ts +133 -0
- package/dist/host/index.js +10 -0
- package/dist/i18n/catalog.en.d.ts +1 -0
- package/dist/i18n/catalog.en.js +411 -0
- package/dist/i18n/catalog.ko.d.ts +1 -0
- package/dist/i18n/catalog.ko.js +68 -0
- package/dist/i18n/index.d.ts +18 -0
- package/dist/i18n/index.js +41 -0
- package/dist/licenses/emscripten.txt +101 -0
- package/dist/licenses/kissfft.txt +28 -0
- package/dist/licenses/pdfjs-dist.txt +177 -0
- package/dist/parsers/a2l/index.d.ts +1 -0
- package/dist/parsers/a2l/index.js +1 -0
- package/dist/parsers/archive/index.d.ts +22 -0
- package/dist/parsers/archive/index.js +46 -0
- package/dist/parsers/archive/model.d.ts +29 -0
- package/dist/parsers/archive/model.js +1 -0
- package/dist/parsers/archive/worker.d.ts +21 -0
- package/dist/parsers/archive/worker.js +1 -0
- package/dist/parsers/arxml/index.d.ts +1 -0
- package/dist/parsers/arxml/index.js +1 -0
- package/dist/parsers/asc/index.d.ts +1 -0
- package/dist/parsers/asc/index.js +1 -0
- package/dist/parsers/audio/index.d.ts +9 -0
- package/dist/parsers/audio/index.js +46 -0
- package/dist/parsers/automotive/index.d.ts +33 -0
- package/dist/parsers/automotive/index.js +973 -0
- package/dist/parsers/blf/index.d.ts +1 -0
- package/dist/parsers/blf/index.js +1 -0
- package/dist/parsers/csv/delimiter.d.ts +31 -0
- package/dist/parsers/csv/delimiter.js +151 -0
- package/dist/parsers/csv/index.d.ts +30 -0
- package/dist/parsers/csv/index.js +108 -0
- package/dist/parsers/csv/parseText.d.ts +20 -0
- package/dist/parsers/csv/parseText.js +108 -0
- package/dist/parsers/csv/sort.d.ts +26 -0
- package/dist/parsers/csv/sort.js +224 -0
- package/dist/parsers/csv/statistics.d.ts +49 -0
- package/dist/parsers/csv/statistics.js +150 -0
- package/dist/parsers/dbc/index.d.ts +56 -0
- package/dist/parsers/dbc/index.js +94 -0
- package/dist/parsers/doc-binary/index.d.ts +35 -0
- package/dist/parsers/doc-binary/index.js +267 -0
- package/dist/parsers/doc-binary/vscode-parser.d.ts +142 -0
- package/dist/parsers/doc-binary/vscode-parser.js +3747 -0
- package/dist/parsers/excel/index.d.ts +17 -0
- package/dist/parsers/excel/index.js +406 -0
- package/dist/parsers/excel/model.d.ts +80 -0
- package/dist/parsers/excel/model.js +48 -0
- package/dist/parsers/excel/serialize.d.ts +3 -0
- package/dist/parsers/excel/serialize.js +78 -0
- package/dist/parsers/hdf5/index.d.ts +75 -0
- package/dist/parsers/hdf5/index.js +658 -0
- package/dist/parsers/hdf5/node.d.ts +21 -0
- package/dist/parsers/hdf5/node.js +95 -0
- package/dist/parsers/json/index.d.ts +15 -0
- package/dist/parsers/json/index.js +102 -0
- package/dist/parsers/json/model.d.ts +42 -0
- package/dist/parsers/json/model.js +140 -0
- package/dist/parsers/json/parse.d.ts +22 -0
- package/dist/parsers/json/parse.js +279 -0
- package/dist/parsers/json/tokenizer.d.ts +17 -0
- package/dist/parsers/json/tokenizer.js +175 -0
- package/dist/parsers/jsonl/index.d.ts +14 -0
- package/dist/parsers/jsonl/index.js +18 -0
- package/dist/parsers/jsonl/model.d.ts +23 -0
- package/dist/parsers/jsonl/model.js +1 -0
- package/dist/parsers/jsonl/parse.d.ts +7 -0
- package/dist/parsers/jsonl/parse.js +34 -0
- package/dist/parsers/markdown/index.d.ts +37 -0
- package/dist/parsers/markdown/index.js +59 -0
- package/dist/parsers/mat/index.d.ts +31 -0
- package/dist/parsers/mat/index.js +426 -0
- package/dist/parsers/mermaid/index.d.ts +5 -0
- package/dist/parsers/mermaid/index.js +3 -0
- package/dist/parsers/mf4/index.d.ts +1 -0
- package/dist/parsers/mf4/index.js +1 -0
- package/dist/parsers/parquet/index.d.ts +21 -0
- package/dist/parsers/parquet/index.js +68 -0
- package/dist/parsers/pcap/index.d.ts +1 -0
- package/dist/parsers/pcap/index.js +1 -0
- package/dist/parsers/pcapng/index.d.ts +1 -0
- package/dist/parsers/pcapng/index.js +1 -0
- package/dist/parsers/plantuml/index.d.ts +5 -0
- package/dist/parsers/plantuml/index.js +4 -0
- package/dist/parsers/ppt-binary/index.d.ts +5 -0
- package/dist/parsers/ppt-binary/index.js +62 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.d.ts +12 -0
- package/dist/parsers/ppt-binary/pptBinaryBuildContext.js +40 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.d.ts +4 -0
- package/dist/parsers/ppt-binary/pptBinaryContainer.js +174 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.d.ts +125 -0
- package/dist/parsers/ppt-binary/pptBinaryTypes.js +1 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.d.ts +9 -0
- package/dist/parsers/ppt-binary/pptSlideLayouts.js +510 -0
- package/dist/parsers/ppt-binary/vscode-parser.d.ts +116 -0
- package/dist/parsers/ppt-binary/vscode-parser.js +2853 -0
- package/dist/parsers/ppt-binary/vscode.d.ts +4 -0
- package/dist/parsers/ppt-binary/vscode.js +12 -0
- package/dist/parsers/pptx/index.d.ts +15 -0
- package/dist/parsers/pptx/index.js +88 -0
- package/dist/parsers/pptx/vscode-parser.d.ts +167 -0
- package/dist/parsers/pptx/vscode-parser.js +1503 -0
- package/dist/parsers/pptx/vscode.d.ts +4 -0
- package/dist/parsers/pptx/vscode.js +18 -0
- package/dist/parsers/pptx/zip-reader.d.ts +3 -0
- package/dist/parsers/pptx/zip-reader.js +20 -0
- package/dist/parsers/proto/index.d.ts +90 -0
- package/dist/parsers/proto/index.js +197 -0
- package/dist/parsers/psd/index.d.ts +16 -0
- package/dist/parsers/psd/index.js +10 -0
- package/dist/parsers/shapefile/dbf.d.ts +23 -0
- package/dist/parsers/shapefile/dbf.js +112 -0
- package/dist/parsers/shapefile/index.d.ts +44 -0
- package/dist/parsers/shapefile/index.js +119 -0
- package/dist/parsers/shapefile/reproject.d.ts +10 -0
- package/dist/parsers/shapefile/reproject.js +41 -0
- package/dist/parsers/slide-model.d.ts +106 -0
- package/dist/parsers/slide-model.js +1 -0
- package/dist/parsers/toml/index.d.ts +15 -0
- package/dist/parsers/toml/index.js +18 -0
- package/dist/parsers/toml/model.d.ts +15 -0
- package/dist/parsers/toml/model.js +1 -0
- package/dist/parsers/toml/parse.d.ts +16 -0
- package/dist/parsers/toml/parse.js +266 -0
- package/dist/parsers/types.d.ts +103 -0
- package/dist/parsers/types.js +106 -0
- package/dist/parsers/video/index.d.ts +6 -0
- package/dist/parsers/video/index.js +2 -0
- package/dist/parsers/yaml/index.d.ts +16 -0
- package/dist/parsers/yaml/index.js +58 -0
- package/dist/parsers/yaml/model.d.ts +22 -0
- package/dist/parsers/yaml/model.js +1 -0
- package/dist/parsers/yaml/self-loading.d.ts +3 -0
- package/dist/parsers/yaml/self-loading.js +19 -0
- package/dist/parsers/zip-scan.d.ts +8 -0
- package/dist/parsers/zip-scan.js +80 -0
- package/dist/registry/container.d.ts +17 -0
- package/dist/registry/container.js +49 -0
- package/dist/registry/index.d.ts +133 -0
- package/dist/registry/index.js +348 -0
- package/dist/registry/probe.d.ts +3 -0
- package/dist/registry/probe.js +177 -0
- package/dist/registry/sniff.d.ts +17 -0
- package/dist/registry/sniff.js +57 -0
- package/dist/styles/archive.css +10 -0
- package/dist/styles/audio.css +66 -0
- package/dist/styles/automotive.css +1 -0
- package/dist/styles/csv.css +262 -0
- package/dist/styles/excel.css +212 -0
- package/dist/styles/fallback.css +48 -0
- package/dist/styles/hdf5.css +3 -0
- package/dist/styles/hwp.css +12 -0
- package/dist/styles/image.css +115 -0
- package/dist/styles/json.css +342 -0
- package/dist/styles/jsonl.css +1 -0
- package/dist/styles/markdown.css +10 -0
- package/dist/styles/mat.css +2 -0
- package/dist/styles/mermaid.css +1 -0
- package/dist/styles/parquet.css +2 -0
- package/dist/styles/pdf.css +551 -0
- package/dist/styles/plantuml.css +1 -0
- package/dist/styles/ppt.css +1 -0
- package/dist/styles/proto.css +2 -0
- package/dist/styles/psd.css +1 -0
- package/dist/styles/shapefile.css +25 -0
- package/dist/styles/toml.css +1 -0
- package/dist/styles/video.css +74 -0
- package/dist/styles/word.css +685 -0
- package/dist/styles/yaml.css +1 -0
- package/dist/viewers/a2l/index.d.ts +15 -0
- package/dist/viewers/a2l/index.js +5 -0
- package/dist/viewers/archive/archive-preview-decoder.d.ts +4 -0
- package/dist/viewers/archive/archive-preview-decoder.js +223 -0
- package/dist/viewers/archive/controller.d.ts +24 -0
- package/dist/viewers/archive/controller.js +18 -0
- package/dist/viewers/archive/index.d.ts +25 -0
- package/dist/viewers/archive/index.js +230 -0
- package/dist/viewers/archive/preview.d.ts +2 -0
- package/dist/viewers/archive/preview.js +2 -0
- package/dist/viewers/archive/styles.d.ts +1 -0
- package/dist/viewers/archive/styles.js +11 -0
- package/dist/viewers/arxml/index.d.ts +15 -0
- package/dist/viewers/arxml/index.js +5 -0
- package/dist/viewers/asc/index.d.ts +15 -0
- package/dist/viewers/asc/index.js +5 -0
- package/dist/viewers/audio/controller.d.ts +33 -0
- package/dist/viewers/audio/controller.js +49 -0
- package/dist/viewers/audio/engine.d.ts +44 -0
- package/dist/viewers/audio/engine.js +108 -0
- package/dist/viewers/audio/index.d.ts +92 -0
- package/dist/viewers/audio/index.js +448 -0
- package/dist/viewers/audio/self-loading.d.ts +7 -0
- package/dist/viewers/audio/self-loading.js +42 -0
- package/dist/viewers/audio/styles.d.ts +1 -0
- package/dist/viewers/audio/styles.js +73 -0
- package/dist/viewers/automotive/index.d.ts +8 -0
- package/dist/viewers/automotive/index.js +152 -0
- package/dist/viewers/automotive/styles.d.ts +1 -0
- package/dist/viewers/automotive/styles.js +1 -0
- package/dist/viewers/avro/index.d.ts +15 -0
- package/dist/viewers/avro/index.js +5 -0
- package/dist/viewers/bag/index.d.ts +15 -0
- package/dist/viewers/bag/index.js +5 -0
- package/dist/viewers/blf/index.d.ts +15 -0
- package/dist/viewers/blf/index.js +5 -0
- package/dist/viewers/csv/controller.d.ts +126 -0
- package/dist/viewers/csv/controller.js +598 -0
- package/dist/viewers/csv/index.d.ts +48 -0
- package/dist/viewers/csv/index.js +846 -0
- package/dist/viewers/csv/styles.d.ts +1 -0
- package/dist/viewers/csv/styles.js +267 -0
- package/dist/viewers/db3/index.d.ts +15 -0
- package/dist/viewers/db3/index.js +5 -0
- package/dist/viewers/dbc/index.d.ts +15 -0
- package/dist/viewers/dbc/index.js +5 -0
- package/dist/viewers/diagram.d.ts +14 -0
- package/dist/viewers/diagram.js +95 -0
- package/dist/viewers/excel/controller.d.ts +108 -0
- package/dist/viewers/excel/controller.js +548 -0
- package/dist/viewers/excel/grid.d.ts +28 -0
- package/dist/viewers/excel/grid.js +99 -0
- package/dist/viewers/excel/index.d.ts +37 -0
- package/dist/viewers/excel/index.js +620 -0
- package/dist/viewers/excel/self-loading.d.ts +6 -0
- package/dist/viewers/excel/self-loading.js +13 -0
- package/dist/viewers/excel/styles.d.ts +1 -0
- package/dist/viewers/excel/styles.js +216 -0
- package/dist/viewers/fallback/index.d.ts +17 -0
- package/dist/viewers/fallback/index.js +130 -0
- package/dist/viewers/fallback/styles.d.ts +1 -0
- package/dist/viewers/fallback/styles.js +49 -0
- package/dist/viewers/hdf5/index.d.ts +19 -0
- package/dist/viewers/hdf5/index.js +170 -0
- package/dist/viewers/hdf5/styles.d.ts +1 -0
- package/dist/viewers/hdf5/styles.js +4 -0
- package/dist/viewers/hwp/controller.d.ts +19 -0
- package/dist/viewers/hwp/controller.js +22 -0
- package/dist/viewers/hwp/index.d.ts +39 -0
- package/dist/viewers/hwp/index.js +271 -0
- package/dist/viewers/hwp/self-loading.d.ts +2 -0
- package/dist/viewers/hwp/self-loading.js +6 -0
- package/dist/viewers/hwp/styles.d.ts +1 -0
- package/dist/viewers/hwp/styles.js +13 -0
- package/dist/viewers/image/controller.d.ts +210 -0
- package/dist/viewers/image/controller.js +336 -0
- package/dist/viewers/image/decode.d.ts +54 -0
- package/dist/viewers/image/decode.js +150 -0
- package/dist/viewers/image/index.d.ts +38 -0
- package/dist/viewers/image/index.js +749 -0
- package/dist/viewers/image/limits.d.ts +18 -0
- package/dist/viewers/image/limits.js +20 -0
- package/dist/viewers/image/styles.d.ts +1 -0
- package/dist/viewers/image/styles.js +119 -0
- package/dist/viewers/json/controller.d.ts +89 -0
- package/dist/viewers/json/controller.js +411 -0
- package/dist/viewers/json/index.d.ts +25 -0
- package/dist/viewers/json/index.js +503 -0
- package/dist/viewers/json/styles.d.ts +1 -0
- package/dist/viewers/json/styles.js +346 -0
- package/dist/viewers/json/transforms.d.ts +27 -0
- package/dist/viewers/json/transforms.js +253 -0
- package/dist/viewers/jsonl/controller.d.ts +48 -0
- package/dist/viewers/jsonl/controller.js +136 -0
- package/dist/viewers/jsonl/index.d.ts +22 -0
- package/dist/viewers/jsonl/index.js +184 -0
- package/dist/viewers/jsonl/styles.d.ts +1 -0
- package/dist/viewers/jsonl/styles.js +1 -0
- package/dist/viewers/markdown/controller.d.ts +50 -0
- package/dist/viewers/markdown/controller.js +62 -0
- package/dist/viewers/markdown/index.d.ts +64 -0
- package/dist/viewers/markdown/index.js +425 -0
- package/dist/viewers/markdown/math.d.ts +21 -0
- package/dist/viewers/markdown/math.js +100 -0
- package/dist/viewers/markdown/self-loading.d.ts +5 -0
- package/dist/viewers/markdown/self-loading.js +48 -0
- package/dist/viewers/markdown/styles.d.ts +1 -0
- package/dist/viewers/markdown/styles.js +11 -0
- package/dist/viewers/mat/index.d.ts +19 -0
- package/dist/viewers/mat/index.js +113 -0
- package/dist/viewers/mat/styles.d.ts +1 -0
- package/dist/viewers/mat/styles.js +3 -0
- package/dist/viewers/media.d.ts +9 -0
- package/dist/viewers/media.js +77 -0
- package/dist/viewers/mermaid/index.d.ts +17 -0
- package/dist/viewers/mermaid/index.js +6 -0
- package/dist/viewers/mermaid/self-loading.d.ts +2 -0
- package/dist/viewers/mermaid/self-loading.js +2 -0
- package/dist/viewers/mf4/index.d.ts +15 -0
- package/dist/viewers/mf4/index.js +5 -0
- package/dist/viewers/parquet/controller.d.ts +37 -0
- package/dist/viewers/parquet/controller.js +26 -0
- package/dist/viewers/parquet/index.d.ts +23 -0
- package/dist/viewers/parquet/index.js +134 -0
- package/dist/viewers/parquet/styles.d.ts +1 -0
- package/dist/viewers/parquet/styles.js +3 -0
- package/dist/viewers/pcap/index.d.ts +15 -0
- package/dist/viewers/pcap/index.js +5 -0
- package/dist/viewers/pcapng/index.d.ts +15 -0
- package/dist/viewers/pcapng/index.js +5 -0
- package/dist/viewers/pdf/controller.d.ts +120 -0
- package/dist/viewers/pdf/controller.js +191 -0
- package/dist/viewers/pdf/editing.d.ts +26 -0
- package/dist/viewers/pdf/editing.js +265 -0
- package/dist/viewers/pdf/index.d.ts +88 -0
- package/dist/viewers/pdf/index.js +1400 -0
- package/dist/viewers/pdf/self-loading.d.ts +6 -0
- package/dist/viewers/pdf/self-loading.js +14 -0
- package/dist/viewers/pdf/styles.d.ts +1 -0
- package/dist/viewers/pdf/styles.js +555 -0
- package/dist/viewers/plantuml/index.d.ts +17 -0
- package/dist/viewers/plantuml/index.js +6 -0
- package/dist/viewers/plantuml/self-loading.d.ts +2 -0
- package/dist/viewers/plantuml/self-loading.js +2 -0
- package/dist/viewers/ppt/chart.d.ts +8 -0
- package/dist/viewers/ppt/chart.js +83 -0
- package/dist/viewers/ppt/controller.d.ts +34 -0
- package/dist/viewers/ppt/controller.js +27 -0
- package/dist/viewers/ppt/index.d.ts +33 -0
- package/dist/viewers/ppt/index.js +101 -0
- package/dist/viewers/ppt/render.d.ts +5 -0
- package/dist/viewers/ppt/render.js +144 -0
- package/dist/viewers/ppt/self-loading.d.ts +9 -0
- package/dist/viewers/ppt/self-loading.js +4 -0
- package/dist/viewers/ppt/styles.d.ts +1 -0
- package/dist/viewers/ppt/styles.js +1 -0
- package/dist/viewers/proto/index.d.ts +17 -0
- package/dist/viewers/proto/index.js +170 -0
- package/dist/viewers/proto/styles.d.ts +1 -0
- package/dist/viewers/proto/styles.js +2 -0
- package/dist/viewers/psd/index.d.ts +27 -0
- package/dist/viewers/psd/index.js +39 -0
- package/dist/viewers/psd/self-loading.d.ts +10 -0
- package/dist/viewers/psd/self-loading.js +20 -0
- package/dist/viewers/psd/styles.d.ts +1 -0
- package/dist/viewers/psd/styles.js +1 -0
- package/dist/viewers/reqif/index.d.ts +15 -0
- package/dist/viewers/reqif/index.js +5 -0
- package/dist/viewers/shapefile/index.d.ts +35 -0
- package/dist/viewers/shapefile/index.js +336 -0
- package/dist/viewers/shapefile/self-loading.d.ts +7 -0
- package/dist/viewers/shapefile/self-loading.js +32 -0
- package/dist/viewers/shapefile/styles.d.ts +1 -0
- package/dist/viewers/shapefile/styles.js +28 -0
- package/dist/viewers/stp/index.d.ts +15 -0
- package/dist/viewers/stp/index.js +5 -0
- package/dist/viewers/structured-styles.d.ts +1 -0
- package/dist/viewers/structured-styles.js +1 -0
- package/dist/viewers/structured.d.ts +36 -0
- package/dist/viewers/structured.js +138 -0
- package/dist/viewers/toml/controller.d.ts +38 -0
- package/dist/viewers/toml/controller.js +61 -0
- package/dist/viewers/toml/index.d.ts +18 -0
- package/dist/viewers/toml/index.js +34 -0
- package/dist/viewers/types.d.ts +36 -0
- package/dist/viewers/types.js +9 -0
- package/dist/viewers/video/controller.d.ts +29 -0
- package/dist/viewers/video/controller.js +54 -0
- package/dist/viewers/video/index.d.ts +18 -0
- package/dist/viewers/video/index.js +305 -0
- package/dist/viewers/video/styles.d.ts +1 -0
- package/dist/viewers/video/styles.js +79 -0
- package/dist/viewers/word/controller.d.ts +19 -0
- package/dist/viewers/word/controller.js +22 -0
- package/dist/viewers/word/docx-preprocess.d.ts +63 -0
- package/dist/viewers/word/docx-preprocess.js +136 -0
- package/dist/viewers/word/index.d.ts +32 -0
- package/dist/viewers/word/index.js +187 -0
- package/dist/viewers/word/normalize-docx.d.ts +2 -0
- package/dist/viewers/word/normalize-docx.js +50 -0
- package/dist/viewers/word/paginate.d.ts +1 -0
- package/dist/viewers/word/paginate.js +1035 -0
- package/dist/viewers/word/self-loading.d.ts +2 -0
- package/dist/viewers/word/self-loading.js +7 -0
- package/dist/viewers/word/styles.d.ts +1 -0
- package/dist/viewers/word/styles.js +686 -0
- package/dist/viewers/yaml/controller.d.ts +42 -0
- package/dist/viewers/yaml/controller.js +41 -0
- package/dist/viewers/yaml/index.d.ts +21 -0
- package/dist/viewers/yaml/index.js +20 -0
- package/package.json +375 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const finite = (value) => typeof value === 'number' && Number.isFinite(value);
|
|
2
|
+
export function computeChartBounds(data) { const explicitMin = data.valueAxis?.min, explicitMax = data.valueAxis?.max; if (data.kind === 'stackedColumn') {
|
|
3
|
+
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));
|
|
4
|
+
const min = finite(explicitMin) ? explicitMin : Math.min(0, ...negative), max = finite(explicitMax) ? explicitMax : Math.max(1, ...positive);
|
|
5
|
+
return min < max ? { min, max } : { min, max: min + 1 };
|
|
6
|
+
} 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(1, ...values); if (min >= max) {
|
|
7
|
+
const pad = Math.max(1, Math.abs(max || 1) * .1);
|
|
8
|
+
min -= pad;
|
|
9
|
+
max += pad;
|
|
10
|
+
} return { min, max }; }
|
|
11
|
+
const decimals = (format) => format?.match(/0\.([0#]+)/)?.[1]?.length ?? 0;
|
|
12
|
+
export function formatChartValue(value, format) { const places = decimals(format), fixed = Math.abs(value).toFixed(places), parts = fixed.split('.'), whole = (parts[0] ?? '0').replace(/\B(?=(\d{3})+(?!\d))/g, ','), numeric = parts[1] === undefined ? whole : `${whole}.${parts[1]}`, prefixed = format?.includes('$') ? `$${numeric}` : numeric; if (value >= 0)
|
|
13
|
+
return prefixed; return format && /;\s*\\?\(.*0/.test(format) ? `(${prefixed})` : `-${prefixed}`; }
|
|
14
|
+
const niceStep = (span) => { const rough = Math.max(span, 1e-6) / 8, p = 10 ** Math.floor(Math.log10(rough)), n = rough / p; return (n > 5 ? 10 : n > 2 ? 5 : n > 1 ? 2 : 1) * p; };
|
|
15
|
+
function legend(ctx, w, h, data, line) { const style = data.legend ?? {}, font = Math.max(10, style.fontSizePx ?? 12), items = data.series.map((s, i) => ({ color: s.color || '#999999', label: s.name || `Series ${i + 1}` })), swatch = line ? 18 : 10, gap = 22, total = items.reduce((n, item) => n + swatch + 6 + item.label.length * font * .55 + gap, 0); let x = Math.max(8, (w - total) / 2); ctx.font = `400 ${font}px Arial, sans-serif`; ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic'; for (const item of items) {
|
|
16
|
+
if (line) {
|
|
17
|
+
ctx.strokeStyle = item.color;
|
|
18
|
+
ctx.lineWidth = 2.5;
|
|
19
|
+
ctx.beginPath();
|
|
20
|
+
ctx.moveTo(x, h - 11);
|
|
21
|
+
ctx.lineTo(x + swatch, h - 11);
|
|
22
|
+
ctx.stroke();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
ctx.fillStyle = item.color;
|
|
26
|
+
ctx.fillRect(x, h - 16, swatch, swatch);
|
|
27
|
+
}
|
|
28
|
+
x += swatch + 6;
|
|
29
|
+
ctx.fillStyle = style.color ?? '#222222';
|
|
30
|
+
ctx.fillText(item.label, x, h - 7);
|
|
31
|
+
x += item.label.length * font * .55 + gap;
|
|
32
|
+
} }
|
|
33
|
+
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; ctx.strokeStyle = axis.gridColor ?? '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) {
|
|
34
|
+
const py = y(tick);
|
|
35
|
+
ctx.beginPath();
|
|
36
|
+
ctx.moveTo(m.left, py);
|
|
37
|
+
ctx.lineTo(m.left + pw, py);
|
|
38
|
+
ctx.stroke();
|
|
39
|
+
ctx.fillStyle = axis.color ?? '#222222';
|
|
40
|
+
ctx.font = `400 ${Math.max(10, axis.fontSizePx ?? 11)}px Arial, sans-serif`;
|
|
41
|
+
ctx.textAlign = 'right';
|
|
42
|
+
ctx.textBaseline = 'middle';
|
|
43
|
+
ctx.fillText(formatChartValue(tick, format), m.left - 12, py);
|
|
44
|
+
} 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(); return { ...m, pw, ph, y, format }; }
|
|
45
|
+
function label(ctx, series, value, x, y, height, axisFormat) { const style = series.dataLabel ?? {}; if (style.showValue === false || Math.abs(value) < .1 || height <= 12)
|
|
46
|
+
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); }
|
|
47
|
+
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) {
|
|
48
|
+
const value = series.values[i] ?? 0;
|
|
49
|
+
if (!finite(value) || value === 0)
|
|
50
|
+
continue;
|
|
51
|
+
const from = value >= 0 ? pos : neg;
|
|
52
|
+
if (value >= 0)
|
|
53
|
+
pos += value;
|
|
54
|
+
else
|
|
55
|
+
neg += value;
|
|
56
|
+
const to = value >= 0 ? pos : neg, top = Math.min(a.y(from), a.y(to)), height = Math.max(1, Math.abs(a.y(from) - a.y(to)));
|
|
57
|
+
ctx.fillStyle = series.color || '#d10000';
|
|
58
|
+
ctx.fillRect(cx - bar / 2, top, bar, height);
|
|
59
|
+
label(ctx, series, value, cx, top + height / 2, height, a.format);
|
|
60
|
+
} ctx.fillStyle = category.color ?? '#222222'; ctx.font = `400 ${Math.max(10, category.fontSizePx ?? 11)}px Arial, sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'alphabetic'; ctx.fillText(name, cx, h - 16); }); legend(ctx, w, h, data, false); }
|
|
61
|
+
function line(ctx, w, h, data, bounds) { const a = axes(ctx, w, h, data, bounds), x = (i) => data.categories.length === 1 ? a.left + a.pw / 2 : a.left + a.pw * i / Math.max(1, data.categories.length - 1), category = data.categoryAxis ?? {}; data.categories.forEach((name, i) => { ctx.fillStyle = category.color ?? '#222222'; ctx.font = `400 ${Math.max(10, category.fontSizePx ?? 11)}px Arial, sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'alphabetic'; ctx.fillText(name, x(i), h - 16); }); data.series.forEach((series, seriesIndex) => { const points = series.values.map((value, i) => ({ value, i })).filter(point => finite(point.value)); if (!points.length)
|
|
62
|
+
return; ctx.strokeStyle = series.color || '#4472c4'; ctx.lineWidth = 3; ctx.beginPath(); points.forEach((point, i) => i ? ctx.lineTo(x(point.i), a.y(point.value)) : ctx.moveTo(x(point.i), a.y(point.value))); ctx.stroke(); for (const point of points) {
|
|
63
|
+
const px = x(point.i), py = a.y(point.value);
|
|
64
|
+
ctx.fillStyle = '#fff';
|
|
65
|
+
ctx.beginPath();
|
|
66
|
+
ctx.arc(px, py, 4.5, 0, Math.PI * 2);
|
|
67
|
+
ctx.fill();
|
|
68
|
+
ctx.strokeStyle = series.color || '#4472c4';
|
|
69
|
+
ctx.stroke();
|
|
70
|
+
const style = series.dataLabel ?? {};
|
|
71
|
+
if (style.showValue !== false) {
|
|
72
|
+
ctx.fillStyle = style.color ?? series.color ?? '#222';
|
|
73
|
+
ctx.font = `600 ${Math.max(9, style.fontSizePx ?? 11)}px Arial, sans-serif`;
|
|
74
|
+
ctx.textAlign = 'center';
|
|
75
|
+
ctx.textBaseline = 'bottom';
|
|
76
|
+
ctx.fillText(formatChartValue(point.value, style.numFmt ?? series.valueFormat ?? a.format), px, py - 10 - seriesIndex * 2);
|
|
77
|
+
}
|
|
78
|
+
} }); legend(ctx, w, h, data, true); }
|
|
79
|
+
export function drawChart(canvas, data) { const ctx = canvas.getContext('2d'); if (!ctx || !data.categories.length || !data.series.length)
|
|
80
|
+
return; ctx.clearRect(0, 0, canvas.width, canvas.height); const bounds = computeChartBounds(data); if (data.kind === 'stackedColumn')
|
|
81
|
+
stacked(ctx, canvas.width, canvas.height, data, bounds);
|
|
82
|
+
else
|
|
83
|
+
line(ctx, canvas.width, canvas.height, data, bounds); }
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type PptRenderMode = 'continuous' | 'single';
|
|
2
|
+
export interface PptViewState {
|
|
3
|
+
currentSlide: number;
|
|
4
|
+
zoom: number;
|
|
5
|
+
mode: PptRenderMode;
|
|
6
|
+
slideCount: number;
|
|
7
|
+
}
|
|
8
|
+
export type PptAction = {
|
|
9
|
+
type: 'next';
|
|
10
|
+
} | {
|
|
11
|
+
type: 'previous';
|
|
12
|
+
} | {
|
|
13
|
+
type: 'jump';
|
|
14
|
+
slide: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'zoom-in';
|
|
17
|
+
} | {
|
|
18
|
+
type: 'zoom-out';
|
|
19
|
+
} | {
|
|
20
|
+
type: 'reset-zoom';
|
|
21
|
+
} | {
|
|
22
|
+
type: 'set-mode';
|
|
23
|
+
mode: PptRenderMode;
|
|
24
|
+
};
|
|
25
|
+
export interface PptController {
|
|
26
|
+
readonly state: PptViewState;
|
|
27
|
+
dispatch(action: PptAction): void;
|
|
28
|
+
subscribe(listener: (state: PptViewState) => void): () => void;
|
|
29
|
+
}
|
|
30
|
+
export declare const PPT_CONTINUOUS_THRESHOLD = 100;
|
|
31
|
+
export declare const PPT_MIN_ZOOM = 0.25;
|
|
32
|
+
export declare const PPT_MAX_ZOOM = 4;
|
|
33
|
+
export declare const PPT_ZOOM_STEP = 0.1;
|
|
34
|
+
export declare function createPptController(slideCount: number): PptController;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const PPT_CONTINUOUS_THRESHOLD = 100;
|
|
2
|
+
export const PPT_MIN_ZOOM = 0.25;
|
|
3
|
+
export const PPT_MAX_ZOOM = 4;
|
|
4
|
+
export const PPT_ZOOM_STEP = 0.1;
|
|
5
|
+
export function createPptController(slideCount) { const count = Math.max(0, Math.floor(slideCount)); let currentSlide = count ? 1 : 0; let zoom = 1; let mode = count > PPT_CONTINUOUS_THRESHOLD ? 'single' : 'continuous'; const listeners = new Set(); const state = () => ({ currentSlide, zoom, mode, slideCount: count }); const emit = () => { const s = state(); listeners.forEach(l => l(s)); }; return { get state() { return state(); }, dispatch(a) { switch (a.type) {
|
|
6
|
+
case 'next':
|
|
7
|
+
currentSlide = Math.min(count, currentSlide + 1);
|
|
8
|
+
break;
|
|
9
|
+
case 'previous':
|
|
10
|
+
currentSlide = Math.max(count ? 1 : 0, currentSlide - 1);
|
|
11
|
+
break;
|
|
12
|
+
case 'jump':
|
|
13
|
+
currentSlide = count ? Math.max(1, Math.min(count, Math.floor(a.slide))) : 0;
|
|
14
|
+
break;
|
|
15
|
+
case 'zoom-in':
|
|
16
|
+
zoom = Math.min(PPT_MAX_ZOOM, Math.round((zoom + PPT_ZOOM_STEP) * 100) / 100);
|
|
17
|
+
break;
|
|
18
|
+
case 'zoom-out':
|
|
19
|
+
zoom = Math.max(PPT_MIN_ZOOM, Math.round((zoom - PPT_ZOOM_STEP) * 100) / 100);
|
|
20
|
+
break;
|
|
21
|
+
case 'reset-zoom':
|
|
22
|
+
zoom = 1;
|
|
23
|
+
break;
|
|
24
|
+
case 'set-mode':
|
|
25
|
+
mode = a.mode;
|
|
26
|
+
break;
|
|
27
|
+
} emit(); }, subscribe(l) { listeners.add(l); return () => listeners.delete(l); } }; }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HostContext } from '../../host/index.js';
|
|
2
|
+
import { type PptxParserDeps } from '../../parsers/pptx/index.js';
|
|
3
|
+
import type { SlideDeck } from '../../parsers/slide-model.js';
|
|
4
|
+
import type { ParseOptions, ParseOutcome, ResourceLimits } from '../../parsers/types.js';
|
|
5
|
+
import { type PdfJsModule } from '../pdf/index.js';
|
|
6
|
+
import { type MountOptions, type ViewerInput } from '../types.js';
|
|
7
|
+
import { type PptController } from './controller.js';
|
|
8
|
+
export * from './controller.js';
|
|
9
|
+
export { pptViewerCss } from './styles.js';
|
|
10
|
+
export * from '../../parsers/slide-model.js';
|
|
11
|
+
export declare const PPT_VIEWER_META: {
|
|
12
|
+
id: string;
|
|
13
|
+
displayNameKey: string;
|
|
14
|
+
extensions: string[];
|
|
15
|
+
priority: number;
|
|
16
|
+
requiredServices: readonly [];
|
|
17
|
+
optionalServices: readonly [];
|
|
18
|
+
inputOwnership: "borrows";
|
|
19
|
+
};
|
|
20
|
+
export interface PptViewerDeps extends Partial<PptxParserDeps> {
|
|
21
|
+
convertToPdf?(input: Uint8Array, signal?: AbortSignal): Promise<Uint8Array>;
|
|
22
|
+
loadPdfjs?(): Promise<PdfJsModule>;
|
|
23
|
+
parseLegacyPptx?(input: Uint8Array, options: ParseOptions): Promise<ParseOutcome<SlideDeck>>;
|
|
24
|
+
}
|
|
25
|
+
export interface PptViewerHandle {
|
|
26
|
+
readonly controller: PptController;
|
|
27
|
+
readonly mode: 'slides' | 'pdf';
|
|
28
|
+
dispose(): void;
|
|
29
|
+
}
|
|
30
|
+
export interface PptMountOptions extends MountOptions {
|
|
31
|
+
limits?: ResourceLimits;
|
|
32
|
+
}
|
|
33
|
+
export declare function mountPptViewer(input: ViewerInput, container: HTMLElement, ctx: HostContext, deps?: PptViewerDeps, options?: PptMountOptions): Promise<PptViewerHandle>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { parsePptBinaryVscode } from '../../parsers/ppt-binary/index.js';
|
|
2
|
+
import { openPptxZip, parsePptx, parsePptxVscode } from '../../parsers/pptx/index.js';
|
|
3
|
+
import { mountPdfViewer } from '../pdf/index.js';
|
|
4
|
+
import { MountAbortedError, VIEWER_ROOT_CLASS } from '../types.js';
|
|
5
|
+
import { createPptController } from './controller.js';
|
|
6
|
+
import { renderSlide } from './render.js';
|
|
7
|
+
import { pptViewerCss } from './styles.js';
|
|
8
|
+
export * from './controller.js';
|
|
9
|
+
export { pptViewerCss } from './styles.js';
|
|
10
|
+
export * from '../../parsers/slide-model.js';
|
|
11
|
+
export const PPT_VIEWER_META = { id: 'ppt', displayNameKey: 'ppt.title', extensions: ['pptx', 'ppt'], priority: 15, requiredServices: [], optionalServices: [], inputOwnership: 'borrows' };
|
|
12
|
+
const el = (tag, cls, text) => { const value = document.createElement(tag); if (cls)
|
|
13
|
+
value.className = cls; if (text !== undefined)
|
|
14
|
+
value.textContent = text; return value; };
|
|
15
|
+
const pdfName = (name) => name.replace(/\.pptx?$/i, '.pdf');
|
|
16
|
+
async function mountPdfFallback(input, container, ctx, deps, options, originalMessageKey) {
|
|
17
|
+
if (!deps.convertToPdf)
|
|
18
|
+
throw new Error(ctx.i18n.t(originalMessageKey));
|
|
19
|
+
if (!deps.loadPdfjs)
|
|
20
|
+
throw new Error(ctx.i18n.t('diag.ppt.pdf-missing-dependency'));
|
|
21
|
+
const bytes = await deps.convertToPdf(input.data, options.signal);
|
|
22
|
+
if (options.signal?.aborted)
|
|
23
|
+
throw new MountAbortedError();
|
|
24
|
+
const pdf = await mountPdfViewer({ fileName: pdfName(input.fileName), data: bytes, ...(input.lastModified === undefined ? {} : { lastModified: input.lastModified }) }, container, ctx, { loadPdfjs: deps.loadPdfjs }, options);
|
|
25
|
+
return { controller: createPptController(0), mode: 'pdf', dispose: () => pdf.dispose() };
|
|
26
|
+
}
|
|
27
|
+
export async function mountPptViewer(input, container, ctx, deps = {}, options = {}) {
|
|
28
|
+
if (options.signal?.aborted)
|
|
29
|
+
throw new MountAbortedError();
|
|
30
|
+
const isPptx = input.fileName.toLowerCase().endsWith('.pptx');
|
|
31
|
+
const outcome = isPptx ? await parsePptxVscode(input.data, options) : await parsePptBinaryVscode(input.data, options);
|
|
32
|
+
if (options.signal?.aborted)
|
|
33
|
+
throw new MountAbortedError();
|
|
34
|
+
if (outcome.result.status === 'failed') {
|
|
35
|
+
if (outcome.result.failure.code !== 'invalid-format')
|
|
36
|
+
throw new Error(ctx.i18n.t(outcome.result.failure.messageKey, outcome.result.failure.args));
|
|
37
|
+
return mountPdfFallback(input, container, ctx, deps, options, outcome.result.failure.messageKey);
|
|
38
|
+
}
|
|
39
|
+
let deck = outcome.result.document;
|
|
40
|
+
if (deck.totalSlides === 0 || deck.slides.length === 0)
|
|
41
|
+
return mountPdfFallback(input, container, ctx, deps, options, 'diag.ppt.empty');
|
|
42
|
+
if (!deck.slides.some(slide => slide.elements.length > 0)) {
|
|
43
|
+
if (isPptx) {
|
|
44
|
+
const legacy = await (deps.parseLegacyPptx?.(input.data, options) ?? parsePptx(input.data, { openZip: openPptxZip }, options));
|
|
45
|
+
if (legacy.result.status !== 'failed' && legacy.result.document.slides.some(slide => slide.elements.length > 0))
|
|
46
|
+
deck = legacy.result.document;
|
|
47
|
+
else
|
|
48
|
+
return mountPdfFallback(input, container, ctx, deps, options, 'diag.ppt.empty');
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
return mountPdfFallback(input, container, ctx, deps, options, 'diag.ppt.empty');
|
|
52
|
+
}
|
|
53
|
+
const root = options.styleIsolation !== 'scoped' && typeof container.attachShadow === 'function' ? (container.shadowRoot ?? container.attachShadow({ mode: 'open' })) : container;
|
|
54
|
+
if (root === container)
|
|
55
|
+
container.classList.add(VIEWER_ROOT_CLASS, 'omni-viewer--ppt');
|
|
56
|
+
else {
|
|
57
|
+
root.replaceChildren();
|
|
58
|
+
const style = el('style');
|
|
59
|
+
style.textContent = pptViewerCss;
|
|
60
|
+
root.append(style);
|
|
61
|
+
}
|
|
62
|
+
const frame = el('section', 'omni-ppt'), toolbar = el('div', 'omni-ppt__toolbar'), prev = el('button', undefined, '‹'), next = el('button', undefined, '›'), jump = el('select'), count = el('span'), minus = el('button', undefined, '−'), plus = el('button', undefined, '+'), reset = el('button', undefined, '100%'), mode = el('button');
|
|
63
|
+
prev.setAttribute('aria-label', ctx.i18n.t('ppt.previous'));
|
|
64
|
+
next.setAttribute('aria-label', ctx.i18n.t('ppt.next'));
|
|
65
|
+
jump.setAttribute('aria-label', ctx.i18n.t('ppt.jump'));
|
|
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);
|
|
68
|
+
const viewport = el('div', 'omni-ppt__viewport'), slides = el('div', 'omni-ppt__slides');
|
|
69
|
+
viewport.append(slides);
|
|
70
|
+
frame.append(toolbar, viewport);
|
|
71
|
+
root.append(frame);
|
|
72
|
+
const controller = createPptController(deck.totalSlides);
|
|
73
|
+
const render = (state = controller.state) => { count.textContent = `${state.currentSlide} / ${state.slideCount}`; reset.textContent = `${Math.round(state.zoom * 100)}%`; mode.textContent = ctx.i18n.t(state.mode === 'continuous' ? 'ppt.mode.single' : 'ppt.mode.continuous'); jump.value = String(state.currentSlide); slides.replaceChildren(); const shown = state.mode === 'single' ? deck.slides.filter(slide => slide.slideNumber === state.currentSlide) : deck.slides; shown.forEach(slide => slides.append(renderSlide(slide, state.zoom))); };
|
|
74
|
+
const scrollCurrent = () => { if (controller.state.mode !== 'continuous')
|
|
75
|
+
return; slides.querySelector(`[aria-label="Slide ${controller.state.currentSlide}"]`)?.scrollIntoView({ block: 'start' }); };
|
|
76
|
+
const navigate = (action) => { controller.dispatch(action); scrollCurrent(); };
|
|
77
|
+
const onKeydown = (event) => { if (!(event.ctrlKey || event.metaKey))
|
|
78
|
+
return; if (event.key === '=' || event.key === '+') {
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
controller.dispatch({ type: 'zoom-in' });
|
|
81
|
+
}
|
|
82
|
+
else if (event.key === '-') {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
controller.dispatch({ type: 'zoom-out' });
|
|
85
|
+
}
|
|
86
|
+
else if (event.key === '0') {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
controller.dispatch({ type: 'reset-zoom' });
|
|
89
|
+
} };
|
|
90
|
+
const off = controller.subscribe(render);
|
|
91
|
+
prev.onclick = () => navigate({ type: 'previous' });
|
|
92
|
+
next.onclick = () => navigate({ type: 'next' });
|
|
93
|
+
jump.onchange = () => navigate({ type: 'jump', slide: Number(jump.value) });
|
|
94
|
+
minus.onclick = () => controller.dispatch({ type: 'zoom-out' });
|
|
95
|
+
plus.onclick = () => controller.dispatch({ type: 'zoom-in' });
|
|
96
|
+
reset.onclick = () => controller.dispatch({ type: 'reset-zoom' });
|
|
97
|
+
mode.onclick = () => controller.dispatch({ type: 'set-mode', mode: controller.state.mode === 'continuous' ? 'single' : 'continuous' });
|
|
98
|
+
document.addEventListener('keydown', onKeydown);
|
|
99
|
+
render();
|
|
100
|
+
return { controller, mode: 'slides', dispose() { off(); document.removeEventListener('keydown', onKeydown); frame.remove(); } };
|
|
101
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Slide, SlideElement } from '../../parsers/slide-model.js';
|
|
2
|
+
export declare function getReadableTextColor(background: string): '#ffffff' | '#111111';
|
|
3
|
+
export declare function presetGeomToSvgPath(preset: string | undefined, w: number, h: number): string | undefined;
|
|
4
|
+
export declare function renderSlideElement(e: SlideElement, fallbackColor?: string): HTMLElement;
|
|
5
|
+
export declare function renderSlide(slide: Slide, zoom: number): HTMLElement;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { drawChart } from './chart.js';
|
|
2
|
+
const HTML_NS = 'http://www.w3.org/1999/xhtml', SVG_NS = 'http://www.w3.org/2000/svg';
|
|
3
|
+
const node = (tag, cls) => { const value = document.createElementNS(HTML_NS, tag); if (cls)
|
|
4
|
+
value.className = cls; return value; };
|
|
5
|
+
const paragraphAlign = (value) => value === 'ctr' ? 'center' : value === 'r' ? 'right' : value === 'just' || value === 'dist' ? 'justify' : value === 'l' ? 'left' : value ?? '';
|
|
6
|
+
export function getReadableTextColor(background) { const raw = background.replace('#', '').trim(), hex = raw.length === 3 ? raw.split('').map(value => value + value).join('') : raw; if (!/^[0-9a-f]{6}$/i.test(hex))
|
|
7
|
+
return '#111111'; const r = parseInt(hex.slice(0, 2), 16), g = parseInt(hex.slice(2, 4), 16), b = parseInt(hex.slice(4, 6), 16); return (.299 * r + .587 * g + .114 * b) / 255 < .58 ? '#ffffff' : '#111111'; }
|
|
8
|
+
function frame(e, kind = e.type) { const box = node('div', `omni-ppt__element omni-ppt__${kind}`); Object.assign(box.style, { left: `${e.x}px`, top: `${e.y}px`, width: `${e.width}px`, height: `${e.height}px`, zIndex: String(e.zIndex), transform: `${e.rotateDeg ? `rotate(${e.rotateDeg}deg) ` : ''}${e.flipH ? 'scaleX(-1) ' : ''}${e.flipV ? 'scaleY(-1)' : ''}`.trim(), transformOrigin: 'top left' }); return box; }
|
|
9
|
+
function applyParagraphStyle(target, p, e, fallbackColor) { const level = Math.max(0, p.level || 0), fontSize = p.fontSizePx ?? (e.isTitle ? 52 : Math.max(16, 24 - level * 2)); target.style.marginLeft = `${level * 16}px`; target.style.textAlign = paragraphAlign(p.align); target.style.fontSize = `${fontSize}px`; target.style.lineHeight = `${Math.max(18, Math.round(fontSize * 1.18))}px`; target.style.color = p.color ?? fallbackColor; target.style.fontWeight = p.bold ? '700' : '400'; if (p.italic)
|
|
10
|
+
target.style.fontStyle = 'italic'; if (e.textStylePreset === 'cover-title') {
|
|
11
|
+
target.style.fontFamily = "'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif";
|
|
12
|
+
target.style.fontWeight = p.bold ? '700' : '400';
|
|
13
|
+
target.style.letterSpacing = '-2px';
|
|
14
|
+
target.style.lineHeight = `${Math.max(24, Math.round(fontSize * .98))}px`;
|
|
15
|
+
}
|
|
16
|
+
else if (e.textStylePreset === 'cover-subtitle') {
|
|
17
|
+
target.style.fontFamily = "'Malgun Gothic', 'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif";
|
|
18
|
+
target.style.fontWeight = p.bold ? '700' : '300';
|
|
19
|
+
target.style.letterSpacing = '-1px';
|
|
20
|
+
target.style.lineHeight = `${Math.max(20, Math.round(fontSize * 1.02))}px`;
|
|
21
|
+
} }
|
|
22
|
+
function textElement(e, fallbackColor) { const box = frame(e, 'text'); if (e.textStylePreset)
|
|
23
|
+
box.dataset.textPreset = e.textStylePreset; if (e.fillColor)
|
|
24
|
+
box.style.backgroundColor = e.fillColor; if (e.borderColor)
|
|
25
|
+
box.style.border = `${Math.max(1, e.borderWidthPx ?? 1)}px solid ${e.borderColor}`; const paragraphs = e.paragraphs ?? [], likelyList = !e.isTitle && paragraphs.length >= 3; for (const p of paragraphs) {
|
|
26
|
+
const line = node('p', 'omni-ppt__text-line');
|
|
27
|
+
applyParagraphStyle(line, p, e, fallbackColor);
|
|
28
|
+
if (p.bullet || (likelyList && p.text.trim())) {
|
|
29
|
+
const bullet = node('span', 'omni-ppt__bullet');
|
|
30
|
+
bullet.textContent = '•';
|
|
31
|
+
bullet.style.color = p.color ?? fallbackColor;
|
|
32
|
+
line.append(bullet);
|
|
33
|
+
}
|
|
34
|
+
if (p.runs?.length) {
|
|
35
|
+
for (const r of p.runs) {
|
|
36
|
+
const span = node('span');
|
|
37
|
+
span.textContent = r.text;
|
|
38
|
+
if (r.fontSizePx)
|
|
39
|
+
span.style.fontSize = `${r.fontSizePx}px`;
|
|
40
|
+
span.style.color = r.color ?? p.color ?? fallbackColor;
|
|
41
|
+
if (r.bold)
|
|
42
|
+
span.style.fontWeight = '700';
|
|
43
|
+
if (r.italic)
|
|
44
|
+
span.style.fontStyle = 'italic';
|
|
45
|
+
line.append(span);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
line.append(document.createTextNode(p.text));
|
|
50
|
+
box.append(line);
|
|
51
|
+
} return box; }
|
|
52
|
+
function imageElement(e) { const box = frame(e, 'image'), img = node('img', 'omni-ppt__image'); img.src = e.src ?? ''; img.alt = ''; const crop = e.srcRect; if (crop && (crop.l || crop.t || crop.r || crop.b)) {
|
|
53
|
+
const visibleW = Math.max(1, 100 - crop.l - crop.r), visibleH = Math.max(1, 100 - crop.t - crop.b), scaleW = 100 / visibleW, scaleH = 100 / visibleH;
|
|
54
|
+
Object.assign(img.style, { position: 'absolute', width: `${100 * scaleW}%`, height: `${100 * scaleH}%`, left: `${-crop.l * scaleW}%`, top: `${-crop.t * scaleH}%`, objectFit: 'fill', maxWidth: 'none', maxHeight: 'none' });
|
|
55
|
+
box.style.overflow = 'hidden';
|
|
56
|
+
} box.append(img); return box; }
|
|
57
|
+
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; }
|
|
58
|
+
function chartElement(e) { const box = frame(e, 'chart'); if (e.chartData) {
|
|
59
|
+
const canvas = node('canvas');
|
|
60
|
+
canvas.width = Math.max(300, Math.floor(e.width));
|
|
61
|
+
canvas.height = Math.max(180, Math.floor(e.height));
|
|
62
|
+
canvas.style.width = '100%';
|
|
63
|
+
canvas.style.height = '100%';
|
|
64
|
+
box.append(canvas);
|
|
65
|
+
queueMicrotask(() => drawChart(canvas, e.chartData));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
box.textContent = e.chartTitle ?? (e.chartKind === 'smartart' ? 'SmartArt' : 'Chart');
|
|
69
|
+
} return box; }
|
|
70
|
+
export function presetGeomToSvgPath(preset, w, h) { if (!preset || w <= 0 || h <= 0)
|
|
71
|
+
return; switch (preset) {
|
|
72
|
+
case 'rightArrow': {
|
|
73
|
+
const bh = h * .6, top = (h - bh) / 2, head = Math.min(w * .35, h * .9), right = w - head;
|
|
74
|
+
return `M 0 ${top} L ${right} ${top} L ${right} 0 L ${w} ${h / 2} L ${right} ${h} L ${right} ${top + bh} L 0 ${top + bh} Z`;
|
|
75
|
+
}
|
|
76
|
+
case 'leftArrow': {
|
|
77
|
+
const bh = h * .6, top = (h - bh) / 2, head = Math.min(w * .35, h * .9);
|
|
78
|
+
return `M ${w} ${top} L ${head} ${top} L ${head} 0 L 0 ${h / 2} L ${head} ${h} L ${head} ${top + bh} L ${w} ${top + bh} Z`;
|
|
79
|
+
}
|
|
80
|
+
case 'upArrow': {
|
|
81
|
+
const bw = w * .6, left = (w - bw) / 2, head = Math.min(h * .35, w * .9);
|
|
82
|
+
return `M ${left} ${h} L ${left} ${head} L 0 ${head} L ${w / 2} 0 L ${w} ${head} L ${left + bw} ${head} L ${left + bw} ${h} Z`;
|
|
83
|
+
}
|
|
84
|
+
case 'downArrow': {
|
|
85
|
+
const bw = w * .6, left = (w - bw) / 2, bottom = h - Math.min(h * .35, w * .9);
|
|
86
|
+
return `M ${left} 0 L ${left + bw} 0 L ${left + bw} ${bottom} L ${w} ${bottom} L ${w / 2} ${h} L 0 ${bottom} L ${left} ${bottom} Z`;
|
|
87
|
+
}
|
|
88
|
+
case 'roundRect': {
|
|
89
|
+
const r = Math.min(w, h) * .12;
|
|
90
|
+
return `M ${r} 0 L ${w - r} 0 Q ${w} 0 ${w} ${r} L ${w} ${h - r} Q ${w} ${h} ${w - r} ${h} L ${r} ${h} Q 0 ${h} 0 ${h - r} L 0 ${r} Q 0 0 ${r} 0 Z`;
|
|
91
|
+
}
|
|
92
|
+
case 'ellipse':
|
|
93
|
+
case 'oval': return `M ${w / 2} 0 A ${w / 2} ${h / 2} 0 1 0 ${w / 2} ${h} A ${w / 2} ${h / 2} 0 1 0 ${w / 2} 0 Z`;
|
|
94
|
+
case 'triangle': return `M ${w / 2} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
95
|
+
case 'rect': return `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
96
|
+
default: return;
|
|
97
|
+
} }
|
|
98
|
+
function marker(defs, id, type, color) { const value = document.createElementNS(SVG_NS, 'marker'); value.id = id; value.setAttribute('markerWidth', '8'); value.setAttribute('markerHeight', '8'); value.setAttribute('refX', type === 'start' ? '0' : '8'); value.setAttribute('refY', '4'); value.setAttribute('orient', 'auto'); value.setAttribute('markerUnits', 'strokeWidth'); const path = document.createElementNS(SVG_NS, 'path'); path.setAttribute('d', type === 'circle' ? 'M 4 1 A 3 3 0 1 0 4 7 A 3 3 0 1 0 4 1' : 'M 0 0 L 8 4 L 0 8 Z'); path.setAttribute('fill', color); value.append(path); defs.append(value); }
|
|
99
|
+
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}`); const stroke = e.borderColor ?? '#000', strokeWidth = String(Math.max(1, e.borderWidthPx ?? 1)); if (e.presetGeom === 'line' || e.presetGeom === 'straightConnector1') {
|
|
100
|
+
box.style.transform = e.rotateDeg ? `rotate(${e.rotateDeg}deg)` : '';
|
|
101
|
+
const defs = document.createElementNS(SVG_NS, 'defs'), suffix = `${e.zIndex}-${Math.round(e.x)}-${Math.round(e.y)}`;
|
|
102
|
+
marker(defs, `arrow-${suffix}`, 'end', stroke);
|
|
103
|
+
marker(defs, `circle-${suffix}`, 'circle', stroke);
|
|
104
|
+
svg.append(defs);
|
|
105
|
+
const line = document.createElementNS(SVG_NS, 'line');
|
|
106
|
+
line.setAttribute('x1', String(e.flipH ? w : 0));
|
|
107
|
+
line.setAttribute('y1', String(e.flipV ? h : 0));
|
|
108
|
+
line.setAttribute('x2', String(e.flipH ? 0 : w));
|
|
109
|
+
line.setAttribute('y2', String(e.flipV ? 0 : h));
|
|
110
|
+
line.setAttribute('stroke', stroke);
|
|
111
|
+
line.setAttribute('stroke-width', strokeWidth);
|
|
112
|
+
const markerUrl = (type) => type && ['oval', 'diamond'].includes(type) ? `url(#circle-${suffix})` : type && ['arrow', 'triangle', 'stealth'].includes(type) ? `url(#arrow-${suffix})` : undefined;
|
|
113
|
+
const start = markerUrl(e.headEnd), end = markerUrl(e.tailEnd);
|
|
114
|
+
if (start)
|
|
115
|
+
line.setAttribute('marker-start', start);
|
|
116
|
+
if (end)
|
|
117
|
+
line.setAttribute('marker-end', end);
|
|
118
|
+
svg.append(line);
|
|
119
|
+
box.style.overflow = 'visible';
|
|
120
|
+
box.append(svg);
|
|
121
|
+
return box;
|
|
122
|
+
} const d = e.customSvgPath ?? presetGeomToSvgPath(e.presetGeom, w, h); if (d) {
|
|
123
|
+
const path = document.createElementNS(SVG_NS, 'path');
|
|
124
|
+
path.setAttribute('d', d);
|
|
125
|
+
path.setAttribute('fill', e.fillColor ?? 'none');
|
|
126
|
+
path.setAttribute('stroke', e.borderColor ?? 'none');
|
|
127
|
+
path.setAttribute('stroke-width', strokeWidth);
|
|
128
|
+
svg.append(path);
|
|
129
|
+
box.append(svg);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
if (e.fillColor)
|
|
133
|
+
box.style.background = e.fillColor;
|
|
134
|
+
if (e.borderColor)
|
|
135
|
+
box.style.border = `${strokeWidth}px solid ${e.borderColor}`;
|
|
136
|
+
} return box; }
|
|
137
|
+
export function renderSlideElement(e, fallbackColor = '#111111') { switch (e.type) {
|
|
138
|
+
case 'text': return textElement(e, fallbackColor);
|
|
139
|
+
case 'image': return imageElement(e);
|
|
140
|
+
case 'table': return tableElement(e);
|
|
141
|
+
case 'chart': return chartElement(e);
|
|
142
|
+
default: return shapeElement(e);
|
|
143
|
+
} }
|
|
144
|
+
export function renderSlide(slide, zoom) { const article = node('article', 'omni-ppt__slide'); article.setAttribute('aria-label', `Slide ${slide.slideNumber}`); Object.assign(article.style, { width: `${slide.widthPx}px`, height: `${slide.heightPx}px`, background: slide.backgroundColor, transform: `scale(${zoom})`, marginBottom: `${slide.heightPx * (zoom - 1)}px` }); const fallbackColor = getReadableTextColor(slide.backgroundColor); slide.elements.map(element => element.type === 'image' && !!element.src && element.width >= slide.widthPx * .8 && element.height >= slide.heightPx * .8 && element.zIndex < 0 ? { ...element, zIndex: 0 } : element).sort((a, b) => a.zIndex - b.zIndex).forEach(element => article.append(renderSlideElement(element, fallbackColor))); return article; }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HostContext } from '../../host/index.js';
|
|
2
|
+
import type { ViewerInput } from '../types.js';
|
|
3
|
+
import type { PdfJsModule } from '../pdf/index.js';
|
|
4
|
+
import { type PptMountOptions, type PptViewerDeps, type PptViewerHandle } from './index.js';
|
|
5
|
+
export * from './index.js';
|
|
6
|
+
export declare const selfLoadingPptPdfDeps: {
|
|
7
|
+
loadPdfjs: () => Promise<PdfJsModule>;
|
|
8
|
+
};
|
|
9
|
+
export declare function mountSelfLoadingPptViewer(input: ViewerInput, container: HTMLElement, ctx: HostContext, deps?: Omit<PptViewerDeps, 'loadPdfjs'>, options?: PptMountOptions): Promise<PptViewerHandle>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { mountPptViewer } from './index.js';
|
|
2
|
+
export * from './index.js';
|
|
3
|
+
export const selfLoadingPptPdfDeps = { loadPdfjs: () => import('pdfjs-dist/build/pdf.mjs') };
|
|
4
|
+
export function mountSelfLoadingPptViewer(input, container, ctx, deps = {}, options = {}) { return mountPptViewer(input, container, ctx, { ...deps, ...selfLoadingPptPdfDeps }, options); }
|
|
@@ -0,0 +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}.omni-ppt__text p{margin:0}.omni-ppt__image{width:100%;height:100%;object-fit:contain}.omni-ppt__table{border-collapse:collapse;width:100%;height:100%;color:#111}.omni-ppt__table td{border:1px solid #777;padding:3px}.omni-ppt__shape{width:100%;height:100%}.omni-ppt__empty{padding:32px;text-align:center}";
|
|
@@ -0,0 +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}.omni-ppt__text p{margin:0}.omni-ppt__image{width:100%;height:100%;object-fit:contain}.omni-ppt__table{border-collapse:collapse;width:100%;height:100%;color:#111}.omni-ppt__table td{border:1px solid #777;padding:3px}.omni-ppt__shape{width:100%;height:100%}.omni-ppt__empty{padding:32px;text-align:center}`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ClipboardService, HostContext } from '../../host/index.js';
|
|
2
|
+
import { type MountOptions, type ViewerHandle, type ViewerInput } from '../types.js';
|
|
3
|
+
export { protoViewerCss } from './styles.js';
|
|
4
|
+
export { parseProto } from '../../parsers/proto/index.js';
|
|
5
|
+
export declare const PROTO_VIEWER_META: {
|
|
6
|
+
id: string;
|
|
7
|
+
displayNameKey: string;
|
|
8
|
+
extensions: string[];
|
|
9
|
+
priority: number;
|
|
10
|
+
requiredServices: readonly [];
|
|
11
|
+
optionalServices: readonly ["clipboard"];
|
|
12
|
+
inputOwnership: "borrows";
|
|
13
|
+
};
|
|
14
|
+
export type ProtoViewerContext = HostContext & {
|
|
15
|
+
clipboard?: ClipboardService;
|
|
16
|
+
};
|
|
17
|
+
export declare function mountProtoViewer(input: ViewerInput, container: HTMLElement, ctx: ProtoViewerContext, options?: MountOptions): Promise<ViewerHandle>;
|