omni-viewer-core 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +228 -2
  2. package/dist/i18n/catalog.de.js +7 -2
  3. package/dist/i18n/catalog.en.js +286 -2
  4. package/dist/i18n/catalog.fr.js +7 -2
  5. package/dist/i18n/catalog.it.js +7 -2
  6. package/dist/i18n/catalog.ja.js +266 -2
  7. package/dist/i18n/catalog.ko.js +266 -2
  8. package/dist/i18n/catalog.th.js +7 -2
  9. package/dist/i18n/catalog.zh-cn.js +260 -2
  10. package/dist/parsers/har/index.d.ts +54 -0
  11. package/dist/parsers/har/index.js +833 -0
  12. package/dist/parsers/har/model.d.ts +172 -0
  13. package/dist/parsers/har/model.js +5 -0
  14. package/dist/parsers/notebook/index.d.ts +17 -0
  15. package/dist/parsers/notebook/index.js +212 -0
  16. package/dist/parsers/notebook/model.d.ts +40 -0
  17. package/dist/parsers/notebook/model.js +1 -0
  18. package/dist/parsers/openvino/index.d.ts +141 -0
  19. package/dist/parsers/openvino/index.js +950 -0
  20. package/dist/parsers/pt2/index.d.ts +266 -0
  21. package/dist/parsers/pt2/index.js +1692 -0
  22. package/dist/parsers/pte/index.d.ts +211 -0
  23. package/dist/parsers/pte/index.js +1196 -0
  24. package/dist/registry/index.d.ts +10 -1
  25. package/dist/registry/index.js +31 -3
  26. package/dist/registry/probe.js +6 -0
  27. package/dist/registry/sniff.d.ts +15 -2
  28. package/dist/registry/sniff.js +46 -1
  29. package/dist/styles/audio.css +82 -18
  30. package/dist/styles/har.css +94 -0
  31. package/dist/styles/notebook.css +17 -0
  32. package/dist/styles/openvino.css +4 -0
  33. package/dist/styles/pt2.css +3 -0
  34. package/dist/styles/pte.css +3 -0
  35. package/dist/viewers/audio/audio-window.d.ts +70 -0
  36. package/dist/viewers/audio/audio-window.js +246 -0
  37. package/dist/viewers/audio/controller.d.ts +10 -1
  38. package/dist/viewers/audio/controller.js +11 -2
  39. package/dist/viewers/audio/index.d.ts +37 -2
  40. package/dist/viewers/audio/index.js +508 -118
  41. package/dist/viewers/audio/mp3-analyzer.d.ts +9 -0
  42. package/dist/viewers/audio/mp3-analyzer.js +50 -0
  43. package/dist/viewers/audio/spectrogram-view.d.ts +121 -0
  44. package/dist/viewers/audio/spectrogram-view.js +303 -0
  45. package/dist/viewers/audio/spectrogram.d.ts +57 -0
  46. package/dist/viewers/audio/spectrogram.js +256 -0
  47. package/dist/viewers/audio/styles.js +82 -18
  48. package/dist/viewers/har/index.d.ts +29 -0
  49. package/dist/viewers/har/index.js +804 -0
  50. package/dist/viewers/har/styles.d.ts +1 -0
  51. package/dist/viewers/har/styles.js +98 -0
  52. package/dist/viewers/markdown/index.js +1 -5
  53. package/dist/viewers/markdown/math.d.ts +11 -1
  54. package/dist/viewers/markdown/math.js +315 -19
  55. package/dist/viewers/markdown/sanitize.d.ts +9 -0
  56. package/dist/viewers/markdown/sanitize.js +7 -0
  57. package/dist/viewers/notebook/index.d.ts +24 -0
  58. package/dist/viewers/notebook/index.js +516 -0
  59. package/dist/viewers/notebook/self-loading.d.ts +3 -0
  60. package/dist/viewers/notebook/self-loading.js +18 -0
  61. package/dist/viewers/notebook/styles.d.ts +1 -0
  62. package/dist/viewers/notebook/styles.js +9 -0
  63. package/dist/viewers/openvino/index.d.ts +33 -0
  64. package/dist/viewers/openvino/index.js +522 -0
  65. package/dist/viewers/openvino/styles.d.ts +1 -0
  66. package/dist/viewers/openvino/styles.js +5 -0
  67. package/dist/viewers/pt2/index.d.ts +21 -0
  68. package/dist/viewers/pt2/index.js +895 -0
  69. package/dist/viewers/pt2/styles.d.ts +1 -0
  70. package/dist/viewers/pt2/styles.js +4 -0
  71. package/dist/viewers/pte/index.d.ts +20 -0
  72. package/dist/viewers/pte/index.js +883 -0
  73. package/dist/viewers/pte/styles.d.ts +1 -0
  74. package/dist/viewers/pte/styles.js +4 -0
  75. package/package.json +57 -1
package/README.md CHANGED
@@ -13,11 +13,14 @@ models, viewers mount into a DOM element, and everything host-specific
13
13
  ## Supported formats
14
14
 
15
15
  - **Documents** — PDF, Word (DOCX and legacy DOC), HWP, PowerPoint (PPTX and
16
- legacy PPT), Markdown, LaTeX (structure and math preview, not typesetting)
16
+ legacy PPT), Markdown, Jupyter Notebook (.ipynb),
17
+ LaTeX (structure and math preview, not typesetting)
17
18
  - **Data & spreadsheets** — Excel, CSV/TSV, JSON, JSONL/NDJSON, YAML, TOML,
18
19
  Parquet, Avro, HDF5, MATLAB MAT, NumPy (NPY/NPZ), Safetensors, GGUF, ONNX, TFLite/LiteRT,
19
20
  Keras (.keras and legacy .h5), Core ML (.mlmodel and .mlpackage),
20
- Protocol Buffers, ReqIF, SQLite
21
+ OpenVINO IR (.xml + .bin), PyTorch Export (.pt2), ExecuTorch (.pte), Protocol
22
+ Buffers, ReqIF, SQLite
23
+ - **Web & network** — HAR (HTTP Archive) request logs
21
24
  - **Media & graphics** — audio (waveform/spectrogram), video, images,
22
25
  Photoshop PSD
23
26
  - **Engineering & automotive** — CAN DBC, AUTOSAR ARXML, ASAM A2L, Vector
@@ -213,6 +216,229 @@ layout. A `.mlmodel` carries no leading magic, so hosts that can read a whole
213
216
  extensionless file can refine routing with `looksLikeCoremlSpec` from
214
217
  `omni-viewer-core/parsers/coreml`.
215
218
 
219
+ ### OpenVINO IR models
220
+
221
+ An OpenVINO IR is two files that share a base name. The `.xml` is ordinary XML
222
+ whose root is `<net name version>` holding `<layers>` and `<edges>`; the `.bin`
223
+ has no header, magic, or index of its own — it is a raw concatenation of
224
+ constant payloads that `Const` layers address by `offset` and `size`. The core
225
+ receives the `.xml` as its input and the `.bin` as a sidecar the adapter reads
226
+ from the same directory:
227
+
228
+ ```ts
229
+ import { mountOpenVinoViewer } from 'omni-viewer-core/viewers/openvino';
230
+
231
+ await mountOpenVinoViewer({ fileName: file.name, data: xmlBytes }, container, ctx, {
232
+ sidecars: { bin: binBytes } // optional
233
+ });
234
+ ```
235
+
236
+ The topology renders in full without the `.bin`. When it is supplied, every
237
+ constant's byte range is checked against it and its leading values are decoded
238
+ for a preview (all fixed-width IR element types, including f16, bf16, and the
239
+ f8 variants); the rest of the weights are never read, so a multi-gigabyte model
240
+ costs nothing beyond its XML. A `.bin` that is too short for the constants, or
241
+ that has bytes no constant references, is surfaced as a warning — the usual
242
+ signs of a mismatched pair (the unreferenced-bytes signal is only trusted when
243
+ every constant was read).
244
+
245
+ The viewer provides a computation graph with layer inspection (ports with
246
+ their producers and consumers, `<data>` attributes, `rt_info`, weight ranges,
247
+ and the sub-graphs a `Loop`, `TensorIterator`, or `If` layer carries in its
248
+ body), plus searchable layer, constant, input/output, and model-information
249
+ panels. IR v10 and v11 are the target; the legacy v7-and-earlier layout
250
+ (`<layer precision>` with `<blobs>`) is read on a best-effort basis and
251
+ flagged.
252
+
253
+ Routing is by content only: `.xml` is shared with every other XML dialect, so
254
+ the descriptor claims no extension and `detectViewer` reaches the viewer
255
+ through its text sample (`looksLikeOpenVinoIr`, also exported from
256
+ `omni-viewer-core/parsers/openvino`). A `.bin` opened on its own has nothing to
257
+ identify it; an adapter that wants to open one should look for the same-named
258
+ `.xml` beside it.
259
+
260
+ ### PyTorch Export packages (.pt2)
261
+
262
+ A `.pt2` is what `torch.export.save` writes: a ZIP whose members sit under a
263
+ folder named after the file, with the serialized Export IR program in
264
+ `models/<name>.json`, parameters and buffers as raw storage bytes under
265
+ `data/weights/` (indexed by `<name>_weights_config.json`), tensor constants and
266
+ script objects under `data/constants/`, pickled sample inputs, optional
267
+ AOTInductor artifacts under `data/aotinductor/`, and user files under `extra/`.
268
+
269
+ ```ts
270
+ import { mountPt2Viewer } from 'omni-viewer-core/viewers/pt2';
271
+
272
+ await mountPt2Viewer({ fileName: file.name, data: bytes }, container, ctx);
273
+ ```
274
+
275
+ `parsePt2` reads the archive's central directory and only the JSON members it
276
+ needs; stored weight payloads are viewed in place and sampled for a short value
277
+ preview (every fixed-width torch dtype, including bf16 and the float8
278
+ variants), so a multi-gigabyte package costs nothing beyond its program JSON.
279
+ The viewer shows the ATen graph — placeholders coloured by signature kind
280
+ (user input, parameter, buffer, constant), operator nodes, higher-order
281
+ control-flow nodes with their `cond` / `while_loop` sub-graphs, and outputs —
282
+ with a node inspector (arguments, producers and consumers, module stack,
283
+ metadata, stack trace), plus searchable tables of nodes (sub-graph nodes
284
+ included), weights and constants with their archive status, the graph
285
+ signature (inputs and outputs by kind, buffer mutations, constant inputs), the
286
+ nn.Module hierarchy with preserved call signatures, model information (schema
287
+ and torch versions, opsets, range constraints, guards, operator counts), and
288
+ the archive listing. Symbolic shapes are printed as expressions (`2*s0`)
289
+ rather than the sympy `srepr` the file stores.
290
+
291
+ A package holding several programs (`package_pt2`) gets a model selector; an
292
+ AOTInductor-only package (`aoti_compile_and_package`) opens on the archive
293
+ tab with its compile metadata. The pre-PT2 `torch.export.save` layout
294
+ (`serialized_exported_program.json` beside torch.save pickles) is read on a
295
+ best-effort basis: the graph is complete, but weights are listed from the
296
+ signature without a preview. Schema 8.x is the target; enums
297
+ written as member names by older versions are accepted.
298
+
299
+ Routing is by the `.pt2` extension; an extensionless package reaches the viewer
300
+ through `probeContainer`, which recognizes the `archive_format` marker
301
+ (`looksLikePt2Archive` is also exported from `omni-viewer-core/parsers/pt2`).
302
+ A plain `torch.save` checkpoint (`.pt` / `.pth`) is not an Export package and
303
+ is not claimed.
304
+
305
+ ### ExecuTorch programs (.pte)
306
+
307
+ A `.pte` is what `to_executorch().save()` writes: a FlatBuffer following
308
+ ExecuTorch's `program.fbs` (file identifier `ET12`), optionally followed by data
309
+ segments — constant tensors, delegate blobs, mutable initial state, and named
310
+ blobs — that an extended header (`eh00`, inserted right after the identifier)
311
+ locates. The parser reads the FlatBuffer directly with no schema compiler and no
312
+ runtime dependency, and never decodes a segment or buffer payload:
313
+
314
+ ```ts
315
+ import { mountPteViewer } from 'omni-viewer-core/viewers/pte';
316
+
317
+ await mountPteViewer({ fileName: file.name, data: bytes }, container, ctx);
318
+ ```
319
+
320
+ `parsePte` keeps every method (`ExecutionPlan`) with its values, instructions,
321
+ operators, and delegates, and resolves where each tensor's bytes live: the
322
+ deprecated inline `constant_buffer`, the constant segment (`constant_segment`
323
+ offsets, with the alignment-padded slot size and absolute file offset), a
324
+ mutable data segment for buffers with initial state, an external `.ptd` file
325
+ (`ExtraTensorInfo.location = EXTERNAL`), a memory-planned arena slot
326
+ (`AllocationDetails`), or nothing at all for runtime inputs. A kernel call's
327
+ outputs come from the return value the emitter appends after the schema
328
+ arguments (the out tensor, a TensorList of the out tensors, or a fresh scalar
329
+ for a symbolic op), and only the out-argument positions in front of it are
330
+ dropped from the inputs; a delegate call carries inputs then outputs with no marker,
331
+ so those are split by data flow — the trailing arguments nothing defined yet.
332
+ A view or alias the emitter gave its own EValue — a tensor nothing writes that
333
+ shares a memory-planned slot with an earlier one — is resolved to the value it
334
+ aliases when the file leaves no other reading, so a delegate fed through
335
+ aliases keeps its inputs and the graph keeps its edges. Every EValue kind
336
+ gets a preview, delegate compile specs are decoded when they are short text,
337
+ and the leading instructions keep their emitter stack traces.
338
+
339
+ The viewer draws each method's kernel, delegate, and move calls as a graph with
340
+ inputs, consumed constants, and outputs, plus an inspector (arguments with their
341
+ values, backend and compile specs for a delegate call, jump destination, stack
342
+ trace), and searchable instruction, value, input/output, delegate, segment, and
343
+ model-information panels; a multi-method program gets a method selector.
344
+ Warnings cover backends the runtime must register, tensors stored in a separate
345
+ `.ptd`, segments the file cannot locate or that run past its end, constant data
346
+ the program never provides, references to segments the program never declares,
347
+ and unrecognized scalar types, value kinds, or instruction kinds.
348
+
349
+ Routing is by the `.pte` extension, validated against the `ET12` identifier at
350
+ offset 4; an extensionless program is detected from the same bytes. A `.ptd`
351
+ data file (`FT01`) is not a program and is not claimed.
352
+
353
+ ### Jupyter notebooks (.ipynb)
354
+
355
+ View nbformat 4 notebooks with their code and saved results in file order.
356
+ Markdown cells reuse the Markdown renderer, syntax highlighter, math renderer,
357
+ and prose styles. Code cells show `In`/`Out` execution counts, stdout/stderr,
358
+ HTML tables, PNG/JPEG/GIF/WebP/SVG images, Markdown, LaTeX, JSON, plain text,
359
+ and error tracebacks. Raw cells display as text. Code is never executed.
360
+
361
+ ```ts
362
+ import { mountNotebookViewer } from 'omni-viewer-core/viewers/notebook';
363
+ import { loadNotebookViewerDeps } from 'omni-viewer-core/viewers/notebook/self-loading';
364
+
365
+ const deps = await loadNotebookViewerDeps();
366
+ const handle = await mountNotebookViewer(
367
+ { fileName: 'analysis.ipynb', data: notebookBytes }, container, hostContext, deps
368
+ );
369
+ // Release the viewer, listeners, and any host-resolved assets on close.
370
+ handle.dispose();
371
+ ```
372
+
373
+ The loader requires the existing optional peers `marked` and `dompurify`;
374
+ `highlight.js` and `katex` enhance code and formulas when installed. Adapters
375
+ can inject the same dependencies they use for Markdown. Load KaTeX CSS and
376
+ fonts inside the viewer's shadow root when using KaTeX, as with Markdown.
377
+ Scoped mounting uses `styles/notebook.css`.
378
+
379
+ Search cells and results, hide all code or outputs, or expand individual cells.
380
+ Persisted `source_hidden`, `outputs_hidden`, and `collapsed` hints set the
381
+ initial expansion state. One static representation is selected per MIME bundle;
382
+ interactive widget/JavaScript outputs fall back to saved plain text, or a
383
+ clear unsupported-output notice. Markdown `attachment:` images work offline.
384
+ Relative images use the optional `documentAssets` service, external links use
385
+ `navigation`, and remote resources and active HTML are blocked.
386
+
387
+ `parseNotebook` is also available from `parsers/notebook`. It returns a typed
388
+ `ParseOutcome<NotebookDocument>` and uses the core JSON parser, retaining raw
389
+ JSON output tokens. Defaults: 64 MiB input, 2,000 cells, 10,000 outputs, 4 MiB
390
+ per preview, and 16 MiB cumulative previews. `limits.maxEntries` counts cells;
391
+ `maxOutputs` and `maxTotalPreviewBytes` are independent limits. Truncation
392
+ returns `partial` with diagnostics. Invalid JSON and unsupported major versions
393
+ return typed failures. See [viewer details](examples/notebook/README.md) and the
394
+ [sample notebook](examples/notebook/analysis.ipynb).
395
+
396
+ ### HAR network logs (.har)
397
+
398
+ A `.har` file is the HTTP Archive a browser's network panel (or curl, Charles,
399
+ Fiddler, Playwright) exports: one JSON `log` holding every request of a session
400
+ with its headers, bodies, sizes, and phase timings. It is the artifact a web or
401
+ API failure usually arrives as, so the viewer is built for triage rather than
402
+ for reading JSON:
403
+
404
+ ```ts
405
+ import { mountHarViewer } from 'omni-viewer-core/viewers/har';
406
+
407
+ await mountHarViewer({ fileName: file.name, data: bytes }, container, ctx);
408
+ ```
409
+
410
+ `parseHar` is a contract parser (`ParseOutcome<HarDocument>`) and reads the
411
+ archive through the core JSON layer, so a truncated export still yields the
412
+ requests it did hold. Each entry keeps its request and response headers, query
413
+ string, cookies, request payload, and response body, and gains what the table
414
+ needs: host, path, status class, resource type (from `_resourceType` when the
415
+ writer supplied one, from the MIME type otherwise), transferred bytes
416
+ (`_transferSize` when present), decoded resource size, and a phase breakdown
417
+ with the TLS handshake taken out of `connect` so a waterfall cannot
418
+ double-count it. Timestamps go through a core ISO-8601 parser rather than
419
+ `new Date(string)`, and the timeline's origin is the earliest entry, so offsets
420
+ and bar positions are the same on every platform. A base64 body is decoded when
421
+ its MIME type is textual (the URL-safe alphabet included) and reported by size
422
+ only when it is not; bodies are kept up to a preview limit, and entries up to
423
+ `HAR_DEFAULT_MAX_ENTRIES` (a larger archive comes back `partial` with a
424
+ diagnostic). A body cut by the preview limit and one the archive stored
425
+ incompletely — several padded base64 chunks, or a tail outside the alphabet —
426
+ are reported as two different things, and a size or start time the archive never
427
+ gave is kept unknown rather than shown as a confident zero.
428
+
429
+ The viewer shows summary figures (requests, elapsed, transferred, resources,
430
+ domains, errors), a request table sortable by every column with a per-entry
431
+ waterfall bar, and filters for method, status class, resource type, domain, and
432
+ free text over URLs, status text, and headers — with an opt-in toggle that
433
+ extends the search to bodies. Selecting a request opens headers (general,
434
+ request, response), payload (query string and request body, form fields
435
+ expanded), response (body pretty-printed through the JSON serializer when it
436
+ really is JSON), cookies, and timings (drawn phases plus the values the archive
437
+ reported, with `-1` shown as not applicable). Page load timings and archive
438
+ metadata get their own tabs. Routing is by the `.har` extension; an
439
+ extensionless archive is recognized by content — a `log` object holding an
440
+ `entries` array — and claimed before the generic JSON viewer.
441
+
216
442
  ### Archive host integration
217
443
 
218
444
  Archive decompression remains adapter-owned: pass an `ArchiveDecoder` to
@@ -3,14 +3,18 @@
3
3
  export const CATALOG_DE = {
4
4
  'audio.title': 'Audio', 'video.title': 'Video', 'dbc.title': 'CAN-Datenbank',
5
5
  'audio.play': 'Wiedergabe', 'audio.pause': 'Pause', 'audio.stop': 'Stopp',
6
- 'audio.loop': 'Schleife', 'audio.clearRegions': 'Bereiche löschen', 'audio.volume': 'Lautstärke',
6
+ 'audio.volume': 'Lautstärke',
7
+ 'audio.loop.region': 'Bereich wiederholen', 'audio.loop.track': 'Titel wiederholen',
7
8
  'audio.region.start': 'Bereichsstart (s)', 'audio.region.end': 'Bereichsende (s)', 'audio.region.duration': 'Bereichslänge (s)',
8
9
  'audio.zoom': 'Zoom', 'audio.zoomIn': 'Vergrößern', 'audio.zoomOut': 'Verkleinern', 'audio.zoomFit': 'Anpassen',
9
10
  'audio.visualization': 'Ansicht', 'audio.vis.waveform': 'Wellenform', 'audio.vis.spectrogram': 'Spektrogramm',
10
11
  'audio.vis.both': 'Beide', 'audio.download': 'Herunterladen',
11
- 'audio.spectrogramScale': 'Skala',
12
+ 'audio.spectrogramScale': 'Spektrogramm-Skala',
12
13
  'audio.scale.linear': 'Linear', 'audio.scale.mel': 'Mel', 'audio.scale.bark': 'Bark', 'audio.scale.erb': 'ERB',
14
+ 'audio.scale.logarithmic': 'Logarithmisch',
15
+ 'audio.frequencyRange': 'Frequenzbereich', 'audio.frequency.full': 'Gesamt', 'audio.frequency.limit': 'Bis {hz}',
13
16
  'audio.error.save': 'Die Audiodatei konnte nicht gespeichert werden.',
17
+ 'audio.error.spectrogram': 'Das Spektrogramm für diesen Bereich konnte nicht gezeichnet werden.',
14
18
  'audio.info.duration': 'Dauer', 'audio.info.sampleRate': 'Abtastrate', 'audio.info.channels': 'Kanäle',
15
19
  'audio.info.channelLevels': 'L Spitze {leftPeak}, RMS {leftRms} | R Spitze {rightPeak}, RMS {rightRms}',
16
20
  'audio.info.bitDepth': 'Bittiefe', 'audio.info.format': 'Format', 'audio.info.fileSize': 'Dateigröße',
@@ -21,6 +25,7 @@ export const CATALOG_DE = {
21
25
  'audio.error.decode': 'Der Browser konnte diesen Audio-Codec nicht dekodieren. Verwenden Sie einen Host mit dem erforderlichen Codec oder transkodieren Sie die Datei.',
22
26
  'audio.warning.analysisFailed': 'Die Wellenform-Voranalyse ist fehlgeschlagen — es wird auf die vollständige Browser-Dekodierung zurückgegriffen, die bei einer Datei dieser Größe langsam sein kann.',
23
27
  'audio.warning.analysisSkipped': 'Die Wellenform-Voranalyse ist für dieses Format nicht verfügbar — es wird auf die vollständige Browser-Dekodierung zurückgegriffen, die bei einer Datei dieser Größe langsam sein kann.',
28
+ 'audio.warning.reducedFidelity': 'Zur Speicherersparnis mit reduzierter Qualität dekodiert; dem Spektrogramm fehlen die höchsten Frequenzen.',
24
29
  'audio.vis.unavailableLarge': 'Das Spektrogramm ist für große Dateien, die aus vorberechneten Spitzenwerten gerendert werden, nicht verfügbar.',
25
30
  'video.play': 'Wiedergabe', 'video.pause': 'Pause', 'video.stop': 'Stopp',
26
31
  'video.skipBack': '⏪ -{seconds} s', 'video.skipForward': '⏩ +{seconds} s',