omni-viewer-core 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/host/index.d.ts +14 -2
- package/dist/i18n/catalog.en.js +8 -0
- package/dist/i18n/catalog.ja.js +9 -1
- package/dist/i18n/catalog.ko.js +8 -0
- package/dist/i18n/catalog.zh-cn.js +8 -1
- package/dist/parsers/pptx/index.d.ts +1 -0
- package/dist/parsers/pptx/index.js +1 -1
- package/dist/parsers/yaml/index.js +14 -2
- package/dist/parsers/yaml/self-loading.js +26 -14
- package/dist/registry/index.js +1 -1
- package/dist/styles/archive.css +1 -1
- package/dist/styles/pdf.css +26 -0
- package/dist/styles/toml.css +1 -1
- package/dist/styles/yaml.css +1 -1
- package/dist/viewers/archive/index.js +60 -11
- package/dist/viewers/archive/styles.d.ts +1 -1
- package/dist/viewers/archive/styles.js +1 -1
- package/dist/viewers/json/controller.js +13 -24
- package/dist/viewers/json/index.js +5 -1
- package/dist/viewers/markdown/index.d.ts +3 -2
- package/dist/viewers/markdown/index.js +29 -14
- package/dist/viewers/pdf/controller.d.ts +14 -2
- package/dist/viewers/pdf/controller.js +28 -10
- package/dist/viewers/pdf/index.d.ts +79 -8
- package/dist/viewers/pdf/index.js +363 -81
- package/dist/viewers/pdf/self-loading.d.ts +3 -3
- package/dist/viewers/pdf/styles.d.ts +1 -1
- package/dist/viewers/pdf/styles.js +26 -0
- package/dist/viewers/ppt/index.js +3 -3
- package/dist/viewers/structured-styles.d.ts +1 -1
- package/dist/viewers/structured-styles.js +1 -1
- package/dist/viewers/structured.d.ts +1 -0
- package/dist/viewers/structured.js +5 -3
- package/dist/viewers/yaml/controller.js +48 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -63,6 +63,34 @@ Viewers follow the same pattern — a `mount*Viewer(input, container, ctx,
|
|
|
63
63
|
deps)` entry per format, plus `self-loading` variants that dynamically import
|
|
64
64
|
their own dependencies for hosts that don't want to wire them manually.
|
|
65
65
|
|
|
66
|
+
### PDF host integration
|
|
67
|
+
|
|
68
|
+
The PDF viewer asks `ctx.assets.resolveAssetUrl` for the exact key
|
|
69
|
+
`assets/pdfjs/pdf.worker.min.mjs`. The published package also exposes that
|
|
70
|
+
worker at `omni-viewer-core/assets/pdfjs/pdf.worker.min.mjs`; a host may return
|
|
71
|
+
its own compatible `pdfjs-dist` worker URL instead, or pass `workerSrc` in the
|
|
72
|
+
PDF mount options. `isEvalSupported` defaults to `false` for CSP-safe hosts.
|
|
73
|
+
|
|
74
|
+
PDF mount options are additive and optional. `saveMode` is `hybrid` by default
|
|
75
|
+
(editable text/markup sidecar; signatures and deleted pages are permanent) and
|
|
76
|
+
may be set to `flattened` for smaller output. `toolbarActions` adds host-owned
|
|
77
|
+
buttons without exposing platform APIs to core. `zoomLevels`, `maxMergeBytes`,
|
|
78
|
+
and `onSaveAsComplete` configure navigation and host save behavior.
|
|
79
|
+
|
|
80
|
+
Large save and merge work can be delegated through the optional
|
|
81
|
+
`PdfViewerDeps.processing` service. VS Code extension-host and Web Worker
|
|
82
|
+
adapters can implement `buildPdf` and/or `mergePdfs`; each receives an
|
|
83
|
+
`AbortSignal` and progress callback. When the service is absent, the default
|
|
84
|
+
`auto` mode preserves the browser `pdf-lib` fallback. `host` requires the
|
|
85
|
+
service and `browser` forces the fallback. `PdfViewerHandle.operation` reports
|
|
86
|
+
running, succeeded, failed, and cancelled states, and `cancelOperation()`
|
|
87
|
+
requests cancellation.
|
|
88
|
+
|
|
89
|
+
`FileSaveService.saveFile` may continue resolving `void` for compatibility.
|
|
90
|
+
New adapters should return `{ status: 'cancelled' }` when a Save As picker is
|
|
91
|
+
dismissed, or `{ status: 'saved', fileName?, uri? }` after any host post-save
|
|
92
|
+
work (for example opening the new file or showing a notification) completes.
|
|
93
|
+
|
|
66
94
|
Parsers never throw on malformed input; they return a
|
|
67
95
|
`ParseOutcome` with a typed failure and diagnostics, and they enforce
|
|
68
96
|
resource limits (input size, cell/row/entry counts, declared decompressed
|
package/dist/host/index.d.ts
CHANGED
|
@@ -27,11 +27,23 @@ export type LogLevel = 'info' | 'warn' | 'error';
|
|
|
27
27
|
export interface LoggerService {
|
|
28
28
|
log(level: LogLevel, message: string): void;
|
|
29
29
|
}
|
|
30
|
+
export type FileSaveResult = {
|
|
31
|
+
status: 'saved';
|
|
32
|
+
fileName?: string;
|
|
33
|
+
uri?: string;
|
|
34
|
+
} | {
|
|
35
|
+
status: 'cancelled';
|
|
36
|
+
};
|
|
30
37
|
export interface FileSaveService {
|
|
31
38
|
/** Creates a new file. Implementations present the platform's save
|
|
32
39
|
* destination picker (or browser-equivalent download prompt) so the
|
|
33
|
-
* user chooses the target path and may change the suggested name.
|
|
34
|
-
|
|
40
|
+
* user chooses the target path and may change the suggested name.
|
|
41
|
+
*
|
|
42
|
+
* Existing implementations may continue returning `void` (treated as a
|
|
43
|
+
* successful save). New implementations should return `cancelled` when the
|
|
44
|
+
* picker is dismissed, and may open the saved file or show host UI before
|
|
45
|
+
* resolving `saved`. */
|
|
46
|
+
saveFile(name: string, data: Uint8Array, mimeType: string): Promise<void | FileSaveResult>;
|
|
35
47
|
}
|
|
36
48
|
export interface ClipboardService {
|
|
37
49
|
writeText(text: string): Promise<void>;
|
package/dist/i18n/catalog.en.js
CHANGED
|
@@ -142,6 +142,7 @@ export const CATALOG_EN = {
|
|
|
142
142
|
'diag.yaml.node-limit': 'The YAML node limit was reached.',
|
|
143
143
|
'diag.yaml.depth-limit': 'The YAML nesting depth limit was reached.',
|
|
144
144
|
'diag.yaml.alias-limit': 'The YAML alias limit was reached.',
|
|
145
|
+
'diag.yaml.duplicate-key': 'Duplicate key "{key}"; the last value is used.',
|
|
145
146
|
'structured.tree': 'Tree', 'structured.flat': 'Flat', 'structured.json': 'JSON', 'structured.raw': 'Raw', 'structured.expandAll': 'Expand all',
|
|
146
147
|
'structured.collapseAll': 'Collapse all', 'structured.expand': 'Expand', 'structured.collapse': 'Collapse',
|
|
147
148
|
'structured.search': 'Search', 'structured.copy': 'Copy', 'structured.copyPath': 'Path',
|
|
@@ -340,6 +341,13 @@ export const CATALOG_EN = {
|
|
|
340
341
|
'pdf.annotationColor': 'Text color',
|
|
341
342
|
'pdf.save': 'Save',
|
|
342
343
|
'pdf.saveAs': 'Save as',
|
|
344
|
+
'pdf.saving': 'Saving PDF…',
|
|
345
|
+
'pdf.operationCancelled': 'PDF operation cancelled.',
|
|
346
|
+
'pdf.workerLoadFailed': 'Unable to load the PDF worker asset.',
|
|
347
|
+
'pdf.saveModeHybrid': 'Editable',
|
|
348
|
+
'pdf.saveModeHybridTitle': 'Hybrid save: text markup remains editable; signatures and deleted pages cannot be recovered.',
|
|
349
|
+
'pdf.saveModeFlattened': 'Compact',
|
|
350
|
+
'pdf.saveModeFlattenedTitle': 'Flattened save: smaller output without an editable sidecar.',
|
|
343
351
|
'pdf.merge': 'Merge PDF',
|
|
344
352
|
'pdf.merging': 'Merging PDF…',
|
|
345
353
|
'pdf.mergeComplete': 'PDF merged. Save or Save as to keep the changes.',
|
package/dist/i18n/catalog.ja.js
CHANGED
|
@@ -146,6 +146,7 @@ export const CATALOG_JA = {
|
|
|
146
146
|
'diag.yaml.node-limit': 'YAMLノードの上限に達しました。',
|
|
147
147
|
'diag.yaml.depth-limit': 'YAMLのネスト深度上限に達しました。',
|
|
148
148
|
'diag.yaml.alias-limit': 'YAMLエイリアスの上限に達しました。',
|
|
149
|
+
'diag.yaml.duplicate-key': 'キー「{key}」が重複しています。最後の値が使用されます。',
|
|
149
150
|
'structured.tree': 'ツリー', 'structured.flat': 'フラット', 'structured.json': 'JSON', 'structured.raw': 'RAW', 'structured.expandAll': 'すべて展開',
|
|
150
151
|
'structured.collapseAll': 'すべて折りたたむ', 'structured.expand': '展開', 'structured.collapse': '折りたたむ',
|
|
151
152
|
'structured.search': '検索', 'structured.copy': 'コピー', 'structured.copyPath': 'パス',
|
|
@@ -247,7 +248,14 @@ export const CATALOG_JA = {
|
|
|
247
248
|
'pdf.twoPagesEven': '見開き(偶数ページを左)', 'pdf.matchTheme': 'テーマの色に合わせる',
|
|
248
249
|
'pdf.resetPages': 'ページをリセット', 'pdf.annotationText': 'テキストを追加',
|
|
249
250
|
'pdf.annotationSize': 'テキストサイズ', 'pdf.annotationColor': 'テキストの色',
|
|
250
|
-
'pdf.save': '保存', 'pdf.saveAs': '名前を付けて保存', 'pdf.
|
|
251
|
+
'pdf.save': '保存', 'pdf.saveAs': '名前を付けて保存', 'pdf.saving': 'PDFを保存中…',
|
|
252
|
+
'pdf.operationCancelled': 'PDFの処理をキャンセルしました。',
|
|
253
|
+
'pdf.workerLoadFailed': 'PDF Workerアセットを読み込めません。',
|
|
254
|
+
'pdf.saveModeHybrid': '編集可能',
|
|
255
|
+
'pdf.saveModeHybridTitle': 'ハイブリッド保存:テキストマークアップは再編集できます。署名と削除したページは復元できません。',
|
|
256
|
+
'pdf.saveModeFlattened': 'コンパクト',
|
|
257
|
+
'pdf.saveModeFlattenedTitle': 'フラット化保存:編集用サイドカーを含まない小さいファイルです。',
|
|
258
|
+
'pdf.merge': 'PDFを結合',
|
|
251
259
|
'pdf.merging': 'PDFを結合中…', 'pdf.mergeComplete': 'PDFを結合しました。変更を保持するには保存してください。',
|
|
252
260
|
'pdf.mergeFailed': 'このPDFを結合できません。', 'pdf.signature': '署名', 'pdf.signatureColor': '署名の色',
|
|
253
261
|
'pdf.signatureClear': 'クリア', 'pdf.signatureUse': '署名を使用',
|
package/dist/i18n/catalog.ko.js
CHANGED
|
@@ -142,6 +142,7 @@ export const CATALOG_KO = {
|
|
|
142
142
|
'diag.yaml.node-limit': 'YAML 노드 한도에 도달했습니다.',
|
|
143
143
|
'diag.yaml.depth-limit': 'YAML 중첩 깊이 한도에 도달했습니다.',
|
|
144
144
|
'diag.yaml.alias-limit': 'YAML 별칭 한도에 도달했습니다.',
|
|
145
|
+
'diag.yaml.duplicate-key': '중복된 키 "{key}"; 마지막 값이 사용됩니다.',
|
|
145
146
|
'structured.tree': '트리', 'structured.flat': '평면', 'structured.json': 'JSON', 'structured.raw': '원시', 'structured.expandAll': '모두 펼치기',
|
|
146
147
|
'structured.collapseAll': '모두 접기', 'structured.expand': '펼치기', 'structured.collapse': '접기',
|
|
147
148
|
'structured.search': '검색', 'structured.copy': '복사', 'structured.copyPath': '경로',
|
|
@@ -340,6 +341,13 @@ export const CATALOG_KO = {
|
|
|
340
341
|
'pdf.annotationColor': '텍스트 색상',
|
|
341
342
|
'pdf.save': '저장',
|
|
342
343
|
'pdf.saveAs': '다른 이름으로 저장',
|
|
344
|
+
'pdf.saving': 'PDF 저장 중…',
|
|
345
|
+
'pdf.operationCancelled': 'PDF 작업이 취소되었습니다.',
|
|
346
|
+
'pdf.workerLoadFailed': 'PDF Worker 자산을 불러올 수 없습니다.',
|
|
347
|
+
'pdf.saveModeHybrid': '재편집 가능',
|
|
348
|
+
'pdf.saveModeHybridTitle': '하이브리드 저장: 텍스트 마크업은 재편집할 수 있으며 서명과 삭제한 페이지는 복구할 수 없습니다.',
|
|
349
|
+
'pdf.saveModeFlattened': '용량 우선',
|
|
350
|
+
'pdf.saveModeFlattenedTitle': '평면화 저장: 편집용 sidecar 없이 더 작은 파일로 저장합니다.',
|
|
343
351
|
'pdf.merge': 'PDF 병합',
|
|
344
352
|
'pdf.merging': 'PDF 병합 중…',
|
|
345
353
|
'pdf.mergeComplete': 'PDF가 병합되었습니다. 저장 또는 다른 이름으로 저장하여 변경 사항을 유지하세요.',
|
|
@@ -134,6 +134,7 @@ export const CATALOG_ZH_CN = {
|
|
|
134
134
|
'diag.yaml.document-limit': '仅解析了前 {count} 个 YAML 文档。',
|
|
135
135
|
'diag.yaml.node-limit': '已达到 YAML 节点限制。', 'diag.yaml.depth-limit': '已达到 YAML 嵌套深度限制。',
|
|
136
136
|
'diag.yaml.alias-limit': '已达到 YAML 别名限制。',
|
|
137
|
+
'diag.yaml.duplicate-key': '键 "{key}" 重复;将使用最后一个值。',
|
|
137
138
|
'structured.tree': '树', 'structured.flat': '平铺', 'structured.json': 'JSON', 'structured.raw': '原始',
|
|
138
139
|
'structured.expandAll': '全部展开', 'structured.collapseAll': '全部折叠',
|
|
139
140
|
'structured.expand': '展开', 'structured.collapse': '折叠', 'structured.search': '搜索',
|
|
@@ -230,7 +231,13 @@ export const CATALOG_ZH_CN = {
|
|
|
230
231
|
'pdf.singlePage': '单页', 'pdf.twoPagesOdd': '双页(奇数页在左)', 'pdf.twoPagesEven': '双页(偶数页在左)',
|
|
231
232
|
'pdf.matchTheme': '匹配主题颜色', 'pdf.resetPages': '重置页面',
|
|
232
233
|
'pdf.annotationText': '添加文本', 'pdf.annotationSize': '文本大小', 'pdf.annotationColor': '文本颜色',
|
|
233
|
-
'pdf.save': '保存', 'pdf.saveAs': '另存为', 'pdf.
|
|
234
|
+
'pdf.save': '保存', 'pdf.saveAs': '另存为', 'pdf.saving': '正在保存 PDF…',
|
|
235
|
+
'pdf.operationCancelled': 'PDF 操作已取消。', 'pdf.workerLoadFailed': '无法加载 PDF Worker 资源。',
|
|
236
|
+
'pdf.saveModeHybrid': '可重新编辑',
|
|
237
|
+
'pdf.saveModeHybridTitle': '混合保存:文本标记可重新编辑;签名和已删除页面无法恢复。',
|
|
238
|
+
'pdf.saveModeFlattened': '精简',
|
|
239
|
+
'pdf.saveModeFlattenedTitle': '扁平化保存:不含编辑 sidecar,文件更小。',
|
|
240
|
+
'pdf.merge': '合并 PDF', 'pdf.merging': '正在合并 PDF…',
|
|
234
241
|
'pdf.mergeComplete': 'PDF 已合并。请保存或另存为以保留更改。', 'pdf.mergeFailed': '无法合并此 PDF。',
|
|
235
242
|
'pdf.signature': '签名', 'pdf.signatureColor': '签名颜色', 'pdf.signatureClear': '清除',
|
|
236
243
|
'pdf.signatureUse': '使用签名', 'pdf.signaturePlace': '点击页面以放置签名。',
|
|
@@ -13,4 +13,5 @@ export interface PptxParserDeps {
|
|
|
13
13
|
export { openPptxZip } from './zip-reader.js';
|
|
14
14
|
import { parsePptxVscode } from './vscode.js';
|
|
15
15
|
export { parsePptxVscode };
|
|
16
|
+
export declare function parsePptxLegacy(input: Uint8Array, deps: PptxParserDeps, options?: ParseOptions): Promise<ParseOutcome<SlideDeck>>;
|
|
16
17
|
export declare function parsePptx(input: Uint8Array, deps: PptxParserDeps, options?: ParseOptions): Promise<ParseOutcome<SlideDeck>>;
|
|
@@ -55,7 +55,7 @@ async function parseSlide(zip, path, n, size) { const xml = await zip.text(path)
|
|
|
55
55
|
else
|
|
56
56
|
elements.push({ ...common, type: 'shape', presetGeom: geom, fillColor: color(block, 'transparent'), borderColor: color(/<a:ln\b[\s\S]*?<\/a:ln>/.exec(block)?.[0] ?? '') });
|
|
57
57
|
} return { slideNumber: n, ...size, backgroundColor: color(/<p:bg\b[\s\S]*?<\/p:bg>/.exec(xml)?.[0] ?? '', '#ffffff') ?? '#ffffff', elements }; }
|
|
58
|
-
async function parsePptxLegacy(input, deps, options = {}) { const started = Date.now(), diagnostics = []; if (input.byteLength > (options.limits?.maxInputBytes ?? 50 * 1024 * 1024))
|
|
58
|
+
export async function parsePptxLegacy(input, deps, options = {}) { const started = Date.now(), diagnostics = []; if (input.byteLength > (options.limits?.maxInputBytes ?? 50 * 1024 * 1024))
|
|
59
59
|
return { result: { status: 'failed', failure: { code: 'limit-exceeded', retryable: false, messageKey: 'diag.limit-exceeded.input' }, diagnostics }, execution: { workerUsed: false, hardLimitEnforced: false, elapsedMillis: Date.now() - started } }; let zip; try {
|
|
60
60
|
zip = await deps.openZip(input, options);
|
|
61
61
|
const presentation = await zip.text('ppt/presentation.xml');
|
|
@@ -18,6 +18,7 @@ export function parseYaml(input, options = {}) {
|
|
|
18
18
|
const ast = options.deps.parse(text);
|
|
19
19
|
const maxDocs = options.limits?.maxDocuments ?? 100;
|
|
20
20
|
const docs = [];
|
|
21
|
+
const duplicates = [];
|
|
21
22
|
for (let i = 0; i < Math.min(ast.length, maxDocs); i++) {
|
|
22
23
|
if (options.signal?.aborted)
|
|
23
24
|
return finish(docs.length ? { status: 'partial', document: { text, documents: docs }, diagnostics: [...diagnostics, { severity: 'warning', code: 'yaml.aborted', messageKey: 'diag.aborted' }] } : { status: 'failed', failure: { code: 'aborted', retryable: true, messageKey: 'diag.aborted' }, diagnostics });
|
|
@@ -28,11 +29,13 @@ export function parseYaml(input, options = {}) {
|
|
|
28
29
|
diagnostics.push({ severity: 'error', code: 'yaml.invalid', messageKey: 'diag.yaml.invalid', location: pos ? `line:${pos.line}:${pos.col}` : `document:${i + 1}` });
|
|
29
30
|
}
|
|
30
31
|
const node = options.deps.normalize ? options.deps.normalize(sourceDoc, i) : normalizeUnknown(sourceDoc, `$doc[${i}]`);
|
|
31
|
-
const violation = validateTree(node, options.limits?.maxEntries ?? 100_000, options.limits?.maxDepth ?? 100, options.limits?.maxAliases ?? 10_000);
|
|
32
|
+
const violation = validateTree(node, options.limits?.maxEntries ?? 100_000, options.limits?.maxDepth ?? 100, options.limits?.maxAliases ?? 10_000, duplicates);
|
|
32
33
|
if (violation)
|
|
33
34
|
diagnostics.push({ severity: 'warning', code: violation, messageKey: `diag.${violation}`, location: `document:${i + 1}` });
|
|
34
35
|
docs.push(node);
|
|
35
36
|
}
|
|
37
|
+
for (const duplicate of duplicates)
|
|
38
|
+
diagnostics.push({ severity: 'warning', code: 'yaml.duplicate-key', messageKey: 'diag.yaml.duplicate-key', args: { key: duplicate.key }, location: duplicate.path });
|
|
36
39
|
if (ast.length > maxDocs)
|
|
37
40
|
diagnostics.push({ severity: 'warning', code: 'yaml.document-limit', messageKey: 'diag.yaml.document-limit', args: { count: maxDocs } });
|
|
38
41
|
return finish(diagnostics.length ? { status: 'partial', document: { text, documents: docs }, diagnostics } : { status: 'ok', document: { text, documents: docs }, diagnostics });
|
|
@@ -44,7 +47,8 @@ export function parseYaml(input, options = {}) {
|
|
|
44
47
|
function normalizeUnknown(value, path, key = '$') { if (Array.isArray(value))
|
|
45
48
|
return { kind: 'seq', key, path, children: value.map((v, i) => normalizeUnknown(v, `${path}[${i}]`, String(i))) }; if (value !== null && typeof value === 'object')
|
|
46
49
|
return { kind: 'map', key, path, children: Object.entries(value).map(([k, v]) => normalizeUnknown(v, `${path}.${k}`, k)) }; return { kind: 'scalar', key, path, value: value, raw: String(value) }; }
|
|
47
|
-
|
|
50
|
+
const MAX_DUPLICATE_KEY_DIAGNOSTICS = 20;
|
|
51
|
+
function validateTree(root, maxNodes, maxDepth, maxAliases, duplicates) { let nodes = 0, aliases = 0; const stack = [{ node: root, depth: 0 }]; while (stack.length) {
|
|
48
52
|
const item = stack.pop();
|
|
49
53
|
nodes++;
|
|
50
54
|
if (nodes > maxNodes)
|
|
@@ -53,6 +57,14 @@ function validateTree(root, maxNodes, maxDepth, maxAliases) { let nodes = 0, ali
|
|
|
53
57
|
return 'yaml.depth-limit';
|
|
54
58
|
if (item.node.kind === 'alias' && ++aliases > maxAliases)
|
|
55
59
|
return 'yaml.alias-limit';
|
|
60
|
+
if (item.node.kind === 'map' && item.node.children) {
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const child of item.node.children) {
|
|
63
|
+
if (seen.has(child.key) && duplicates.length < MAX_DUPLICATE_KEY_DIAGNOSTICS && !duplicates.some(d => d.path === item.node.path && d.key === child.key))
|
|
64
|
+
duplicates.push({ path: item.node.path, key: child.key });
|
|
65
|
+
seen.add(child.key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
56
68
|
for (const child of item.node.children ?? [])
|
|
57
69
|
stack.push({ node: child, depth: item.depth + 1 });
|
|
58
70
|
} return null; }
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
/** Dynamic optional-peer loader; base parser/viewer modules never import yaml. */
|
|
2
2
|
export async function loadYamlParserDeps() {
|
|
3
3
|
const yaml = await import('yaml');
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
const normalizeDocument = (document, index) => {
|
|
5
|
+
const anchors = new Map();
|
|
6
|
+
const record = (node) => { if (node.anchorId)
|
|
7
|
+
anchors.set(node.anchorId, node); return node; };
|
|
8
|
+
const normalize = (node, path, key = '$') => {
|
|
9
|
+
const n = node;
|
|
10
|
+
if (n.contents !== undefined)
|
|
11
|
+
return normalize(n.contents, path, key);
|
|
12
|
+
const common = { key, path, range: n.range, anchorId: n.anchor };
|
|
13
|
+
if (n.type === 'ALIAS' || n.constructor?.name === 'Alias') {
|
|
14
|
+
const name = String(n.source ?? '');
|
|
15
|
+
const target = anchors.get(name);
|
|
16
|
+
if (target?.kind !== 'scalar')
|
|
17
|
+
return { ...common, kind: 'alias', aliasOf: name, raw: `*${name}` };
|
|
18
|
+
return { ...common, kind: 'alias', aliasOf: name, value: target.value ?? null, raw: target.raw ?? `*${name}` };
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(n.items)) {
|
|
21
|
+
const pairs = n.items.every(item => item && typeof item === 'object' && 'key' in item);
|
|
22
|
+
const seen = new Map();
|
|
23
|
+
return record({ ...common, kind: pairs ? 'map' : 'seq', children: n.items.map((item, i) => { const pair = item; const keyValue = typeof pair.key?.value === 'symbol' ? pair.key?.source ?? '<<' : pair.key?.value; const childKey = pairs ? String(keyValue ?? pair.key?.source ?? i) : String(i); const occurrence = (seen.get(childKey) ?? 0) + 1; seen.set(childKey, occurrence); return normalize(pairs ? pair.value : item, pairs ? `${path}.${childKey}${occurrence > 1 ? `#${occurrence}` : ''}` : `${path}[${i}]`, childKey); }) });
|
|
24
|
+
}
|
|
25
|
+
const value = n.value ?? node;
|
|
26
|
+
return record({ ...common, kind: 'scalar', value: value, raw: n.source === undefined ? String(value) : String(n.source) });
|
|
27
|
+
};
|
|
28
|
+
return normalize(document, `$doc[${index}]`);
|
|
17
29
|
};
|
|
18
|
-
return { parse: (text) => yaml.parseAllDocuments(text, { schema: 'core', customTags: []
|
|
30
|
+
return { parse: (text) => yaml.parseAllDocuments(text, { schema: 'core', customTags: [], uniqueKeys: false, merge: true }), normalize: normalizeDocument };
|
|
19
31
|
}
|
package/dist/registry/index.js
CHANGED
|
@@ -162,7 +162,7 @@ export const IMAGE_VIEWER_DESCRIPTOR = {
|
|
|
162
162
|
export const MARKDOWN_VIEWER_DESCRIPTOR = {
|
|
163
163
|
id: 'markdown', displayNameKey: 'markdown.title',
|
|
164
164
|
extensions: ['md', 'markdown', 'mdown', 'mkdn', 'mkd'], priority: 10,
|
|
165
|
-
requiredServices: [], optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback']
|
|
165
|
+
requiredServices: [], optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback', 'save']
|
|
166
166
|
};
|
|
167
167
|
export const ARCHIVE_MAGIC_SIGNATURES = [
|
|
168
168
|
[{ offset: 0, bytes: [0x50, 0x4b, 0x03, 0x04] }], [{ offset: 0, bytes: [0x50, 0x4b, 0x05, 0x06] }],
|
package/dist/styles/archive.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}
|
|
5
5
|
.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}
|
|
6
6
|
.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}
|
|
7
|
-
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
7
|
+
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
8
8
|
.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}
|
|
9
9
|
.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}
|
|
10
10
|
.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}
|
package/dist/styles/pdf.css
CHANGED
|
@@ -51,6 +51,14 @@
|
|
|
51
51
|
color: var(--omni-fg-muted, #9d9d9d);
|
|
52
52
|
margin-right: 8px;
|
|
53
53
|
}
|
|
54
|
+
.omni-pdf__save-mode {
|
|
55
|
+
padding: 2px 6px;
|
|
56
|
+
color: var(--omni-fg-muted, #9d9d9d);
|
|
57
|
+
border: 1px solid var(--omni-border, #3c3c3c);
|
|
58
|
+
border-radius: 999px;
|
|
59
|
+
font-size: 11px;
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
}
|
|
54
62
|
.omni-pdf__page-input {
|
|
55
63
|
width: 52px;
|
|
56
64
|
padding: 3px 5px;
|
|
@@ -265,10 +273,28 @@
|
|
|
265
273
|
.omni-pdf__thumb.drop-before::before { top: -6px; }
|
|
266
274
|
.omni-pdf__thumb.drop-after::after { bottom: -6px; }
|
|
267
275
|
.omni-pdf__thumb canvas {
|
|
276
|
+
display: block;
|
|
268
277
|
max-width: 100%;
|
|
269
278
|
background: #ffffff;
|
|
270
279
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
|
271
280
|
}
|
|
281
|
+
.omni-pdf__thumb-page {
|
|
282
|
+
position: relative;
|
|
283
|
+
max-width: 100%;
|
|
284
|
+
}
|
|
285
|
+
.omni-pdf__thumb-annotation {
|
|
286
|
+
position: absolute;
|
|
287
|
+
z-index: 1;
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
pointer-events: none;
|
|
290
|
+
color: #111111; /* ink on paper */
|
|
291
|
+
white-space: nowrap;
|
|
292
|
+
}
|
|
293
|
+
.omni-pdf__thumb-annotation img { display: block; }
|
|
294
|
+
.omni-pdf__thumb-annotation.omni-pdf__underline::after,
|
|
295
|
+
.omni-pdf__thumb-annotation.omni-pdf__strikeout::after {
|
|
296
|
+
height: 1px;
|
|
297
|
+
}
|
|
272
298
|
.omni-pdf__thumb-label {
|
|
273
299
|
font-family: var(--omni-font-mono, Monaco, Menlo, monospace);
|
|
274
300
|
font-size: 10px;
|
package/dist/styles/toml.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}
|
|
1
|
+
:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__preview-body{position:relative;flex:1;min-height:200px}.omni-structured__preview-body>.omni-structured__tree,.omni-structured__preview-body>.omni-structured__json-preview,.omni-structured__preview-body>.omni-structured__raw-preview{position:absolute;inset:0}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}
|
package/dist/styles/yaml.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}
|
|
1
|
+
:host,.omni-viewer--toml,.omni-viewer--yaml{display:block;height:100%;color:var(--omni-fg,#ddd);background:var(--omni-bg,#1e1e1e);font:13px var(--omni-font,system-ui)}.omni-structured{height:100%;display:flex;flex-direction:column;box-sizing:border-box}.omni-structured__bar{display:flex;gap:5px;align-items:center;padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured button,.omni-structured input,.omni-structured select{font:inherit;background:var(--omni-button-bg,#333);color:inherit;border:1px solid var(--omni-border,#555);border-radius:3px;padding:3px 7px}.omni-structured button:disabled{opacity:.5}.omni-structured__search{min-width:180px}.omni-structured__diagnostics{white-space:pre-wrap;color:var(--omni-error,#f48771);padding:6px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__workspace{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-rows:minmax(0,1fr);gap:12px;flex:1;min-height:0;padding:12px}.omni-structured__pane{display:flex;flex-direction:column;min-width:0;min-height:0;border:1px solid var(--omni-border,#444);border-radius:8px;overflow:hidden;background:var(--omni-input-bg,#171717)}.omni-structured__pane-header{display:flex;justify-content:space-between;gap:12px;padding:10px 12px;border-bottom:1px solid var(--omni-border,#444)}.omni-structured__pane-header span{color:var(--omni-fg-muted,#999)}.omni-structured__editor,.omni-structured__tree{box-sizing:border-box;flex:1;min-height:0;padding:10px;background:transparent;color:inherit;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace)}.omni-structured__editor{resize:none;border:0;outline:none;line-height:1.5;white-space:pre;overflow:auto}.omni-structured__source-surface{position:relative;flex:1;min-height:0;overflow:hidden}.omni-structured__source-highlight{position:absolute;inset:0;box-sizing:border-box;margin:0;padding:10px;overflow:auto;pointer-events:none;font:inherit;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5;white-space:pre}.omni-structured__pane--highlighted-source .omni-structured__editor{position:relative;z-index:1;width:100%;height:100%;padding:10px;color:transparent;caret-color:var(--omni-fg,#ddd);-webkit-text-fill-color:transparent}.omni-structured__source-key{color:#9cdcfe}.omni-structured__source-value{color:#ce9178}.omni-structured__source-table{color:#c586c0}.omni-structured__source-comment{color:var(--omni-fg-muted,#999)}.omni-structured__tree{overflow:auto}.omni-structured__preview-body{position:relative;flex:1;min-height:200px}.omni-structured__preview-body>.omni-structured__tree,.omni-structured__preview-body>.omni-structured__json-preview,.omni-structured__preview-body>.omni-structured__raw-preview{position:absolute;inset:0}.omni-structured__node{display:flex;gap:6px;align-items:baseline;min-height:25px}.omni-structured__toggle{width:24px;padding:0!important;border:0!important;background:transparent!important}.omni-structured__key{color:var(--omni-accent,#5aa9e6)}.omni-structured__kind{color:var(--omni-fg-muted,#999)}.omni-structured__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.omni-structured__actions{margin-left:auto;display:flex;gap:4px;opacity:0}.omni-structured__node:hover .omni-structured__actions,.omni-structured__actions:focus-within{opacity:1}.omni-structured__raw-preview,.omni-structured__json-preview{flex:1;min-height:0;margin:0;padding:10px;overflow:auto;font-family:var(--omni-font-mono,ui-monospace,monospace);line-height:1.5}.omni-structured__flat-row .omni-structured__key{flex-shrink:0}@media(max-width:720px){.omni-structured__workspace{grid-template-columns:1fr;overflow:auto}.omni-structured__pane{min-height:320px}}
|
|
@@ -294,20 +294,57 @@ export async function mountArchiveViewer(inputOrSource, container, ctx, depsOrOp
|
|
|
294
294
|
const ROW_HEIGHT = 43;
|
|
295
295
|
const OVERSCAN = 8;
|
|
296
296
|
const FALLBACK_VIEWPORT_HEIGHT = 600;
|
|
297
|
-
|
|
297
|
+
let lastStart = -1;
|
|
298
|
+
let lastEnd = -1;
|
|
299
|
+
let lastLength = -1;
|
|
300
|
+
let lastQuery;
|
|
301
|
+
let lastExpanded;
|
|
302
|
+
/** `force` covers state changes (search/select/expand) that must repaint the
|
|
303
|
+
* rows; scroll events pass `false` so an unchanged window is a no-op. */
|
|
304
|
+
const render = (force = true) => {
|
|
298
305
|
const visible = controller.visibleEntries();
|
|
306
|
+
// Measure before mutating: emptying the tbody first would collapse the
|
|
307
|
+
// scroll height, so the layout flush from reading clientHeight clamps
|
|
308
|
+
// scrollTop to 0 and every scroll snaps back to the top of the list.
|
|
309
|
+
const viewportHeight = tableWrap.clientHeight || FALLBACK_VIEWPORT_HEIGHT;
|
|
310
|
+
const scrollTop = tableWrap.scrollTop;
|
|
311
|
+
const start = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - OVERSCAN);
|
|
312
|
+
const count = Math.ceil(viewportHeight / ROW_HEIGHT) + OVERSCAN * 2;
|
|
313
|
+
const end = Math.min(visible.length, start + count);
|
|
314
|
+
// Most scroll ticks land inside the overscan margin. Rebuilding the rows
|
|
315
|
+
// anyway would destroy the focused row on every tick, and the resulting
|
|
316
|
+
// focus restore plus scroll anchoring drags the viewport around.
|
|
317
|
+
// The rendered window is fully determined by these; the controller hands
|
|
318
|
+
// out a fresh `expanded` set per toggle, so identity comparison is enough.
|
|
319
|
+
const sameWindow = start === lastStart && end === lastEnd && visible.length === lastLength
|
|
320
|
+
&& controller.state.query === lastQuery && controller.state.expanded === lastExpanded;
|
|
321
|
+
// Most scroll ticks land inside the overscan margin. Rebuilding the rows
|
|
322
|
+
// anyway would destroy the focused row on every tick, and the resulting
|
|
323
|
+
// focus restore plus scroll anchoring drags the viewport around.
|
|
324
|
+
if (!force && sameWindow)
|
|
325
|
+
return;
|
|
326
|
+
// Selecting an entry only flips a class. Rebuilding every row for that is
|
|
327
|
+
// what threw the viewport back to the top the moment a row was clicked,
|
|
328
|
+
// so patch the existing rows in place and leave the scroll state alone.
|
|
329
|
+
if (sameWindow) {
|
|
330
|
+
for (const row of tbody.querySelectorAll('.omni-archive__entry'))
|
|
331
|
+
row.classList.toggle('is-selected', Number(row.dataset.entryId) === controller.state.selectedId);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
lastStart = start;
|
|
335
|
+
lastEnd = end;
|
|
336
|
+
lastLength = visible.length;
|
|
337
|
+
lastQuery = controller.state.query;
|
|
338
|
+
lastExpanded = controller.state.expanded;
|
|
299
339
|
visibleStat[1] = visible.length;
|
|
300
340
|
stats.querySelectorAll('.omni-archive__stat-value')[3].textContent = visible.length.toLocaleString();
|
|
301
|
-
tbody.replaceChildren();
|
|
302
341
|
empty.hidden = visible.length !== 0;
|
|
303
342
|
table.hidden = visible.length === 0;
|
|
304
|
-
const viewportHeight = tableWrap.clientHeight || FALLBACK_VIEWPORT_HEIGHT;
|
|
305
|
-
const start = Math.max(0, Math.floor(tableWrap.scrollTop / ROW_HEIGHT) - OVERSCAN);
|
|
306
|
-
const count = Math.ceil(viewportHeight / ROW_HEIGHT) + OVERSCAN * 2;
|
|
307
|
-
const end = Math.min(visible.length, start + count);
|
|
308
343
|
const spacer = (height) => { const row = element('tr', 'omni-archive__spacer'); const cell = element('td'); cell.colSpan = 5; cell.style.height = `${height}px`; row.append(cell); return row; };
|
|
344
|
+
// Build off-DOM and swap once, so the tbody is never laid out empty.
|
|
345
|
+
const rows = [];
|
|
309
346
|
if (start > 0)
|
|
310
|
-
|
|
347
|
+
rows.push(spacer(start * ROW_HEIGHT));
|
|
311
348
|
for (const entry of visible.slice(start, end)) {
|
|
312
349
|
const row = element('tr', `omni-archive__entry${controller.state.selectedId === entry.entryId ? ' is-selected' : ''}`);
|
|
313
350
|
row.tabIndex = 0;
|
|
@@ -320,14 +357,26 @@ export async function mountArchiveViewer(inputOrSource, container, ctx, depsOrOp
|
|
|
320
357
|
event.preventDefault();
|
|
321
358
|
void select(entry);
|
|
322
359
|
} });
|
|
323
|
-
|
|
360
|
+
rows.push(row);
|
|
324
361
|
}
|
|
325
362
|
if (end < visible.length)
|
|
326
|
-
|
|
363
|
+
rows.push(spacer((visible.length - end) * ROW_HEIGHT));
|
|
364
|
+
// Recycling rows destroys the focused one; hand focus back to its
|
|
365
|
+
// replacement so keyboard navigation survives a scroll.
|
|
366
|
+
const active = (typeof ShadowRoot !== 'undefined' && root instanceof ShadowRoot ? root.activeElement : document.activeElement);
|
|
367
|
+
const focusedId = active instanceof HTMLElement && tbody.contains(active) ? active.dataset.entryId : undefined;
|
|
368
|
+
tbody.replaceChildren(...rows);
|
|
369
|
+
// Swapping the rows can still make the browser adjust the offset (focus
|
|
370
|
+
// fixup, anchoring, a transient height change). The list is virtualized,
|
|
371
|
+
// so the pre-swap offset is authoritative — put it back.
|
|
372
|
+
if (tableWrap.scrollTop !== scrollTop)
|
|
373
|
+
tableWrap.scrollTop = scrollTop;
|
|
374
|
+
if (focusedId !== undefined)
|
|
375
|
+
rows.find(row => row.dataset.entryId === focusedId)?.focus({ preventScroll: true });
|
|
327
376
|
};
|
|
328
|
-
tableWrap.addEventListener('scroll', render, { passive: true });
|
|
377
|
+
tableWrap.addEventListener('scroll', () => render(false), { passive: true });
|
|
329
378
|
search.addEventListener('input', () => { tableWrap.scrollTop = 0; controller.dispatch({ type: 'set-search', query: search.value }); });
|
|
330
|
-
const unsubscribe = controller.subscribe(render);
|
|
379
|
+
const unsubscribe = controller.subscribe(() => render(true));
|
|
331
380
|
render();
|
|
332
381
|
return { dispose() { ticket++; extraction?.abort(); saveExtraction?.abort(); clearPreviewMedia(); unsubscribe(); wrap.remove(); void handle?.close(); handle = undefined; } };
|
|
333
382
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const archiveViewerCss = "\n.omni-viewer--archive{all:initial}.omni-viewer--archive :where(header,section,aside,div,h1,h2,p,span,label,input,table,thead,tbody,tr,th,td,pre){all:revert}\n:host,.omni-viewer--archive{display:block;min-height:100%;box-sizing:border-box;color:var(--omni-fg,#d4d4d4);background:radial-gradient(circle at top left,color-mix(in srgb,var(--omni-accent,#d97706) 14%,transparent),transparent 28%),var(--omni-bg,#1e1e1e);font:13px var(--omni-font,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif)}\n.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}\n.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}\n.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}\n.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}\n.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}\n.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}\n.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}\n@media(max-width:720px){.omni-viewer--archive{padding:16px 10px 24px}.omni-archive__hero{flex-direction:column}.omni-archive__pills{justify-content:flex-start}.omni-archive__table th:nth-child(n+3),.omni-archive__table td:nth-child(n+3){display:none}.omni-archive__workspace{grid-template-columns:1fr}.omni-archive__preview-panel{min-height:280px}}\n";
|
|
1
|
+
export declare const archiveViewerCss = "\n.omni-viewer--archive{all:initial}.omni-viewer--archive :where(header,section,aside,div,h1,h2,p,span,label,input,table,thead,tbody,tr,th,td,pre){all:revert}\n:host,.omni-viewer--archive{display:block;min-height:100%;box-sizing:border-box;color:var(--omni-fg,#d4d4d4);background:radial-gradient(circle at top left,color-mix(in srgb,var(--omni-accent,#d97706) 14%,transparent),transparent 28%),var(--omni-bg,#1e1e1e);font:13px var(--omni-font,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif)}\n.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}\n.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}\n.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}\n.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}\n.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,\"SFMono-Regular\",Consolas,monospace)}\n.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}\n.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}\n@media(max-width:720px){.omni-viewer--archive{padding:16px 10px 24px}.omni-archive__hero{flex-direction:column}.omni-archive__pills{justify-content:flex-start}.omni-archive__table th:nth-child(n+3),.omni-archive__table td:nth-child(n+3){display:none}.omni-archive__workspace{grid-template-columns:1fr}.omni-archive__preview-panel{min-height:280px}}\n";
|
|
@@ -4,7 +4,7 @@ export const archiveViewerCss = `
|
|
|
4
4
|
.omni-viewer--archive *{box-sizing:border-box}.omni-viewer--archive{padding:28px 20px 40px}.omni-archive__hero,.omni-archive__controls,.omni-archive__stats,.omni-archive__table-wrap,.omni-archive__preview-panel{background:var(--omni-bg-secondary,#252526);border:1px solid var(--omni-border,#3c3c3c);border-radius:18px;box-shadow:0 20px 40px rgba(0,0,0,.12)}
|
|
5
5
|
.omni-archive__hero{display:flex;justify-content:space-between;gap:16px;padding:24px;align-items:flex-start}.omni-archive__eyebrow{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--omni-accent,#d97706);font-weight:700}.omni-archive__hero h1{margin:8px 0 6px;font-size:clamp(26px,4vw,40px);line-height:1.1}.omni-archive__subtitle,.omni-archive__preview-meta{color:var(--omni-muted-fg,#9d9d9d);line-height:1.5}.omni-archive__pills{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}.omni-archive__pill{padding:10px 14px;border-radius:999px;background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent);border:1px solid color-mix(in srgb,var(--omni-accent,#d97706) 35%,transparent);font-weight:600;white-space:nowrap}
|
|
6
6
|
.omni-archive__controls,.omni-archive__stats,.omni-archive__workspace{margin-top:16px}.omni-archive__controls{padding:16px}.omni-archive__search-label{display:block;font-weight:600}.omni-archive__search{display:block;width:100%;margin-top:8px;padding:12px 14px;border-radius:12px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-input-bg,#3c3c3c);color:var(--omni-fg,#d4d4d4);font:inherit}.omni-archive__stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;padding:16px}.omni-archive__stat{padding:14px;border-radius:14px;background:color-mix(in srgb,var(--omni-bg-secondary,#252526) 76%,#000);border:1px solid var(--omni-border,#3c3c3c)}.omni-archive__stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__stat-value{font-size:24px;font-weight:700}
|
|
7
|
-
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
7
|
+
.omni-archive__workspace{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(320px,.95fr);gap:16px;align-items:start}.omni-archive__table-wrap{overflow:auto;min-width:0;max-height:70vh;overflow-anchor:none}.omni-archive__table{width:100%;table-layout:fixed;border-collapse:collapse}.omni-archive__table th,.omni-archive__table td{padding:12px 14px;border-bottom:1px solid var(--omni-border,#3c3c3c);text-align:left;vertical-align:top;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.omni-archive__table th:first-child{width:42%}.omni-archive__table th{position:sticky;top:0;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--omni-muted-fg,#9d9d9d);background:var(--omni-bg-secondary,#252526);z-index:1}.omni-archive__path{font-family:var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}.omni-archive__entry{height:43px;cursor:pointer;transition:background-color 120ms ease}.omni-archive__entry:hover{background:color-mix(in srgb,var(--omni-accent,#d97706) 8%,transparent)}.omni-archive__entry:focus-visible{outline:2px solid var(--omni-accent,#d97706);outline-offset:-2px}.omni-archive__entry.is-selected{background:color-mix(in srgb,var(--omni-accent,#d97706) 16%,transparent)}.omni-archive__empty{padding:26px 18px;text-align:center;color:var(--omni-muted-fg,#9d9d9d)}
|
|
8
8
|
.omni-archive__preview-panel{padding:18px;min-height:420px;display:flex;flex-direction:column;gap:14px}.omni-archive__preview-header{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}.omni-archive__preview-kicker{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--omni-muted-fg,#9d9d9d);margin-bottom:6px}.omni-archive__preview-header h2{margin:0;font-size:18px;line-height:1.35;word-break:break-word}.omni-archive__preview-badge{border-radius:999px;padding:8px 12px;font-size:12px;font-weight:700;background:var(--omni-badge-bg,#3a3d41);white-space:nowrap}.omni-archive__preview-meta{margin:0}.omni-archive__preview{margin:0;flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;color:var(--omni-fg,#d4d4d4);font:12px/1.55 var(--omni-mono-font,"SFMono-Regular",Consolas,monospace)}
|
|
9
9
|
.omni-archive__save{align-self:flex-start;padding:8px 12px;border:1px solid var(--omni-border,#3c3c3c);border-radius:6px;background:var(--omni-button-bg,#3a3d41);color:var(--omni-fg,#d4d4d4);font:inherit;cursor:pointer}.omni-archive__save:hover:not(:disabled){background:var(--omni-button-hover-bg,#45494e)}.omni-archive__save:disabled{opacity:.55;cursor:not-allowed}.omni-archive__spacer td{padding:0!important;border:0!important}
|
|
10
10
|
.omni-archive__preview-media{flex:1;display:flex;align-items:center;justify-content:center;border-radius:14px;border:1px solid var(--omni-border,#3c3c3c);background:var(--omni-code-bg,#181818);padding:14px;overflow:auto}.omni-archive__audio{width:100%}.omni-archive__video{max-width:100%;max-height:100%}.omni-archive__image{max-width:100%;max-height:100%;object-fit:contain}
|
|
@@ -187,27 +187,16 @@ export function createJsonController(input, options = {}) {
|
|
|
187
187
|
state.statusMessage = { key: messageKey };
|
|
188
188
|
};
|
|
189
189
|
const requireValidRoot = () => state.status === 'ok' && state.root ? state.root : null;
|
|
190
|
-
/**
|
|
191
|
-
|
|
190
|
+
/** Raw-text transforms replace the scratchpad immediately (J11, vscode
|
|
191
|
+
* routing) so they chain naturally; failures surface as status messages. */
|
|
192
|
+
const runRawTransform = (action, result) => {
|
|
192
193
|
const transformed = typeof result === 'string' ? { ok: true, output: result } : result;
|
|
193
|
-
|
|
194
|
-
action
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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;
|
|
194
|
+
if (transformed.ok) {
|
|
195
|
+
replaceScratchpad(transformed.output, TOOL_MESSAGE[action]);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
state.statusMessage = { key: TRANSFORM_ERROR[transformed.error] ?? 'json.tool.failed' };
|
|
199
|
+
}
|
|
211
200
|
};
|
|
212
201
|
const runConverter = (action, convert) => {
|
|
213
202
|
const root = requireValidRoot();
|
|
@@ -269,16 +258,16 @@ export function createJsonController(input, options = {}) {
|
|
|
269
258
|
break;
|
|
270
259
|
}
|
|
271
260
|
case 'escape':
|
|
272
|
-
|
|
261
|
+
runRawTransform('escape', escapeText(state.scratchpad));
|
|
273
262
|
break;
|
|
274
263
|
case 'unescape':
|
|
275
|
-
|
|
264
|
+
runRawTransform('unescape', unescapeText(state.scratchpad));
|
|
276
265
|
break;
|
|
277
266
|
case 'base64-encode':
|
|
278
|
-
|
|
267
|
+
runRawTransform('base64-encode', base64Encode(state.scratchpad));
|
|
279
268
|
break;
|
|
280
269
|
case 'base64-decode':
|
|
281
|
-
|
|
270
|
+
runRawTransform('base64-decode', base64Decode(state.scratchpad));
|
|
282
271
|
break;
|
|
283
272
|
case 'to-csv':
|
|
284
273
|
runConverter('to-csv', jsonToCsv);
|