@ours.network/fleet 0.15.6 → 0.16.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 +44 -7
- package/dist/config.d.ts +6 -0
- package/dist/config.js +8 -1
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +29 -2
- package/dist/owner-channel/channel.d.ts +9 -0
- package/dist/owner-channel/channel.js +35 -4
- package/dist/owner-channel/commands.d.ts +8 -0
- package/dist/owner-channel/commands.js +12 -1
- package/dist/owner-channel/notices.d.ts +19 -0
- package/dist/owner-channel/notices.js +16 -0
- package/dist/web/fleet-config-service.js +51 -15
- package/dist/web/runtime.js +9 -2
- package/dist/web/server.d.ts +6 -2
- package/dist/web/server.js +50 -0
- package/dist/web/topology-draft-store.d.ts +80 -0
- package/dist/web/topology-draft-store.js +337 -0
- package/dist/web/topology-model.d.ts +62 -0
- package/dist/web/topology-model.js +220 -0
- package/dist/web/topology-promote.d.ts +31 -0
- package/dist/web/topology-promote.js +168 -0
- package/dist/web/yaml-document-edit.d.ts +31 -0
- package/dist/web/yaml-document-edit.js +401 -0
- package/dist/web-app/assets/{TerminalView-hZpyUFY_.js → TerminalView-Mxxypj9w.js} +1 -1
- package/dist/web-app/assets/index-CsHEL0f6.js +10 -0
- package/dist/web-app/assets/{index-COg4Azq1.css → index-DuC-xnX4.css} +1 -1
- package/dist/web-app/index.html +2 -2
- package/package.json +1 -1
- package/dist/web-app/assets/index-Cde9auW0.js +0 -10
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { Document, isMap, isPair, isScalar, isSeq, parseDocument, } from 'yaml';
|
|
2
|
+
/**
|
|
3
|
+
* Surgical fleet-YAML editing.
|
|
4
|
+
*
|
|
5
|
+
* The web console edits a JSON *model* of the configuration, but the file on
|
|
6
|
+
* disk belongs to the user. A `parse -> plain JS -> stringify` round-trip keeps
|
|
7
|
+
* the data and destroys everything around it. Re-rendering the parsed document
|
|
8
|
+
* is barely better: `yaml` keeps a comment's text but not its position, so
|
|
9
|
+
* `permissions: # note` comes back as `permissions:` with the note moved onto
|
|
10
|
+
* the next line, shifting everything below it. On `examples/fleet.yaml` a plain
|
|
11
|
+
* round-trip rewrites 80 of 147 lines.
|
|
12
|
+
*
|
|
13
|
+
* So edits are computed as *text splices* against the original bytes. Only the
|
|
14
|
+
* regions that genuinely changed are replaced; everything else is byte-identical
|
|
15
|
+
* by construction, which makes an unchanged save a true no-op and keeps a trailing
|
|
16
|
+
* inline comment attached to the scalar it annotates.
|
|
17
|
+
*
|
|
18
|
+
* The contract is deliberately narrower than "nothing else ever changes":
|
|
19
|
+
*
|
|
20
|
+
* - A *scalar* edit replaces only the value's source range, so the trailing
|
|
21
|
+
* comment beside it survives untouched.
|
|
22
|
+
* - Changing the *length* of a block sequence replaces that collection WHOLESALE,
|
|
23
|
+
* because an index-shifting edit cannot be attributed to particular items. Inline
|
|
24
|
+
* comments written on the individual items of that sequence are therefore LOST.
|
|
25
|
+
* The loss is bounded to the one collection being edited — every other line in
|
|
26
|
+
* the file keeps its bytes — and the caller diffs the real before/after text, so
|
|
27
|
+
* it is visible in review before anything is written and the save can be declined.
|
|
28
|
+
* - A model change that cannot be expressed as a splice at all falls back to
|
|
29
|
+
* re-rendering the whole document. That path does reflow, which is the other
|
|
30
|
+
* reason the caller diffs real bytes: reflow must be visible, never silent.
|
|
31
|
+
* - Deleting a mapping entry leaves any comment written above it in place. Removing
|
|
32
|
+
* a value must not quietly remove the operator's prose about it.
|
|
33
|
+
*/
|
|
34
|
+
const PARSE_OPTIONS = { strict: true, uniqueKeys: true, prettyErrors: true };
|
|
35
|
+
const MIN_INDENT = 1;
|
|
36
|
+
const MAX_INDENT = 8;
|
|
37
|
+
const DEFAULT_INDENT = 2;
|
|
38
|
+
/**
|
|
39
|
+
* Apply `model` to `source`, returning the new document text.
|
|
40
|
+
*
|
|
41
|
+
* Guarantees:
|
|
42
|
+
* - an unchanged model returns `source` byte for byte;
|
|
43
|
+
* - regions outside a change keep their bytes whenever the change is spliceable;
|
|
44
|
+
* - the result parses back to exactly `model`, verified before returning, so a
|
|
45
|
+
* planning bug can never silently write something else to disk.
|
|
46
|
+
*/
|
|
47
|
+
export function renderModelOntoSource(source, model) {
|
|
48
|
+
const document = parseSource(source);
|
|
49
|
+
const formatting = detectFormatting(source);
|
|
50
|
+
const plan = [];
|
|
51
|
+
const spliceable = planEdits(document, source, formatting, [], documentValue(document), model, plan);
|
|
52
|
+
const rendered = !spliceable ? reRender(document, source, formatting, model)
|
|
53
|
+
: plan.length === 0 ? source
|
|
54
|
+
: applySplices(source, plan);
|
|
55
|
+
const check = parseSource(rendered);
|
|
56
|
+
if (!deepEqual(documentValue(check), model))
|
|
57
|
+
throw new Error('surgical YAML edit did not reproduce the requested model');
|
|
58
|
+
return rendered;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Replace every `defaults.env` / `roles.*.env` value — and every `vars:` entry
|
|
62
|
+
* those values interpolate — with `marker`.
|
|
63
|
+
*
|
|
64
|
+
* Splices the scalars out of the original bytes rather than re-rendering, so the
|
|
65
|
+
* only difference from `source` is the secrets themselves. Redaction must not be
|
|
66
|
+
* able to introduce (or conceal) a formatting change in a review diff.
|
|
67
|
+
*/
|
|
68
|
+
export function redactSourceSecrets(source, marker) {
|
|
69
|
+
const document = parseSource(source);
|
|
70
|
+
const secretVars = new Set();
|
|
71
|
+
const plan = [];
|
|
72
|
+
const redactEnvMap = (path) => {
|
|
73
|
+
const node = document.getIn(path, true);
|
|
74
|
+
if (!isMap(node))
|
|
75
|
+
return;
|
|
76
|
+
for (const item of node.items) {
|
|
77
|
+
if (!isPair(item) || !isScalar(item.value))
|
|
78
|
+
continue;
|
|
79
|
+
if (typeof item.value.value === 'string')
|
|
80
|
+
for (const match of item.value.value.matchAll(/\$\{(\w+)\}/g))
|
|
81
|
+
secretVars.add(match[1]);
|
|
82
|
+
pushValueSplice(plan, item.value, marker);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
redactEnvMap(['defaults', 'env']);
|
|
86
|
+
for (const name of mapKeys(document, ['roles']))
|
|
87
|
+
redactEnvMap(['roles', name, 'env']);
|
|
88
|
+
const vars = document.getIn(['vars'], true);
|
|
89
|
+
if (isMap(vars))
|
|
90
|
+
for (const item of vars.items) {
|
|
91
|
+
if (isPair(item) && isScalar(item.key) && isScalar(item.value)
|
|
92
|
+
&& secretVars.has(String(item.key.value)))
|
|
93
|
+
pushValueSplice(plan, item.value, marker);
|
|
94
|
+
}
|
|
95
|
+
return plan.length === 0 ? source : applySplices(source, plan);
|
|
96
|
+
}
|
|
97
|
+
function pushValueSplice(plan, node, marker) {
|
|
98
|
+
const range = node.range;
|
|
99
|
+
if (range)
|
|
100
|
+
plan.push({ start: range[0], end: range[1], text: marker });
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Best-effort detection of the document's block indentation, used only when a
|
|
104
|
+
* change is not spliceable and the document has to be re-rendered. Detection
|
|
105
|
+
* failure falls back to the library defaults; correctness never depends on it.
|
|
106
|
+
*/
|
|
107
|
+
export function detectFormatting(source) {
|
|
108
|
+
const lines = source.split('\n')
|
|
109
|
+
.filter(line => line.trim() !== '' && !line.trimStart().startsWith('#'));
|
|
110
|
+
let indent;
|
|
111
|
+
let indentSeq;
|
|
112
|
+
let previousWidth = 0;
|
|
113
|
+
let previousOpensBlock = false;
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
const width = line.length - line.trimStart().length;
|
|
116
|
+
const isItem = /^-(\s|$)/.test(line.trimStart());
|
|
117
|
+
if (previousOpensBlock && width >= previousWidth) {
|
|
118
|
+
if (isItem)
|
|
119
|
+
indentSeq ??= width > previousWidth;
|
|
120
|
+
if (width > previousWidth)
|
|
121
|
+
indent ??= width - previousWidth;
|
|
122
|
+
}
|
|
123
|
+
previousOpensBlock = !isItem && /:\s*(#.*)?$/.test(line);
|
|
124
|
+
previousWidth = width;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
indent: indent !== undefined && indent >= MIN_INDENT && indent <= MAX_INDENT
|
|
128
|
+
? indent : DEFAULT_INDENT,
|
|
129
|
+
indentSeq: indentSeq ?? true,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/* ------------------------------------------------------------------ *
|
|
133
|
+
* Planning: turn a model diff into text splices
|
|
134
|
+
* ------------------------------------------------------------------ */
|
|
135
|
+
/**
|
|
136
|
+
* Walk `next` against `current` and record the splices that would realise it.
|
|
137
|
+
* Returns false as soon as a change cannot be expressed against the source text,
|
|
138
|
+
* in which case the caller re-renders instead.
|
|
139
|
+
*/
|
|
140
|
+
function planEdits(document, source, formatting, path, current, next, plan) {
|
|
141
|
+
if (deepEqual(current, next))
|
|
142
|
+
return true;
|
|
143
|
+
if (isPlainObject(current) && isPlainObject(next)) {
|
|
144
|
+
const node = document.getIn(path, true);
|
|
145
|
+
if (!isMap(node))
|
|
146
|
+
return false;
|
|
147
|
+
// Removing every entry one by one would leave `watchdogs:` with no value,
|
|
148
|
+
// which reads back as null rather than the empty mapping that was asked for.
|
|
149
|
+
if (path.length > 0 && Object.keys(next).length === 0 && Object.keys(current).length > 0)
|
|
150
|
+
return planReplace(document, source, formatting, path, next, plan);
|
|
151
|
+
for (const key of Object.keys(current))
|
|
152
|
+
if (!hasOwn(next, key) && !planDelete(node, source, key, plan))
|
|
153
|
+
return false;
|
|
154
|
+
for (const key of Object.keys(next)) {
|
|
155
|
+
if (hasOwn(current, key)) {
|
|
156
|
+
if (!planEdits(document, source, formatting, [...path, key], own(current, key), own(next, key), plan))
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
else if (!planInsert(node, source, formatting, key, own(next, key), plan))
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (Array.isArray(current) && Array.isArray(next) && current.length === next.length)
|
|
165
|
+
return next.every((value, index) => planEdits(document, source, formatting, [...path, index], current[index], value, plan));
|
|
166
|
+
return planReplace(document, source, formatting, path, next, plan);
|
|
167
|
+
}
|
|
168
|
+
/** Replace one value in place, keeping the key, its layout and its comment. */
|
|
169
|
+
function planReplace(document, source, formatting, path, next, plan) {
|
|
170
|
+
if (path.length === 0)
|
|
171
|
+
return false;
|
|
172
|
+
const node = document.getIn(path, true);
|
|
173
|
+
if (!isNodeWithRange(node))
|
|
174
|
+
return false;
|
|
175
|
+
// A block scalar's body cannot be swapped for an inline one by splicing.
|
|
176
|
+
if (isScalar(node) && (node.type === 'BLOCK_LITERAL' || node.type === 'BLOCK_FOLDED'))
|
|
177
|
+
return false;
|
|
178
|
+
const start = node.range[0];
|
|
179
|
+
// A block collection's range runs past the newline that terminates it, while a
|
|
180
|
+
// scalar's stops at the value. Splicing over that newline would pull the next
|
|
181
|
+
// line up onto this one, so leave it where it is.
|
|
182
|
+
let end = node.range[1];
|
|
183
|
+
while (end > start && source[end - 1] === '\n')
|
|
184
|
+
end -= 1;
|
|
185
|
+
const ownLine = source.slice(lineStart(source, start), start).trim() === '';
|
|
186
|
+
// `roles: [Alice]` must not come back as a block list just because it grew.
|
|
187
|
+
const flow = (isSeq(node) || isMap(node)) && node.flow === true;
|
|
188
|
+
const text = renderValue(next, formatting, flow);
|
|
189
|
+
// An emptied collection belongs beside its key, not alone on the next line.
|
|
190
|
+
if (ownLine && (text === '{}' || text === '[]')
|
|
191
|
+
&& planCollapseOntoKey(document, source, path, start, end, text, plan))
|
|
192
|
+
return true;
|
|
193
|
+
if (text.includes('\n') && !ownLine)
|
|
194
|
+
return false;
|
|
195
|
+
plan.push({ start, end, text: ownLine ? indentContinuation(text, columnOf(source, start)) : text });
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
/** Rewrite `key:\n <block>` as `key: {}` when the block became empty. */
|
|
199
|
+
function planCollapseOntoKey(document, source, path, start, end, text, plan) {
|
|
200
|
+
const key = path[path.length - 1];
|
|
201
|
+
if (typeof key !== 'string')
|
|
202
|
+
return false;
|
|
203
|
+
const parent = document.getIn(path.slice(0, -1), true);
|
|
204
|
+
const pair = parent === undefined ? undefined : findPair(parent, key);
|
|
205
|
+
if (!pair || !isNodeWithRange(pair.key))
|
|
206
|
+
return false;
|
|
207
|
+
const colon = source.indexOf(':', pair.key.range[1]);
|
|
208
|
+
// Anything but whitespace between the key and its value is a comment; keep it.
|
|
209
|
+
if (colon < 0 || colon >= start || source.slice(colon + 1, start).trim() !== '')
|
|
210
|
+
return false;
|
|
211
|
+
plan.push({ start: colon + 1, end, text: ` ${text}` });
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
/** Remove a whole `key: value` entry, including the line it sits on. */
|
|
215
|
+
function planDelete(node, source, key, plan) {
|
|
216
|
+
const pair = findPair(node, key);
|
|
217
|
+
if (!pair || !isNodeWithRange(pair.key))
|
|
218
|
+
return false;
|
|
219
|
+
const end = isNodeWithRange(pair.value) ? pair.value.range[2] : pair.key.range[2];
|
|
220
|
+
plan.push({ start: lineStart(source, pair.key.range[0]), end, text: '' });
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
/** Append a new `key: value` entry to an existing block mapping. */
|
|
224
|
+
function planInsert(node, source, formatting, key, value, plan) {
|
|
225
|
+
if (!isMap(node) || node.flow)
|
|
226
|
+
return false;
|
|
227
|
+
const last = [...node.items].reverse().find(isPair);
|
|
228
|
+
// An empty mapping has no sibling to copy an indent or a position from.
|
|
229
|
+
if (!last || !isNodeWithRange(last.key))
|
|
230
|
+
return false;
|
|
231
|
+
const column = columnOf(source, last.key.range[0]);
|
|
232
|
+
const at = isNodeWithRange(last.value) ? last.value.range[2] : last.key.range[2];
|
|
233
|
+
const entry = indentContinuation(renderValue({ [key]: value }, formatting), column);
|
|
234
|
+
const prefix = at > 0 && source[at - 1] !== '\n' ? '\n' : '';
|
|
235
|
+
plan.push({ start: at, end: at, text: `${prefix}${' '.repeat(column)}${entry}\n` });
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
function applySplices(source, plan) {
|
|
239
|
+
const ordered = [...plan].sort((a, b) => b.start - a.start || b.end - a.end);
|
|
240
|
+
let out = source;
|
|
241
|
+
for (const splice of ordered)
|
|
242
|
+
out = out.slice(0, splice.start) + splice.text + out.slice(splice.end);
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
/* ------------------------------------------------------------------ *
|
|
246
|
+
* Fallback: re-render the whole document
|
|
247
|
+
* ------------------------------------------------------------------ */
|
|
248
|
+
function reRender(document, source, formatting, model) {
|
|
249
|
+
applyToDocument(document, [], documentValue(document), model);
|
|
250
|
+
return restoreCommentAlignment(source, document.toString({ lineWidth: 0, ...formatting }));
|
|
251
|
+
}
|
|
252
|
+
function applyToDocument(document, path, current, next) {
|
|
253
|
+
if (deepEqual(current, next))
|
|
254
|
+
return;
|
|
255
|
+
if (isPlainObject(current) && isPlainObject(next)) {
|
|
256
|
+
for (const key of Object.keys(next))
|
|
257
|
+
applyToDocument(document, [...path, key], own(current, key), own(next, key));
|
|
258
|
+
for (const key of Object.keys(current))
|
|
259
|
+
if (!hasOwn(next, key))
|
|
260
|
+
document.deleteIn([...path, key]);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (Array.isArray(current) && Array.isArray(next) && current.length === next.length) {
|
|
264
|
+
for (const [index, value] of next.entries())
|
|
265
|
+
applyToDocument(document, [...path, index], current[index], value);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (next === undefined) {
|
|
269
|
+
document.deleteIn(path);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Mutating an existing scalar keeps its comment and quoting; replacing it loses both.
|
|
273
|
+
const existing = document.getIn(path, true);
|
|
274
|
+
if (isScalar(existing) && isScalarValue(next) && existing.type !== 'BLOCK_LITERAL' && existing.type !== 'BLOCK_FOLDED') {
|
|
275
|
+
existing.value = next;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (path.length === 0)
|
|
279
|
+
document.contents = document.createNode(next);
|
|
280
|
+
else
|
|
281
|
+
document.setIn(path, document.createNode(next));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* `yaml` keeps a comment's text but not the padding in front of it. Put the
|
|
285
|
+
* original spacing back on lines that are otherwise byte-identical to the
|
|
286
|
+
* source; ambiguous or altered lines are left as rendered, and the caller
|
|
287
|
+
* re-parses the result, so this can never change what the file means.
|
|
288
|
+
*/
|
|
289
|
+
function restoreCommentAlignment(source, rendered) {
|
|
290
|
+
const AMBIGUOUS = '';
|
|
291
|
+
const originals = new Map();
|
|
292
|
+
for (const line of source.split('\n')) {
|
|
293
|
+
const at = trailingCommentIndex(line);
|
|
294
|
+
if (at < 0)
|
|
295
|
+
continue;
|
|
296
|
+
const collapsed = `${line.slice(0, at).replace(/\s+$/, '')} ${line.slice(at)}`;
|
|
297
|
+
if (collapsed === line)
|
|
298
|
+
continue;
|
|
299
|
+
originals.set(collapsed, originals.has(collapsed) ? AMBIGUOUS : line);
|
|
300
|
+
}
|
|
301
|
+
if (originals.size === 0)
|
|
302
|
+
return rendered;
|
|
303
|
+
return rendered.split('\n').map(line => originals.get(line) || line).join('\n');
|
|
304
|
+
}
|
|
305
|
+
/** Index of a line's trailing `#` comment, ignoring `#` inside quoted scalars. */
|
|
306
|
+
function trailingCommentIndex(line) {
|
|
307
|
+
let quote;
|
|
308
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
309
|
+
const character = line[index];
|
|
310
|
+
if (quote === "'") {
|
|
311
|
+
if (character !== "'")
|
|
312
|
+
continue;
|
|
313
|
+
if (line[index + 1] === "'")
|
|
314
|
+
index += 1;
|
|
315
|
+
else
|
|
316
|
+
quote = undefined;
|
|
317
|
+
}
|
|
318
|
+
else if (quote === '"') {
|
|
319
|
+
if (character === '\\')
|
|
320
|
+
index += 1;
|
|
321
|
+
else if (character === '"')
|
|
322
|
+
quote = undefined;
|
|
323
|
+
}
|
|
324
|
+
else if (character === '"' || character === "'")
|
|
325
|
+
quote = character;
|
|
326
|
+
else if (character === '#' && index > 0 && /\s/.test(line[index - 1]))
|
|
327
|
+
return index;
|
|
328
|
+
}
|
|
329
|
+
return -1;
|
|
330
|
+
}
|
|
331
|
+
/* ------------------------------------------------------------------ *
|
|
332
|
+
* Shared helpers
|
|
333
|
+
* ------------------------------------------------------------------ */
|
|
334
|
+
function parseSource(source) {
|
|
335
|
+
const document = parseDocument(source, PARSE_OPTIONS);
|
|
336
|
+
if (document.errors.length)
|
|
337
|
+
throw new Error(document.errors.map(error => error.message).join('; '));
|
|
338
|
+
return document;
|
|
339
|
+
}
|
|
340
|
+
function documentValue(document) {
|
|
341
|
+
return document.contents == null ? {} : document.toJS({ maxAliasCount: 100 });
|
|
342
|
+
}
|
|
343
|
+
function mapKeys(document, path) {
|
|
344
|
+
const node = document.getIn(path, true);
|
|
345
|
+
if (!isMap(node))
|
|
346
|
+
return [];
|
|
347
|
+
return node.items.flatMap(item => (isPair(item) && isScalar(item.key) ? [String(item.key.value)] : []));
|
|
348
|
+
}
|
|
349
|
+
function findPair(node, key) {
|
|
350
|
+
if (!isMap(node))
|
|
351
|
+
return undefined;
|
|
352
|
+
return node.items.find((item) => isPair(item) && isScalar(item.key) && String(item.key.value) === key);
|
|
353
|
+
}
|
|
354
|
+
function renderValue(value, formatting, flow = false) {
|
|
355
|
+
const document = new Document(value);
|
|
356
|
+
if (flow && (isSeq(document.contents) || isMap(document.contents)))
|
|
357
|
+
document.contents.flow = true;
|
|
358
|
+
return document
|
|
359
|
+
.toString({ lineWidth: 0, indent: formatting.indent, indentSeq: formatting.indentSeq })
|
|
360
|
+
.replace(/\n$/, '');
|
|
361
|
+
}
|
|
362
|
+
/** Indent every line but the first, which is spliced in after existing text. */
|
|
363
|
+
function indentContinuation(text, column) {
|
|
364
|
+
if (!text.includes('\n'))
|
|
365
|
+
return text;
|
|
366
|
+
const pad = ' '.repeat(column);
|
|
367
|
+
return text.split('\n').map((line, index) => (index === 0 || line === '' ? line : `${pad}${line}`)).join('\n');
|
|
368
|
+
}
|
|
369
|
+
const lineStart = (source, offset) => source.lastIndexOf('\n', offset - 1) + 1;
|
|
370
|
+
const columnOf = (source, offset) => offset - lineStart(source, offset);
|
|
371
|
+
function isNodeWithRange(node) {
|
|
372
|
+
return (isScalar(node) || isMap(node) || isSeq(node)) && Array.isArray(node.range);
|
|
373
|
+
}
|
|
374
|
+
function isScalarValue(value) {
|
|
375
|
+
return value === null || ['string', 'number', 'boolean'].includes(typeof value);
|
|
376
|
+
}
|
|
377
|
+
function isPlainObject(value) {
|
|
378
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
379
|
+
}
|
|
380
|
+
function hasOwn(value, key) {
|
|
381
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
382
|
+
}
|
|
383
|
+
function own(value, key) {
|
|
384
|
+
return hasOwn(value, key) ? value[key] : undefined;
|
|
385
|
+
}
|
|
386
|
+
function deepEqual(a, b) {
|
|
387
|
+
if (a === b)
|
|
388
|
+
return true;
|
|
389
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
390
|
+
return Number.isNaN(a) && Number.isNaN(b);
|
|
391
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
|
|
392
|
+
return false;
|
|
393
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
394
|
+
return false;
|
|
395
|
+
if (Array.isArray(a) && Array.isArray(b))
|
|
396
|
+
return a.length === b.length && a.every((item, index) => deepEqual(item, b[index]));
|
|
397
|
+
const [left, right] = [a, b];
|
|
398
|
+
const keys = Object.keys(left);
|
|
399
|
+
return keys.length === Object.keys(right).length
|
|
400
|
+
&& keys.every(key => hasOwn(right, key) && deepEqual(left[key], right[key]));
|
|
401
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as le,a as Ee,j as re}from"./index-
|
|
1
|
+
import{r as le,a as Ee,j as re}from"./index-CsHEL0f6.js";var ge={exports:{}},Se;function ke(){return Se||(Se=1,(function(se,ne){(function(Q,X){se.exports=X()})(globalThis,(()=>(()=>{var Q={4567:function(B,r,o){var l=this&&this.__decorate||function(e,i,a,v){var f,g=arguments.length,c=g<3?i:v===null?v=Object.getOwnPropertyDescriptor(i,a):v;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")c=Reflect.decorate(e,i,a,v);else for(var m=e.length-1;m>=0;m--)(f=e[m])&&(c=(g<3?f(c):g>3?f(i,a,c):f(i,a))||c);return g>3&&c&&Object.defineProperty(i,a,c),c},_=this&&this.__param||function(e,i){return function(a,v){i(a,v,e)}};Object.defineProperty(r,"__esModule",{value:!0}),r.AccessibilityManager=void 0;const n=o(9042),d=o(9924),u=o(844),p=o(4725),h=o(2585),t=o(3656);let s=r.AccessibilityManager=class extends u.Disposable{constructor(e,i,a,v){super(),this._terminal=e,this._coreBrowserService=a,this._renderService=v,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let f=0;f<this._terminal.rows;f++)this._rowElements[f]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[f]);if(this._topBoundaryFocusListener=f=>this._handleBoundaryFocus(f,0),this._bottomBoundaryFocusListener=f=>this._handleBoundaryFocus(f,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions(),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=this._coreBrowserService.mainDocument.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this.register(new d.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this.register(this._terminal.onResize((f=>this._handleResize(f.rows)))),this.register(this._terminal.onRender((f=>this._refreshRows(f.start,f.end)))),this.register(this._terminal.onScroll((()=>this._refreshRows()))),this.register(this._terminal.onA11yChar((f=>this._handleChar(f)))),this.register(this._terminal.onLineFeed((()=>this._handleChar(`
|
|
2
2
|
`)))),this.register(this._terminal.onA11yTab((f=>this._handleTab(f)))),this.register(this._terminal.onKey((f=>this._handleKey(f.key)))),this.register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this.register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this.register((0,t.addDisposableDomListener)(document,"selectionchange",(()=>this._handleSelectionChange()))),this.register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRows(),this.register((0,u.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let i=0;i<e;i++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,e===`
|
|
3
3
|
`&&(this._liveRegionLineCount++,this._liveRegionLineCount===21&&(this._liveRegion.textContent+=n.tooMuchOutput)))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),new RegExp("\\p{Control}","u").test(e)||this._charsToConsume.push(e)}_refreshRows(e,i){this._liveRegionDebouncer.refresh(e,i,this._terminal.rows)}_renderRows(e,i){const a=this._terminal.buffer,v=a.lines.length.toString();for(let f=e;f<=i;f++){const g=a.lines.get(a.ydisp+f),c=[],m=g?.translateToString(!0,void 0,void 0,c)||"",E=(a.ydisp+f+1).toString(),k=this._rowElements[f];k&&(m.length===0?(k.innerText=" ",this._rowColumns.set(k,[0,1])):(k.textContent=m,this._rowColumns.set(k,c)),k.setAttribute("aria-posinset",E),k.setAttribute("aria-setsize",v))}this._announceCharacters()}_announceCharacters(){this._charsToAnnounce.length!==0&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,i){const a=e.target,v=this._rowElements[i===0?1:this._rowElements.length-2];if(a.getAttribute("aria-posinset")===(i===0?"1":`${this._terminal.buffer.lines.length}`)||e.relatedTarget!==v)return;let f,g;if(i===0?(f=a,g=this._rowElements.pop(),this._rowContainer.removeChild(g)):(f=this._rowElements.shift(),g=a,this._rowContainer.removeChild(f)),f.removeEventListener("focus",this._topBoundaryFocusListener),g.removeEventListener("focus",this._bottomBoundaryFocusListener),i===0){const c=this._createAccessibilityTreeNode();this._rowElements.unshift(c),this._rowContainer.insertAdjacentElement("afterbegin",c)}else{const c=this._createAccessibilityTreeNode();this._rowElements.push(c),this._rowContainer.appendChild(c)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(i===0?-1:1),this._rowElements[i===0?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(this._rowElements.length===0)return;const e=document.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let i={node:e.anchorNode,offset:e.anchorOffset},a={node:e.focusNode,offset:e.focusOffset};if((i.node.compareDocumentPosition(a.node)&Node.DOCUMENT_POSITION_PRECEDING||i.node===a.node&&i.offset>a.offset)&&([i,a]=[a,i]),i.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(i={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(i.node))return;const v=this._rowElements.slice(-1)[0];if(a.node.compareDocumentPosition(v)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(a={node:v,offset:v.textContent?.length??0}),!this._rowContainer.contains(a.node))return;const f=({node:m,offset:E})=>{const k=m instanceof Text?m.parentNode:m;let D=parseInt(k?.getAttribute("aria-posinset"),10)-1;if(isNaN(D))return console.warn("row is invalid. Race condition?"),null;const b=this._rowColumns.get(k);if(!b)return console.warn("columns is null. Race condition?"),null;let x=E<b.length?b[E]:b.slice(-1)[0]+1;return x>=this._terminal.cols&&(++D,x=0),{row:D,column:x}},g=f(i),c=f(a);if(g&&c){if(g.row>c.row||g.row===c.row&&g.column>=c.column)throw new Error("invalid range");this._terminal.select(g.column,g.row,(c.row-g.row)*this._terminal.cols-g.column+c.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let i=this._rowContainer.children.length;i<this._terminal.rows;i++)this._rowElements[i]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[i]);for(;this._rowElements.length>e;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){this._accessibilityContainer.style.width=`${this._renderService.dimensions.css.canvas.width}px`,this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e<this._terminal.rows;e++)this._refreshRowDimensions(this._rowElements[e])}}_refreshRowDimensions(e){e.style.height=`${this._renderService.dimensions.css.cell.height}px`}};r.AccessibilityManager=s=l([_(1,h.IInstantiationService),_(2,p.ICoreBrowserService),_(3,p.IRenderService)],s)},3614:(B,r)=>{function o(d){return d.replace(/\r?\n/g,"\r")}function l(d,u){return u?"\x1B[200~"+d+"\x1B[201~":d}function _(d,u,p,h){d=l(d=o(d),p.decPrivateModes.bracketedPasteMode&&h.rawOptions.ignoreBracketedPasteMode!==!0),p.triggerDataEvent(d,!0),u.value=""}function n(d,u,p){const h=p.getBoundingClientRect(),t=d.clientX-h.left-10,s=d.clientY-h.top-10;u.style.width="20px",u.style.height="20px",u.style.left=`${t}px`,u.style.top=`${s}px`,u.style.zIndex="1000",u.focus()}Object.defineProperty(r,"__esModule",{value:!0}),r.rightClickHandler=r.moveTextAreaUnderMouseCursor=r.paste=r.handlePasteEvent=r.copyHandler=r.bracketTextForPaste=r.prepareTextForTerminal=void 0,r.prepareTextForTerminal=o,r.bracketTextForPaste=l,r.copyHandler=function(d,u){d.clipboardData&&d.clipboardData.setData("text/plain",u.selectionText),d.preventDefault()},r.handlePasteEvent=function(d,u,p,h){d.stopPropagation(),d.clipboardData&&_(d.clipboardData.getData("text/plain"),u,p,h)},r.paste=_,r.moveTextAreaUnderMouseCursor=n,r.rightClickHandler=function(d,u,p,h,t){n(d,u,p),t&&h.rightClickSelect(d),u.value=h.selectionText,u.select()}},7239:(B,r,o)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.ColorContrastCache=void 0;const l=o(1505);r.ColorContrastCache=class{constructor(){this._color=new l.TwoKeyMap,this._css=new l.TwoKeyMap}setCss(_,n,d){this._css.set(_,n,d)}getCss(_,n){return this._css.get(_,n)}setColor(_,n,d){this._color.set(_,n,d)}getColor(_,n){return this._color.get(_,n)}clear(){this._color.clear(),this._css.clear()}}},3656:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.addDisposableDomListener=void 0,r.addDisposableDomListener=function(o,l,_,n){o.addEventListener(l,_,n);let d=!1;return{dispose:()=>{d||(d=!0,o.removeEventListener(l,_,n))}}}},3551:function(B,r,o){var l=this&&this.__decorate||function(s,e,i,a){var v,f=arguments.length,g=f<3?e:a===null?a=Object.getOwnPropertyDescriptor(e,i):a;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")g=Reflect.decorate(s,e,i,a);else for(var c=s.length-1;c>=0;c--)(v=s[c])&&(g=(f<3?v(g):f>3?v(e,i,g):v(e,i))||g);return f>3&&g&&Object.defineProperty(e,i,g),g},_=this&&this.__param||function(s,e){return function(i,a){e(i,a,s)}};Object.defineProperty(r,"__esModule",{value:!0}),r.Linkifier=void 0;const n=o(3656),d=o(8460),u=o(844),p=o(2585),h=o(4725);let t=r.Linkifier=class extends u.Disposable{get currentLink(){return this._currentLink}constructor(s,e,i,a,v){super(),this._element=s,this._mouseService=e,this._renderService=i,this._bufferService=a,this._linkProviderService=v,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this.register(new d.EventEmitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this.register(new d.EventEmitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this.register((0,u.getDisposeArrayDisposable)(this._linkCacheDisposables)),this.register((0,u.toDisposable)((()=>{this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this.register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this.register((0,n.addDisposableDomListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this.register((0,n.addDisposableDomListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this.register((0,n.addDisposableDomListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(s){this._lastMouseEvent=s;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);if(!e)return;this._isMouseOut=!1;const i=s.composedPath();for(let a=0;a<i.length;a++){const v=i[a];if(v.classList.contains("xterm"))break;if(v.classList.contains("xterm-hover"))return}this._lastBufferCell&&e.x===this._lastBufferCell.x&&e.y===this._lastBufferCell.y||(this._handleHover(e),this._lastBufferCell=e)}_handleHover(s){if(this._activeLine!==s.y||this._wasResized)return this._clearCurrentLink(),this._askForLink(s,!1),void(this._wasResized=!1);this._currentLink&&this._linkAtPosition(this._currentLink.link,s)||(this._clearCurrentLink(),this._askForLink(s,!0))}_askForLink(s,e){this._activeProviderReplies&&e||(this._activeProviderReplies?.forEach((a=>{a?.forEach((v=>{v.link.dispose&&v.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=s.y);let i=!1;for(const[a,v]of this._linkProviderService.linkProviders.entries())e?this._activeProviderReplies?.get(a)&&(i=this._checkLinkProviderResult(a,s,i)):v.provideLinks(s.y,(f=>{if(this._isMouseOut)return;const g=f?.map((c=>({link:c})));this._activeProviderReplies?.set(a,g),i=this._checkLinkProviderResult(a,s,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(s.y,this._activeProviderReplies)}))}_removeIntersectingLinks(s,e){const i=new Set;for(let a=0;a<e.size;a++){const v=e.get(a);if(v)for(let f=0;f<v.length;f++){const g=v[f],c=g.link.range.start.y<s?0:g.link.range.start.x,m=g.link.range.end.y>s?this._bufferService.cols:g.link.range.end.x;for(let E=c;E<=m;E++){if(i.has(E)){v.splice(f--,1);break}i.add(E)}}}}_checkLinkProviderResult(s,e,i){if(!this._activeProviderReplies)return i;const a=this._activeProviderReplies.get(s);let v=!1;for(let f=0;f<s;f++)this._activeProviderReplies.has(f)&&!this._activeProviderReplies.get(f)||(v=!0);if(!v&&a){const f=a.find((g=>this._linkAtPosition(g.link,e)));f&&(i=!0,this._handleNewLink(f))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let f=0;f<this._activeProviderReplies.size;f++){const g=this._activeProviderReplies.get(f)?.find((c=>this._linkAtPosition(c.link,e)));if(g){i=!0,this._handleNewLink(g);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(s){if(!this._currentLink)return;const e=this._positionFromMouseEvent(s,this._element,this._mouseService);e&&this._mouseDownLink===this._currentLink&&this._linkAtPosition(this._currentLink.link,e)&&this._currentLink.link.activate(s,this._currentLink.link.text)}_clearCurrentLink(s,e){this._currentLink&&this._lastMouseEvent&&(!s||!e||this._currentLink.link.range.start.y>=s&&this._currentLink.link.range.end.y<=e)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,u.disposeArray)(this._linkCacheDisposables))}_handleNewLink(s){if(!this._lastMouseEvent)return;const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._linkAtPosition(s.link,e)&&(this._currentLink=s,this._currentLink.state={decorations:{underline:s.link.decorations===void 0||s.link.decorations.underline,pointerCursor:s.link.decorations===void 0||s.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,s.link,this._lastMouseEvent),s.link.decorations={},Object.defineProperties(s.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:i=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==i&&(this._currentLink.state.decorations.pointerCursor=i,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",i))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:i=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==i&&(this._currentLink.state.decorations.underline=i,this._currentLink.state.isHovered&&this._fireUnderlineEvent(s.link,i))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((i=>{if(!this._currentLink)return;const a=i.start===0?0:i.start+1+this._bufferService.buffer.ydisp,v=this._bufferService.buffer.ydisp+1+i.end;if(this._currentLink.link.range.start.y>=a&&this._currentLink.link.range.end.y<=v&&(this._clearCurrentLink(a,v),this._lastMouseEvent)){const f=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);f&&this._askForLink(f,!1)}}))))}_linkHover(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!0),this._currentLink.state.decorations.pointerCursor&&s.classList.add("xterm-cursor-pointer")),e.hover&&e.hover(i,e.text)}_fireUnderlineEvent(s,e){const i=s.range,a=this._bufferService.buffer.ydisp,v=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-a-1,i.end.x,i.end.y-a-1,void 0);(e?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(v)}_linkLeave(s,e,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(e,!1),this._currentLink.state.decorations.pointerCursor&&s.classList.remove("xterm-cursor-pointer")),e.leave&&e.leave(i,e.text)}_linkAtPosition(s,e){const i=s.range.start.y*this._bufferService.cols+s.range.start.x,a=s.range.end.y*this._bufferService.cols+s.range.end.x,v=e.y*this._bufferService.cols+e.x;return i<=v&&v<=a}_positionFromMouseEvent(s,e,i){const a=i.getCoords(s,e,this._bufferService.cols,this._bufferService.rows);if(a)return{x:a[0],y:a[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(s,e,i,a,v){return{x1:s,y1:e,x2:i,y2:a,cols:this._bufferService.cols,fg:v}}};r.Linkifier=t=l([_(1,h.IMouseService),_(2,h.IRenderService),_(3,p.IBufferService),_(4,h.ILinkProviderService)],t)},9042:(B,r)=>{Object.defineProperty(r,"__esModule",{value:!0}),r.tooMuchOutput=r.promptLabel=void 0,r.promptLabel="Terminal input",r.tooMuchOutput="Too much output to announce, navigate to rows manually to read"},3730:function(B,r,o){var l=this&&this.__decorate||function(h,t,s,e){var i,a=arguments.length,v=a<3?t:e===null?e=Object.getOwnPropertyDescriptor(t,s):e;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")v=Reflect.decorate(h,t,s,e);else for(var f=h.length-1;f>=0;f--)(i=h[f])&&(v=(a<3?i(v):a>3?i(t,s,v):i(t,s))||v);return a>3&&v&&Object.defineProperty(t,s,v),v},_=this&&this.__param||function(h,t){return function(s,e){t(s,e,h)}};Object.defineProperty(r,"__esModule",{value:!0}),r.OscLinkProvider=void 0;const n=o(511),d=o(2585);let u=r.OscLinkProvider=class{constructor(h,t,s){this._bufferService=h,this._optionsService=t,this._oscLinkService=s}provideLinks(h,t){const s=this._bufferService.buffer.lines.get(h-1);if(!s)return void t(void 0);const e=[],i=this._optionsService.rawOptions.linkHandler,a=new n.CellData,v=s.getTrimmedLength();let f=-1,g=-1,c=!1;for(let m=0;m<v;m++)if(g!==-1||s.hasContent(m)){if(s.loadCell(m,a),a.hasExtendedAttrs()&&a.extended.urlId){if(g===-1){g=m,f=a.extended.urlId;continue}c=a.extended.urlId!==f}else g!==-1&&(c=!0);if(c||g!==-1&&m===v-1){const E=this._oscLinkService.getLinkData(f)?.uri;if(E){const k={start:{x:g+1,y:h},end:{x:m+(c||m!==v-1?0:1),y:h}};let D=!1;if(!i?.allowNonHttpProtocols)try{const b=new URL(E);["http:","https:"].includes(b.protocol)||(D=!0)}catch{D=!0}D||e.push({text:E,range:k,activate:(b,x)=>i?i.activate(b,x,k):p(0,x),hover:(b,x)=>i?.hover?.(b,x,k),leave:(b,x)=>i?.leave?.(b,x,k)})}c=!1,a.hasExtendedAttrs()&&a.extended.urlId?(g=m,f=a.extended.urlId):(g=-1,f=-1)}}t(e)}};function p(h,t){if(confirm(`Do you want to navigate to ${t}?
|
|
4
4
|
|