omni-viewer-core 0.4.0 → 0.6.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 +29 -1
- package/dist/host/index.d.ts +14 -2
- package/dist/i18n/catalog.en.js +21 -0
- package/dist/i18n/catalog.ja.js +22 -2
- package/dist/i18n/catalog.ko.js +21 -0
- package/dist/i18n/catalog.zh-cn.js +18 -1
- package/dist/parsers/safetensors/index.d.ts +34 -0
- package/dist/parsers/safetensors/index.js +233 -0
- package/dist/parsers/toml/model.d.ts +10 -0
- package/dist/parsers/toml/parse.js +104 -34
- package/dist/parsers/yaml/index.js +14 -2
- package/dist/parsers/yaml/self-loading.js +26 -14
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.js +6 -2
- package/dist/styles/pdf.css +203 -0
- package/dist/styles/safetensors.css +3 -0
- package/dist/styles/toml.css +1 -1
- package/dist/styles/yaml.css +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 +22 -2
- package/dist/viewers/pdf/controller.js +32 -10
- package/dist/viewers/pdf/editing.d.ts +11 -0
- package/dist/viewers/pdf/editing.js +19 -16
- package/dist/viewers/pdf/index.d.ts +81 -9
- package/dist/viewers/pdf/index.js +558 -104
- 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 +203 -0
- package/dist/viewers/safetensors/index.d.ts +19 -0
- package/dist/viewers/safetensors/index.js +170 -0
- package/dist/viewers/safetensors/styles.d.ts +1 -0
- package/dist/viewers/safetensors/styles.js +4 -0
- package/dist/viewers/structured-styles.d.ts +1 -1
- package/dist/viewers/structured-styles.js +1 -1
- package/dist/viewers/structured.d.ts +16 -0
- package/dist/viewers/structured.js +199 -48
- package/dist/viewers/toml/controller.d.ts +27 -1
- package/dist/viewers/toml/controller.js +64 -10
- package/dist/viewers/yaml/controller.js +48 -5
- package/package.json +10 -1
|
@@ -2,11 +2,14 @@ import { tomlPath } from './model.js';
|
|
|
2
2
|
export function parseTomlText(text, limits = {}) {
|
|
3
3
|
const settings = typeof limits === 'number' ? { maxDepth: limits } : limits;
|
|
4
4
|
const maxDepth = settings.maxDepth ?? 100, maxNodes = settings.maxNodes ?? 100_000, maxTables = settings.maxTables ?? 50_000, maxArrayLength = settings.maxArrayLength ?? 100_000;
|
|
5
|
-
const root = { kind: 'table', key: '$', path: '', raw: '', line: 1, children: [] };
|
|
5
|
+
const root = { kind: 'table', key: '$', path: '', raw: '', line: 1, range: { start: 0, end: text.length }, children: [] };
|
|
6
6
|
const diagnostics = [];
|
|
7
7
|
let current = root, nodeCount = 1, tableCount = 1, stopped = false;
|
|
8
8
|
const tables = new Map([['', root]]);
|
|
9
|
-
|
|
9
|
+
// Offsets stay relative to the untouched `text` so ranges address the same
|
|
10
|
+
// string a host renders. A leading BOM is dropped by `trim()` instead.
|
|
11
|
+
const lines = logicalTomlLines(text);
|
|
12
|
+
const lineAt = lineIndexer(text);
|
|
10
13
|
const error = (code, line) => diagnostics.push({ severity: 'error', code, messageKey: `diag.${code}`, location: `line:${line}` });
|
|
11
14
|
const unquote = (value) => {
|
|
12
15
|
if (value.startsWith('"""') && value.endsWith('"""')) {
|
|
@@ -34,11 +37,15 @@ export function parseTomlText(text, limits = {}) {
|
|
|
34
37
|
return value.slice(1, -1);
|
|
35
38
|
return value;
|
|
36
39
|
};
|
|
37
|
-
|
|
40
|
+
/** Split on top-level commas, reporting where each trimmed part starts so
|
|
41
|
+
* inline-table and array members can carry their own range. */
|
|
42
|
+
const split = (value, base) => {
|
|
38
43
|
const out = [];
|
|
39
44
|
let start = 0;
|
|
40
45
|
let quote = '';
|
|
41
46
|
let depth = 0;
|
|
47
|
+
const push = (from, to) => { const part = value.slice(from, to); const lead = part.length - part.trimStart().length; const trimmed = part.trim(); if (trimmed)
|
|
48
|
+
out.push({ text: trimmed, at: base + from + lead }); };
|
|
42
49
|
for (let i = 0; i < value.length; i++) {
|
|
43
50
|
const c = value[i];
|
|
44
51
|
if (quote) {
|
|
@@ -52,16 +59,18 @@ export function parseTomlText(text, limits = {}) {
|
|
|
52
59
|
else if (c === ']' || c === '}')
|
|
53
60
|
depth--;
|
|
54
61
|
else if (c === ',' && depth === 0) {
|
|
55
|
-
|
|
62
|
+
push(start, i);
|
|
56
63
|
start = i + 1;
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
|
-
|
|
60
|
-
return out
|
|
66
|
+
push(start, value.length);
|
|
67
|
+
return out;
|
|
61
68
|
};
|
|
62
69
|
const keyParts = (key) => splitKey(key).map(unquote);
|
|
63
|
-
const makeValue = (key, path, rawValue, line, depth) => {
|
|
70
|
+
const makeValue = (key, path, rawValue, line, depth, start) => {
|
|
64
71
|
const raw = rawValue.trim();
|
|
72
|
+
const from = start + (rawValue.length - rawValue.trimStart().length);
|
|
73
|
+
const range = { start: from, end: from + raw.length };
|
|
65
74
|
let kind = 'string';
|
|
66
75
|
let value = unquote(raw);
|
|
67
76
|
if (/^(true|false)$/.test(raw)) {
|
|
@@ -76,34 +85,37 @@ export function parseTomlText(text, limits = {}) {
|
|
|
76
85
|
kind = 'datetime';
|
|
77
86
|
else if (raw.startsWith('[') && raw.endsWith(']')) {
|
|
78
87
|
kind = 'array';
|
|
79
|
-
const values = split(raw.slice(1, -1));
|
|
88
|
+
const values = split(raw.slice(1, -1), from + 1);
|
|
80
89
|
if (values.length > maxArrayLength) {
|
|
81
90
|
error('toml.array-limit', line);
|
|
82
91
|
values.length = maxArrayLength;
|
|
83
92
|
}
|
|
84
|
-
const children = values.map((
|
|
93
|
+
const children = values.map((entry, i) => makeValue(String(i), `${path}[${i}]`, entry.text, lineAt(entry.at), depth + 1, entry.at));
|
|
85
94
|
nodeCount += children.length;
|
|
86
|
-
return { kind, key, path, raw, line, children };
|
|
95
|
+
return { kind, key, path, raw, line, range, children };
|
|
87
96
|
}
|
|
88
97
|
else if (raw.startsWith('{') && raw.endsWith('}')) {
|
|
89
98
|
kind = 'table';
|
|
90
99
|
const children = [];
|
|
91
|
-
for (const pair of split(raw.slice(1, -1))) {
|
|
92
|
-
const eq = indexOfEquals(pair);
|
|
100
|
+
for (const pair of split(raw.slice(1, -1), from + 1)) {
|
|
101
|
+
const eq = indexOfEquals(pair.text);
|
|
93
102
|
if (eq < 0) {
|
|
94
|
-
error('toml.invalid-inline-table',
|
|
103
|
+
error('toml.invalid-inline-table', lineAt(pair.at));
|
|
95
104
|
continue;
|
|
96
105
|
}
|
|
97
|
-
const childKey = unquote(pair.slice(0, eq).trim());
|
|
98
|
-
|
|
106
|
+
const childKey = unquote(pair.text.slice(0, eq).trim());
|
|
107
|
+
const child = makeValue(childKey, tomlPath(path, childKey), pair.text.slice(eq + 1), lineAt(pair.at), depth + 1, pair.at + eq + 1);
|
|
108
|
+
child.range = { start: pair.at, end: pair.at + pair.text.length };
|
|
109
|
+
children.push(child);
|
|
99
110
|
}
|
|
100
|
-
return { kind, key, path, raw, line, children };
|
|
111
|
+
return { kind, key, path, raw, line, range, children };
|
|
101
112
|
}
|
|
102
113
|
if (depth > maxDepth)
|
|
103
114
|
error('toml.max-depth', line);
|
|
104
115
|
nodeCount++;
|
|
105
|
-
return { kind, key, path, value, raw, line };
|
|
116
|
+
return { kind, key, path, value, raw, line, range };
|
|
106
117
|
};
|
|
118
|
+
let pending = [];
|
|
107
119
|
for (const logical of lines) {
|
|
108
120
|
if (settings.signal?.aborted) {
|
|
109
121
|
error('aborted', logical.line);
|
|
@@ -116,9 +128,22 @@ export function parseTomlText(text, limits = {}) {
|
|
|
116
128
|
break;
|
|
117
129
|
}
|
|
118
130
|
const i = logical.line - 1;
|
|
119
|
-
const
|
|
120
|
-
|
|
131
|
+
const uncommented = stripComment(logical.text);
|
|
132
|
+
const trailing = commentBody(logical.text.slice(uncommented.length));
|
|
133
|
+
const source = uncommented.trim();
|
|
134
|
+
// Comment runs stay attached to the next declaration; a blank line ends
|
|
135
|
+
// the run so a file-header comment never leaks onto unrelated keys.
|
|
136
|
+
if (!source) {
|
|
137
|
+
if (trailing === undefined)
|
|
138
|
+
pending = [];
|
|
139
|
+
else
|
|
140
|
+
pending.push(trailing);
|
|
121
141
|
continue;
|
|
142
|
+
}
|
|
143
|
+
const comment = [...pending, ...(trailing === undefined ? [] : [trailing])].join('\n') || undefined;
|
|
144
|
+
pending = [];
|
|
145
|
+
const base = logical.offset + (logical.text.length - logical.text.trimStart().length);
|
|
146
|
+
const declaration = { start: base, end: base + source.length };
|
|
122
147
|
const arrayTable = source.match(/^\[\[(.+)\]\]$/);
|
|
123
148
|
const table = source.match(/^\[(.+)\]$/);
|
|
124
149
|
if (arrayTable || table) {
|
|
@@ -134,12 +159,12 @@ export function parseTomlText(text, limits = {}) {
|
|
|
134
159
|
const parent = tables.get(parentPath) ?? root;
|
|
135
160
|
let collection = parent.children.find(node => node.key === parts.at(-1) && node.kind === 'array');
|
|
136
161
|
if (!collection) {
|
|
137
|
-
collection = { kind: 'array', key: parts.at(-1), path, raw: '', line: i + 1, children: [] };
|
|
162
|
+
collection = { kind: 'array', key: parts.at(-1), path, raw: '', line: i + 1, range: declaration, children: [] };
|
|
138
163
|
parent.children.push(collection);
|
|
139
164
|
nodeCount++;
|
|
140
165
|
}
|
|
141
166
|
const index = collection.children.length;
|
|
142
|
-
const node = { kind: 'table', key: String(index), path: `${path}[${index}]`, raw: '', line: i + 1, children: [] };
|
|
167
|
+
const node = { kind: 'table', key: String(index), path: `${path}[${index}]`, raw: '', line: i + 1, range: declaration, ...(comment ? { comment } : {}), children: [] };
|
|
143
168
|
collection.children.push(node);
|
|
144
169
|
tables.set(path, node);
|
|
145
170
|
current = node;
|
|
@@ -149,7 +174,7 @@ export function parseTomlText(text, limits = {}) {
|
|
|
149
174
|
else {
|
|
150
175
|
let node = tables.get(path);
|
|
151
176
|
if (!node) {
|
|
152
|
-
node = { kind: 'table', key: parts.at(-1), path, raw: '', line: i + 1, children: [] };
|
|
177
|
+
node = { kind: 'table', key: parts.at(-1), path, raw: '', line: i + 1, range: declaration, ...(comment ? { comment } : {}), children: [] };
|
|
153
178
|
(tables.get(parts.slice(0, -1).join('.')) ?? root).children.push(node);
|
|
154
179
|
tables.set(path, node);
|
|
155
180
|
nodeCount++;
|
|
@@ -175,7 +200,7 @@ export function parseTomlText(text, limits = {}) {
|
|
|
175
200
|
for (const part of parts.slice(0, -1)) {
|
|
176
201
|
let nested = parent.children.find(n => n.key === part && n.kind === 'table');
|
|
177
202
|
if (!nested) {
|
|
178
|
-
nested = { kind: 'table', key: part, path: tomlPath(parent.path, part), raw: '', line: i + 1, children: [] };
|
|
203
|
+
nested = { kind: 'table', key: part, path: tomlPath(parent.path, part), raw: '', line: i + 1, range: declaration, children: [] };
|
|
179
204
|
parent.children.push(nested);
|
|
180
205
|
}
|
|
181
206
|
parent = nested;
|
|
@@ -183,25 +208,45 @@ export function parseTomlText(text, limits = {}) {
|
|
|
183
208
|
const key = parts.at(-1);
|
|
184
209
|
if (parent.children.some(n => n.key === key))
|
|
185
210
|
error('toml.duplicate-key', i + 1);
|
|
186
|
-
else
|
|
187
|
-
|
|
211
|
+
else {
|
|
212
|
+
const node = makeValue(key, tomlPath(parent.path, key), source.slice(eq + 1), i + 1, 0, base + eq + 1);
|
|
213
|
+
// The declaration reads better than the bare value when a host turns
|
|
214
|
+
// the range into a selection, so widen it to cover `key = value`.
|
|
215
|
+
node.range = declaration;
|
|
216
|
+
if (comment)
|
|
217
|
+
node.comment = comment;
|
|
218
|
+
parent.children.push(node);
|
|
219
|
+
}
|
|
188
220
|
}
|
|
189
221
|
return { document: { text, root }, diagnostics };
|
|
190
222
|
}
|
|
191
223
|
/** Group physical lines while a multiline string or a composite array/table
|
|
192
224
|
* remains open. This preserves the declaration line for diagnostics. */
|
|
193
225
|
function logicalTomlLines(text) {
|
|
194
|
-
const physical =
|
|
226
|
+
const physical = [];
|
|
227
|
+
{
|
|
228
|
+
const separator = /\r\n|\n|\r/g;
|
|
229
|
+
let last = 0;
|
|
230
|
+
let match;
|
|
231
|
+
while ((match = separator.exec(text))) {
|
|
232
|
+
physical.push({ text: text.slice(last, match.index), offset: last });
|
|
233
|
+
last = match.index + match[0].length;
|
|
234
|
+
}
|
|
235
|
+
physical.push({ text: text.slice(last), offset: last });
|
|
236
|
+
}
|
|
195
237
|
const out = [];
|
|
196
|
-
let
|
|
238
|
+
let open = false;
|
|
197
239
|
let start = 1;
|
|
240
|
+
let offset = 0;
|
|
198
241
|
let triple = '';
|
|
199
242
|
let depth = 0;
|
|
200
243
|
for (let i = 0; i < physical.length; i++) {
|
|
201
|
-
const line = physical[i]
|
|
202
|
-
if (!
|
|
244
|
+
const { text: line, offset: at } = physical[i];
|
|
245
|
+
if (!open) {
|
|
246
|
+
open = true;
|
|
203
247
|
start = i + 1;
|
|
204
|
-
|
|
248
|
+
offset = at;
|
|
249
|
+
}
|
|
205
250
|
for (let p = 0; p < line.length; p++) {
|
|
206
251
|
if (line.startsWith('"""', p) || line.startsWith("'''", p)) {
|
|
207
252
|
const token = line.slice(p, p + 3);
|
|
@@ -219,16 +264,41 @@ function logicalTomlLines(text) {
|
|
|
219
264
|
else if (c === ']' || c === '}')
|
|
220
265
|
depth--;
|
|
221
266
|
}
|
|
267
|
+
// Slicing the original text (rather than re-joining) keeps every offset
|
|
268
|
+
// inside a logical line usable as an index into `text`.
|
|
222
269
|
if (!triple && depth <= 0) {
|
|
223
|
-
out.push({ line: start, text:
|
|
224
|
-
|
|
270
|
+
out.push({ line: start, text: text.slice(offset, at + line.length), offset });
|
|
271
|
+
open = false;
|
|
225
272
|
depth = 0;
|
|
226
273
|
}
|
|
227
274
|
}
|
|
228
|
-
if (
|
|
229
|
-
out.push({ line: start, text:
|
|
275
|
+
if (open)
|
|
276
|
+
out.push({ line: start, text: text.slice(offset), offset });
|
|
230
277
|
return out;
|
|
231
278
|
}
|
|
279
|
+
/** 1-based line lookup for offsets, used by inline members that may sit on a
|
|
280
|
+
* different physical line than their declaration. */
|
|
281
|
+
function lineIndexer(text) {
|
|
282
|
+
const starts = [0];
|
|
283
|
+
for (let i = 0; i < text.length; i++) {
|
|
284
|
+
const c = text[i];
|
|
285
|
+
if (c === '\n')
|
|
286
|
+
starts.push(i + 1);
|
|
287
|
+
else if (c === '\r') {
|
|
288
|
+
if (text[i + 1] === '\n')
|
|
289
|
+
i++;
|
|
290
|
+
starts.push(i + 1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return offset => { let low = 0, high = starts.length - 1; while (low < high) {
|
|
294
|
+
const mid = (low + high + 1) >> 1;
|
|
295
|
+
if (starts[mid] <= offset)
|
|
296
|
+
low = mid;
|
|
297
|
+
else
|
|
298
|
+
high = mid - 1;
|
|
299
|
+
} return low + 1; };
|
|
300
|
+
}
|
|
301
|
+
function commentBody(rest) { const text = rest.trimStart(); return text.startsWith('#') ? text.slice(1).trim() : undefined; }
|
|
232
302
|
function stripComment(line) { let q = ''; for (let i = 0; i < line.length; i++) {
|
|
233
303
|
const c = line[i];
|
|
234
304
|
if (q) {
|
|
@@ -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.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare const REQIF_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
|
61
61
|
export declare const HDF5_MAGIC_PREFIX: readonly [137, 72, 68, 70, 13, 10, 26, 10];
|
|
62
62
|
export declare const HDF5_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
63
63
|
export declare const MAT_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
64
|
+
export declare const SAFETENSORS_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
64
65
|
export declare const AUDIO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
65
66
|
export declare const VIDEO_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
|
66
67
|
export declare const DBC_VIEWER_DESCRIPTOR: ViewerDescriptor;
|
package/dist/registry/index.js
CHANGED
|
@@ -72,7 +72,7 @@ export const PDF_VIEWER_DESCRIPTOR = {
|
|
|
72
72
|
priority: 20,
|
|
73
73
|
magicPrefix: [0x25, 0x50, 0x44, 0x46, 0x2d], // '%PDF-'
|
|
74
74
|
requiredServices: [],
|
|
75
|
-
optionalServices: ['save', 'writeback', 'filePick']
|
|
75
|
+
optionalServices: ['save', 'writeback', 'filePick', 'clipboard']
|
|
76
76
|
};
|
|
77
77
|
export const PARQUET_MAGIC_SIGNATURES = [[{ offset: 0, bytes: [0x50, 0x41, 0x52, 0x31] }]];
|
|
78
78
|
export const PARQUET_VIEWER_DESCRIPTOR = { id: 'parquet', displayNameKey: 'parquet.title', extensions: ['parquet'], priority: 20, magicSignatures: PARQUET_MAGIC_SIGNATURES, requiredServices: [], optionalServices: ['clipboard', 'save'] };
|
|
@@ -87,6 +87,9 @@ export const HDF5_MAGIC_PREFIX = [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a
|
|
|
87
87
|
// signature still enables extensionless content routing below.
|
|
88
88
|
export const HDF5_VIEWER_DESCRIPTOR = { id: 'hdf5', displayNameKey: 'hdf5.title', extensions: ['h5', 'hdf5', 'he5'], priority: 20, requiredServices: [], optionalServices: ['clipboard'] };
|
|
89
89
|
export const MAT_VIEWER_DESCRIPTOR = { id: 'mat', displayNameKey: 'mat.title', extensions: ['mat'], priority: 20, requiredServices: [], optionalServices: ['clipboard'] };
|
|
90
|
+
// Safetensors has no fixed leading magic (the file opens with an 8-byte header
|
|
91
|
+
// length), so it is admitted by extension and validated by the parser.
|
|
92
|
+
export const SAFETENSORS_VIEWER_DESCRIPTOR = { id: 'safetensors', displayNameKey: 'safetensors.title', extensions: ['safetensors'], priority: 20, requiredServices: [], optionalServices: ['clipboard'] };
|
|
90
93
|
export const AUDIO_VIEWER_DESCRIPTOR = { id: 'audio', displayNameKey: 'audio.title', extensions: ['mp3', 'wav', 'pcm', 'aiff', 'aif', 'aifc', 'amr', 'awb', 'ogg', 'flac', 'ac3', 'aac', 'm4a'], priority: 20, requiredServices: [], optionalServices: [] };
|
|
91
94
|
export const VIDEO_VIEWER_DESCRIPTOR = { id: 'video', displayNameKey: 'video.title', extensions: ['mp4', 'mts', 'm2ts', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'], priority: 20, requiredServices: [], optionalServices: [] };
|
|
92
95
|
export const DBC_VIEWER_DESCRIPTOR = { id: 'dbc', displayNameKey: 'dbc.title', extensions: ['dbc'], priority: 20, requiredServices: [], optionalServices: ['clipboard'] };
|
|
@@ -162,7 +165,7 @@ export const IMAGE_VIEWER_DESCRIPTOR = {
|
|
|
162
165
|
export const MARKDOWN_VIEWER_DESCRIPTOR = {
|
|
163
166
|
id: 'markdown', displayNameKey: 'markdown.title',
|
|
164
167
|
extensions: ['md', 'markdown', 'mdown', 'mkdn', 'mkd'], priority: 10,
|
|
165
|
-
requiredServices: [], optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback']
|
|
168
|
+
requiredServices: [], optionalServices: ['clipboard', 'navigation', 'documentAssets', 'writeback', 'save']
|
|
166
169
|
};
|
|
167
170
|
export const ARCHIVE_MAGIC_SIGNATURES = [
|
|
168
171
|
[{ offset: 0, bytes: [0x50, 0x4b, 0x03, 0x04] }], [{ offset: 0, bytes: [0x50, 0x4b, 0x05, 0x06] }],
|
|
@@ -200,6 +203,7 @@ export const CORE_VIEWER_DESCRIPTORS = [
|
|
|
200
203
|
REQIF_VIEWER_DESCRIPTOR,
|
|
201
204
|
HDF5_VIEWER_DESCRIPTOR,
|
|
202
205
|
MAT_VIEWER_DESCRIPTOR,
|
|
206
|
+
SAFETENSORS_VIEWER_DESCRIPTOR,
|
|
203
207
|
PPT_VIEWER_DESCRIPTOR,
|
|
204
208
|
WORD_VIEWER_DESCRIPTOR,
|
|
205
209
|
HWP_VIEWER_DESCRIPTOR,
|
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;
|
|
@@ -175,6 +183,17 @@
|
|
|
175
183
|
min-width: 56px;
|
|
176
184
|
text-align: center;
|
|
177
185
|
}
|
|
186
|
+
.omni-pdf__thumbs-toggle {
|
|
187
|
+
width: 30px;
|
|
188
|
+
height: 28px;
|
|
189
|
+
padding: 0;
|
|
190
|
+
margin-right: auto;
|
|
191
|
+
font-size: 16px;
|
|
192
|
+
line-height: 1;
|
|
193
|
+
}
|
|
194
|
+
.omni-pdf__thumbs-toggle.is-collapsed {
|
|
195
|
+
opacity: 0.6;
|
|
196
|
+
}
|
|
178
197
|
.omni-pdf__view-menu-wrap { position: relative; }
|
|
179
198
|
.omni-pdf__view-menu-btn {
|
|
180
199
|
width: 30px;
|
|
@@ -219,12 +238,19 @@
|
|
|
219
238
|
background: var(--omni-border, #3c3c3c);
|
|
220
239
|
}
|
|
221
240
|
.omni-pdf__body {
|
|
241
|
+
position: relative;
|
|
222
242
|
flex: 1;
|
|
223
243
|
min-height: 0;
|
|
224
244
|
display: grid;
|
|
225
245
|
grid-template-columns: 160px minmax(0, 1fr);
|
|
226
246
|
overflow: hidden;
|
|
227
247
|
}
|
|
248
|
+
.omni-pdf__body--thumbs-collapsed {
|
|
249
|
+
grid-template-columns: minmax(0, 1fr);
|
|
250
|
+
}
|
|
251
|
+
.omni-pdf__body--thumbs-collapsed .omni-pdf__thumbs {
|
|
252
|
+
display: none;
|
|
253
|
+
}
|
|
228
254
|
.omni-pdf__thumbs {
|
|
229
255
|
overflow: auto;
|
|
230
256
|
min-height: 0;
|
|
@@ -265,10 +291,28 @@
|
|
|
265
291
|
.omni-pdf__thumb.drop-before::before { top: -6px; }
|
|
266
292
|
.omni-pdf__thumb.drop-after::after { bottom: -6px; }
|
|
267
293
|
.omni-pdf__thumb canvas {
|
|
294
|
+
display: block;
|
|
268
295
|
max-width: 100%;
|
|
269
296
|
background: #ffffff;
|
|
270
297
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
|
271
298
|
}
|
|
299
|
+
.omni-pdf__thumb-page {
|
|
300
|
+
position: relative;
|
|
301
|
+
max-width: 100%;
|
|
302
|
+
}
|
|
303
|
+
.omni-pdf__thumb-annotation {
|
|
304
|
+
position: absolute;
|
|
305
|
+
z-index: 1;
|
|
306
|
+
overflow: hidden;
|
|
307
|
+
pointer-events: none;
|
|
308
|
+
color: #111111; /* ink on paper */
|
|
309
|
+
white-space: nowrap;
|
|
310
|
+
}
|
|
311
|
+
.omni-pdf__thumb-annotation img { display: block; }
|
|
312
|
+
.omni-pdf__thumb-annotation.omni-pdf__underline::after,
|
|
313
|
+
.omni-pdf__thumb-annotation.omni-pdf__strikeout::after {
|
|
314
|
+
height: 1px;
|
|
315
|
+
}
|
|
272
316
|
.omni-pdf__thumb-label {
|
|
273
317
|
font-family: var(--omni-font-mono, Monaco, Menlo, monospace);
|
|
274
318
|
font-size: 10px;
|
|
@@ -438,6 +482,161 @@
|
|
|
438
482
|
content: '';
|
|
439
483
|
background: var(--omni-hl-color, #ffeb3b);
|
|
440
484
|
}
|
|
485
|
+
/* Floating action bar shown above a selected text-markup annotation. */
|
|
486
|
+
.omni-pdf__markup-toolbar {
|
|
487
|
+
position: absolute;
|
|
488
|
+
z-index: 5;
|
|
489
|
+
display: flex;
|
|
490
|
+
align-items: center;
|
|
491
|
+
gap: 2px;
|
|
492
|
+
padding: 4px;
|
|
493
|
+
transform: translateY(calc(-100% - 8px));
|
|
494
|
+
background: var(--omni-bg-secondary, #252526);
|
|
495
|
+
border: 1px solid var(--omni-border, #3c3c3c);
|
|
496
|
+
border-radius: 10px;
|
|
497
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
|
|
498
|
+
cursor: default;
|
|
499
|
+
}
|
|
500
|
+
.omni-pdf button.omni-pdf__markup-tool {
|
|
501
|
+
display: inline-flex;
|
|
502
|
+
align-items: center;
|
|
503
|
+
justify-content: center;
|
|
504
|
+
width: 30px;
|
|
505
|
+
height: 30px;
|
|
506
|
+
padding: 0;
|
|
507
|
+
border: 0;
|
|
508
|
+
border-radius: 8px;
|
|
509
|
+
background: transparent;
|
|
510
|
+
color: var(--omni-fg, #e8e8e8);
|
|
511
|
+
font-size: 15px;
|
|
512
|
+
line-height: 1;
|
|
513
|
+
cursor: pointer;
|
|
514
|
+
}
|
|
515
|
+
.omni-pdf button.omni-pdf__markup-tool:hover:not(:disabled) { background: var(--omni-button-hover-bg, #45494e); }
|
|
516
|
+
.omni-pdf button.omni-pdf__markup-tool:disabled { opacity: 0.4; cursor: default; }
|
|
517
|
+
.omni-pdf__markup-color::before {
|
|
518
|
+
content: '';
|
|
519
|
+
width: 18px;
|
|
520
|
+
height: 18px;
|
|
521
|
+
border-radius: 5px;
|
|
522
|
+
background: var(--omni-hl-color, #ffeb3b);
|
|
523
|
+
border: 1px solid rgba(0, 0, 0, 0.25);
|
|
524
|
+
}
|
|
525
|
+
.omni-pdf__markup-color-wrap { position: relative; display: inline-flex; }
|
|
526
|
+
.omni-pdf__markup-palette {
|
|
527
|
+
position: absolute;
|
|
528
|
+
top: calc(100% + 6px);
|
|
529
|
+
left: 50%;
|
|
530
|
+
transform: translateX(-50%);
|
|
531
|
+
display: none;
|
|
532
|
+
grid-template-columns: repeat(3, auto);
|
|
533
|
+
gap: 6px;
|
|
534
|
+
padding: 8px;
|
|
535
|
+
background: var(--omni-bg-secondary, #252526);
|
|
536
|
+
border: 1px solid var(--omni-border, #3c3c3c);
|
|
537
|
+
border-radius: 8px;
|
|
538
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
|
|
539
|
+
z-index: 6;
|
|
540
|
+
}
|
|
541
|
+
.omni-pdf__markup-palette.is-open { display: grid; }
|
|
542
|
+
.omni-pdf button.omni-pdf__markup-swatch,
|
|
543
|
+
.omni-pdf button.omni-pdf__markup-swatch:hover:not(:disabled) {
|
|
544
|
+
width: 22px;
|
|
545
|
+
height: 22px;
|
|
546
|
+
padding: 0;
|
|
547
|
+
border-radius: 5px;
|
|
548
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
549
|
+
background: var(--omni-hl-color, #ffeb3b);
|
|
550
|
+
cursor: pointer;
|
|
551
|
+
}
|
|
552
|
+
.omni-pdf button.omni-pdf__markup-swatch.is-current {
|
|
553
|
+
outline: 2px solid var(--omni-accent, #0e639c);
|
|
554
|
+
outline-offset: 1px;
|
|
555
|
+
}
|
|
556
|
+
/* Left overlay panel listing every text-markup annotation. */
|
|
557
|
+
.omni-pdf__markup-list {
|
|
558
|
+
position: absolute;
|
|
559
|
+
top: 0;
|
|
560
|
+
left: 0;
|
|
561
|
+
bottom: 0;
|
|
562
|
+
z-index: 6;
|
|
563
|
+
display: none;
|
|
564
|
+
flex-direction: column;
|
|
565
|
+
width: min(280px, 70%);
|
|
566
|
+
background: var(--omni-bg-secondary, #252526);
|
|
567
|
+
border-right: 1px solid var(--omni-border, #3c3c3c);
|
|
568
|
+
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.35);
|
|
569
|
+
overflow-y: auto;
|
|
570
|
+
}
|
|
571
|
+
.omni-pdf__markup-list.is-open { display: flex; }
|
|
572
|
+
.omni-pdf__markup-list-head {
|
|
573
|
+
position: sticky;
|
|
574
|
+
top: 0;
|
|
575
|
+
display: flex;
|
|
576
|
+
align-items: center;
|
|
577
|
+
justify-content: space-between;
|
|
578
|
+
padding: 10px 12px;
|
|
579
|
+
background: var(--omni-bg-secondary, #252526);
|
|
580
|
+
border-bottom: 1px solid var(--omni-border, #3c3c3c);
|
|
581
|
+
}
|
|
582
|
+
.omni-pdf__markup-list-title { font-weight: 600; }
|
|
583
|
+
.omni-pdf button.omni-pdf__markup-list-close {
|
|
584
|
+
width: 26px;
|
|
585
|
+
height: 26px;
|
|
586
|
+
padding: 0;
|
|
587
|
+
border: 0;
|
|
588
|
+
border-radius: 6px;
|
|
589
|
+
background: transparent;
|
|
590
|
+
color: var(--omni-fg, #e8e8e8);
|
|
591
|
+
font-size: 18px;
|
|
592
|
+
line-height: 1;
|
|
593
|
+
cursor: pointer;
|
|
594
|
+
}
|
|
595
|
+
.omni-pdf button.omni-pdf__markup-list-close:hover:not(:disabled) { background: var(--omni-button-hover-bg, #45494e); }
|
|
596
|
+
.omni-pdf__markup-list-empty {
|
|
597
|
+
padding: 16px 12px;
|
|
598
|
+
color: var(--omni-fg-muted, #9d9d9d);
|
|
599
|
+
font-size: 13px;
|
|
600
|
+
}
|
|
601
|
+
.omni-pdf button.omni-pdf__markup-item {
|
|
602
|
+
display: flex;
|
|
603
|
+
align-items: center;
|
|
604
|
+
gap: 8px;
|
|
605
|
+
width: 100%;
|
|
606
|
+
padding: 10px 12px;
|
|
607
|
+
border: 0;
|
|
608
|
+
border-bottom: 1px solid var(--omni-border, #3c3c3c);
|
|
609
|
+
background: transparent;
|
|
610
|
+
color: var(--omni-fg, #e8e8e8);
|
|
611
|
+
text-align: left;
|
|
612
|
+
cursor: pointer;
|
|
613
|
+
}
|
|
614
|
+
.omni-pdf button.omni-pdf__markup-item:hover:not(:disabled) { background: var(--omni-button-hover-bg, #45494e); }
|
|
615
|
+
.omni-pdf button.omni-pdf__markup-item.is-selected {
|
|
616
|
+
background: var(--omni-button-hover-bg, #45494e);
|
|
617
|
+
box-shadow: inset 3px 0 0 var(--omni-accent, #0e639c);
|
|
618
|
+
}
|
|
619
|
+
.omni-pdf__markup-item-dot {
|
|
620
|
+
flex: none;
|
|
621
|
+
width: 14px;
|
|
622
|
+
height: 14px;
|
|
623
|
+
border-radius: 3px;
|
|
624
|
+
border: 1px solid rgba(255, 255, 255, 0.25);
|
|
625
|
+
background: var(--omni-hl-color, #ffeb3b);
|
|
626
|
+
}
|
|
627
|
+
.omni-pdf__markup-item-text {
|
|
628
|
+
flex: 1;
|
|
629
|
+
min-width: 0;
|
|
630
|
+
overflow: hidden;
|
|
631
|
+
text-overflow: ellipsis;
|
|
632
|
+
white-space: nowrap;
|
|
633
|
+
font-size: 13px;
|
|
634
|
+
}
|
|
635
|
+
.omni-pdf__markup-item-page {
|
|
636
|
+
flex: none;
|
|
637
|
+
color: var(--omni-fg-muted, #9d9d9d);
|
|
638
|
+
font-size: 12px;
|
|
639
|
+
}
|
|
441
640
|
.omni-pdf__text-editor {
|
|
442
641
|
position: absolute;
|
|
443
642
|
z-index: 3;
|
|
@@ -543,9 +742,13 @@
|
|
|
543
742
|
grid-template-columns: minmax(0, 1fr);
|
|
544
743
|
grid-template-rows: minmax(120px, 30vh) minmax(0, 1fr);
|
|
545
744
|
}
|
|
745
|
+
.omni-pdf__body--thumbs-collapsed {
|
|
746
|
+
grid-template-rows: minmax(0, 1fr);
|
|
747
|
+
}
|
|
546
748
|
.omni-pdf__thumbs {
|
|
547
749
|
border-right: 0;
|
|
548
750
|
border-bottom: 1px solid var(--omni-border, #3c3c3c);
|
|
549
751
|
}
|
|
550
752
|
.omni-pdf__pages { grid-row: 2; }
|
|
753
|
+
.omni-pdf__body--thumbs-collapsed .omni-pdf__pages { grid-row: 1; }
|
|
551
754
|
}
|