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,18 @@
|
|
|
1
|
+
export interface ImageLimits {
|
|
2
|
+
/** Bytes; enforced before any decoder touches the input. */
|
|
3
|
+
maxInputBytes: number;
|
|
4
|
+
/** Decoded preview pixel cap (safe downscale territory). */
|
|
5
|
+
maxDecodedPixels: number;
|
|
6
|
+
/** Editable/export canvas pixel cap — beyond it editing/export disable. */
|
|
7
|
+
maxCanvasPixels: number;
|
|
8
|
+
/** Per-axis canvas dimension cap (browser canvas ceiling). */
|
|
9
|
+
maxCanvasDimension: number;
|
|
10
|
+
/** Undo history depth. */
|
|
11
|
+
maxUndoEntries: number;
|
|
12
|
+
/** Undo history byte budget. */
|
|
13
|
+
maxUndoBytes: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const IMAGE_LIMITS: ImageLimits;
|
|
16
|
+
/** Whether an image of this size can back an editable/exportable canvas
|
|
17
|
+
* (image.md §4): both the pixel count and each axis must be within budget. */
|
|
18
|
+
export declare function canvasEditable(width: number, height: number, limits?: ImageLimits): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Image viewer default limits (docs/viewers/image.md §4, I6). These are the
|
|
2
|
+
// *image-viewer* defaults — intentionally lower than core DEFAULT_LIMITS
|
|
3
|
+
// (256 MiB) because decoded pixel buffers (width × height × 4) dwarf the file
|
|
4
|
+
// size. Adapters keep any stricter existing limit and may override the rest,
|
|
5
|
+
// recording the value + memory measurement in their smoke.
|
|
6
|
+
export const IMAGE_LIMITS = {
|
|
7
|
+
maxInputBytes: 100 * 1024 * 1024, // 100 MiB
|
|
8
|
+
maxDecodedPixels: 64 * 1_000_000, // 64 MP
|
|
9
|
+
maxCanvasPixels: 32 * 1_000_000, // 32 MP
|
|
10
|
+
maxCanvasDimension: 16_384,
|
|
11
|
+
maxUndoEntries: 100,
|
|
12
|
+
maxUndoBytes: 16 * 1024 * 1024 // 16 MiB
|
|
13
|
+
};
|
|
14
|
+
/** Whether an image of this size can back an editable/exportable canvas
|
|
15
|
+
* (image.md §4): both the pixel count and each axis must be within budget. */
|
|
16
|
+
export function canvasEditable(width, height, limits = IMAGE_LIMITS) {
|
|
17
|
+
return (width * height <= limits.maxCanvasPixels &&
|
|
18
|
+
width <= limits.maxCanvasDimension &&
|
|
19
|
+
height <= limits.maxCanvasDimension);
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const imageViewerCss = "\n/* Reverse-contamination guard (DESIGN.md \u00A76): scoped mode neutralizes host\n element styles; custom properties still pierce. Shadow mode: harmless. */\n.omni-viewer--image {\n all: initial;\n}\n.omni-viewer--image :where(button, select, option, input, label, canvas, img) {\n all: revert;\n}\n\n:host, .omni-viewer--image {\n display: block;\n height: 100%;\n box-sizing: border-box;\n color: var(--omni-fg, #d4d4d4);\n background: var(--omni-bg, #1e1e1e);\n font-family: var(--omni-font, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif);\n font-size: var(--omni-font-size, 13px);\n}\n.omni-image {\n display: flex;\n flex-direction: column;\n height: 100%;\n min-height: 0;\n}\n.omni-image__toolbar {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n padding: 6px 8px;\n border-bottom: 1px solid var(--omni-border, #333);\n}\n.omni-image__toolbar button,\n.omni-image__toolbar select {\n background: var(--omni-button-bg, #2a2a2a);\n color: inherit;\n border: 1px solid var(--omni-border, #444);\n border-radius: 4px;\n padding: 3px 8px;\n cursor: pointer;\n}\n.omni-image__toolbar button[aria-pressed=\"true\"] {\n background: var(--omni-accent, #0e639c);\n border-color: var(--omni-accent, #0e639c);\n}\n.omni-image__toolbar button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.omni-image__spacer { flex: 1; }\n.omni-image__meta { opacity: 0.8; font-variant-numeric: tabular-nums; }\n.omni-image__diagnostics {\n padding: 4px 8px;\n border-bottom: 1px solid var(--omni-border, #333);\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.omni-image__diag-error { color: var(--omni-error, #f14c4c); }\n\n/* Scrollable stage; checkerboard shows transparency (image.md \u00A75). */\n.omni-image__stage {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: auto;\n display: grid;\n place-items: center;\n background-color: var(--omni-bg, #1e1e1e);\n background-image:\n linear-gradient(45deg, var(--omni-checker, #2a2a2a) 25%, transparent 25%),\n linear-gradient(-45deg, var(--omni-checker, #2a2a2a) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--omni-checker, #2a2a2a) 75%),\n linear-gradient(-45deg, transparent 75%, var(--omni-checker, #2a2a2a) 75%);\n background-size: 20px 20px;\n background-position: 0 0, 0 10px, 10px -10px, -10px 0;\n}\n.omni-image__canvas-wrap {\n position: relative;\n line-height: 0;\n transform-origin: center center;\n}\n.omni-image__canvas-wrap canvas,\n.omni-image__canvas-wrap img {\n display: block;\n max-width: none;\n}\n.omni-image__overlay {\n position: absolute;\n inset: 0;\n outline: none;\n}\n.omni-image__aria-live {\n position: absolute;\n width: 1px; height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n white-space: nowrap;\n}\n.omni-image__empty {\n padding: 16px;\n opacity: 0.8;\n}\n.omni-image__toast {\n position: absolute;\n left: 50%;\n bottom: 16px;\n transform: translateX(-50%);\n background: var(--omni-toast-bg, #333);\n color: var(--omni-fg, #d4d4d4);\n padding: 6px 12px;\n border-radius: 4px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.4);\n}\n";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Image viewer stylesheet (DESIGN.md §6). Single source: the build emits
|
|
2
|
+
// dist/styles/image.css and the renderer injects this constant into the shadow
|
|
3
|
+
// root. Only --omni-* tokens (with fallbacks) — never platform variables.
|
|
4
|
+
export const imageViewerCss = `
|
|
5
|
+
/* Reverse-contamination guard (DESIGN.md §6): scoped mode neutralizes host
|
|
6
|
+
element styles; custom properties still pierce. Shadow mode: harmless. */
|
|
7
|
+
.omni-viewer--image {
|
|
8
|
+
all: initial;
|
|
9
|
+
}
|
|
10
|
+
.omni-viewer--image :where(button, select, option, input, label, canvas, img) {
|
|
11
|
+
all: revert;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:host, .omni-viewer--image {
|
|
15
|
+
display: block;
|
|
16
|
+
height: 100%;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
color: var(--omni-fg, #d4d4d4);
|
|
19
|
+
background: var(--omni-bg, #1e1e1e);
|
|
20
|
+
font-family: var(--omni-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
|
|
21
|
+
font-size: var(--omni-font-size, 13px);
|
|
22
|
+
}
|
|
23
|
+
.omni-image {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
height: 100%;
|
|
27
|
+
min-height: 0;
|
|
28
|
+
}
|
|
29
|
+
.omni-image__toolbar {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
flex-wrap: wrap;
|
|
33
|
+
gap: 6px;
|
|
34
|
+
padding: 6px 8px;
|
|
35
|
+
border-bottom: 1px solid var(--omni-border, #333);
|
|
36
|
+
}
|
|
37
|
+
.omni-image__toolbar button,
|
|
38
|
+
.omni-image__toolbar select {
|
|
39
|
+
background: var(--omni-button-bg, #2a2a2a);
|
|
40
|
+
color: inherit;
|
|
41
|
+
border: 1px solid var(--omni-border, #444);
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
padding: 3px 8px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
}
|
|
46
|
+
.omni-image__toolbar button[aria-pressed="true"] {
|
|
47
|
+
background: var(--omni-accent, #0e639c);
|
|
48
|
+
border-color: var(--omni-accent, #0e639c);
|
|
49
|
+
}
|
|
50
|
+
.omni-image__toolbar button:disabled {
|
|
51
|
+
opacity: 0.5;
|
|
52
|
+
cursor: not-allowed;
|
|
53
|
+
}
|
|
54
|
+
.omni-image__spacer { flex: 1; }
|
|
55
|
+
.omni-image__meta { opacity: 0.8; font-variant-numeric: tabular-nums; }
|
|
56
|
+
.omni-image__diagnostics {
|
|
57
|
+
padding: 4px 8px;
|
|
58
|
+
border-bottom: 1px solid var(--omni-border, #333);
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
gap: 2px;
|
|
62
|
+
}
|
|
63
|
+
.omni-image__diag-error { color: var(--omni-error, #f14c4c); }
|
|
64
|
+
|
|
65
|
+
/* Scrollable stage; checkerboard shows transparency (image.md §5). */
|
|
66
|
+
.omni-image__stage {
|
|
67
|
+
position: relative;
|
|
68
|
+
flex: 1;
|
|
69
|
+
min-height: 0;
|
|
70
|
+
overflow: auto;
|
|
71
|
+
display: grid;
|
|
72
|
+
place-items: center;
|
|
73
|
+
background-color: var(--omni-bg, #1e1e1e);
|
|
74
|
+
background-image:
|
|
75
|
+
linear-gradient(45deg, var(--omni-checker, #2a2a2a) 25%, transparent 25%),
|
|
76
|
+
linear-gradient(-45deg, var(--omni-checker, #2a2a2a) 25%, transparent 25%),
|
|
77
|
+
linear-gradient(45deg, transparent 75%, var(--omni-checker, #2a2a2a) 75%),
|
|
78
|
+
linear-gradient(-45deg, transparent 75%, var(--omni-checker, #2a2a2a) 75%);
|
|
79
|
+
background-size: 20px 20px;
|
|
80
|
+
background-position: 0 0, 0 10px, 10px -10px, -10px 0;
|
|
81
|
+
}
|
|
82
|
+
.omni-image__canvas-wrap {
|
|
83
|
+
position: relative;
|
|
84
|
+
line-height: 0;
|
|
85
|
+
transform-origin: center center;
|
|
86
|
+
}
|
|
87
|
+
.omni-image__canvas-wrap canvas,
|
|
88
|
+
.omni-image__canvas-wrap img {
|
|
89
|
+
display: block;
|
|
90
|
+
max-width: none;
|
|
91
|
+
}
|
|
92
|
+
.omni-image__overlay {
|
|
93
|
+
position: absolute;
|
|
94
|
+
inset: 0;
|
|
95
|
+
outline: none;
|
|
96
|
+
}
|
|
97
|
+
.omni-image__aria-live {
|
|
98
|
+
position: absolute;
|
|
99
|
+
width: 1px; height: 1px;
|
|
100
|
+
overflow: hidden;
|
|
101
|
+
clip: rect(0 0 0 0);
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
}
|
|
104
|
+
.omni-image__empty {
|
|
105
|
+
padding: 16px;
|
|
106
|
+
opacity: 0.8;
|
|
107
|
+
}
|
|
108
|
+
.omni-image__toast {
|
|
109
|
+
position: absolute;
|
|
110
|
+
left: 50%;
|
|
111
|
+
bottom: 16px;
|
|
112
|
+
transform: translateX(-50%);
|
|
113
|
+
background: var(--omni-toast-bg, #333);
|
|
114
|
+
color: var(--omni-fg, #d4d4d4);
|
|
115
|
+
padding: 6px 12px;
|
|
116
|
+
border-radius: 4px;
|
|
117
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Diagnostic, ParseFailure } from '../../parsers/types.js';
|
|
2
|
+
import { type JsonNode, type JsonParseOptions, type JsonValueKind } from '../../parsers/json/index.js';
|
|
3
|
+
export type JsonViewMode = 'tree' | 'source';
|
|
4
|
+
export type JsonSourceForm = 'verbatim' | 'pretty' | 'minified';
|
|
5
|
+
export type JsonToolAction = 'pretty' | 'minify' | 'sort-keys' | 'validate' | 'to-csv' | 'to-xml' | 'to-yaml' | 'escape' | 'unescape' | 'base64-encode' | 'base64-decode';
|
|
6
|
+
export interface JsonStatusMessage {
|
|
7
|
+
key: string;
|
|
8
|
+
args?: Record<string, string | number>;
|
|
9
|
+
}
|
|
10
|
+
export interface JsonToolResult {
|
|
11
|
+
action: JsonToolAction;
|
|
12
|
+
titleKey: string;
|
|
13
|
+
output: string;
|
|
14
|
+
error?: JsonStatusMessage;
|
|
15
|
+
}
|
|
16
|
+
export type JsonAction = {
|
|
17
|
+
type: 'set-view-mode';
|
|
18
|
+
mode: JsonViewMode;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'set-source-form';
|
|
21
|
+
form: JsonSourceForm;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'toggle-node';
|
|
24
|
+
id: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'expand-all';
|
|
27
|
+
} | {
|
|
28
|
+
type: 'collapse-all';
|
|
29
|
+
} | {
|
|
30
|
+
type: 'set-search';
|
|
31
|
+
search: string;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'next-match';
|
|
34
|
+
} | {
|
|
35
|
+
type: 'prev-match';
|
|
36
|
+
} | {
|
|
37
|
+
type: 'run-tool';
|
|
38
|
+
action: JsonToolAction;
|
|
39
|
+
} | {
|
|
40
|
+
type: 'apply-result-to-editor';
|
|
41
|
+
} | {
|
|
42
|
+
type: 'dismiss-result';
|
|
43
|
+
} | {
|
|
44
|
+
type: 'edit-scratchpad';
|
|
45
|
+
text: string;
|
|
46
|
+
};
|
|
47
|
+
export interface JsonStatistics {
|
|
48
|
+
total: number;
|
|
49
|
+
maxDepth: number;
|
|
50
|
+
counts: Record<JsonValueKind, number>;
|
|
51
|
+
}
|
|
52
|
+
export interface JsonViewState {
|
|
53
|
+
status: 'ok' | 'partial' | 'failed';
|
|
54
|
+
viewMode: JsonViewMode;
|
|
55
|
+
sourceForm: JsonSourceForm;
|
|
56
|
+
root: JsonNode | null;
|
|
57
|
+
expanded: ReadonlySet<string>;
|
|
58
|
+
search: string;
|
|
59
|
+
matchCount: number;
|
|
60
|
+
currentMatch: number;
|
|
61
|
+
scratchpad: string;
|
|
62
|
+
toolResult: JsonToolResult | null;
|
|
63
|
+
statusMessage: JsonStatusMessage | null;
|
|
64
|
+
statistics: JsonStatistics | null;
|
|
65
|
+
diagnostics: Diagnostic[];
|
|
66
|
+
failure: ParseFailure | null;
|
|
67
|
+
}
|
|
68
|
+
export interface JsonController {
|
|
69
|
+
readonly state: JsonViewState;
|
|
70
|
+
dispatch(action: JsonAction): void;
|
|
71
|
+
subscribe(listener: (state: JsonViewState) => void): () => void;
|
|
72
|
+
/** Verbatim source (J14) — the current editable text, valid or not (J20). */
|
|
73
|
+
verbatimText(): string;
|
|
74
|
+
/** The text for the active source form (verbatim / pretty / minified). */
|
|
75
|
+
sourceText(): string;
|
|
76
|
+
/** Stable id of a node, used for toggle/copy/match addressing (P1b). */
|
|
77
|
+
nodeId(node: JsonNode): string | undefined;
|
|
78
|
+
/** JSONPath of a node by id (copy Path, J16). */
|
|
79
|
+
nodePath(id: string): string;
|
|
80
|
+
/** Copyable value of a node by id (copy Value, J16). */
|
|
81
|
+
nodeValue(id: string): string;
|
|
82
|
+
/** Ordered matching node ids (conformance kit — DOM-free). */
|
|
83
|
+
readonly matches: readonly string[];
|
|
84
|
+
}
|
|
85
|
+
export interface JsonControllerOptions {
|
|
86
|
+
limits?: JsonParseOptions['limits'];
|
|
87
|
+
maxDepth?: number;
|
|
88
|
+
}
|
|
89
|
+
export declare function createJsonController(input: Uint8Array | string, options?: JsonControllerOptions): JsonController;
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
// JSON behavior controller (DESIGN.md §3-②) — DOM-free interaction state for
|
|
2
|
+
// the JSON viewer. The DOM renderer is one consumer; the conformance kit drives
|
|
3
|
+
// this directly (docs/viewers/json.md 부록 A). Decisions: default view = source
|
|
4
|
+
// (J9), source has three forms (J14), toolbox routing = vscode (J11/부록 B-1),
|
|
5
|
+
// search over key/path/type/value (J16). Toolbox transforms live in
|
|
6
|
+
// transforms.ts (부록 B).
|
|
7
|
+
import { decodeUtf8 } from '../../parsers/types.js';
|
|
8
|
+
import { parseJson } from '../../parsers/json/index.js';
|
|
9
|
+
import { base64Decode, base64Encode, escapeText, jsonToCsv, jsonToXml, jsonToYaml, serialize, toPlainValue, unescapeText } from './transforms.js';
|
|
10
|
+
const TOOL_MESSAGE = {
|
|
11
|
+
pretty: 'json.tool.formatted',
|
|
12
|
+
minify: 'json.tool.minified',
|
|
13
|
+
'sort-keys': 'json.tool.sorted',
|
|
14
|
+
escape: 'json.tool.escaped',
|
|
15
|
+
unescape: 'json.tool.unescaped',
|
|
16
|
+
'base64-encode': 'json.tool.base64Encoded',
|
|
17
|
+
'base64-decode': 'json.tool.base64Decoded'
|
|
18
|
+
};
|
|
19
|
+
const CONVERTER_TITLE = {
|
|
20
|
+
'to-csv': 'json.tool.csvOutput',
|
|
21
|
+
'to-xml': 'json.tool.xmlOutput',
|
|
22
|
+
'to-yaml': 'json.tool.yamlOutput'
|
|
23
|
+
};
|
|
24
|
+
const TRANSFORM_ERROR = {
|
|
25
|
+
'invalid-unicode': 'json.tool.unescapeFailed',
|
|
26
|
+
'invalid-hex': 'json.tool.unescapeFailed',
|
|
27
|
+
trailing: 'json.tool.unescapeFailed',
|
|
28
|
+
'invalid-base64': 'json.tool.base64Failed',
|
|
29
|
+
'csv-requires-objects': 'json.tool.csvRequiresObjects'
|
|
30
|
+
};
|
|
31
|
+
export function createJsonController(input, options = {}) {
|
|
32
|
+
const listeners = new Set();
|
|
33
|
+
// Fallback verbatim text so a failed parse still shows the source (J20).
|
|
34
|
+
const decodedInput = typeof input === 'string' ? input : decodeUtf8(input);
|
|
35
|
+
// Nodes are addressed by a unique id, not by JSONPath: duplicate object
|
|
36
|
+
// keys share a path (e.g. both '$.a' in {"a":1,"a":2}) but must be toggled,
|
|
37
|
+
// searched, and copied independently (P1b). The id equals the path when
|
|
38
|
+
// unique and gets a '#n' suffix for duplicates, so the common case stays
|
|
39
|
+
// path-readable while duplicates stay addressable.
|
|
40
|
+
let nodeById = new Map();
|
|
41
|
+
let idOf = new WeakMap();
|
|
42
|
+
let parentIdOf = new Map();
|
|
43
|
+
let order = [];
|
|
44
|
+
let matchList = [];
|
|
45
|
+
/** Re-parse `text` and rebuild the derived indices; returns core fields. */
|
|
46
|
+
const analyze = (text) => {
|
|
47
|
+
const parseOptions = {};
|
|
48
|
+
if (options.limits !== undefined)
|
|
49
|
+
parseOptions.limits = options.limits;
|
|
50
|
+
if (options.maxDepth !== undefined)
|
|
51
|
+
parseOptions.maxDepth = options.maxDepth;
|
|
52
|
+
const { result } = parseJson(text, parseOptions);
|
|
53
|
+
nodeById = new Map();
|
|
54
|
+
idOf = new WeakMap();
|
|
55
|
+
parentIdOf = new Map();
|
|
56
|
+
order = [];
|
|
57
|
+
let root = null;
|
|
58
|
+
let statistics = null;
|
|
59
|
+
if (result.status !== 'failed') {
|
|
60
|
+
root = result.document.root;
|
|
61
|
+
const counts = {
|
|
62
|
+
object: 0,
|
|
63
|
+
array: 0,
|
|
64
|
+
string: 0,
|
|
65
|
+
number: 0,
|
|
66
|
+
boolean: 0,
|
|
67
|
+
null: 0
|
|
68
|
+
};
|
|
69
|
+
let total = 0;
|
|
70
|
+
let maxDepth = 0;
|
|
71
|
+
const walk = (node, parentId) => {
|
|
72
|
+
let id = node.path;
|
|
73
|
+
if (nodeById.has(id)) {
|
|
74
|
+
let n = 2;
|
|
75
|
+
while (nodeById.has(`${node.path}#${n}`))
|
|
76
|
+
n++;
|
|
77
|
+
id = `${node.path}#${n}`;
|
|
78
|
+
}
|
|
79
|
+
nodeById.set(id, node);
|
|
80
|
+
idOf.set(node, id);
|
|
81
|
+
parentIdOf.set(id, parentId);
|
|
82
|
+
order.push(id);
|
|
83
|
+
total++;
|
|
84
|
+
counts[node.kind]++;
|
|
85
|
+
if (node.depth > maxDepth)
|
|
86
|
+
maxDepth = node.depth;
|
|
87
|
+
for (const child of node.children ?? [])
|
|
88
|
+
walk(child, id);
|
|
89
|
+
};
|
|
90
|
+
walk(root, null);
|
|
91
|
+
statistics = { total, maxDepth, counts };
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
status: result.status,
|
|
95
|
+
root,
|
|
96
|
+
statistics,
|
|
97
|
+
diagnostics: result.diagnostics,
|
|
98
|
+
failure: result.status === 'failed' ? result.failure : null
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
/** Container node ids at depth < 2 (J9 initial expand: depth 0 and 1). */
|
|
102
|
+
const initialExpanded = () => {
|
|
103
|
+
const set = new Set();
|
|
104
|
+
for (const id of order) {
|
|
105
|
+
const node = nodeById.get(id);
|
|
106
|
+
if ((node.kind === 'object' || node.kind === 'array') && node.depth < 2) {
|
|
107
|
+
set.add(id);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return set;
|
|
111
|
+
};
|
|
112
|
+
const isContainer = (node) => node.kind === 'object' || node.kind === 'array';
|
|
113
|
+
const searchableText = (node) => {
|
|
114
|
+
let valueText = '';
|
|
115
|
+
if (node.kind === 'string')
|
|
116
|
+
valueText = typeof node.value === 'string' ? node.value : '';
|
|
117
|
+
else if (node.kind === 'number')
|
|
118
|
+
valueText = node.rawNumber ?? '';
|
|
119
|
+
else if (node.kind === 'boolean')
|
|
120
|
+
valueText = String(node.value);
|
|
121
|
+
else if (node.kind === 'null')
|
|
122
|
+
valueText = 'null';
|
|
123
|
+
return `${node.key} ${node.path} ${node.kind} ${valueText}`.toLowerCase();
|
|
124
|
+
};
|
|
125
|
+
const recomputeMatches = (query) => {
|
|
126
|
+
matchList = [];
|
|
127
|
+
const q = query.trim().toLowerCase();
|
|
128
|
+
if (!q)
|
|
129
|
+
return;
|
|
130
|
+
for (const id of order) {
|
|
131
|
+
if (searchableText(nodeById.get(id)).includes(q))
|
|
132
|
+
matchList.push(id);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/** Expand every ancestor container of the current matches (chrome). */
|
|
136
|
+
const expandMatchAncestors = (expanded) => {
|
|
137
|
+
for (const id of matchList) {
|
|
138
|
+
let parent = parentIdOf.get(id) ?? null;
|
|
139
|
+
while (parent) {
|
|
140
|
+
expanded.add(parent);
|
|
141
|
+
parent = parentIdOf.get(parent) ?? null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const initial = analyze(decodedInput);
|
|
146
|
+
const state = {
|
|
147
|
+
status: initial.status,
|
|
148
|
+
viewMode: 'tree', // Preview defaults to the navigable tree.
|
|
149
|
+
sourceForm: 'pretty', // J14 (vscode text = re-serialized pretty)
|
|
150
|
+
root: initial.root,
|
|
151
|
+
expanded: initialExpanded(),
|
|
152
|
+
search: '',
|
|
153
|
+
matchCount: 0,
|
|
154
|
+
currentMatch: -1,
|
|
155
|
+
scratchpad: decodedInput,
|
|
156
|
+
toolResult: null,
|
|
157
|
+
statusMessage: null,
|
|
158
|
+
statistics: initial.statistics,
|
|
159
|
+
diagnostics: initial.diagnostics,
|
|
160
|
+
failure: initial.failure
|
|
161
|
+
};
|
|
162
|
+
const notify = () => {
|
|
163
|
+
for (const listener of listeners)
|
|
164
|
+
listener(state);
|
|
165
|
+
};
|
|
166
|
+
/** Apply a fresh scratchpad text: re-parse, refresh derived state. */
|
|
167
|
+
const setScratchpad = (text) => {
|
|
168
|
+
const next = analyze(text);
|
|
169
|
+
state.scratchpad = text;
|
|
170
|
+
state.status = next.status;
|
|
171
|
+
state.root = next.root;
|
|
172
|
+
state.statistics = next.statistics;
|
|
173
|
+
state.diagnostics = next.diagnostics;
|
|
174
|
+
state.failure = next.failure;
|
|
175
|
+
// The tree changed (edit/transform) and node ids are regenerated, so
|
|
176
|
+
// apply the initial expand rule to the new tree rather than carrying
|
|
177
|
+
// over stale ids — new depth-1 containers get expanded (P2a).
|
|
178
|
+
recomputeMatches(state.search);
|
|
179
|
+
state.matchCount = matchList.length;
|
|
180
|
+
state.currentMatch = matchList.length ? 0 : -1;
|
|
181
|
+
const expanded = initialExpanded();
|
|
182
|
+
expandMatchAncestors(expanded);
|
|
183
|
+
state.expanded = expanded;
|
|
184
|
+
};
|
|
185
|
+
const replaceScratchpad = (text, messageKey) => {
|
|
186
|
+
setScratchpad(text);
|
|
187
|
+
state.statusMessage = { key: messageKey };
|
|
188
|
+
};
|
|
189
|
+
const requireValidRoot = () => state.status === 'ok' && state.root ? state.root : null;
|
|
190
|
+
/** Show raw-text conversions for review without overwriting the editor. */
|
|
191
|
+
const runRawResult = (action, result) => {
|
|
192
|
+
const transformed = typeof result === 'string' ? { ok: true, output: result } : result;
|
|
193
|
+
state.toolResult = {
|
|
194
|
+
action,
|
|
195
|
+
titleKey: TOOL_MESSAGE[action],
|
|
196
|
+
output: transformed.ok ? transformed.output : '',
|
|
197
|
+
...(transformed.ok
|
|
198
|
+
? {}
|
|
199
|
+
: { error: { key: TRANSFORM_ERROR[transformed.error] ?? 'json.tool.failed' } })
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
/** Chain Base64 operations through their latest successful result while
|
|
203
|
+
* leaving the editor's original JSON untouched. */
|
|
204
|
+
const base64Input = () => {
|
|
205
|
+
const previous = state.toolResult;
|
|
206
|
+
return previous
|
|
207
|
+
&& !previous.error
|
|
208
|
+
&& (previous.action === 'base64-encode' || previous.action === 'base64-decode')
|
|
209
|
+
? previous.output
|
|
210
|
+
: state.scratchpad;
|
|
211
|
+
};
|
|
212
|
+
const runConverter = (action, convert) => {
|
|
213
|
+
const root = requireValidRoot();
|
|
214
|
+
if (!root) {
|
|
215
|
+
state.toolResult = {
|
|
216
|
+
action,
|
|
217
|
+
titleKey: CONVERTER_TITLE[action],
|
|
218
|
+
output: '',
|
|
219
|
+
error: { key: 'json.tool.invalid' }
|
|
220
|
+
};
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const result = convert(toPlainValue(root));
|
|
224
|
+
state.toolResult = {
|
|
225
|
+
action,
|
|
226
|
+
titleKey: CONVERTER_TITLE[action],
|
|
227
|
+
output: result.ok ? result.output : '',
|
|
228
|
+
...(result.ok ? {} : { error: { key: TRANSFORM_ERROR[result.error] ?? 'json.tool.failed' } })
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
const runTool = (action) => {
|
|
232
|
+
state.statusMessage = null;
|
|
233
|
+
switch (action) {
|
|
234
|
+
case 'pretty': {
|
|
235
|
+
const root = requireValidRoot();
|
|
236
|
+
if (root)
|
|
237
|
+
replaceScratchpad(serialize(root, true), TOOL_MESSAGE.pretty);
|
|
238
|
+
else
|
|
239
|
+
state.statusMessage = { key: 'json.tool.invalid' };
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
case 'minify': {
|
|
243
|
+
const root = requireValidRoot();
|
|
244
|
+
if (root)
|
|
245
|
+
replaceScratchpad(serialize(root, false), TOOL_MESSAGE.minify);
|
|
246
|
+
else
|
|
247
|
+
state.statusMessage = { key: 'json.tool.invalid' };
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
case 'sort-keys': {
|
|
251
|
+
const root = requireValidRoot();
|
|
252
|
+
if (root)
|
|
253
|
+
replaceScratchpad(serialize(root, true, true), TOOL_MESSAGE['sort-keys']);
|
|
254
|
+
else
|
|
255
|
+
state.statusMessage = { key: 'json.tool.invalid' };
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case 'validate': {
|
|
259
|
+
const root = requireValidRoot();
|
|
260
|
+
if (root) {
|
|
261
|
+
const summary = root.kind === 'array'
|
|
262
|
+
? `Array(${root.children?.length ?? 0})`
|
|
263
|
+
: root.kind;
|
|
264
|
+
state.statusMessage = { key: 'json.tool.valid', args: { summary } };
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
state.statusMessage = { key: 'json.tool.invalid' };
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case 'escape':
|
|
272
|
+
runRawResult('escape', escapeText(state.scratchpad));
|
|
273
|
+
break;
|
|
274
|
+
case 'unescape':
|
|
275
|
+
runRawResult('unescape', unescapeText(state.scratchpad));
|
|
276
|
+
break;
|
|
277
|
+
case 'base64-encode':
|
|
278
|
+
runRawResult('base64-encode', base64Encode(base64Input()));
|
|
279
|
+
break;
|
|
280
|
+
case 'base64-decode':
|
|
281
|
+
runRawResult('base64-decode', base64Decode(base64Input()));
|
|
282
|
+
break;
|
|
283
|
+
case 'to-csv':
|
|
284
|
+
runConverter('to-csv', jsonToCsv);
|
|
285
|
+
break;
|
|
286
|
+
case 'to-xml':
|
|
287
|
+
runConverter('to-xml', jsonToXml);
|
|
288
|
+
break;
|
|
289
|
+
case 'to-yaml':
|
|
290
|
+
runConverter('to-yaml', (v) => jsonToYaml(v));
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const dispatch = (action) => {
|
|
295
|
+
switch (action.type) {
|
|
296
|
+
case 'set-view-mode':
|
|
297
|
+
state.viewMode = action.mode;
|
|
298
|
+
break;
|
|
299
|
+
case 'set-source-form':
|
|
300
|
+
state.sourceForm = action.form;
|
|
301
|
+
break;
|
|
302
|
+
case 'toggle-node': {
|
|
303
|
+
const node = nodeById.get(action.id);
|
|
304
|
+
if (!node || !isContainer(node))
|
|
305
|
+
return;
|
|
306
|
+
const expanded = new Set(state.expanded);
|
|
307
|
+
if (expanded.has(action.id))
|
|
308
|
+
expanded.delete(action.id);
|
|
309
|
+
else
|
|
310
|
+
expanded.add(action.id);
|
|
311
|
+
state.expanded = expanded;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case 'expand-all': {
|
|
315
|
+
const expanded = new Set();
|
|
316
|
+
for (const id of order) {
|
|
317
|
+
if (isContainer(nodeById.get(id)))
|
|
318
|
+
expanded.add(id);
|
|
319
|
+
}
|
|
320
|
+
state.expanded = expanded;
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
case 'collapse-all':
|
|
324
|
+
state.expanded =
|
|
325
|
+
state.root && isContainer(state.root) && order.length
|
|
326
|
+
? new Set([order[0]])
|
|
327
|
+
: new Set();
|
|
328
|
+
break;
|
|
329
|
+
case 'set-search': {
|
|
330
|
+
state.search = action.search;
|
|
331
|
+
recomputeMatches(action.search);
|
|
332
|
+
state.matchCount = matchList.length;
|
|
333
|
+
state.currentMatch = matchList.length ? 0 : -1;
|
|
334
|
+
const expanded = new Set(state.expanded);
|
|
335
|
+
expandMatchAncestors(expanded);
|
|
336
|
+
state.expanded = expanded;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
case 'next-match':
|
|
340
|
+
if (matchList.length) {
|
|
341
|
+
state.currentMatch = (state.currentMatch + 1) % matchList.length;
|
|
342
|
+
}
|
|
343
|
+
break;
|
|
344
|
+
case 'prev-match':
|
|
345
|
+
if (matchList.length) {
|
|
346
|
+
state.currentMatch =
|
|
347
|
+
(state.currentMatch - 1 + matchList.length) % matchList.length;
|
|
348
|
+
}
|
|
349
|
+
break;
|
|
350
|
+
case 'run-tool':
|
|
351
|
+
runTool(action.action);
|
|
352
|
+
break;
|
|
353
|
+
case 'apply-result-to-editor':
|
|
354
|
+
if (state.toolResult && !state.toolResult.error) {
|
|
355
|
+
setScratchpad(state.toolResult.output);
|
|
356
|
+
state.toolResult = null;
|
|
357
|
+
state.statusMessage = { key: 'json.tool.applied' };
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
case 'dismiss-result':
|
|
361
|
+
state.toolResult = null;
|
|
362
|
+
break;
|
|
363
|
+
case 'edit-scratchpad':
|
|
364
|
+
setScratchpad(action.text);
|
|
365
|
+
state.statusMessage = null;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
notify();
|
|
369
|
+
};
|
|
370
|
+
const nodeValue = (id) => {
|
|
371
|
+
const node = nodeById.get(id);
|
|
372
|
+
if (!node)
|
|
373
|
+
return '';
|
|
374
|
+
if (isContainer(node))
|
|
375
|
+
return serialize(node, true);
|
|
376
|
+
if (node.kind === 'string')
|
|
377
|
+
return typeof node.value === 'string' ? node.value : '';
|
|
378
|
+
if (node.kind === 'number')
|
|
379
|
+
return node.rawNumber ?? '';
|
|
380
|
+
if (node.kind === 'boolean')
|
|
381
|
+
return String(node.value);
|
|
382
|
+
return 'null';
|
|
383
|
+
};
|
|
384
|
+
const sourceText = () => {
|
|
385
|
+
if (state.sourceForm === 'verbatim')
|
|
386
|
+
return state.scratchpad;
|
|
387
|
+
if (state.root && state.status === 'ok') {
|
|
388
|
+
return serialize(state.root, state.sourceForm === 'pretty');
|
|
389
|
+
}
|
|
390
|
+
// Can't re-serialize invalid JSON — show the verbatim text instead.
|
|
391
|
+
return state.scratchpad;
|
|
392
|
+
};
|
|
393
|
+
return {
|
|
394
|
+
get state() {
|
|
395
|
+
return state;
|
|
396
|
+
},
|
|
397
|
+
dispatch,
|
|
398
|
+
subscribe(listener) {
|
|
399
|
+
listeners.add(listener);
|
|
400
|
+
return () => listeners.delete(listener);
|
|
401
|
+
},
|
|
402
|
+
verbatimText: () => state.scratchpad,
|
|
403
|
+
sourceText,
|
|
404
|
+
nodeId: (node) => idOf.get(node),
|
|
405
|
+
nodePath: (id) => nodeById.get(id)?.path ?? id,
|
|
406
|
+
nodeValue,
|
|
407
|
+
get matches() {
|
|
408
|
+
return matchList;
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|