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,336 @@
|
|
|
1
|
+
import { parseDbf } from '../../parsers/shapefile/dbf.js';
|
|
2
|
+
import { parseShapefile } from '../../parsers/shapefile/index.js';
|
|
3
|
+
import { isGeographicWkt, projectionName, reprojectModel } from '../../parsers/shapefile/reproject.js';
|
|
4
|
+
import { MountAbortedError, VIEWER_ROOT_CLASS } from '../types.js';
|
|
5
|
+
import { shapefileViewerCss } from './styles.js';
|
|
6
|
+
export { parseShapefile } from '../../parsers/shapefile/index.js';
|
|
7
|
+
export { parseDbf } from '../../parsers/shapefile/dbf.js';
|
|
8
|
+
export { isGeographicWkt, projectionName, reprojectModel } from '../../parsers/shapefile/reproject.js';
|
|
9
|
+
export { shapefileViewerCss } from './styles.js';
|
|
10
|
+
export const SHAPEFILE_VIEWER_META = {
|
|
11
|
+
id: 'shapefile',
|
|
12
|
+
displayNameKey: 'shapefile.title',
|
|
13
|
+
extensions: ['shp'],
|
|
14
|
+
priority: 20,
|
|
15
|
+
requiredServices: [],
|
|
16
|
+
optionalServices: [],
|
|
17
|
+
inputOwnership: 'borrows'
|
|
18
|
+
};
|
|
19
|
+
const ZOOM_FACTOR = 1.5;
|
|
20
|
+
export async function mountShapefileViewer(input, container, ctx, options = {}) {
|
|
21
|
+
if (options.signal?.aborted)
|
|
22
|
+
throw new MountAbortedError();
|
|
23
|
+
const t = (key, args) => ctx.i18n.t(key, args);
|
|
24
|
+
let model = parseShapefile(input.data, {
|
|
25
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
26
|
+
...(options.maxBytes ? { maxBytes: options.maxBytes } : {}),
|
|
27
|
+
...(options.maxFeatures ? { maxFeatures: options.maxFeatures } : {}),
|
|
28
|
+
...(options.maxVertices ? { maxVertices: options.maxVertices } : {})
|
|
29
|
+
});
|
|
30
|
+
const warnings = [...model.warnings];
|
|
31
|
+
// Optional .prj reprojection to WGS84 (only for projected systems).
|
|
32
|
+
let projectionLabel = null;
|
|
33
|
+
const wkt = options.sidecars?.prj?.trim();
|
|
34
|
+
if (wkt) {
|
|
35
|
+
projectionLabel = projectionName(wkt) ?? wkt.slice(0, 40);
|
|
36
|
+
if (!isGeographicWkt(wkt) && options.createReprojector) {
|
|
37
|
+
try {
|
|
38
|
+
const convert = options.createReprojector(wkt);
|
|
39
|
+
if (convert) {
|
|
40
|
+
model = reprojectModel(model, convert);
|
|
41
|
+
projectionLabel = `${projectionLabel} → WGS84`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
warnings.push(t('shapefile.reprojectFailed'));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Optional .dbf attribute table; records pair with features by ordinal.
|
|
50
|
+
let attributes = null;
|
|
51
|
+
if (options.sidecars?.dbf) {
|
|
52
|
+
try {
|
|
53
|
+
attributes = parseDbf(options.sidecars.dbf, {
|
|
54
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
55
|
+
...(options.maxFeatures ? { maxRecords: options.maxFeatures } : {})
|
|
56
|
+
});
|
|
57
|
+
warnings.push(...attributes.warnings);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
warnings.push(`${t('shapefile.dbfFailed')} ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const root = options.styleIsolation !== 'scoped' && container.attachShadow
|
|
64
|
+
? (container.shadowRoot ?? container.attachShadow({ mode: 'open' }))
|
|
65
|
+
: container;
|
|
66
|
+
if (root === container)
|
|
67
|
+
container.classList.add(VIEWER_ROOT_CLASS, 'omni-viewer--shapefile');
|
|
68
|
+
else {
|
|
69
|
+
const style = document.createElement('style');
|
|
70
|
+
style.textContent = shapefileViewerCss;
|
|
71
|
+
root.append(style);
|
|
72
|
+
}
|
|
73
|
+
const disposers = [];
|
|
74
|
+
const listen = (target, type, handler, opts) => {
|
|
75
|
+
target.addEventListener(type, handler, opts);
|
|
76
|
+
disposers.push(() => target.removeEventListener(type, handler, opts));
|
|
77
|
+
};
|
|
78
|
+
const shell = element('section', `${VIEWER_ROOT_CLASS} omni-shp`);
|
|
79
|
+
const header = element('header', 'omni-shp__header');
|
|
80
|
+
const title = element('div', 'omni-shp__title', input.fileName);
|
|
81
|
+
const summaryParts = [
|
|
82
|
+
model.shapeTypeName,
|
|
83
|
+
`${model.features.length.toLocaleString()} features`,
|
|
84
|
+
`${model.vertexCount.toLocaleString()} vertices`
|
|
85
|
+
];
|
|
86
|
+
if (attributes)
|
|
87
|
+
summaryParts.push(`${attributes.fields.length.toLocaleString()} attribute fields`);
|
|
88
|
+
const summary = element('div', 'omni-shp__summary', summaryParts.join(' · '));
|
|
89
|
+
header.append(title, summary);
|
|
90
|
+
const toolbar = element('div', 'omni-shp__toolbar');
|
|
91
|
+
const zoomIn = button(t('shapefile.zoomIn'));
|
|
92
|
+
const zoomOut = button(t('shapefile.zoomOut'));
|
|
93
|
+
const reset = button(t('shapefile.reset'));
|
|
94
|
+
toolbar.append(zoomIn, zoomOut, reset);
|
|
95
|
+
if (projectionLabel)
|
|
96
|
+
toolbar.append(element('span', 'omni-shp__projection', t('shapefile.projection', { name: projectionLabel })));
|
|
97
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
98
|
+
svg.classList.add('omni-shp__map');
|
|
99
|
+
svg.setAttribute('role', 'img');
|
|
100
|
+
const initialView = renderModel(svg, model);
|
|
101
|
+
let view = initialView ? { ...initialView } : null;
|
|
102
|
+
const attributesPanel = element('div', 'omni-shp__attributes');
|
|
103
|
+
const attributesTitle = element('div', 'omni-shp__attributes-title', t('shapefile.attributes'));
|
|
104
|
+
const attributesBody = element('div');
|
|
105
|
+
attributesBody.className = 'omni-shp__attributes-empty';
|
|
106
|
+
attributesBody.textContent = t('shapefile.noSelection');
|
|
107
|
+
attributesPanel.append(attributesTitle, attributesBody);
|
|
108
|
+
const warning = element('div', 'omni-shp__warning');
|
|
109
|
+
warning.hidden = warnings.length === 0;
|
|
110
|
+
warning.textContent = warnings.join('\n');
|
|
111
|
+
shell.append(header, toolbar, svg, attributesPanel, warning);
|
|
112
|
+
root.append(shell);
|
|
113
|
+
// --- pan / zoom ---------------------------------------------------------
|
|
114
|
+
const applyView = () => {
|
|
115
|
+
if (view)
|
|
116
|
+
svg.setAttribute('viewBox', `${view.x} ${view.y} ${view.w} ${view.h}`);
|
|
117
|
+
};
|
|
118
|
+
const zoomAt = (factor, cx, cy) => {
|
|
119
|
+
if (!view)
|
|
120
|
+
return;
|
|
121
|
+
const centerX = cx ?? view.x + view.w / 2;
|
|
122
|
+
const centerY = cy ?? view.y + view.h / 2;
|
|
123
|
+
view = {
|
|
124
|
+
x: centerX - (centerX - view.x) / factor,
|
|
125
|
+
y: centerY - (centerY - view.y) / factor,
|
|
126
|
+
w: view.w / factor,
|
|
127
|
+
h: view.h / factor
|
|
128
|
+
};
|
|
129
|
+
applyView();
|
|
130
|
+
};
|
|
131
|
+
listen(zoomIn, 'click', () => zoomAt(ZOOM_FACTOR));
|
|
132
|
+
listen(zoomOut, 'click', () => zoomAt(1 / ZOOM_FACTOR));
|
|
133
|
+
listen(reset, 'click', () => { if (initialView) {
|
|
134
|
+
view = { ...initialView };
|
|
135
|
+
applyView();
|
|
136
|
+
} });
|
|
137
|
+
listen(svg, 'wheel', ((event) => {
|
|
138
|
+
if (!view)
|
|
139
|
+
return;
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
const rect = svg.getBoundingClientRect();
|
|
142
|
+
const point = rect.width > 0 && rect.height > 0
|
|
143
|
+
? {
|
|
144
|
+
x: view.x + ((event.clientX - rect.left) / rect.width) * view.w,
|
|
145
|
+
y: view.y + ((event.clientY - rect.top) / rect.height) * view.h
|
|
146
|
+
}
|
|
147
|
+
: undefined;
|
|
148
|
+
zoomAt(event.deltaY > 0 ? 1 / ZOOM_FACTOR : ZOOM_FACTOR, point?.x, point?.y);
|
|
149
|
+
}), { passive: false });
|
|
150
|
+
let panFrom = null;
|
|
151
|
+
listen(svg, 'pointerdown', ((event) => {
|
|
152
|
+
if (!view)
|
|
153
|
+
return;
|
|
154
|
+
panFrom = { clientX: event.clientX, clientY: event.clientY, view: { ...view } };
|
|
155
|
+
svg.classList.add('is-panning');
|
|
156
|
+
}));
|
|
157
|
+
listen(document, 'pointermove', ((event) => {
|
|
158
|
+
if (!panFrom || !view)
|
|
159
|
+
return;
|
|
160
|
+
const rect = svg.getBoundingClientRect();
|
|
161
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
162
|
+
return;
|
|
163
|
+
view = {
|
|
164
|
+
...view,
|
|
165
|
+
x: panFrom.view.x - ((event.clientX - panFrom.clientX) / rect.width) * panFrom.view.w,
|
|
166
|
+
y: panFrom.view.y - ((event.clientY - panFrom.clientY) / rect.height) * panFrom.view.h
|
|
167
|
+
};
|
|
168
|
+
applyView();
|
|
169
|
+
}));
|
|
170
|
+
listen(document, 'pointerup', (() => {
|
|
171
|
+
panFrom = null;
|
|
172
|
+
svg.classList.remove('is-panning');
|
|
173
|
+
}));
|
|
174
|
+
// --- feature selection ---------------------------------------------------
|
|
175
|
+
let selected = null;
|
|
176
|
+
const renderAttributes = (featureIndex) => {
|
|
177
|
+
const feature = model.features[featureIndex];
|
|
178
|
+
attributesTitle.textContent = `${t('shapefile.attributes')} — ${t('shapefile.featureLabel', { id: feature?.id ?? featureIndex + 1 })}`;
|
|
179
|
+
const record = attributes?.records[featureIndex];
|
|
180
|
+
if (!record) {
|
|
181
|
+
attributesBody.className = 'omni-shp__attributes-empty';
|
|
182
|
+
attributesBody.textContent = t(attributes ? 'shapefile.noRecord' : 'shapefile.noAttributes');
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
attributesBody.className = '';
|
|
186
|
+
const table = document.createElement('table');
|
|
187
|
+
for (const field of attributes.fields) {
|
|
188
|
+
const row = document.createElement('tr');
|
|
189
|
+
const name = document.createElement('th');
|
|
190
|
+
name.textContent = field.name;
|
|
191
|
+
const value = document.createElement('td');
|
|
192
|
+
value.textContent = formatValue(record[field.name]);
|
|
193
|
+
row.append(name, value);
|
|
194
|
+
table.append(row);
|
|
195
|
+
}
|
|
196
|
+
attributesBody.replaceChildren(table);
|
|
197
|
+
};
|
|
198
|
+
listen(svg, 'click', ((event) => {
|
|
199
|
+
const target = event.target.closest?.('[data-feature]');
|
|
200
|
+
selected?.classList.remove('is-selected');
|
|
201
|
+
if (!target) {
|
|
202
|
+
selected = null;
|
|
203
|
+
attributesTitle.textContent = t('shapefile.attributes');
|
|
204
|
+
attributesBody.className = 'omni-shp__attributes-empty';
|
|
205
|
+
attributesBody.textContent = t('shapefile.noSelection');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
selected = target;
|
|
209
|
+
target.classList.add('is-selected');
|
|
210
|
+
renderAttributes(Number(target.getAttribute('data-feature')));
|
|
211
|
+
}));
|
|
212
|
+
if (options.signal?.aborted) {
|
|
213
|
+
disposers.forEach((dispose) => dispose());
|
|
214
|
+
shell.remove();
|
|
215
|
+
throw new MountAbortedError();
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
dispose() {
|
|
219
|
+
disposers.forEach((dispose) => dispose());
|
|
220
|
+
shell.remove();
|
|
221
|
+
if (root === container)
|
|
222
|
+
container.classList.remove(VIEWER_ROOT_CLASS, 'omni-viewer--shapefile');
|
|
223
|
+
else
|
|
224
|
+
root.replaceChildren();
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
// --- SVG rendering (source coordinates, y flipped) ---------------------------
|
|
229
|
+
function renderModel(svg, model) {
|
|
230
|
+
const bbox = model.bbox && (model.bbox[2] > model.bbox[0] || model.bbox[3] > model.bbox[1])
|
|
231
|
+
? model.bbox
|
|
232
|
+
: deriveBBox(model);
|
|
233
|
+
if (!bbox)
|
|
234
|
+
return null;
|
|
235
|
+
const [minX, minY, maxX, maxY] = bbox;
|
|
236
|
+
const span = Math.max(maxX - minX, maxY - minY);
|
|
237
|
+
const pad = span * 0.03 || 1;
|
|
238
|
+
const viewWidth = maxX - minX + 2 * pad;
|
|
239
|
+
const viewHeight = maxY - minY + 2 * pad;
|
|
240
|
+
const pointRadius = Math.max(viewWidth, viewHeight) * 0.006;
|
|
241
|
+
const view = { x: minX - pad, y: -(maxY + pad), w: viewWidth, h: viewHeight };
|
|
242
|
+
svg.setAttribute('viewBox', `${view.x} ${view.y} ${view.w} ${view.h}`);
|
|
243
|
+
model.features.forEach((feature, index) => {
|
|
244
|
+
if (feature.geometry)
|
|
245
|
+
appendGeometry(svg, feature.geometry, pointRadius, index);
|
|
246
|
+
});
|
|
247
|
+
return view;
|
|
248
|
+
}
|
|
249
|
+
function appendGeometry(svg, g, pointRadius, featureIndex) {
|
|
250
|
+
const ns = 'http://www.w3.org/2000/svg';
|
|
251
|
+
const tag = (node) => node.setAttribute('data-feature', String(featureIndex));
|
|
252
|
+
const point = (coordinates) => {
|
|
253
|
+
const c = document.createElementNS(ns, 'circle');
|
|
254
|
+
c.classList.add('omni-shp__point');
|
|
255
|
+
c.setAttribute('cx', String(coordinates[0]));
|
|
256
|
+
c.setAttribute('cy', String(-coordinates[1]));
|
|
257
|
+
c.setAttribute('r', String(pointRadius));
|
|
258
|
+
tag(c);
|
|
259
|
+
svg.append(c);
|
|
260
|
+
};
|
|
261
|
+
if (g.type === 'Point') {
|
|
262
|
+
point(g.coordinates);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (g.type === 'MultiPoint') {
|
|
266
|
+
g.coordinates.forEach(point);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (g.type === 'LineString' || g.type === 'MultiLineString') {
|
|
270
|
+
const groups = g.type === 'LineString' ? [g.coordinates] : g.coordinates;
|
|
271
|
+
for (const points of groups) {
|
|
272
|
+
const p = document.createElementNS(ns, 'path');
|
|
273
|
+
p.classList.add('omni-shp__geometry');
|
|
274
|
+
p.setAttribute('d', points.map((x, i) => `${i ? 'L' : 'M'}${x[0]} ${-x[1]}`).join(' '));
|
|
275
|
+
p.setAttribute('fill', 'none');
|
|
276
|
+
tag(p);
|
|
277
|
+
svg.append(p);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const polygons = g.type === 'Polygon' ? [g.coordinates] : g.coordinates;
|
|
282
|
+
for (const rings of polygons) {
|
|
283
|
+
const p = document.createElementNS(ns, 'path');
|
|
284
|
+
p.classList.add('omni-shp__geometry');
|
|
285
|
+
p.setAttribute('d', rings.map(points => points.map((x, i) => `${i ? 'L' : 'M'}${x[0]} ${-x[1]}`).join(' ') + ' Z').join(' '));
|
|
286
|
+
p.setAttribute('fill-rule', 'evenodd');
|
|
287
|
+
p.setAttribute('clip-rule', 'evenodd');
|
|
288
|
+
tag(p);
|
|
289
|
+
svg.append(p);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function deriveBBox(model) {
|
|
293
|
+
let b = null;
|
|
294
|
+
const visit = (p) => {
|
|
295
|
+
b = b ? [Math.min(b[0], p[0]), Math.min(b[1], p[1]), Math.max(b[2], p[0]), Math.max(b[3], p[1])] : [p[0], p[1], p[0], p[1]];
|
|
296
|
+
};
|
|
297
|
+
for (const f of model.features) {
|
|
298
|
+
const g = f.geometry;
|
|
299
|
+
if (!g)
|
|
300
|
+
continue;
|
|
301
|
+
if (g.type === 'Point')
|
|
302
|
+
visit(g.coordinates);
|
|
303
|
+
else if (g.type === 'MultiLineString' || g.type === 'Polygon')
|
|
304
|
+
for (const ring of g.coordinates)
|
|
305
|
+
ring.forEach(visit);
|
|
306
|
+
else if (g.type === 'MultiPolygon')
|
|
307
|
+
for (const polygon of g.coordinates)
|
|
308
|
+
for (const ring of polygon)
|
|
309
|
+
ring.forEach(visit);
|
|
310
|
+
else
|
|
311
|
+
g.coordinates.forEach(visit);
|
|
312
|
+
}
|
|
313
|
+
return b;
|
|
314
|
+
}
|
|
315
|
+
function formatValue(value) {
|
|
316
|
+
if (value === null || value === undefined || value === '')
|
|
317
|
+
return '—';
|
|
318
|
+
if (typeof value === 'boolean')
|
|
319
|
+
return value ? 'true' : 'false';
|
|
320
|
+
return String(value);
|
|
321
|
+
}
|
|
322
|
+
function element(tag, className, text) {
|
|
323
|
+
const node = document.createElement(tag);
|
|
324
|
+
if (className)
|
|
325
|
+
node.className = className;
|
|
326
|
+
if (text !== undefined)
|
|
327
|
+
node.textContent = text;
|
|
328
|
+
return node;
|
|
329
|
+
}
|
|
330
|
+
function button(label) {
|
|
331
|
+
const node = document.createElement('button');
|
|
332
|
+
node.type = 'button';
|
|
333
|
+
node.className = 'omni-shp__btn';
|
|
334
|
+
node.textContent = label;
|
|
335
|
+
return node;
|
|
336
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ShapefileMountOptions, ShapefileViewerContext } from './index.js';
|
|
2
|
+
import type { ViewerHandle, ViewerInput } from '../types.js';
|
|
3
|
+
export * from './index.js';
|
|
4
|
+
/** mountShapefileViewer with a proj4-backed WKT→WGS84 reprojector. Because
|
|
5
|
+
* mount options are synchronous, proj4 is loaded first; when the peer is
|
|
6
|
+
* missing the viewer simply renders native coordinates. */
|
|
7
|
+
export declare function mountSelfLoadingShapefileViewer(input: ViewerInput, container: HTMLElement, ctx: ShapefileViewerContext, options?: Omit<ShapefileMountOptions, 'createReprojector'>): Promise<ViewerHandle>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Opt-in self-loading entry (ADR 14): the only place in the core where the
|
|
2
|
+
// optional peer `proj4` is actually imported. Platforms whose bundler
|
|
3
|
+
// resolves the peer use this; others import `viewers/shapefile` and inject
|
|
4
|
+
// their own `createReprojector`.
|
|
5
|
+
import { mountShapefileViewer } from './index.js';
|
|
6
|
+
export * from './index.js';
|
|
7
|
+
let proj4Promise;
|
|
8
|
+
async function loadProj4() {
|
|
9
|
+
proj4Promise ??= import('proj4')
|
|
10
|
+
.then((module) => {
|
|
11
|
+
const mod = module;
|
|
12
|
+
return mod.default ?? module;
|
|
13
|
+
})
|
|
14
|
+
.catch(() => null);
|
|
15
|
+
return proj4Promise;
|
|
16
|
+
}
|
|
17
|
+
/** mountShapefileViewer with a proj4-backed WKT→WGS84 reprojector. Because
|
|
18
|
+
* mount options are synchronous, proj4 is loaded first; when the peer is
|
|
19
|
+
* missing the viewer simply renders native coordinates. */
|
|
20
|
+
export async function mountSelfLoadingShapefileViewer(input, container, ctx, options = {}) {
|
|
21
|
+
const proj4 = await loadProj4();
|
|
22
|
+
const createReprojector = proj4
|
|
23
|
+
? (wkt) => {
|
|
24
|
+
const transform = proj4(wkt, 'EPSG:4326');
|
|
25
|
+
return (position) => transform.forward([position[0], position[1]]);
|
|
26
|
+
}
|
|
27
|
+
: undefined;
|
|
28
|
+
return mountShapefileViewer(input, container, ctx, {
|
|
29
|
+
...options,
|
|
30
|
+
...(createReprojector ? { createReprojector } : {})
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const shapefileViewerCss = "\n.omni-shp{box-sizing:border-box;min-height:100%;padding:16px;background:var(--omni-bg,#1e1e1e);color:var(--omni-fg,#ddd);font:14px/1.45 system-ui,sans-serif}\n.omni-shp *{box-sizing:border-box}\n.omni-shp__header{display:flex;justify-content:space-between;gap:16px;margin-bottom:12px}\n.omni-shp__title{font-size:18px;font-weight:650}\n.omni-shp__summary{color:var(--omni-muted,#aaa)}\n.omni-shp__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:10px}\n.omni-shp__btn{background:var(--omni-accent,#0e639c);color:var(--omni-accent-fg,#fff);border:none;padding:5px 11px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}\n.omni-shp__btn:hover{background:var(--omni-accent-hover,#1177bb)}\n.omni-shp__projection{margin-left:auto;color:var(--omni-muted,#aaa);font-size:12px}\n.omni-shp__map{width:100%;height:min(62vh,640px);border:1px solid var(--omni-border,#444);border-radius:8px;background:#eef2f5;cursor:grab;touch-action:none}\n.omni-shp__map.is-panning{cursor:grabbing}\n.omni-shp__geometry{fill:rgba(47,126,216,.24);stroke:#1d66b1;stroke-width:1.5;vector-effect:non-scaling-stroke}\n.omni-shp__geometry:hover{fill:rgba(47,126,216,.42)}\n.omni-shp__point{fill:#d64045;stroke:#fff;vector-effect:non-scaling-stroke}\n.omni-shp__point:hover{fill:#ff5a60}\n.omni-shp__geometry.is-selected,.omni-shp__point.is-selected{stroke:#ffb020;stroke-width:2.5}\n.omni-shp__attributes{margin-top:10px;border:1px solid var(--omni-border,#444);border-radius:8px;background:var(--omni-panel-bg,#252526);padding:10px 12px}\n.omni-shp__attributes-title{font-weight:600;margin-bottom:6px}\n.omni-shp__attributes-empty{color:var(--omni-muted,#aaa)}\n.omni-shp__attributes table{border-collapse:collapse;width:100%}\n.omni-shp__attributes th,.omni-shp__attributes td{text-align:left;border-bottom:1px solid var(--omni-border,#3a3a3a);padding:3px 8px;font-size:12px}\n.omni-shp__attributes th{color:var(--omni-muted,#aaa);font-weight:500;width:35%}\n.omni-shp__warning{margin-top:10px;padding:10px;border:1px solid #b98b2f;border-radius:6px;background:#3b2d10;color:#ffd98a;white-space:pre-wrap}\n.omni-shp__warning[hidden]{display:none}\n";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Shapefile viewer stylesheet. Single source for both delivery forms
|
|
2
|
+
// (DESIGN.md §6). Only --omni-* tokens.
|
|
3
|
+
export const shapefileViewerCss = `
|
|
4
|
+
.omni-shp{box-sizing:border-box;min-height:100%;padding:16px;background:var(--omni-bg,#1e1e1e);color:var(--omni-fg,#ddd);font:14px/1.45 system-ui,sans-serif}
|
|
5
|
+
.omni-shp *{box-sizing:border-box}
|
|
6
|
+
.omni-shp__header{display:flex;justify-content:space-between;gap:16px;margin-bottom:12px}
|
|
7
|
+
.omni-shp__title{font-size:18px;font-weight:650}
|
|
8
|
+
.omni-shp__summary{color:var(--omni-muted,#aaa)}
|
|
9
|
+
.omni-shp__toolbar{display:flex;align-items:center;gap:8px;margin-bottom:10px}
|
|
10
|
+
.omni-shp__btn{background:var(--omni-accent,#0e639c);color:var(--omni-accent-fg,#fff);border:none;padding:5px 11px;border-radius:4px;cursor:pointer;font-size:12px;font-family:inherit}
|
|
11
|
+
.omni-shp__btn:hover{background:var(--omni-accent-hover,#1177bb)}
|
|
12
|
+
.omni-shp__projection{margin-left:auto;color:var(--omni-muted,#aaa);font-size:12px}
|
|
13
|
+
.omni-shp__map{width:100%;height:min(62vh,640px);border:1px solid var(--omni-border,#444);border-radius:8px;background:#eef2f5;cursor:grab;touch-action:none}
|
|
14
|
+
.omni-shp__map.is-panning{cursor:grabbing}
|
|
15
|
+
.omni-shp__geometry{fill:rgba(47,126,216,.24);stroke:#1d66b1;stroke-width:1.5;vector-effect:non-scaling-stroke}
|
|
16
|
+
.omni-shp__geometry:hover{fill:rgba(47,126,216,.42)}
|
|
17
|
+
.omni-shp__point{fill:#d64045;stroke:#fff;vector-effect:non-scaling-stroke}
|
|
18
|
+
.omni-shp__point:hover{fill:#ff5a60}
|
|
19
|
+
.omni-shp__geometry.is-selected,.omni-shp__point.is-selected{stroke:#ffb020;stroke-width:2.5}
|
|
20
|
+
.omni-shp__attributes{margin-top:10px;border:1px solid var(--omni-border,#444);border-radius:8px;background:var(--omni-panel-bg,#252526);padding:10px 12px}
|
|
21
|
+
.omni-shp__attributes-title{font-weight:600;margin-bottom:6px}
|
|
22
|
+
.omni-shp__attributes-empty{color:var(--omni-muted,#aaa)}
|
|
23
|
+
.omni-shp__attributes table{border-collapse:collapse;width:100%}
|
|
24
|
+
.omni-shp__attributes th,.omni-shp__attributes td{text-align:left;border-bottom:1px solid var(--omni-border,#3a3a3a);padding:3px 8px;font-size:12px}
|
|
25
|
+
.omni-shp__attributes th{color:var(--omni-muted,#aaa);font-weight:500;width:35%}
|
|
26
|
+
.omni-shp__warning{margin-top:10px;padding:10px;border:1px solid #b98b2f;border-radius:6px;background:#3b2d10;color:#ffd98a;white-space:pre-wrap}
|
|
27
|
+
.omni-shp__warning[hidden]{display:none}
|
|
28
|
+
`;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AutomotiveViewerContext } from '../automotive/index.js';
|
|
2
|
+
import type { MountOptions, ViewerHandle, ViewerInput } from '../types.js';
|
|
3
|
+
export { parseStp } from '../../parsers/automotive/index.js';
|
|
4
|
+
export { automotiveViewerCss as stpViewerCss } from '../automotive/styles.js';
|
|
5
|
+
export type StpViewerContext = AutomotiveViewerContext;
|
|
6
|
+
export declare const STP_VIEWER_META: {
|
|
7
|
+
id: string;
|
|
8
|
+
displayNameKey: string;
|
|
9
|
+
extensions: string[];
|
|
10
|
+
priority: number;
|
|
11
|
+
requiredServices: readonly [];
|
|
12
|
+
optionalServices: readonly ["clipboard"];
|
|
13
|
+
inputOwnership: "borrows";
|
|
14
|
+
};
|
|
15
|
+
export declare const mountStpViewer: (input: ViewerInput, container: HTMLElement, ctx: StpViewerContext, options?: MountOptions) => Promise<ViewerHandle>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { mountAutomotiveViewer } from '../automotive/index.js';
|
|
2
|
+
export { parseStp } from '../../parsers/automotive/index.js';
|
|
3
|
+
export { automotiveViewerCss as stpViewerCss } from '../automotive/styles.js';
|
|
4
|
+
export const STP_VIEWER_META = { id: 'stp', displayNameKey: 'stp.title', extensions: ['stp', 'step'], priority: 20, requiredServices: [], optionalServices: ['clipboard'], inputOwnership: 'borrows' };
|
|
5
|
+
export const mountStpViewer = (input, container, ctx, options = {}) => mountAutomotiveViewer('stp', input, container, ctx, options);
|
|
@@ -0,0 +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}}";
|
|
@@ -0,0 +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}}`;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ClipboardService, FileSaveService, FileWritebackService, HostContext } from '../host/index.js';
|
|
2
|
+
import { type MountOptions, type ViewerHandle, type ViewerInput } from './types.js';
|
|
3
|
+
export interface StructuredNode {
|
|
4
|
+
key: string;
|
|
5
|
+
path: string;
|
|
6
|
+
kind: string;
|
|
7
|
+
raw?: string;
|
|
8
|
+
value?: unknown;
|
|
9
|
+
children?: readonly StructuredNode[];
|
|
10
|
+
}
|
|
11
|
+
export interface StructuredState {
|
|
12
|
+
text: string;
|
|
13
|
+
mode: 'tree' | 'flat' | 'json' | 'raw';
|
|
14
|
+
search: string;
|
|
15
|
+
expanded: ReadonlySet<string>;
|
|
16
|
+
diagnostics: readonly {
|
|
17
|
+
messageKey: string;
|
|
18
|
+
location?: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
export interface StructuredController<S extends StructuredState> {
|
|
22
|
+
readonly state: S;
|
|
23
|
+
subscribe(listener: (state: S) => void): () => void;
|
|
24
|
+
dispatch(action: unknown): void;
|
|
25
|
+
nodePath(id: string): string;
|
|
26
|
+
nodeValue(id: string): string;
|
|
27
|
+
nodeJson?(id: string): string;
|
|
28
|
+
documentJson?(): string;
|
|
29
|
+
}
|
|
30
|
+
export type StructuredContext = HostContext & {
|
|
31
|
+
clipboard?: ClipboardService;
|
|
32
|
+
save?: FileSaveService;
|
|
33
|
+
writeback?: FileWritebackService;
|
|
34
|
+
};
|
|
35
|
+
export type StructuredSourceHighlighter = (target: HTMLElement, text: string) => void;
|
|
36
|
+
export declare function mountStructured<S extends StructuredState>(input: ViewerInput, container: HTMLElement, ctx: StructuredContext, options: MountOptions, className: string, css: string, controller: StructuredController<S>, sourceHighlighter?: StructuredSourceHighlighter): ViewerHandle;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { asciiLower } from '../parsers/csv/index.js';
|
|
2
|
+
import { VIEWER_ROOT_CLASS } from './types.js';
|
|
3
|
+
export function mountStructured(input, container, ctx, options, className, css, controller, sourceHighlighter) {
|
|
4
|
+
let root;
|
|
5
|
+
if ((options.styleIsolation ?? 'shadow') === 'shadow' && typeof container.attachShadow === 'function') {
|
|
6
|
+
root = container.shadowRoot ?? container.attachShadow({ mode: 'open' });
|
|
7
|
+
const style = document.createElement('style');
|
|
8
|
+
style.textContent = css;
|
|
9
|
+
root.append(style);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
container.classList.add(VIEWER_ROOT_CLASS, className);
|
|
13
|
+
root = container;
|
|
14
|
+
}
|
|
15
|
+
const el = (tag, cls, value) => { const node = document.createElement(tag); if (cls)
|
|
16
|
+
node.className = cls; if (value !== undefined)
|
|
17
|
+
node.textContent = value; return node; };
|
|
18
|
+
const frame = el('div', 'omni-structured');
|
|
19
|
+
root.append(frame);
|
|
20
|
+
const disposers = [];
|
|
21
|
+
const on = (node, type, listener) => { node.addEventListener(type, listener); disposers.push(() => node.removeEventListener(type, listener)); };
|
|
22
|
+
const toolbar = el('div', 'omni-structured__bar');
|
|
23
|
+
const expand = el('button', undefined, ctx.i18n.t('structured.expandAll'));
|
|
24
|
+
const collapse = el('button', undefined, ctx.i18n.t('structured.collapseAll'));
|
|
25
|
+
const search = el('input', 'omni-structured__search');
|
|
26
|
+
const documentSelect = el('select');
|
|
27
|
+
const save = el('button', undefined, ctx.i18n.t('json.save'));
|
|
28
|
+
const saveAs = el('button', undefined, ctx.i18n.t('json.saveAs'));
|
|
29
|
+
search.type = 'search';
|
|
30
|
+
search.placeholder = ctx.i18n.t('structured.search');
|
|
31
|
+
[expand, collapse, save, saveAs].forEach(button => { button.type = 'button'; });
|
|
32
|
+
on(expand, 'click', () => controller.dispatch({ type: 'expand-all' }));
|
|
33
|
+
on(collapse, 'click', () => controller.dispatch({ type: 'collapse-all' }));
|
|
34
|
+
on(search, 'input', () => controller.dispatch({ type: 'set-search', search: search.value }));
|
|
35
|
+
on(save, 'click', () => { if (ctx.writeback)
|
|
36
|
+
void ctx.writeback.write(new TextEncoder().encode(controller.state.text)); });
|
|
37
|
+
on(saveAs, 'click', () => { if (ctx.save)
|
|
38
|
+
void ctx.save.saveFile(input.fileName, new TextEncoder().encode(controller.state.text), 'text/plain'); });
|
|
39
|
+
toolbar.append(expand, collapse, search, documentSelect, save, saveAs);
|
|
40
|
+
const diagnostics = el('div', 'omni-structured__diagnostics');
|
|
41
|
+
diagnostics.setAttribute('aria-live', 'polite');
|
|
42
|
+
const workspace = el('div', 'omni-structured__workspace');
|
|
43
|
+
const sourcePane = el('section', 'omni-structured__pane omni-structured__editor-pane');
|
|
44
|
+
const sourceHeader = el('header', 'omni-structured__pane-header');
|
|
45
|
+
sourceHeader.append(el('strong', undefined, ctx.i18n.t('json.editor.title')), el('span', undefined, ctx.i18n.t('json.editor.description')));
|
|
46
|
+
const editor = el('textarea', 'omni-structured__editor');
|
|
47
|
+
editor.spellcheck = false;
|
|
48
|
+
on(editor, 'input', () => controller.dispatch({ type: 'edit-source', text: editor.value }));
|
|
49
|
+
const sourceHighlight = sourceHighlighter ? el('pre', 'omni-structured__source-highlight') : undefined;
|
|
50
|
+
if (sourceHighlight) {
|
|
51
|
+
const surface = el('div', 'omni-structured__source-surface');
|
|
52
|
+
surface.append(sourceHighlight, editor);
|
|
53
|
+
on(editor, 'scroll', () => { sourceHighlight.scrollTop = editor.scrollTop; sourceHighlight.scrollLeft = editor.scrollLeft; });
|
|
54
|
+
sourcePane.classList.add('omni-structured__pane--highlighted-source');
|
|
55
|
+
sourcePane.append(sourceHeader, surface);
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
sourcePane.append(sourceHeader, editor);
|
|
59
|
+
const previewPane = el('section', 'omni-structured__pane omni-structured__preview-pane');
|
|
60
|
+
const previewHeader = el('header', 'omni-structured__pane-header');
|
|
61
|
+
const treeMode = el('button', undefined, ctx.i18n.t('structured.tree'));
|
|
62
|
+
const flatMode = el('button', undefined, ctx.i18n.t('structured.flat'));
|
|
63
|
+
const jsonMode = el('button', undefined, ctx.i18n.t('structured.json'));
|
|
64
|
+
const rawMode = el('button', undefined, ctx.i18n.t('structured.raw'));
|
|
65
|
+
treeMode.type = flatMode.type = jsonMode.type = rawMode.type = 'button';
|
|
66
|
+
on(treeMode, 'click', () => controller.dispatch({ type: 'set-mode', mode: 'tree' }));
|
|
67
|
+
on(flatMode, 'click', () => controller.dispatch({ type: 'set-mode', mode: 'flat' }));
|
|
68
|
+
on(jsonMode, 'click', () => controller.dispatch({ type: 'set-mode', mode: 'json' }));
|
|
69
|
+
on(rawMode, 'click', () => controller.dispatch({ type: 'set-mode', mode: 'raw' }));
|
|
70
|
+
const modes = el('span', 'omni-structured__preview-modes');
|
|
71
|
+
modes.append(treeMode, flatMode);
|
|
72
|
+
if (controller.documentJson)
|
|
73
|
+
modes.append(jsonMode);
|
|
74
|
+
modes.append(rawMode);
|
|
75
|
+
previewHeader.append(el('strong', undefined, ctx.i18n.t('json.preview.title')), el('span', undefined, ctx.i18n.t('structured.treeDescription')), modes);
|
|
76
|
+
const treeBody = el('div', 'omni-structured__tree');
|
|
77
|
+
const flatBody = el('div', 'omni-structured__tree omni-structured__flat');
|
|
78
|
+
const jsonPreview = el('pre', 'omni-structured__json-preview');
|
|
79
|
+
const rawPreview = el('pre', 'omni-structured__raw-preview');
|
|
80
|
+
previewPane.append(previewHeader, treeBody, flatBody, jsonPreview, rawPreview);
|
|
81
|
+
workspace.append(sourcePane, previewPane);
|
|
82
|
+
frame.append(toolbar, diagnostics, workspace);
|
|
83
|
+
const copy = (value) => { if (value !== undefined)
|
|
84
|
+
void ctx.clipboard?.writeText(value); };
|
|
85
|
+
const subtreeMatches = (node, query) => !query || asciiLower(`${node.key} ${node.path} ${node.raw ?? node.value ?? ''}`).includes(query) || !!node.children?.some(child => subtreeMatches(child, query));
|
|
86
|
+
const renderFlatRows = (node, query, out) => { if (node.children?.length) {
|
|
87
|
+
for (const child of node.children)
|
|
88
|
+
renderFlatRows(child, query, out);
|
|
89
|
+
return;
|
|
90
|
+
} const value = node.raw ?? String(node.value ?? ''); if (query && !asciiLower(`${node.key} ${node.path} ${value}`).includes(query))
|
|
91
|
+
return; const row = el('div', 'omni-structured__node omni-structured__flat-row'); row.append(el('span', 'omni-structured__key', node.path || '$'), el('span', 'omni-structured__value', value)); out.push(row); };
|
|
92
|
+
const renderNode = (node, depth, state) => { const id = node.path || '$', query = asciiLower(state.search.trim()); if (!subtreeMatches(node, query))
|
|
93
|
+
return null; const wrap = el('div'); const row = el('div', 'omni-structured__node'); row.style.paddingInlineStart = `${depth * 16}px`; const hasChildren = !!node.children?.length; if (hasChildren) {
|
|
94
|
+
const toggle = el('button', 'omni-structured__toggle', state.expanded.has(id) ? '▾' : '▸');
|
|
95
|
+
toggle.type = 'button';
|
|
96
|
+
toggle.setAttribute('aria-expanded', String(state.expanded.has(id)));
|
|
97
|
+
toggle.setAttribute('aria-label', ctx.i18n.t(state.expanded.has(id) ? 'structured.collapse' : 'structured.expand'));
|
|
98
|
+
toggle.addEventListener('click', () => controller.dispatch({ type: 'toggle-node', id }));
|
|
99
|
+
row.append(toggle);
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
row.append(el('span', 'omni-structured__toggle')); row.append(el('span', 'omni-structured__key', node.key || '$'), el('span', 'omni-structured__kind', node.kind), el('span', 'omni-structured__value', node.raw ?? String(node.value ?? ''))); const actions = el('span', 'omni-structured__actions'); for (const [key, value] of [['structured.copyPath', controller.nodePath(id)], ['structured.copyValue', controller.nodeValue(id)], ['structured.copyJson', controller.nodeJson?.(id)]]) {
|
|
103
|
+
if (value === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
const button = el('button', undefined, ctx.i18n.t(key));
|
|
106
|
+
button.type = 'button';
|
|
107
|
+
button.disabled = !ctx.clipboard;
|
|
108
|
+
button.title = ctx.clipboard ? '' : ctx.i18n.t('common.noClipboard');
|
|
109
|
+
button.addEventListener('click', () => copy(value));
|
|
110
|
+
actions.append(button);
|
|
111
|
+
} row.append(actions); wrap.append(row); if (hasChildren && state.expanded.has(id))
|
|
112
|
+
for (const child of node.children) {
|
|
113
|
+
const rendered = renderNode(child, depth + 1, state);
|
|
114
|
+
if (rendered)
|
|
115
|
+
wrap.append(rendered);
|
|
116
|
+
} return wrap; };
|
|
117
|
+
const render = (state) => { if (editor.value !== state.text && document.activeElement !== editor)
|
|
118
|
+
editor.value = state.text; if (sourceHighlight && sourceHighlighter)
|
|
119
|
+
sourceHighlighter(sourceHighlight, state.text); if (sourceHighlighter)
|
|
120
|
+
sourceHighlighter(rawPreview, state.text);
|
|
121
|
+
else
|
|
122
|
+
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) {
|
|
124
|
+
docs.forEach((_, index) => { const option = el('option', undefined, String(index + 1)); option.value = String(index); option.selected = index === shaped.selectedDocument; documentSelect.append(option); });
|
|
125
|
+
documentSelect.onchange = () => controller.dispatch({ type: 'select-document', index: Number(documentSelect.value) });
|
|
126
|
+
} 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)
|
|
127
|
+
treeBody.append(rendered); }); if (state.mode === 'flat') {
|
|
128
|
+
const rows = [];
|
|
129
|
+
roots.forEach(node => renderFlatRows(node, asciiLower(state.search.trim()), rows));
|
|
130
|
+
flatBody.append(...rows);
|
|
131
|
+
} };
|
|
132
|
+
const unsubscribe = controller.subscribe(render);
|
|
133
|
+
render(controller.state);
|
|
134
|
+
return { dispose() { unsubscribe(); disposers.splice(0).forEach(dispose => dispose()); frame.remove(); if (root instanceof ShadowRoot)
|
|
135
|
+
root.replaceChildren();
|
|
136
|
+
else
|
|
137
|
+
container.classList.remove(VIEWER_ROOT_CLASS, className); } };
|
|
138
|
+
}
|