@astryx-vue/lab 0.1.1 → 0.2.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 +1 -0
- package/dist/index.d.ts +278 -15
- package/dist/index.js +1546 -31
- package/lab.css +1 -0
- package/package.json +3 -3
- package/styles/diff-viewer.css +340 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, h, mergeModels, mergeProps, nextTick, normalizeClass, normalizeStyle, onScopeDispose, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, useId, useModel, useTemplateRef, watch, withCtx } from "vue";
|
|
1
|
+
import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, getCurrentInstance, guardReactiveProps, h, mergeModels, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onScopeDispose, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useAttrs, useId, useModel, useSlots, useTemplateRef, watch, withCtx } from "vue";
|
|
2
2
|
import "../styles/circular-progress.css";
|
|
3
|
-
import { AlertDialog, BottomSheet, Button, CheckboxIndicator, ComplexSelector, EmptyState, Field, FieldStatus, Icon, IconButton, Item, List, Text, TextInput, Token, Tooltip, VisuallyHidden, useAnnounce, useTranslator } from "@astryx-vue/core";
|
|
3
|
+
import { AlertDialog, BottomSheet, Button, CheckboxIndicator, ComplexSelector, EmptyState, Field, FieldStatus, Icon, IconButton, Item, List, SyntaxTheme, Text, TextInput, Token, Tooltip, VisuallyHidden, tokenize, useAnnounce, useScrollableArea, useTranslator } from "@astryx-vue/core";
|
|
4
|
+
import "../styles/diff-viewer.css";
|
|
4
5
|
import "../styles/info-tip.css";
|
|
5
6
|
import "../styles/list-input.css";
|
|
6
7
|
import "../styles/log-stream.css";
|
|
@@ -31,9 +32,9 @@ function defaultFormatValueLabel(value, max) {
|
|
|
31
32
|
}
|
|
32
33
|
//#endregion
|
|
33
34
|
//#region src/CircularProgress/CircularProgress.vue
|
|
34
|
-
const _hoisted_1$
|
|
35
|
-
const _hoisted_2$
|
|
36
|
-
const _hoisted_3$
|
|
35
|
+
const _hoisted_1$13 = ["data-size", "data-variant"];
|
|
36
|
+
const _hoisted_2$12 = { class: "astryx-circular-progress__ring" };
|
|
37
|
+
const _hoisted_3$11 = [
|
|
37
38
|
"aria-valuenow",
|
|
38
39
|
"aria-valuemin",
|
|
39
40
|
"aria-valuemax",
|
|
@@ -42,21 +43,21 @@ const _hoisted_3$9 = [
|
|
|
42
43
|
"height",
|
|
43
44
|
"viewBox"
|
|
44
45
|
];
|
|
45
|
-
const _hoisted_4$
|
|
46
|
+
const _hoisted_4$10 = [
|
|
46
47
|
"data-variant",
|
|
47
48
|
"cx",
|
|
48
49
|
"cy",
|
|
49
50
|
"r",
|
|
50
51
|
"stroke-width"
|
|
51
52
|
];
|
|
52
|
-
const _hoisted_5$
|
|
53
|
+
const _hoisted_5$9 = [
|
|
53
54
|
"data-variant",
|
|
54
55
|
"cx",
|
|
55
56
|
"cy",
|
|
56
57
|
"r",
|
|
57
58
|
"stroke-width"
|
|
58
59
|
];
|
|
59
|
-
const _hoisted_6$
|
|
60
|
+
const _hoisted_6$8 = [
|
|
60
61
|
"data-variant",
|
|
61
62
|
"cx",
|
|
62
63
|
"cy",
|
|
@@ -65,7 +66,7 @@ const _hoisted_6$6 = [
|
|
|
65
66
|
"stroke-dasharray",
|
|
66
67
|
"stroke-dashoffset"
|
|
67
68
|
];
|
|
68
|
-
const _hoisted_7$
|
|
69
|
+
const _hoisted_7$6 = {
|
|
69
70
|
key: 0,
|
|
70
71
|
class: "astryx-circular-progress__center"
|
|
71
72
|
};
|
|
@@ -137,7 +138,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
137
138
|
"astryx-circular-progress__visually-hidden": !showLabel.value,
|
|
138
139
|
"astryx-circular-progress__label--disabled": showLabel.value && __props.isDisabled
|
|
139
140
|
}])
|
|
140
|
-
}, toDisplayString(__props.label), 3), createElementVNode("div", _hoisted_2$
|
|
141
|
+
}, toDisplayString(__props.label), 3), createElementVNode("div", _hoisted_2$12, [(openBlock(), createElementBlock("svg", {
|
|
141
142
|
class: normalizeClass(["astryx-circular-progress__svg", { "astryx-circular-progress__svg--indeterminate": __props.isIndeterminate }]),
|
|
142
143
|
role: "progressbar",
|
|
143
144
|
"aria-labelledby": labelId,
|
|
@@ -155,7 +156,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
155
156
|
cy: geometry.value.center,
|
|
156
157
|
r: geometry.value.radius,
|
|
157
158
|
"stroke-width": geometry.value.strokeWidth
|
|
158
|
-
}, null, 8, _hoisted_4$
|
|
159
|
+
}, null, 8, _hoisted_4$10), __props.isIndeterminate ? (openBlock(), createElementBlock("circle", {
|
|
159
160
|
key: 0,
|
|
160
161
|
class: "astryx-circular-progress__fill astryx-circular-progress__fill--indeterminate",
|
|
161
162
|
"data-variant": fillVariant.value,
|
|
@@ -163,7 +164,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
163
164
|
cy: geometry.value.center,
|
|
164
165
|
r: geometry.value.radius,
|
|
165
166
|
"stroke-width": geometry.value.strokeWidth
|
|
166
|
-
}, null, 8, _hoisted_5$
|
|
167
|
+
}, null, 8, _hoisted_5$9)) : (openBlock(), createElementBlock("circle", {
|
|
167
168
|
key: 1,
|
|
168
169
|
class: "astryx-circular-progress__fill",
|
|
169
170
|
"data-variant": fillVariant.value,
|
|
@@ -173,17 +174,1531 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
173
174
|
"stroke-width": geometry.value.strokeWidth,
|
|
174
175
|
"stroke-dasharray": geometry.value.circumference,
|
|
175
176
|
"stroke-dashoffset": dashoffset.value
|
|
176
|
-
}, null, 8, _hoisted_6$
|
|
177
|
+
}, null, 8, _hoisted_6$8))], 10, _hoisted_3$11)), _ctx.$slots.default || hasCenterContent.value ? (openBlock(), createElementBlock("div", _hoisted_7$6, [renderSlot(_ctx.$slots, "default", {}, () => [hasCenterContent.value ? (openBlock(), createElementBlock("span", {
|
|
177
178
|
key: 0,
|
|
178
179
|
class: normalizeClass(["astryx-circular-progress__value", { "astryx-circular-progress__value--disabled": __props.isDisabled }])
|
|
179
|
-
}, toDisplayString(valueText.value), 3)) : createCommentVNode("v-if", true)])])) : createCommentVNode("v-if", true)])], 16, _hoisted_1$
|
|
180
|
+
}, toDisplayString(valueText.value), 3)) : createCommentVNode("v-if", true)])])) : createCommentVNode("v-if", true)])], 16, _hoisted_1$13);
|
|
180
181
|
};
|
|
181
182
|
}
|
|
182
183
|
});
|
|
183
184
|
//#endregion
|
|
185
|
+
//#region src/DiffViewer/createDiffFile.ts
|
|
186
|
+
function splitLines(value) {
|
|
187
|
+
if (value === "") return [];
|
|
188
|
+
const lines = value.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
189
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
190
|
+
return lines;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Most changed lines the minimal search explores before giving up. The trace
|
|
194
|
+
* it keeps grows with the square of this, so the cap bounds memory to a few
|
|
195
|
+
* megabytes; past it the changed middle is shown as one replaced block.
|
|
196
|
+
*/
|
|
197
|
+
const MAX_EDIT_DISTANCE = 2e3;
|
|
198
|
+
/**
|
|
199
|
+
* Line edit script from `a` to `b`. Shared leading and trailing lines are
|
|
200
|
+
* context without a search; the middle uses Myers' O(ND) shortest edit script,
|
|
201
|
+
* or, when it needs more than MAX_EDIT_DISTANCE edits, removes the old middle
|
|
202
|
+
* and adds the new one.
|
|
203
|
+
*/
|
|
204
|
+
function diffLines(a, b) {
|
|
205
|
+
let head = 0;
|
|
206
|
+
while (head < a.length && head < b.length && a[head] === b[head]) head++;
|
|
207
|
+
let tailA = a.length;
|
|
208
|
+
let tailB = b.length;
|
|
209
|
+
while (tailA > head && tailB > head && a[tailA - 1] === b[tailB - 1]) {
|
|
210
|
+
tailA--;
|
|
211
|
+
tailB--;
|
|
212
|
+
}
|
|
213
|
+
const ops = [];
|
|
214
|
+
for (let i = 0; i < head; i++) ops.push({
|
|
215
|
+
kind: "context",
|
|
216
|
+
a: i,
|
|
217
|
+
b: i
|
|
218
|
+
});
|
|
219
|
+
const middle = myers(a, b, head, tailA, tailB);
|
|
220
|
+
if (middle != null) for (const op of middle) ops.push(op);
|
|
221
|
+
else {
|
|
222
|
+
for (let i = head; i < tailA; i++) ops.push({
|
|
223
|
+
kind: "removed",
|
|
224
|
+
a: i
|
|
225
|
+
});
|
|
226
|
+
for (let j = head; j < tailB; j++) ops.push({
|
|
227
|
+
kind: "added",
|
|
228
|
+
b: j
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
for (let i = 0; tailA + i < a.length; i++) ops.push({
|
|
232
|
+
kind: "context",
|
|
233
|
+
a: tailA + i,
|
|
234
|
+
b: tailB + i
|
|
235
|
+
});
|
|
236
|
+
return ops;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Myers' shortest edit script over `a[start, endA)` and `b[start, endB)`,
|
|
240
|
+
* with absolute indexes in the ops; null when it needs more than
|
|
241
|
+
* MAX_EDIT_DISTANCE edits.
|
|
242
|
+
*/
|
|
243
|
+
function myers(a, b, start, endA, endB) {
|
|
244
|
+
const n = endA - start;
|
|
245
|
+
const m = endB - start;
|
|
246
|
+
const max = n + m;
|
|
247
|
+
const offset = max + 1;
|
|
248
|
+
const v = new Int32Array(2 * max + 3);
|
|
249
|
+
const trace = [];
|
|
250
|
+
let done = false;
|
|
251
|
+
for (let d = 0; d <= max && !done; d++) {
|
|
252
|
+
if (d > MAX_EDIT_DISTANCE) return null;
|
|
253
|
+
trace.push(v.slice(offset - d, offset + d + 1));
|
|
254
|
+
for (let k = -d; k <= d; k += 2) {
|
|
255
|
+
let x = k === -d || k !== d && v[offset + k - 1] < v[offset + k + 1] ? v[offset + k + 1] : v[offset + k - 1] + 1;
|
|
256
|
+
let y = x - k;
|
|
257
|
+
while (x < n && y < m && a[start + x] === b[start + y]) {
|
|
258
|
+
x++;
|
|
259
|
+
y++;
|
|
260
|
+
}
|
|
261
|
+
v[offset + k] = x;
|
|
262
|
+
if (x >= n && y >= m) {
|
|
263
|
+
done = true;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const ops = [];
|
|
269
|
+
let x = n;
|
|
270
|
+
let y = m;
|
|
271
|
+
for (let d = trace.length - 1; d >= 0; d--) {
|
|
272
|
+
const vd = trace[d];
|
|
273
|
+
const at = (k) => vd[k + d] ?? 0;
|
|
274
|
+
const k = x - y;
|
|
275
|
+
const prevK = k === -d || k !== d && at(k - 1) < at(k + 1) ? k + 1 : k - 1;
|
|
276
|
+
const prevX = at(prevK);
|
|
277
|
+
const prevY = prevX - prevK;
|
|
278
|
+
while (x > prevX && y > prevY) {
|
|
279
|
+
x--;
|
|
280
|
+
y--;
|
|
281
|
+
ops.push({
|
|
282
|
+
kind: "context",
|
|
283
|
+
a: start + x,
|
|
284
|
+
b: start + y
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
if (d > 0) {
|
|
288
|
+
if (x === prevX) ops.push({
|
|
289
|
+
kind: "added",
|
|
290
|
+
b: start + prevY
|
|
291
|
+
});
|
|
292
|
+
else ops.push({
|
|
293
|
+
kind: "removed",
|
|
294
|
+
a: start + prevX
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
x = prevX;
|
|
298
|
+
y = prevY;
|
|
299
|
+
}
|
|
300
|
+
ops.reverse();
|
|
301
|
+
return ops;
|
|
302
|
+
}
|
|
303
|
+
function formatRange(start, count) {
|
|
304
|
+
return count === 1 ? `${start}` : `${start},${count}`;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Compute a {@link DiffViewerFile} from two versions of a text, for callers
|
|
308
|
+
* that hold file contents rather than a patch.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```
|
|
312
|
+
* const file = createDiffFile({oldValue: before, newValue: after, path: 'src/app.ts'});
|
|
313
|
+
* <DiffViewer file={file} />
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
function createDiffFile({ oldValue, newValue, path, oldPath: oldPathOption, newPath: newPathOption, contextLines = 3 }) {
|
|
317
|
+
const oldPath = oldPathOption === void 0 ? path ?? null : oldPathOption;
|
|
318
|
+
const newPath = newPathOption === void 0 ? path ?? null : newPathOption;
|
|
319
|
+
const context = Number.isNaN(contextLines) ? 3 : Math.max(0, Math.floor(contextLines));
|
|
320
|
+
let status = "modified";
|
|
321
|
+
if (oldPathOption === null) status = "added";
|
|
322
|
+
else if (newPathOption === null) status = "deleted";
|
|
323
|
+
else if (oldPath != null && newPath != null && oldPath !== newPath) status = "renamed";
|
|
324
|
+
const a = splitLines(oldValue);
|
|
325
|
+
const b = splitLines(newValue);
|
|
326
|
+
const ops = diffLines(a, b);
|
|
327
|
+
const changed = [];
|
|
328
|
+
ops.forEach((op, i) => {
|
|
329
|
+
if (op.kind !== "context") changed.push(i);
|
|
330
|
+
});
|
|
331
|
+
const hunks = [];
|
|
332
|
+
let g = 0;
|
|
333
|
+
while (g < changed.length) {
|
|
334
|
+
let last = changed[g];
|
|
335
|
+
let h = g + 1;
|
|
336
|
+
while (h < changed.length && changed[h] - last - 1 <= context * 2) {
|
|
337
|
+
last = changed[h];
|
|
338
|
+
h++;
|
|
339
|
+
}
|
|
340
|
+
const start = Math.max(0, changed[g] - context);
|
|
341
|
+
const end = Math.min(ops.length - 1, last + context);
|
|
342
|
+
const lines = [];
|
|
343
|
+
let oldCount = 0;
|
|
344
|
+
let newCount = 0;
|
|
345
|
+
for (let i = start; i <= end; i++) {
|
|
346
|
+
const op = ops[i];
|
|
347
|
+
if (op.kind === "context") {
|
|
348
|
+
lines.push({
|
|
349
|
+
kind: "context",
|
|
350
|
+
content: a[op.a],
|
|
351
|
+
oldLineNumber: op.a + 1,
|
|
352
|
+
newLineNumber: op.b + 1
|
|
353
|
+
});
|
|
354
|
+
oldCount++;
|
|
355
|
+
newCount++;
|
|
356
|
+
} else if (op.kind === "removed") {
|
|
357
|
+
lines.push({
|
|
358
|
+
kind: "removed",
|
|
359
|
+
content: a[op.a],
|
|
360
|
+
oldLineNumber: op.a + 1,
|
|
361
|
+
newLineNumber: null
|
|
362
|
+
});
|
|
363
|
+
oldCount++;
|
|
364
|
+
} else {
|
|
365
|
+
lines.push({
|
|
366
|
+
kind: "added",
|
|
367
|
+
content: b[op.b],
|
|
368
|
+
oldLineNumber: null,
|
|
369
|
+
newLineNumber: op.b + 1
|
|
370
|
+
});
|
|
371
|
+
newCount++;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
let oldBefore = 0;
|
|
375
|
+
let newBefore = 0;
|
|
376
|
+
for (let i = 0; i < start; i++) {
|
|
377
|
+
const op = ops[i];
|
|
378
|
+
if (op.kind !== "added") oldBefore++;
|
|
379
|
+
if (op.kind !== "removed") newBefore++;
|
|
380
|
+
}
|
|
381
|
+
const oldStart = oldCount === 0 ? oldBefore : oldBefore + 1;
|
|
382
|
+
const newStart = newCount === 0 ? newBefore : newBefore + 1;
|
|
383
|
+
hunks.push({
|
|
384
|
+
header: `@@ -${formatRange(oldStart, oldCount)} +${formatRange(newStart, newCount)} @@`,
|
|
385
|
+
oldStart,
|
|
386
|
+
oldLines: oldCount,
|
|
387
|
+
newStart,
|
|
388
|
+
newLines: newCount,
|
|
389
|
+
lines
|
|
390
|
+
});
|
|
391
|
+
g = h;
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
oldPath: status === "added" ? null : oldPath,
|
|
395
|
+
newPath: status === "deleted" ? null : newPath,
|
|
396
|
+
status,
|
|
397
|
+
isBinary: false,
|
|
398
|
+
hunks
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/DiffViewer/RenderVNode.ts
|
|
403
|
+
var RenderVNode_default = defineComponent({
|
|
404
|
+
name: "AstryxDiffViewerRenderVNode",
|
|
405
|
+
props: { content: { required: true } },
|
|
406
|
+
setup(props) {
|
|
407
|
+
return () => props.content;
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/DiffViewer/DiffViewerTable.vue
|
|
412
|
+
const _hoisted_1$12 = {
|
|
413
|
+
key: 0,
|
|
414
|
+
scope: "col",
|
|
415
|
+
class: "astryx-diff-viewer__column-header"
|
|
416
|
+
};
|
|
417
|
+
const _hoisted_2$11 = {
|
|
418
|
+
key: 1,
|
|
419
|
+
scope: "col",
|
|
420
|
+
class: "astryx-diff-viewer__column-header"
|
|
421
|
+
};
|
|
422
|
+
const _hoisted_3$10 = {
|
|
423
|
+
scope: "col",
|
|
424
|
+
class: "astryx-diff-viewer__column-header"
|
|
425
|
+
};
|
|
426
|
+
const _hoisted_4$9 = {
|
|
427
|
+
scope: "col",
|
|
428
|
+
class: "astryx-diff-viewer__column-header"
|
|
429
|
+
};
|
|
430
|
+
const _hoisted_5$8 = { key: 0 };
|
|
431
|
+
const _hoisted_6$7 = ["colspan"];
|
|
432
|
+
const _hoisted_7$5 = { class: "astryx-diff-viewer__ltr" };
|
|
433
|
+
const _hoisted_8$4 = ["aria-label", "onClick"];
|
|
434
|
+
const _hoisted_9$4 = ["aria-label", "onClick"];
|
|
435
|
+
const _hoisted_10$4 = ["data-diff"];
|
|
436
|
+
const _hoisted_11$2 = { class: "astryx-diff-viewer__ltr" };
|
|
437
|
+
const _hoisted_12$1 = ["data-kind"];
|
|
438
|
+
const _hoisted_13 = { key: 0 };
|
|
439
|
+
const _hoisted_14 = ["colspan"];
|
|
440
|
+
const _hoisted_15 = { class: "astryx-diff-viewer__annotation-content" };
|
|
441
|
+
const _hoisted_16 = {
|
|
442
|
+
key: 0,
|
|
443
|
+
class: "astryx-diff-viewer__gutter-col"
|
|
444
|
+
};
|
|
445
|
+
const _hoisted_17 = {
|
|
446
|
+
key: 1,
|
|
447
|
+
class: "astryx-diff-viewer__gutter-col"
|
|
448
|
+
};
|
|
449
|
+
const _hoisted_18 = {
|
|
450
|
+
key: 0,
|
|
451
|
+
scope: "col",
|
|
452
|
+
class: "astryx-diff-viewer__column-header"
|
|
453
|
+
};
|
|
454
|
+
const _hoisted_19 = {
|
|
455
|
+
scope: "col",
|
|
456
|
+
class: "astryx-diff-viewer__column-header"
|
|
457
|
+
};
|
|
458
|
+
const _hoisted_20 = {
|
|
459
|
+
scope: "col",
|
|
460
|
+
class: "astryx-diff-viewer__column-header"
|
|
461
|
+
};
|
|
462
|
+
const _hoisted_21 = {
|
|
463
|
+
key: 1,
|
|
464
|
+
scope: "col",
|
|
465
|
+
class: "astryx-diff-viewer__column-header"
|
|
466
|
+
};
|
|
467
|
+
const _hoisted_22 = {
|
|
468
|
+
scope: "col",
|
|
469
|
+
class: "astryx-diff-viewer__column-header"
|
|
470
|
+
};
|
|
471
|
+
const _hoisted_23 = {
|
|
472
|
+
scope: "col",
|
|
473
|
+
class: "astryx-diff-viewer__column-header"
|
|
474
|
+
};
|
|
475
|
+
const _hoisted_24 = { key: 0 };
|
|
476
|
+
const _hoisted_25 = ["colspan"];
|
|
477
|
+
const _hoisted_26 = { class: "astryx-diff-viewer__ltr" };
|
|
478
|
+
const _hoisted_27 = ["aria-label", "onClick"];
|
|
479
|
+
const _hoisted_28 = ["data-diff"];
|
|
480
|
+
const _hoisted_29 = { class: "astryx-diff-viewer__ltr" };
|
|
481
|
+
const _hoisted_30 = ["data-kind"];
|
|
482
|
+
const _hoisted_31 = ["aria-label", "onClick"];
|
|
483
|
+
const _hoisted_32 = ["data-diff"];
|
|
484
|
+
const _hoisted_33 = { class: "astryx-diff-viewer__ltr" };
|
|
485
|
+
const _hoisted_34 = ["data-kind"];
|
|
486
|
+
const _hoisted_35 = { key: 0 };
|
|
487
|
+
const _hoisted_36 = ["colspan"];
|
|
488
|
+
const _hoisted_37 = {
|
|
489
|
+
key: 0,
|
|
490
|
+
class: "astryx-diff-viewer__annotation-content"
|
|
491
|
+
};
|
|
492
|
+
const _hoisted_38 = ["colspan"];
|
|
493
|
+
const _hoisted_39 = {
|
|
494
|
+
key: 0,
|
|
495
|
+
class: "astryx-diff-viewer__annotation-content"
|
|
496
|
+
};
|
|
497
|
+
const _sfc_main$13 = /*@__PURE__*/ defineComponent({
|
|
498
|
+
name: "AstryxDiffViewerTable",
|
|
499
|
+
__name: "DiffViewerTable",
|
|
500
|
+
props: {
|
|
501
|
+
viewMode: {},
|
|
502
|
+
unifiedRows: {},
|
|
503
|
+
splitRows: {},
|
|
504
|
+
hasLineNumbers: { type: Boolean },
|
|
505
|
+
isWrapped: { type: Boolean },
|
|
506
|
+
hasLineNumberClick: { type: Boolean },
|
|
507
|
+
digits: {}
|
|
508
|
+
},
|
|
509
|
+
emits: ["lineNumberClick"],
|
|
510
|
+
setup(__props, { emit: __emit }) {
|
|
511
|
+
const props = __props;
|
|
512
|
+
const emit = __emit;
|
|
513
|
+
const unifiedColumns = computed(() => (props.hasLineNumbers ? 2 : 0) + 2);
|
|
514
|
+
const splitSideColumns = computed(() => (props.hasLineNumbers ? 1 : 0) + 2);
|
|
515
|
+
const tableStyle = computed(() => ({ "--astryx-diff-viewer-gutter-width": `${props.digits}ch` }));
|
|
516
|
+
function lineNumber(cell, side) {
|
|
517
|
+
if (cell == null) return null;
|
|
518
|
+
return side === "old" ? cell.line.oldLineNumber : cell.line.newLineNumber;
|
|
519
|
+
}
|
|
520
|
+
/** Only the gutter of the line's own anchor side is a button. */
|
|
521
|
+
function lineAction(cell, side) {
|
|
522
|
+
if (cell == null || cell.ref.side !== side || lineNumber(cell, side) == null) return null;
|
|
523
|
+
return cell.ref;
|
|
524
|
+
}
|
|
525
|
+
function emitLineNumberClick(cell, side, event) {
|
|
526
|
+
const action = lineAction(cell, side);
|
|
527
|
+
if (action != null) emit("lineNumberClick", action, event);
|
|
528
|
+
}
|
|
529
|
+
function lineNumberButtonLabel(side, number) {
|
|
530
|
+
return `${side === "old" ? "Original" : "Changed"} line ${number ?? ""}`;
|
|
531
|
+
}
|
|
532
|
+
function diffKind(cell) {
|
|
533
|
+
const kind = cell?.line.kind;
|
|
534
|
+
return kind !== "context" ? kind : void 0;
|
|
535
|
+
}
|
|
536
|
+
function cellClass(cell) {
|
|
537
|
+
return {
|
|
538
|
+
"is-added": cell?.line.kind === "added",
|
|
539
|
+
"is-removed": cell?.line.kind === "removed",
|
|
540
|
+
"is-filler": cell == null
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function oldNote(row) {
|
|
544
|
+
if (row.type !== "pair" || row.old == null || row.old.line.kind === "context") return null;
|
|
545
|
+
return row.old.note ?? null;
|
|
546
|
+
}
|
|
547
|
+
function newNote(row) {
|
|
548
|
+
return row.type === "pair" ? row.new?.note ?? null : null;
|
|
549
|
+
}
|
|
550
|
+
return (_ctx, _cache) => {
|
|
551
|
+
return __props.viewMode === "unified" ? (openBlock(), createElementBlock("table", {
|
|
552
|
+
key: 0,
|
|
553
|
+
class: normalizeClass(["astryx-diff-viewer__table", { "astryx-diff-viewer__table--wrapped": __props.isWrapped }]),
|
|
554
|
+
style: normalizeStyle(tableStyle.value)
|
|
555
|
+
}, [
|
|
556
|
+
createElementVNode("colgroup", null, [
|
|
557
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[0] || (_cache[0] = createElementVNode("col", { class: "astryx-diff-viewer__gutter-col" }, null, -1)), _cache[1] || (_cache[1] = createElementVNode("col", { class: "astryx-diff-viewer__gutter-col" }, null, -1))], 64)) : createCommentVNode("v-if", true),
|
|
558
|
+
_cache[2] || (_cache[2] = createElementVNode("col", { class: "astryx-diff-viewer__marker-col" }, null, -1)),
|
|
559
|
+
_cache[3] || (_cache[3] = createElementVNode("col", null, null, -1))
|
|
560
|
+
]),
|
|
561
|
+
createElementVNode("thead", null, [createElementVNode("tr", null, [
|
|
562
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("th", _hoisted_1$12, [createVNode(unref(VisuallyHidden), null, {
|
|
563
|
+
default: withCtx(() => [..._cache[4] || (_cache[4] = [createTextVNode("Original line number", -1)])]),
|
|
564
|
+
_: 1
|
|
565
|
+
})])) : createCommentVNode("v-if", true),
|
|
566
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("th", _hoisted_2$11, [createVNode(unref(VisuallyHidden), null, {
|
|
567
|
+
default: withCtx(() => [..._cache[5] || (_cache[5] = [createTextVNode("Changed line number", -1)])]),
|
|
568
|
+
_: 1
|
|
569
|
+
})])) : createCommentVNode("v-if", true),
|
|
570
|
+
createElementVNode("th", _hoisted_3$10, [createVNode(unref(VisuallyHidden), null, {
|
|
571
|
+
default: withCtx(() => [..._cache[6] || (_cache[6] = [createTextVNode("Change", -1)])]),
|
|
572
|
+
_: 1
|
|
573
|
+
})]),
|
|
574
|
+
createElementVNode("th", _hoisted_4$9, [createVNode(unref(VisuallyHidden), null, {
|
|
575
|
+
default: withCtx(() => [..._cache[7] || (_cache[7] = [createTextVNode("Code", -1)])]),
|
|
576
|
+
_: 1
|
|
577
|
+
})])
|
|
578
|
+
])]),
|
|
579
|
+
createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.unifiedRows, (row) => {
|
|
580
|
+
return openBlock(), createElementBlock(Fragment, { key: row.key }, [row.type === "hunk" ? (openBlock(), createElementBlock("tr", _hoisted_5$8, [createElementVNode("td", {
|
|
581
|
+
colspan: unifiedColumns.value,
|
|
582
|
+
class: "astryx-diff-viewer__hunk"
|
|
583
|
+
}, [createElementVNode("span", _hoisted_7$5, toDisplayString(row.header), 1)], 8, _hoisted_6$7)])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("tr", null, [
|
|
584
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("td", {
|
|
585
|
+
key: 0,
|
|
586
|
+
class: normalizeClass(["astryx-diff-viewer__gutter", cellClass(row.cell)])
|
|
587
|
+
}, [__props.hasLineNumberClick && lineAction(row.cell, "old") != null ? (openBlock(), createElementBlock("button", {
|
|
588
|
+
key: 0,
|
|
589
|
+
type: "button",
|
|
590
|
+
class: "astryx-diff-viewer__gutter-button",
|
|
591
|
+
"data-diff-line-button": "",
|
|
592
|
+
"aria-label": lineNumberButtonLabel("old", lineNumber(row.cell, "old")),
|
|
593
|
+
onClick: ($event) => emitLineNumberClick(row.cell, "old", $event)
|
|
594
|
+
}, toDisplayString(lineNumber(row.cell, "old")), 9, _hoisted_8$4)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(lineNumber(row.cell, "old")), 1)], 64))], 2)) : createCommentVNode("v-if", true),
|
|
595
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("td", {
|
|
596
|
+
key: 1,
|
|
597
|
+
class: normalizeClass(["astryx-diff-viewer__gutter", cellClass(row.cell)])
|
|
598
|
+
}, [__props.hasLineNumberClick && lineAction(row.cell, "new") != null ? (openBlock(), createElementBlock("button", {
|
|
599
|
+
key: 0,
|
|
600
|
+
type: "button",
|
|
601
|
+
class: "astryx-diff-viewer__gutter-button",
|
|
602
|
+
"data-diff-line-button": "",
|
|
603
|
+
"aria-label": lineNumberButtonLabel("new", lineNumber(row.cell, "new")),
|
|
604
|
+
onClick: ($event) => emitLineNumberClick(row.cell, "new", $event)
|
|
605
|
+
}, toDisplayString(lineNumber(row.cell, "new")), 9, _hoisted_9$4)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(lineNumber(row.cell, "new")), 1)], 64))], 2)) : createCommentVNode("v-if", true),
|
|
606
|
+
createElementVNode("td", { class: normalizeClass(["astryx-diff-viewer__marker", cellClass(row.cell)]) }, [row.cell.line.kind === "added" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[9] || (_cache[9] = createElementVNode("span", { "aria-hidden": "true" }, "+", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
607
|
+
default: withCtx(() => [..._cache[8] || (_cache[8] = [createTextVNode("Added", -1)])]),
|
|
608
|
+
_: 1
|
|
609
|
+
})], 64)) : row.cell.line.kind === "removed" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[11] || (_cache[11] = createElementVNode("span", { "aria-hidden": "true" }, "-", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
610
|
+
default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode("Removed", -1)])]),
|
|
611
|
+
_: 1
|
|
612
|
+
})], 64)) : createCommentVNode("v-if", true)], 2),
|
|
613
|
+
createElementVNode("td", {
|
|
614
|
+
class: normalizeClass(["astryx-diff-viewer__code", [cellClass(row.cell), { "astryx-diff-viewer__code--wrapped": __props.isWrapped }]]),
|
|
615
|
+
"data-diff": diffKind(row.cell)
|
|
616
|
+
}, [createElementVNode("span", _hoisted_11$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(row.cell.groups, (group, groupIndex) => {
|
|
617
|
+
return openBlock(), createElementBlock(Fragment, { key: groupIndex }, [group.changed ? (openBlock(), createElementBlock("span", {
|
|
618
|
+
key: 0,
|
|
619
|
+
class: "astryx-diff-viewer__change",
|
|
620
|
+
"data-diff-change": "",
|
|
621
|
+
"data-kind": row.cell.line.kind
|
|
622
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(group.parts, (part, partIndex) => {
|
|
623
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
624
|
+
key: 0,
|
|
625
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
626
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
627
|
+
}), 128))], 8, _hoisted_12$1)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(group.parts, (part, partIndex) => {
|
|
628
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
629
|
+
key: 0,
|
|
630
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
631
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
632
|
+
}), 128))], 64);
|
|
633
|
+
}), 128))])], 10, _hoisted_10$4)
|
|
634
|
+
]), row.cell.note != null ? (openBlock(), createElementBlock("tr", _hoisted_13, [createElementVNode("td", {
|
|
635
|
+
colspan: unifiedColumns.value,
|
|
636
|
+
class: "astryx-diff-viewer__annotation"
|
|
637
|
+
}, [createElementVNode("div", _hoisted_15, [createVNode(unref(RenderVNode_default), { content: row.cell.note }, null, 8, ["content"])])], 8, _hoisted_14)])) : createCommentVNode("v-if", true)], 64))], 64);
|
|
638
|
+
}), 128))])
|
|
639
|
+
], 6)) : (openBlock(), createElementBlock("table", {
|
|
640
|
+
key: 1,
|
|
641
|
+
class: normalizeClass(["astryx-diff-viewer__table", { "astryx-diff-viewer__table--wrapped": __props.isWrapped }]),
|
|
642
|
+
style: normalizeStyle(tableStyle.value)
|
|
643
|
+
}, [
|
|
644
|
+
createElementVNode("colgroup", null, [
|
|
645
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("col", _hoisted_16)) : createCommentVNode("v-if", true),
|
|
646
|
+
_cache[12] || (_cache[12] = createElementVNode("col", { class: "astryx-diff-viewer__marker-col" }, null, -1)),
|
|
647
|
+
_cache[13] || (_cache[13] = createElementVNode("col", null, null, -1)),
|
|
648
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("col", _hoisted_17)) : createCommentVNode("v-if", true),
|
|
649
|
+
_cache[14] || (_cache[14] = createElementVNode("col", { class: "astryx-diff-viewer__marker-col" }, null, -1)),
|
|
650
|
+
_cache[15] || (_cache[15] = createElementVNode("col", null, null, -1))
|
|
651
|
+
]),
|
|
652
|
+
createElementVNode("thead", null, [createElementVNode("tr", null, [
|
|
653
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("th", _hoisted_18, [createVNode(unref(VisuallyHidden), null, {
|
|
654
|
+
default: withCtx(() => [..._cache[16] || (_cache[16] = [createTextVNode("Original line number", -1)])]),
|
|
655
|
+
_: 1
|
|
656
|
+
})])) : createCommentVNode("v-if", true),
|
|
657
|
+
createElementVNode("th", _hoisted_19, [createVNode(unref(VisuallyHidden), null, {
|
|
658
|
+
default: withCtx(() => [..._cache[17] || (_cache[17] = [createTextVNode("Change", -1)])]),
|
|
659
|
+
_: 1
|
|
660
|
+
})]),
|
|
661
|
+
createElementVNode("th", _hoisted_20, [createVNode(unref(VisuallyHidden), null, {
|
|
662
|
+
default: withCtx(() => [..._cache[18] || (_cache[18] = [createTextVNode("Original code", -1)])]),
|
|
663
|
+
_: 1
|
|
664
|
+
})]),
|
|
665
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("th", _hoisted_21, [createVNode(unref(VisuallyHidden), null, {
|
|
666
|
+
default: withCtx(() => [..._cache[19] || (_cache[19] = [createTextVNode("Changed line number", -1)])]),
|
|
667
|
+
_: 1
|
|
668
|
+
})])) : createCommentVNode("v-if", true),
|
|
669
|
+
createElementVNode("th", _hoisted_22, [createVNode(unref(VisuallyHidden), null, {
|
|
670
|
+
default: withCtx(() => [..._cache[20] || (_cache[20] = [createTextVNode("Change", -1)])]),
|
|
671
|
+
_: 1
|
|
672
|
+
})]),
|
|
673
|
+
createElementVNode("th", _hoisted_23, [createVNode(unref(VisuallyHidden), null, {
|
|
674
|
+
default: withCtx(() => [..._cache[21] || (_cache[21] = [createTextVNode("Changed code", -1)])]),
|
|
675
|
+
_: 1
|
|
676
|
+
})])
|
|
677
|
+
])]),
|
|
678
|
+
createElementVNode("tbody", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.splitRows, (row) => {
|
|
679
|
+
return openBlock(), createElementBlock(Fragment, { key: row.key }, [row.type === "hunk" ? (openBlock(), createElementBlock("tr", _hoisted_24, [createElementVNode("td", {
|
|
680
|
+
colspan: splitSideColumns.value * 2,
|
|
681
|
+
class: "astryx-diff-viewer__hunk"
|
|
682
|
+
}, [createElementVNode("span", _hoisted_26, toDisplayString(row.header), 1)], 8, _hoisted_25)])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("tr", null, [
|
|
683
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("td", {
|
|
684
|
+
key: 0,
|
|
685
|
+
class: normalizeClass(["astryx-diff-viewer__gutter", cellClass(row.old)])
|
|
686
|
+
}, [__props.hasLineNumberClick && lineAction(row.old, "old") != null ? (openBlock(), createElementBlock("button", {
|
|
687
|
+
key: 0,
|
|
688
|
+
type: "button",
|
|
689
|
+
class: "astryx-diff-viewer__gutter-button",
|
|
690
|
+
"data-diff-line-button": "",
|
|
691
|
+
"aria-label": lineNumberButtonLabel("old", lineNumber(row.old, "old")),
|
|
692
|
+
onClick: ($event) => emitLineNumberClick(row.old, "old", $event)
|
|
693
|
+
}, toDisplayString(lineNumber(row.old, "old")), 9, _hoisted_27)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(lineNumber(row.old, "old")), 1)], 64))], 2)) : createCommentVNode("v-if", true),
|
|
694
|
+
createElementVNode("td", { class: normalizeClass(["astryx-diff-viewer__marker", cellClass(row.old)]) }, [row.old?.line.kind === "added" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[23] || (_cache[23] = createElementVNode("span", { "aria-hidden": "true" }, "+", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
695
|
+
default: withCtx(() => [..._cache[22] || (_cache[22] = [createTextVNode("Added", -1)])]),
|
|
696
|
+
_: 1
|
|
697
|
+
})], 64)) : row.old?.line.kind === "removed" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[25] || (_cache[25] = createElementVNode("span", { "aria-hidden": "true" }, "-", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
698
|
+
default: withCtx(() => [..._cache[24] || (_cache[24] = [createTextVNode("Removed", -1)])]),
|
|
699
|
+
_: 1
|
|
700
|
+
})], 64)) : createCommentVNode("v-if", true)], 2),
|
|
701
|
+
createElementVNode("td", {
|
|
702
|
+
class: normalizeClass(["astryx-diff-viewer__code astryx-diff-viewer__code--old", [cellClass(row.old), { "astryx-diff-viewer__code--wrapped": __props.isWrapped }]]),
|
|
703
|
+
"data-diff": diffKind(row.old)
|
|
704
|
+
}, [createElementVNode("span", _hoisted_29, [(openBlock(true), createElementBlock(Fragment, null, renderList(row.old?.groups ?? [], (group, groupIndex) => {
|
|
705
|
+
return openBlock(), createElementBlock(Fragment, { key: groupIndex }, [group.changed ? (openBlock(), createElementBlock("span", {
|
|
706
|
+
key: 0,
|
|
707
|
+
class: "astryx-diff-viewer__change",
|
|
708
|
+
"data-diff-change": "",
|
|
709
|
+
"data-kind": row.old?.line.kind
|
|
710
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(group.parts, (part, partIndex) => {
|
|
711
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
712
|
+
key: 0,
|
|
713
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
714
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
715
|
+
}), 128))], 8, _hoisted_30)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(group.parts, (part, partIndex) => {
|
|
716
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
717
|
+
key: 0,
|
|
718
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
719
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
720
|
+
}), 128))], 64);
|
|
721
|
+
}), 128))])], 10, _hoisted_28),
|
|
722
|
+
__props.hasLineNumbers ? (openBlock(), createElementBlock("td", {
|
|
723
|
+
key: 1,
|
|
724
|
+
class: normalizeClass(["astryx-diff-viewer__gutter", cellClass(row.new)])
|
|
725
|
+
}, [__props.hasLineNumberClick && lineAction(row.new, "new") != null ? (openBlock(), createElementBlock("button", {
|
|
726
|
+
key: 0,
|
|
727
|
+
type: "button",
|
|
728
|
+
class: "astryx-diff-viewer__gutter-button",
|
|
729
|
+
"data-diff-line-button": "",
|
|
730
|
+
"aria-label": lineNumberButtonLabel("new", lineNumber(row.new, "new")),
|
|
731
|
+
onClick: ($event) => emitLineNumberClick(row.new, "new", $event)
|
|
732
|
+
}, toDisplayString(lineNumber(row.new, "new")), 9, _hoisted_31)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(lineNumber(row.new, "new")), 1)], 64))], 2)) : createCommentVNode("v-if", true),
|
|
733
|
+
createElementVNode("td", { class: normalizeClass(["astryx-diff-viewer__marker", cellClass(row.new)]) }, [row.new?.line.kind === "added" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [_cache[27] || (_cache[27] = createElementVNode("span", { "aria-hidden": "true" }, "+", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
734
|
+
default: withCtx(() => [..._cache[26] || (_cache[26] = [createTextVNode("Added", -1)])]),
|
|
735
|
+
_: 1
|
|
736
|
+
})], 64)) : row.new?.line.kind === "removed" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [_cache[29] || (_cache[29] = createElementVNode("span", { "aria-hidden": "true" }, "-", -1)), createVNode(unref(VisuallyHidden), null, {
|
|
737
|
+
default: withCtx(() => [..._cache[28] || (_cache[28] = [createTextVNode("Removed", -1)])]),
|
|
738
|
+
_: 1
|
|
739
|
+
})], 64)) : createCommentVNode("v-if", true)], 2),
|
|
740
|
+
createElementVNode("td", {
|
|
741
|
+
class: normalizeClass(["astryx-diff-viewer__code", [cellClass(row.new), { "astryx-diff-viewer__code--wrapped": __props.isWrapped }]]),
|
|
742
|
+
"data-diff": diffKind(row.new)
|
|
743
|
+
}, [createElementVNode("span", _hoisted_33, [(openBlock(true), createElementBlock(Fragment, null, renderList(row.new?.groups ?? [], (group, groupIndex) => {
|
|
744
|
+
return openBlock(), createElementBlock(Fragment, { key: groupIndex }, [group.changed ? (openBlock(), createElementBlock("span", {
|
|
745
|
+
key: 0,
|
|
746
|
+
class: "astryx-diff-viewer__change",
|
|
747
|
+
"data-diff-change": "",
|
|
748
|
+
"data-kind": row.new?.line.kind
|
|
749
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(group.parts, (part, partIndex) => {
|
|
750
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
751
|
+
key: 0,
|
|
752
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
753
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
754
|
+
}), 128))], 8, _hoisted_34)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(group.parts, (part, partIndex) => {
|
|
755
|
+
return openBlock(), createElementBlock(Fragment, { key: partIndex }, [part.type != null ? (openBlock(), createElementBlock("span", {
|
|
756
|
+
key: 0,
|
|
757
|
+
class: normalizeClass(`astryx-token-${part.type}`)
|
|
758
|
+
}, toDisplayString(part.text), 3)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(part.text), 1)], 64))], 64);
|
|
759
|
+
}), 128))], 64);
|
|
760
|
+
}), 128))])], 10, _hoisted_32)
|
|
761
|
+
]), oldNote(row) != null || newNote(row) != null ? (openBlock(), createElementBlock("tr", _hoisted_35, [createElementVNode("td", {
|
|
762
|
+
colspan: splitSideColumns.value,
|
|
763
|
+
class: "astryx-diff-viewer__annotation astryx-diff-viewer__code--old"
|
|
764
|
+
}, [oldNote(row) != null ? (openBlock(), createElementBlock("div", _hoisted_37, [createVNode(unref(RenderVNode_default), { content: oldNote(row) }, null, 8, ["content"])])) : createCommentVNode("v-if", true)], 8, _hoisted_36), createElementVNode("td", {
|
|
765
|
+
colspan: splitSideColumns.value,
|
|
766
|
+
class: "astryx-diff-viewer__annotation"
|
|
767
|
+
}, [newNote(row) != null ? (openBlock(), createElementBlock("div", _hoisted_39, [createVNode(unref(RenderVNode_default), { content: newNote(row) }, null, 8, ["content"])])) : createCommentVNode("v-if", true)], 8, _hoisted_38)])) : createCommentVNode("v-if", true)], 64))], 64);
|
|
768
|
+
}), 128))])
|
|
769
|
+
], 6));
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
//#endregion
|
|
774
|
+
//#region src/DiffViewer/language.ts
|
|
775
|
+
/**
|
|
776
|
+
* @file language.ts
|
|
777
|
+
* @input A file path from a diff
|
|
778
|
+
* @output inferLanguage(path): the tokenizer language for that extension
|
|
779
|
+
* @position Lab helper used by DiffViewer when `language` is not given
|
|
780
|
+
*/
|
|
781
|
+
const EXTENSION_LANGUAGES = {
|
|
782
|
+
ts: "ts",
|
|
783
|
+
mts: "ts",
|
|
784
|
+
cts: "ts",
|
|
785
|
+
tsx: "tsx",
|
|
786
|
+
js: "js",
|
|
787
|
+
mjs: "js",
|
|
788
|
+
cjs: "js",
|
|
789
|
+
jsx: "jsx",
|
|
790
|
+
json: "json",
|
|
791
|
+
html: "html",
|
|
792
|
+
htm: "html",
|
|
793
|
+
xml: "xml",
|
|
794
|
+
svg: "svg",
|
|
795
|
+
css: "css",
|
|
796
|
+
scss: "scss",
|
|
797
|
+
less: "less",
|
|
798
|
+
py: "python",
|
|
799
|
+
sh: "bash",
|
|
800
|
+
bash: "bash",
|
|
801
|
+
zsh: "zsh",
|
|
802
|
+
php: "php",
|
|
803
|
+
hack: "hack",
|
|
804
|
+
yaml: "yaml",
|
|
805
|
+
yml: "yaml",
|
|
806
|
+
md: "markdown",
|
|
807
|
+
markdown: "markdown"
|
|
808
|
+
};
|
|
809
|
+
/** `src/app.ts` → `ts`; unknown or missing extensions render as plaintext. */
|
|
810
|
+
function inferLanguage(path) {
|
|
811
|
+
if (path == null) return "plaintext";
|
|
812
|
+
const base = path.slice(path.lastIndexOf("/") + 1);
|
|
813
|
+
const dot = base.lastIndexOf(".");
|
|
814
|
+
if (dot <= 0) return "plaintext";
|
|
815
|
+
return EXTENSION_LANGUAGES[base.slice(dot + 1).toLowerCase()] ?? "plaintext";
|
|
816
|
+
}
|
|
817
|
+
//#endregion
|
|
818
|
+
//#region src/DiffViewer/inlineDiff.ts
|
|
819
|
+
/**
|
|
820
|
+
* Upper bound on the comparison table (tokens × tokens). Beyond it a pair is
|
|
821
|
+
* reported as having no inline ranges, so a very long minified line cannot
|
|
822
|
+
* stall rendering; its row tint still marks it as changed.
|
|
823
|
+
*/
|
|
824
|
+
const MAX_CELLS = 25e4;
|
|
825
|
+
const EMPTY = {
|
|
826
|
+
old: [],
|
|
827
|
+
new: []
|
|
828
|
+
};
|
|
829
|
+
const WORD_PATTERN = /\w+|\s+|[^\w\s]/gu;
|
|
830
|
+
function split(line, granularity) {
|
|
831
|
+
if (granularity === "char") {
|
|
832
|
+
let offset = 0;
|
|
833
|
+
return Array.from(line, (text) => {
|
|
834
|
+
const start = offset;
|
|
835
|
+
offset += text.length;
|
|
836
|
+
return {
|
|
837
|
+
text,
|
|
838
|
+
start,
|
|
839
|
+
end: offset
|
|
840
|
+
};
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
const pieces = [];
|
|
844
|
+
for (const match of line.matchAll(WORD_PATTERN)) {
|
|
845
|
+
const start = match.index ?? 0;
|
|
846
|
+
pieces.push({
|
|
847
|
+
text: match[0],
|
|
848
|
+
start,
|
|
849
|
+
end: start + match[0].length
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
return pieces;
|
|
853
|
+
}
|
|
854
|
+
function isBlank(text) {
|
|
855
|
+
return text.trim() === "";
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Merge the changed pieces into ranges: adjacent pieces join, and in word
|
|
859
|
+
* mode so do two changes separated only by whitespace, so `a b` → `c d`
|
|
860
|
+
* reads as one change rather than two with a gap. Ranges of only whitespace
|
|
861
|
+
* (re-indentation) are dropped: a highlighted blank says little.
|
|
862
|
+
*/
|
|
863
|
+
function toRanges(pieces, changed, granularity) {
|
|
864
|
+
const ranges = [];
|
|
865
|
+
let i = 0;
|
|
866
|
+
while (i < pieces.length) {
|
|
867
|
+
if (!changed[i]) {
|
|
868
|
+
i++;
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
const start = pieces[i].start;
|
|
872
|
+
let end = pieces[i].end;
|
|
873
|
+
i++;
|
|
874
|
+
while (i < pieces.length) {
|
|
875
|
+
if (changed[i]) {
|
|
876
|
+
end = pieces[i].end;
|
|
877
|
+
i++;
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
if (granularity === "word" && isBlank(pieces[i].text) && i + 1 < pieces.length && changed[i + 1]) {
|
|
881
|
+
end = pieces[i + 1].end;
|
|
882
|
+
i += 2;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
break;
|
|
886
|
+
}
|
|
887
|
+
if (pieces.some((p) => p.start >= start && p.end <= end && !isBlank(p.text))) ranges.push([start, end]);
|
|
888
|
+
}
|
|
889
|
+
return ranges;
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Below this share of the shorter line's non-whitespace characters in
|
|
893
|
+
* common, two paired lines are treated as unrelated: highlighting most of
|
|
894
|
+
* both would say no more than the row tint.
|
|
895
|
+
*/
|
|
896
|
+
const MIN_SHARED_RATIO = .5;
|
|
897
|
+
function contentLength(pieces) {
|
|
898
|
+
let length = 0;
|
|
899
|
+
for (const piece of pieces) if (!isBlank(piece.text)) length += piece.text.length;
|
|
900
|
+
return length;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* The changed ranges of a removed line and the added line that replaced it.
|
|
904
|
+
* Returns no ranges when the lines are mostly different (less than half of
|
|
905
|
+
* the shorter line's non-whitespace text in common: the whole line changed,
|
|
906
|
+
* which the row tint already says) or when they are too long to compare.
|
|
907
|
+
*/
|
|
908
|
+
function diffInlineRanges(oldLine, newLine, granularity) {
|
|
909
|
+
if (oldLine === newLine) return EMPTY;
|
|
910
|
+
const a = split(oldLine, granularity);
|
|
911
|
+
const b = split(newLine, granularity);
|
|
912
|
+
const n = a.length;
|
|
913
|
+
const m = b.length;
|
|
914
|
+
if (n === 0 || m === 0 || n * m > MAX_CELLS) return EMPTY;
|
|
915
|
+
const width = m + 1;
|
|
916
|
+
const lengths = new Uint32Array((n + 1) * width);
|
|
917
|
+
for (let i = n - 1; i >= 0; i--) for (let j = m - 1; j >= 0; j--) lengths[i * width + j] = a[i].text === b[j].text ? lengths[(i + 1) * width + j + 1] + 1 : Math.max(lengths[(i + 1) * width + j], lengths[i * width + j + 1]);
|
|
918
|
+
const oldChanged = Array.from({ length: n }).fill(true);
|
|
919
|
+
const newChanged = Array.from({ length: m }).fill(true);
|
|
920
|
+
let shared = 0;
|
|
921
|
+
let i = 0;
|
|
922
|
+
let j = 0;
|
|
923
|
+
while (i < n && j < m) if (a[i].text === b[j].text) {
|
|
924
|
+
oldChanged[i] = false;
|
|
925
|
+
newChanged[j] = false;
|
|
926
|
+
if (!isBlank(a[i].text)) shared += a[i].text.length;
|
|
927
|
+
i++;
|
|
928
|
+
j++;
|
|
929
|
+
} else if (lengths[(i + 1) * width + j] >= lengths[i * width + j + 1]) i++;
|
|
930
|
+
else j++;
|
|
931
|
+
const shorter = Math.min(contentLength(a), contentLength(b));
|
|
932
|
+
if (shared === 0 || shared < shorter * MIN_SHARED_RATIO) return EMPTY;
|
|
933
|
+
return {
|
|
934
|
+
old: toRanges(a, oldChanged, granularity),
|
|
935
|
+
new: toRanges(b, newChanged, granularity)
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
//#endregion
|
|
939
|
+
//#region src/DiffViewer/lineParts.ts
|
|
940
|
+
/**
|
|
941
|
+
* Cut a line at every syntax-token and inline-highlight boundary, so each
|
|
942
|
+
* piece carries one token type and one changed/unchanged state.
|
|
943
|
+
*/
|
|
944
|
+
function buildPieces(text, tokens, emphasis) {
|
|
945
|
+
const segments = [];
|
|
946
|
+
let cursor = 0;
|
|
947
|
+
for (const token of tokens) {
|
|
948
|
+
const start = Math.max(token.start, cursor);
|
|
949
|
+
const end = Math.min(token.end, text.length);
|
|
950
|
+
if (start > cursor) segments.push({
|
|
951
|
+
start: cursor,
|
|
952
|
+
end: start,
|
|
953
|
+
type: null
|
|
954
|
+
});
|
|
955
|
+
if (end > start) segments.push({
|
|
956
|
+
start,
|
|
957
|
+
end,
|
|
958
|
+
type: token.type
|
|
959
|
+
});
|
|
960
|
+
cursor = Math.max(cursor, end);
|
|
961
|
+
}
|
|
962
|
+
if (cursor < text.length) segments.push({
|
|
963
|
+
start: cursor,
|
|
964
|
+
end: text.length,
|
|
965
|
+
type: null
|
|
966
|
+
});
|
|
967
|
+
const cuts = /* @__PURE__ */ new Set();
|
|
968
|
+
for (const [start, end] of emphasis) {
|
|
969
|
+
cuts.add(start);
|
|
970
|
+
cuts.add(end);
|
|
971
|
+
}
|
|
972
|
+
const isChanged = (offset) => emphasis.some(([start, end]) => offset >= start && offset < end);
|
|
973
|
+
const pieces = [];
|
|
974
|
+
for (const segment of segments) {
|
|
975
|
+
let start = segment.start;
|
|
976
|
+
for (let offset = segment.start + 1; offset < segment.end; offset++) if (cuts.has(offset)) {
|
|
977
|
+
pieces.push({
|
|
978
|
+
start,
|
|
979
|
+
end: offset,
|
|
980
|
+
type: segment.type,
|
|
981
|
+
changed: isChanged(start)
|
|
982
|
+
});
|
|
983
|
+
start = offset;
|
|
984
|
+
}
|
|
985
|
+
pieces.push({
|
|
986
|
+
start,
|
|
987
|
+
end: segment.end,
|
|
988
|
+
type: segment.type,
|
|
989
|
+
changed: isChanged(start)
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
return pieces;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Group a line into runs that DiffViewer can render without extra wrappers:
|
|
996
|
+
* unchanged runs keep their token spans, and adjacent changed pieces share one
|
|
997
|
+
* `data-diff-change` element.
|
|
998
|
+
*/
|
|
999
|
+
function buildLineGroups(text, tokens, emphasis) {
|
|
1000
|
+
if (text === "") return [{
|
|
1001
|
+
changed: false,
|
|
1002
|
+
parts: [{
|
|
1003
|
+
text: "",
|
|
1004
|
+
type: null,
|
|
1005
|
+
changed: false
|
|
1006
|
+
}]
|
|
1007
|
+
}];
|
|
1008
|
+
const pieces = buildPieces(text, tokens, emphasis);
|
|
1009
|
+
const groups = [];
|
|
1010
|
+
for (const piece of pieces) {
|
|
1011
|
+
const part = {
|
|
1012
|
+
text: text.slice(piece.start, piece.end),
|
|
1013
|
+
type: piece.type,
|
|
1014
|
+
changed: piece.changed
|
|
1015
|
+
};
|
|
1016
|
+
const last = groups[groups.length - 1];
|
|
1017
|
+
if (last != null && last.changed === piece.changed) {
|
|
1018
|
+
last.parts.push(part);
|
|
1019
|
+
continue;
|
|
1020
|
+
}
|
|
1021
|
+
groups.push({
|
|
1022
|
+
changed: piece.changed,
|
|
1023
|
+
parts: [part]
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
return groups;
|
|
1027
|
+
}
|
|
1028
|
+
//#endregion
|
|
1029
|
+
//#region src/DiffViewer/rows.ts
|
|
1030
|
+
function lineRef(line, hunkIndex) {
|
|
1031
|
+
if (line.kind === "removed") return line.oldLineNumber == null ? null : {
|
|
1032
|
+
side: "old",
|
|
1033
|
+
lineNumber: line.oldLineNumber,
|
|
1034
|
+
line,
|
|
1035
|
+
hunkIndex
|
|
1036
|
+
};
|
|
1037
|
+
return line.newLineNumber == null ? null : {
|
|
1038
|
+
side: "new",
|
|
1039
|
+
lineNumber: line.newLineNumber,
|
|
1040
|
+
line,
|
|
1041
|
+
hunkIndex
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Pair each run of removed lines with the run of added lines after it, line by
|
|
1046
|
+
* line (the same pairing split view shows), and mark what changed inside each
|
|
1047
|
+
* pair.
|
|
1048
|
+
*/
|
|
1049
|
+
function addInlineEmphasis(cells, granularity) {
|
|
1050
|
+
let i = 0;
|
|
1051
|
+
while (i < cells.length) {
|
|
1052
|
+
if (cells[i].line.kind !== "removed") {
|
|
1053
|
+
i++;
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
const removed = [];
|
|
1057
|
+
const added = [];
|
|
1058
|
+
while (i < cells.length && cells[i].line.kind === "removed") removed.push(cells[i++]);
|
|
1059
|
+
while (i < cells.length && cells[i].line.kind === "added") added.push(cells[i++]);
|
|
1060
|
+
const pairs = Math.min(removed.length, added.length);
|
|
1061
|
+
for (let r = 0; r < pairs; r++) {
|
|
1062
|
+
const ranges = diffInlineRanges(removed[r].line.content, added[r].line.content, granularity);
|
|
1063
|
+
removed[r].emphasis = ranges.old;
|
|
1064
|
+
added[r].emphasis = ranges.new;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Tokenize each hunk's old and new side as continuous text, so constructs that
|
|
1070
|
+
* span lines (block comments, template strings) keep their paint within the
|
|
1071
|
+
* hunk, then hand each line the tokens of the side it renders from.
|
|
1072
|
+
*/
|
|
1073
|
+
function buildCells(file, language, inlineHighlight) {
|
|
1074
|
+
return file.hunks.map((hunk, hunkIndex) => {
|
|
1075
|
+
const oldLines = hunk.lines.filter((line) => line.kind !== "added");
|
|
1076
|
+
const newLines = hunk.lines.filter((line) => line.kind !== "removed");
|
|
1077
|
+
const highlight = language !== "plaintext";
|
|
1078
|
+
const oldTokens = highlight ? tokenize(oldLines.map((line) => line.content).join("\n"), language) : [];
|
|
1079
|
+
const newTokens = highlight ? tokenize(newLines.map((line) => line.content).join("\n"), language) : [];
|
|
1080
|
+
let oldIndex = 0;
|
|
1081
|
+
let newIndex = 0;
|
|
1082
|
+
const cells = [];
|
|
1083
|
+
for (const line of hunk.lines) {
|
|
1084
|
+
let tokens;
|
|
1085
|
+
if (line.kind === "removed") tokens = oldTokens[oldIndex++] ?? [];
|
|
1086
|
+
else if (line.kind === "added") tokens = newTokens[newIndex++] ?? [];
|
|
1087
|
+
else {
|
|
1088
|
+
oldIndex++;
|
|
1089
|
+
tokens = newTokens[newIndex++] ?? [];
|
|
1090
|
+
}
|
|
1091
|
+
const ref = lineRef(line, hunkIndex);
|
|
1092
|
+
if (ref != null) cells.push({
|
|
1093
|
+
line,
|
|
1094
|
+
ref,
|
|
1095
|
+
tokens,
|
|
1096
|
+
emphasis: [],
|
|
1097
|
+
groups: []
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
if (inlineHighlight !== "none") addInlineEmphasis(cells, inlineHighlight);
|
|
1101
|
+
for (const cell of cells) cell.groups = buildLineGroups(cell.line.content, cell.tokens, cell.emphasis);
|
|
1102
|
+
return cells;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
function buildUnifiedRows(file, cells) {
|
|
1106
|
+
const rows = [];
|
|
1107
|
+
file.hunks.forEach((hunk, h) => {
|
|
1108
|
+
rows.push({
|
|
1109
|
+
type: "hunk",
|
|
1110
|
+
key: `h${h}`,
|
|
1111
|
+
header: hunk.header
|
|
1112
|
+
});
|
|
1113
|
+
cells[h].forEach((cell, i) => {
|
|
1114
|
+
rows.push({
|
|
1115
|
+
type: "line",
|
|
1116
|
+
key: `h${h}l${i}`,
|
|
1117
|
+
cell
|
|
1118
|
+
});
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
return rows;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Pair each run of removed lines with the run of added lines that follows it,
|
|
1125
|
+
* row by row; context lines appear on both sides of one row.
|
|
1126
|
+
*/
|
|
1127
|
+
function buildSplitRows(file, cells) {
|
|
1128
|
+
const rows = [];
|
|
1129
|
+
file.hunks.forEach((hunk, h) => {
|
|
1130
|
+
rows.push({
|
|
1131
|
+
type: "hunk",
|
|
1132
|
+
key: `h${h}`,
|
|
1133
|
+
header: hunk.header
|
|
1134
|
+
});
|
|
1135
|
+
const hunkCells = cells[h];
|
|
1136
|
+
let i = 0;
|
|
1137
|
+
while (i < hunkCells.length) {
|
|
1138
|
+
const cell = hunkCells[i];
|
|
1139
|
+
if (cell.line.kind === "context") {
|
|
1140
|
+
rows.push({
|
|
1141
|
+
type: "pair",
|
|
1142
|
+
key: `h${h}l${i}`,
|
|
1143
|
+
old: cell,
|
|
1144
|
+
new: cell
|
|
1145
|
+
});
|
|
1146
|
+
i++;
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
const removed = [];
|
|
1150
|
+
const added = [];
|
|
1151
|
+
while (i < hunkCells.length && hunkCells[i].line.kind === "removed") removed.push(hunkCells[i++]);
|
|
1152
|
+
while (i < hunkCells.length && hunkCells[i].line.kind === "added") added.push(hunkCells[i++]);
|
|
1153
|
+
const count = Math.max(removed.length, added.length);
|
|
1154
|
+
for (let r = 0; r < count; r++) rows.push({
|
|
1155
|
+
type: "pair",
|
|
1156
|
+
key: `h${h}l${i}r${r}`,
|
|
1157
|
+
old: removed[r] ?? null,
|
|
1158
|
+
new: added[r] ?? null
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
return rows;
|
|
1163
|
+
}
|
|
1164
|
+
//#endregion
|
|
1165
|
+
//#region src/DiffViewer/DiffViewer.vue
|
|
1166
|
+
const _hoisted_1$11 = ["data-view-mode"];
|
|
1167
|
+
const _hoisted_2$10 = ["aria-expanded"];
|
|
1168
|
+
const _hoisted_3$9 = { class: "astryx-diff-viewer__path" };
|
|
1169
|
+
const _hoisted_4$8 = {
|
|
1170
|
+
key: 1,
|
|
1171
|
+
class: "astryx-diff-viewer__path"
|
|
1172
|
+
};
|
|
1173
|
+
const _hoisted_5$7 = {
|
|
1174
|
+
key: 3,
|
|
1175
|
+
class: "astryx-diff-viewer__stats"
|
|
1176
|
+
};
|
|
1177
|
+
const _hoisted_6$6 = {
|
|
1178
|
+
"aria-hidden": "true",
|
|
1179
|
+
class: "astryx-diff-viewer__stat-added"
|
|
1180
|
+
};
|
|
1181
|
+
const _hoisted_7$4 = {
|
|
1182
|
+
"aria-hidden": "true",
|
|
1183
|
+
class: "astryx-diff-viewer__stat-removed"
|
|
1184
|
+
};
|
|
1185
|
+
const _hoisted_8$3 = {
|
|
1186
|
+
key: 4,
|
|
1187
|
+
class: "astryx-diff-viewer__header-end"
|
|
1188
|
+
};
|
|
1189
|
+
const _hoisted_9$3 = ["hidden"];
|
|
1190
|
+
const _hoisted_10$3 = {
|
|
1191
|
+
key: 0,
|
|
1192
|
+
class: "astryx-diff-viewer__message"
|
|
1193
|
+
};
|
|
1194
|
+
const _hoisted_11$1 = {
|
|
1195
|
+
key: 1,
|
|
1196
|
+
class: "astryx-diff-viewer__message"
|
|
1197
|
+
};
|
|
1198
|
+
const _hoisted_12 = ["aria-label"];
|
|
1199
|
+
const _sfc_main$1 = /*@__PURE__*/ defineComponent({
|
|
1200
|
+
name: "AstryxDiffViewer",
|
|
1201
|
+
inheritAttrs: false,
|
|
1202
|
+
__name: "DiffViewer",
|
|
1203
|
+
props: {
|
|
1204
|
+
file: {},
|
|
1205
|
+
viewMode: { default: "unified" },
|
|
1206
|
+
language: { default: void 0 },
|
|
1207
|
+
hasLineNumbers: {
|
|
1208
|
+
type: Boolean,
|
|
1209
|
+
default: true
|
|
1210
|
+
},
|
|
1211
|
+
isWrapped: {
|
|
1212
|
+
type: Boolean,
|
|
1213
|
+
default: false
|
|
1214
|
+
},
|
|
1215
|
+
inlineHighlight: { default: "word" },
|
|
1216
|
+
isCollapsible: {
|
|
1217
|
+
type: Boolean,
|
|
1218
|
+
default: true
|
|
1219
|
+
},
|
|
1220
|
+
isCollapsed: {
|
|
1221
|
+
type: Boolean,
|
|
1222
|
+
default: void 0
|
|
1223
|
+
},
|
|
1224
|
+
defaultIsCollapsed: {
|
|
1225
|
+
type: Boolean,
|
|
1226
|
+
default: false
|
|
1227
|
+
},
|
|
1228
|
+
maxHeight: { default: void 0 },
|
|
1229
|
+
syntaxTheme: { default: void 0 },
|
|
1230
|
+
label: { default: "Diff" },
|
|
1231
|
+
renderLineAnnotation: {
|
|
1232
|
+
type: Function,
|
|
1233
|
+
default: void 0
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
emits: [
|
|
1237
|
+
"collapsedChange",
|
|
1238
|
+
"update:isCollapsed",
|
|
1239
|
+
"lineNumberClick"
|
|
1240
|
+
],
|
|
1241
|
+
setup(__props, { emit: __emit }) {
|
|
1242
|
+
const props = __props;
|
|
1243
|
+
const emit = __emit;
|
|
1244
|
+
const slots = useSlots();
|
|
1245
|
+
const attrs = useAttrs();
|
|
1246
|
+
const instance = getCurrentInstance();
|
|
1247
|
+
const bodyId = `astryx-diff-viewer-body-${useId()}`;
|
|
1248
|
+
const rootAttrs = computed(() => {
|
|
1249
|
+
const { class: _class, style: _style, ...rest } = attrs;
|
|
1250
|
+
return rest;
|
|
1251
|
+
});
|
|
1252
|
+
const rootClass = computed(() => attrs.class);
|
|
1253
|
+
const rootStyle = computed(() => attrs.style);
|
|
1254
|
+
const path = computed(() => props.file.newPath ?? props.file.oldPath);
|
|
1255
|
+
const displayPath = computed(() => {
|
|
1256
|
+
if (props.file.status === "renamed" && props.file.oldPath != null && props.file.newPath != null) return `${props.file.oldPath} → ${props.file.newPath}`;
|
|
1257
|
+
return path.value;
|
|
1258
|
+
});
|
|
1259
|
+
const language = computed(() => props.language ?? inferLanguage(path.value));
|
|
1260
|
+
const scrollLabel = computed(() => displayPath.value ?? props.label);
|
|
1261
|
+
const cells = computed(() => {
|
|
1262
|
+
const built = buildCells(props.file, language.value, props.inlineHighlight);
|
|
1263
|
+
const annotate = props.renderLineAnnotation;
|
|
1264
|
+
if (annotate == null) return built;
|
|
1265
|
+
return built.map((hunk) => hunk.map((cell) => {
|
|
1266
|
+
const note = annotate(cell.ref);
|
|
1267
|
+
return {
|
|
1268
|
+
...cell,
|
|
1269
|
+
note: typeof note === "boolean" ? null : note
|
|
1270
|
+
};
|
|
1271
|
+
}));
|
|
1272
|
+
});
|
|
1273
|
+
const unifiedRows = computed(() => props.viewMode === "unified" ? buildUnifiedRows(props.file, cells.value) : []);
|
|
1274
|
+
const splitRows = computed(() => props.viewMode === "split" ? buildSplitRows(props.file, cells.value) : []);
|
|
1275
|
+
const stats = computed(() => {
|
|
1276
|
+
let added = 0;
|
|
1277
|
+
let removed = 0;
|
|
1278
|
+
let maxLineNumber = 1;
|
|
1279
|
+
for (const hunk of props.file.hunks) for (const line of hunk.lines) {
|
|
1280
|
+
if (line.kind === "added") added++;
|
|
1281
|
+
else if (line.kind === "removed") removed++;
|
|
1282
|
+
maxLineNumber = Math.max(maxLineNumber, line.oldLineNumber ?? 0, line.newLineNumber ?? 0);
|
|
1283
|
+
}
|
|
1284
|
+
return {
|
|
1285
|
+
added,
|
|
1286
|
+
removed,
|
|
1287
|
+
digits: String(maxLineNumber).length
|
|
1288
|
+
};
|
|
1289
|
+
});
|
|
1290
|
+
const addedText = computed(() => `+${stats.value.added}`);
|
|
1291
|
+
const removedText = computed(() => `−${stats.value.removed}`);
|
|
1292
|
+
const statsText = computed(() => {
|
|
1293
|
+
const lines = (count, word) => `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
1294
|
+
return `${lines(stats.value.added, "line")} added, ${lines(stats.value.removed, "line")} removed`;
|
|
1295
|
+
});
|
|
1296
|
+
const uncontrolledCollapsed = ref(props.defaultIsCollapsed);
|
|
1297
|
+
const canCollapse = computed(() => props.isCollapsible && displayPath.value != null);
|
|
1298
|
+
const isCollapsed = computed(() => canCollapse.value && (props.isCollapsed ?? uncontrolledCollapsed.value));
|
|
1299
|
+
function toggleCollapsed() {
|
|
1300
|
+
const next = !isCollapsed.value;
|
|
1301
|
+
if (props.isCollapsed === void 0) uncontrolledCollapsed.value = next;
|
|
1302
|
+
emit("update:isCollapsed", next);
|
|
1303
|
+
emit("collapsedChange", next);
|
|
1304
|
+
}
|
|
1305
|
+
const STATUS_COLORS = {
|
|
1306
|
+
added: "green",
|
|
1307
|
+
deleted: "red",
|
|
1308
|
+
renamed: "blue"
|
|
1309
|
+
};
|
|
1310
|
+
const statusColor = computed(() => STATUS_COLORS[props.file.status]);
|
|
1311
|
+
const statusLabel = computed(() => {
|
|
1312
|
+
switch (props.file.status) {
|
|
1313
|
+
case "added": return "Added";
|
|
1314
|
+
case "deleted": return "Deleted";
|
|
1315
|
+
case "renamed": return "Renamed";
|
|
1316
|
+
default: return null;
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
const hasHeader = computed(() => displayPath.value != null || slots.headerEnd != null);
|
|
1320
|
+
const scrollStyle = computed(() => props.maxHeight == null ? void 0 : { maxHeight: typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight });
|
|
1321
|
+
const scroll = useScrollableArea({
|
|
1322
|
+
axis: "both",
|
|
1323
|
+
keyboardAccess: {
|
|
1324
|
+
owner: "viewport",
|
|
1325
|
+
role: "group",
|
|
1326
|
+
label: props.label
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
const hasLineNumberClick = computed(() => instance?.vnode.props?.onLineNumberClick != null);
|
|
1330
|
+
function relayLineNumberClick(ref, event) {
|
|
1331
|
+
emit("lineNumberClick", ref, event);
|
|
1332
|
+
}
|
|
1333
|
+
return (_ctx, _cache) => {
|
|
1334
|
+
return openBlock(), createElementBlock("div", mergeProps(rootAttrs.value, {
|
|
1335
|
+
class: ["astryx-diff-viewer", rootClass.value],
|
|
1336
|
+
style: rootStyle.value,
|
|
1337
|
+
"data-view-mode": __props.viewMode
|
|
1338
|
+
}), [(openBlock(), createBlock(resolveDynamicComponent(__props.syntaxTheme != null ? unref(SyntaxTheme) : "div"), normalizeProps(guardReactiveProps(__props.syntaxTheme != null ? { theme: __props.syntaxTheme } : {})), {
|
|
1339
|
+
default: withCtx(() => [hasHeader.value ? (openBlock(), createElementBlock("div", {
|
|
1340
|
+
key: 0,
|
|
1341
|
+
class: normalizeClass(["astryx-diff-viewer__header", { "astryx-diff-viewer__header--divided": !isCollapsed.value }])
|
|
1342
|
+
}, [
|
|
1343
|
+
canCollapse.value ? (openBlock(), createElementBlock("button", {
|
|
1344
|
+
key: 0,
|
|
1345
|
+
type: "button",
|
|
1346
|
+
class: "astryx-diff-viewer__toggle",
|
|
1347
|
+
"aria-expanded": !isCollapsed.value,
|
|
1348
|
+
"aria-controls": bodyId,
|
|
1349
|
+
onClick: toggleCollapsed
|
|
1350
|
+
}, [createElementVNode("span", { class: normalizeClass(["astryx-diff-viewer__chevron", { "astryx-diff-viewer__chevron--expanded": !isCollapsed.value }]) }, [createVNode(unref(Icon), {
|
|
1351
|
+
icon: "chevronRight",
|
|
1352
|
+
size: "xsm",
|
|
1353
|
+
color: "inherit"
|
|
1354
|
+
})], 2), createElementVNode("span", _hoisted_3$9, toDisplayString(displayPath.value), 1)], 8, _hoisted_2$10)) : displayPath.value != null ? (openBlock(), createElementBlock("span", _hoisted_4$8, toDisplayString(displayPath.value), 1)) : createCommentVNode("v-if", true),
|
|
1355
|
+
statusColor.value != null && statusLabel.value != null ? (openBlock(), createBlock(unref(Token), {
|
|
1356
|
+
key: 2,
|
|
1357
|
+
size: "sm",
|
|
1358
|
+
color: statusColor.value,
|
|
1359
|
+
label: statusLabel.value
|
|
1360
|
+
}, null, 8, ["color", "label"])) : createCommentVNode("v-if", true),
|
|
1361
|
+
stats.value.added > 0 || stats.value.removed > 0 ? (openBlock(), createElementBlock("span", _hoisted_5$7, [
|
|
1362
|
+
createElementVNode("span", _hoisted_6$6, toDisplayString(addedText.value), 1),
|
|
1363
|
+
createElementVNode("span", _hoisted_7$4, toDisplayString(removedText.value), 1),
|
|
1364
|
+
createVNode(unref(VisuallyHidden), null, {
|
|
1365
|
+
default: withCtx(() => [createTextVNode(toDisplayString(statsText.value), 1)]),
|
|
1366
|
+
_: 1
|
|
1367
|
+
})
|
|
1368
|
+
])) : createCommentVNode("v-if", true),
|
|
1369
|
+
_ctx.$slots.headerEnd ? (openBlock(), createElementBlock("div", _hoisted_8$3, [renderSlot(_ctx.$slots, "headerEnd")])) : createCommentVNode("v-if", true)
|
|
1370
|
+
], 2)) : createCommentVNode("v-if", true), createElementVNode("div", {
|
|
1371
|
+
id: bodyId,
|
|
1372
|
+
hidden: isCollapsed.value
|
|
1373
|
+
}, [!isCollapsed.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.file.isBinary ? (openBlock(), createElementBlock("div", _hoisted_10$3, " Binary file not shown ")) : __props.file.hunks.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_11$1, " No content changes ")) : (openBlock(), createElementBlock("div", mergeProps({
|
|
1374
|
+
key: 2,
|
|
1375
|
+
ref: unref(scroll).viewport
|
|
1376
|
+
}, unref(scroll).viewportAttrs.value, {
|
|
1377
|
+
class: "astryx-diff-viewer__viewport",
|
|
1378
|
+
"aria-label": scrollLabel.value,
|
|
1379
|
+
style: [unref(scroll).viewportStyle.value, scrollStyle.value]
|
|
1380
|
+
}), [createElementVNode("div", {
|
|
1381
|
+
ref: unref(scroll).content,
|
|
1382
|
+
class: "astryx-diff-viewer__content"
|
|
1383
|
+
}, [createVNode(_sfc_main$13, {
|
|
1384
|
+
"view-mode": __props.viewMode,
|
|
1385
|
+
"unified-rows": unifiedRows.value,
|
|
1386
|
+
"split-rows": splitRows.value,
|
|
1387
|
+
"has-line-numbers": __props.hasLineNumbers,
|
|
1388
|
+
"is-wrapped": __props.isWrapped,
|
|
1389
|
+
"has-line-number-click": hasLineNumberClick.value,
|
|
1390
|
+
digits: stats.value.digits,
|
|
1391
|
+
onLineNumberClick: relayLineNumberClick
|
|
1392
|
+
}, null, 8, [
|
|
1393
|
+
"view-mode",
|
|
1394
|
+
"unified-rows",
|
|
1395
|
+
"split-rows",
|
|
1396
|
+
"has-line-numbers",
|
|
1397
|
+
"is-wrapped",
|
|
1398
|
+
"has-line-number-click",
|
|
1399
|
+
"digits"
|
|
1400
|
+
])], 512)], 16, _hoisted_12))], 64)) : createCommentVNode("v-if", true)], 8, _hoisted_9$3)]),
|
|
1401
|
+
_: 3
|
|
1402
|
+
}, 16))], 16, _hoisted_1$11);
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
//#endregion
|
|
1407
|
+
//#region src/DiffViewer/parseUnifiedDiff.ts
|
|
1408
|
+
const HUNK_HEADER = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
1409
|
+
const GIT_HEADER_PREFIX = "diff --git ";
|
|
1410
|
+
const BINARY_PREFIX = "Binary files ";
|
|
1411
|
+
const BINARY_SUFFIX = " differ";
|
|
1412
|
+
const BINARY_SEPARATOR = " and ";
|
|
1413
|
+
/**
|
|
1414
|
+
* The two paths of a `Binary files X and Y differ` line, or null when the
|
|
1415
|
+
* line is not one. Plain string matching, not a regex, so any line (however
|
|
1416
|
+
* long, whatever characters it holds) is read in one pass. Like the greedy
|
|
1417
|
+
* pattern it replaces, the separator is the last ` and `.
|
|
1418
|
+
*/
|
|
1419
|
+
function parseBinaryLine(line) {
|
|
1420
|
+
if (!line.startsWith(BINARY_PREFIX) || !line.endsWith(BINARY_SUFFIX)) return null;
|
|
1421
|
+
const middle = line.slice(13, line.length - 7);
|
|
1422
|
+
const split = middle.lastIndexOf(BINARY_SEPARATOR);
|
|
1423
|
+
if (split <= 0 || split + 5 >= middle.length) return null;
|
|
1424
|
+
return {
|
|
1425
|
+
oldPath: middle.slice(0, split),
|
|
1426
|
+
newPath: middle.slice(split + 5)
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
const C_ESCAPES = {
|
|
1430
|
+
"a": 7,
|
|
1431
|
+
"b": 8,
|
|
1432
|
+
"t": 9,
|
|
1433
|
+
"n": 10,
|
|
1434
|
+
"v": 11,
|
|
1435
|
+
"f": 12,
|
|
1436
|
+
"r": 13,
|
|
1437
|
+
"\"": 34,
|
|
1438
|
+
"\\": 92
|
|
1439
|
+
};
|
|
1440
|
+
const utf8 = new TextDecoder();
|
|
1441
|
+
const utf8Encoder = new TextEncoder();
|
|
1442
|
+
/**
|
|
1443
|
+
* Read a git C-style quoted path starting at `start` (the opening quote).
|
|
1444
|
+
* Git quotes a path that holds non-ASCII bytes or special characters and
|
|
1445
|
+
* writes those bytes as octal escapes (`"caf\303\251.md"`), so the escapes
|
|
1446
|
+
* are collected as bytes and decoded as UTF-8. Returns the path and the index
|
|
1447
|
+
* just past the closing quote, or null when the quote never closes.
|
|
1448
|
+
*/
|
|
1449
|
+
function readQuoted(text, start) {
|
|
1450
|
+
const bytes = [];
|
|
1451
|
+
let i = start + 1;
|
|
1452
|
+
while (i < text.length) {
|
|
1453
|
+
const ch = text[i];
|
|
1454
|
+
if (ch === "\"") return {
|
|
1455
|
+
value: utf8.decode(new Uint8Array(bytes)),
|
|
1456
|
+
end: i + 1
|
|
1457
|
+
};
|
|
1458
|
+
if (ch === "\\" && i + 1 < text.length) {
|
|
1459
|
+
const next = text[i + 1];
|
|
1460
|
+
const octal = /^[0-7]{3}/.exec(text.slice(i + 1, i + 4));
|
|
1461
|
+
if (octal != null) {
|
|
1462
|
+
bytes.push(Number.parseInt(octal[0], 8));
|
|
1463
|
+
i += 4;
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1466
|
+
if (next in C_ESCAPES) {
|
|
1467
|
+
bytes.push(C_ESCAPES[next]);
|
|
1468
|
+
i += 2;
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
const codePoint = text.codePointAt(i) ?? 0;
|
|
1473
|
+
const char = String.fromCodePoint(codePoint);
|
|
1474
|
+
bytes.push(...utf8Encoder.encode(char));
|
|
1475
|
+
i += char.length;
|
|
1476
|
+
}
|
|
1477
|
+
return null;
|
|
1478
|
+
}
|
|
1479
|
+
/** The path written in `raw`, unquoted and decoded when git quoted it. */
|
|
1480
|
+
function unquotePath(raw) {
|
|
1481
|
+
if (raw.startsWith("\"")) {
|
|
1482
|
+
const quoted = readQuoted(raw, 0);
|
|
1483
|
+
if (quoted != null) return quoted.value;
|
|
1484
|
+
}
|
|
1485
|
+
return raw;
|
|
1486
|
+
}
|
|
1487
|
+
function stripPrefix(path, prefix) {
|
|
1488
|
+
return path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* The two paths of a `diff --git` line. Git quotes a path that holds special
|
|
1492
|
+
* or non-ASCII characters; quoted paths are read whole and decoded. An
|
|
1493
|
+
* unquoted path may hold spaces, so a space can look like the separator: when
|
|
1494
|
+
* both sides name the same path (every change except a rename) the separator
|
|
1495
|
+
* is the middle space. A rename writes `rename from` / `rename to` lines,
|
|
1496
|
+
* which replace these paths, so the first-space split is only a fallback.
|
|
1497
|
+
*/
|
|
1498
|
+
function parseGitHeader(line) {
|
|
1499
|
+
if (!line.startsWith(GIT_HEADER_PREFIX)) return null;
|
|
1500
|
+
const rest = line.slice(11);
|
|
1501
|
+
const paths = (left, right) => ({
|
|
1502
|
+
oldPath: stripPrefix(left, "a/"),
|
|
1503
|
+
newPath: stripPrefix(right, "b/")
|
|
1504
|
+
});
|
|
1505
|
+
if (rest.startsWith("\"")) {
|
|
1506
|
+
const left = readQuoted(rest, 0);
|
|
1507
|
+
if (left != null && rest[left.end] === " ") return paths(left.value, unquotePath(rest.slice(left.end + 1)));
|
|
1508
|
+
}
|
|
1509
|
+
if (rest.endsWith("\"")) for (let split = rest.lastIndexOf(" \""); split > 0; split = rest.lastIndexOf(" \"", split - 1)) {
|
|
1510
|
+
const right = readQuoted(rest, split + 1);
|
|
1511
|
+
if (right != null && right.end === rest.length) return paths(rest.slice(0, split), right.value);
|
|
1512
|
+
}
|
|
1513
|
+
if (rest.length % 2 === 1) {
|
|
1514
|
+
const middle = (rest.length - 1) / 2;
|
|
1515
|
+
const left = rest.slice(0, middle);
|
|
1516
|
+
const right = rest.slice(middle + 1);
|
|
1517
|
+
if (rest[middle] === " " && stripPrefix(left, "a/") === stripPrefix(right, "b/")) return paths(left, right);
|
|
1518
|
+
}
|
|
1519
|
+
const space = rest.indexOf(" ");
|
|
1520
|
+
return space <= 0 ? null : paths(rest.slice(0, space), rest.slice(space + 1));
|
|
1521
|
+
}
|
|
1522
|
+
function emptyFile() {
|
|
1523
|
+
return {
|
|
1524
|
+
oldPath: null,
|
|
1525
|
+
newPath: null,
|
|
1526
|
+
status: null,
|
|
1527
|
+
isBinary: false,
|
|
1528
|
+
hunks: []
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* `a/src/x.ts` → `src/x.ts`; `/dev/null` → null; drops a trailing
|
|
1533
|
+
* tab+timestamp and decodes a quoted path.
|
|
1534
|
+
*/
|
|
1535
|
+
function cleanPath(raw, prefix) {
|
|
1536
|
+
let path = unquotePath(raw.split(" ")[0].trim());
|
|
1537
|
+
if (path === "/dev/null") return null;
|
|
1538
|
+
if (path.startsWith(prefix)) path = path.slice(prefix.length);
|
|
1539
|
+
return path;
|
|
1540
|
+
}
|
|
1541
|
+
function finalize(file) {
|
|
1542
|
+
let status = file.status;
|
|
1543
|
+
if (status == null) {
|
|
1544
|
+
if (file.oldPath == null && file.newPath != null) status = "added";
|
|
1545
|
+
else if (file.newPath == null && file.oldPath != null) status = "deleted";
|
|
1546
|
+
else status = "modified";
|
|
1547
|
+
}
|
|
1548
|
+
return {
|
|
1549
|
+
oldPath: status === "added" ? null : file.oldPath,
|
|
1550
|
+
newPath: status === "deleted" ? null : file.newPath,
|
|
1551
|
+
status,
|
|
1552
|
+
isBinary: file.isBinary,
|
|
1553
|
+
hunks: file.hunks
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function hasContent(file) {
|
|
1557
|
+
return file.hunks.length > 0 || file.isBinary || file.oldPath != null || file.newPath != null || file.status != null;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Parse a unified diff into one {@link DiffViewerFile} per file.
|
|
1561
|
+
*
|
|
1562
|
+
* Accepts `git diff` output (with `diff --git`, mode, rename, copy, and
|
|
1563
|
+
* binary lines), classic `diff -u` output (`---`/`+++` pairs), several files in one
|
|
1564
|
+
* patch, and a bare `@@` hunk with no file header (returned as one file with
|
|
1565
|
+
* null paths). Input it cannot read yields `[]`; it never throws.
|
|
1566
|
+
*
|
|
1567
|
+
* @example
|
|
1568
|
+
* ```
|
|
1569
|
+
* const files = parseUnifiedDiff(patch);
|
|
1570
|
+
* files.map((file) => <DiffViewer key={file.newPath ?? file.oldPath} file={file} />);
|
|
1571
|
+
* ```
|
|
1572
|
+
*/
|
|
1573
|
+
function parseUnifiedDiff(patch) {
|
|
1574
|
+
const lines = patch.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
1575
|
+
const files = [];
|
|
1576
|
+
let file = null;
|
|
1577
|
+
let hunk = null;
|
|
1578
|
+
let oldRemaining = 0;
|
|
1579
|
+
let newRemaining = 0;
|
|
1580
|
+
let oldLine = 0;
|
|
1581
|
+
let newLine = 0;
|
|
1582
|
+
const flush = () => {
|
|
1583
|
+
if (file != null && hasContent(file)) files.push(finalize(file));
|
|
1584
|
+
file = null;
|
|
1585
|
+
hunk = null;
|
|
1586
|
+
};
|
|
1587
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1588
|
+
const line = lines[i];
|
|
1589
|
+
if (hunk != null && (oldRemaining > 0 || newRemaining > 0)) {
|
|
1590
|
+
const glyph = line[0];
|
|
1591
|
+
if (glyph === "\\") continue;
|
|
1592
|
+
if (glyph === "+") {
|
|
1593
|
+
hunk.lines.push({
|
|
1594
|
+
kind: "added",
|
|
1595
|
+
content: line.slice(1),
|
|
1596
|
+
oldLineNumber: null,
|
|
1597
|
+
newLineNumber: newLine++
|
|
1598
|
+
});
|
|
1599
|
+
newRemaining--;
|
|
1600
|
+
continue;
|
|
1601
|
+
}
|
|
1602
|
+
if (glyph === "-") {
|
|
1603
|
+
hunk.lines.push({
|
|
1604
|
+
kind: "removed",
|
|
1605
|
+
content: line.slice(1),
|
|
1606
|
+
oldLineNumber: oldLine++,
|
|
1607
|
+
newLineNumber: null
|
|
1608
|
+
});
|
|
1609
|
+
oldRemaining--;
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
if (glyph === " " || line === "") {
|
|
1613
|
+
if (line === "" && i === lines.length - 1) continue;
|
|
1614
|
+
hunk.lines.push({
|
|
1615
|
+
kind: "context",
|
|
1616
|
+
content: line.slice(1),
|
|
1617
|
+
oldLineNumber: oldLine++,
|
|
1618
|
+
newLineNumber: newLine++
|
|
1619
|
+
});
|
|
1620
|
+
oldRemaining--;
|
|
1621
|
+
newRemaining--;
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
hunk = null;
|
|
1625
|
+
}
|
|
1626
|
+
if (hunk != null && line.startsWith("\\")) continue;
|
|
1627
|
+
const gitPaths = parseGitHeader(line);
|
|
1628
|
+
if (gitPaths != null) {
|
|
1629
|
+
flush();
|
|
1630
|
+
file = emptyFile();
|
|
1631
|
+
file.oldPath = gitPaths.oldPath;
|
|
1632
|
+
file.newPath = gitPaths.newPath;
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (line.startsWith("--- ") && (lines[i + 1]?.startsWith("+++ ") ?? false)) {
|
|
1636
|
+
if (file == null || file.hunks.length > 0 || file.isBinary) {
|
|
1637
|
+
flush();
|
|
1638
|
+
file = emptyFile();
|
|
1639
|
+
}
|
|
1640
|
+
const current = file;
|
|
1641
|
+
current.oldPath = cleanPath(line.slice(4), "a/");
|
|
1642
|
+
current.newPath = cleanPath(lines[i + 1].slice(4), "b/");
|
|
1643
|
+
if (current.oldPath == null && current.status == null) current.status = "added";
|
|
1644
|
+
else if (current.newPath == null && current.status == null) current.status = "deleted";
|
|
1645
|
+
i++;
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
const header = HUNK_HEADER.exec(line);
|
|
1649
|
+
if (header) {
|
|
1650
|
+
file ??= emptyFile();
|
|
1651
|
+
const oldStart = Number(header[1]);
|
|
1652
|
+
const oldLines = header[2] == null ? 1 : Number(header[2]);
|
|
1653
|
+
const newStart = Number(header[3]);
|
|
1654
|
+
const newLines = header[4] == null ? 1 : Number(header[4]);
|
|
1655
|
+
const next = {
|
|
1656
|
+
header: line,
|
|
1657
|
+
oldStart,
|
|
1658
|
+
oldLines,
|
|
1659
|
+
newStart,
|
|
1660
|
+
newLines,
|
|
1661
|
+
lines: []
|
|
1662
|
+
};
|
|
1663
|
+
file.hunks.push(next);
|
|
1664
|
+
hunk = next;
|
|
1665
|
+
oldRemaining = oldLines;
|
|
1666
|
+
newRemaining = newLines;
|
|
1667
|
+
oldLine = oldLines === 0 ? oldStart + 1 : oldStart;
|
|
1668
|
+
newLine = newLines === 0 ? newStart + 1 : newStart;
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
const binary = parseBinaryLine(line);
|
|
1672
|
+
if (binary != null && (file == null || file.hunks.length > 0 || file.isBinary)) {
|
|
1673
|
+
flush();
|
|
1674
|
+
const own = emptyFile();
|
|
1675
|
+
own.oldPath = cleanPath(binary.oldPath, "a/");
|
|
1676
|
+
own.newPath = cleanPath(binary.newPath, "b/");
|
|
1677
|
+
own.isBinary = true;
|
|
1678
|
+
file = own;
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
if (file == null) continue;
|
|
1682
|
+
const current = file;
|
|
1683
|
+
if (line.startsWith("new file mode")) current.status = "added";
|
|
1684
|
+
else if (line.startsWith("deleted file mode")) current.status = "deleted";
|
|
1685
|
+
else if (line.startsWith("rename from ")) {
|
|
1686
|
+
current.oldPath = unquotePath(line.slice(12));
|
|
1687
|
+
current.status = "renamed";
|
|
1688
|
+
} else if (line.startsWith("rename to ")) {
|
|
1689
|
+
current.newPath = unquotePath(line.slice(10));
|
|
1690
|
+
current.status = "renamed";
|
|
1691
|
+
} else if (line.startsWith("copy from ")) current.oldPath = unquotePath(line.slice(10));
|
|
1692
|
+
else if (line.startsWith("copy to ")) current.newPath = unquotePath(line.slice(8));
|
|
1693
|
+
else if (line.startsWith("Binary files ") || line === "GIT binary patch") current.isBinary = true;
|
|
1694
|
+
}
|
|
1695
|
+
flush();
|
|
1696
|
+
return files;
|
|
1697
|
+
}
|
|
1698
|
+
//#endregion
|
|
184
1699
|
//#region src/InfoTip/InfoTip.vue
|
|
185
1700
|
const _hoisted_1$10 = ["aria-label"];
|
|
186
|
-
const _sfc_main$
|
|
1701
|
+
const _sfc_main$2 = /*@__PURE__*/ defineComponent({
|
|
187
1702
|
name: "AstryxInfoTip",
|
|
188
1703
|
__name: "InfoTip",
|
|
189
1704
|
props: {
|
|
@@ -310,7 +1825,7 @@ const _hoisted_7$3 = {
|
|
|
310
1825
|
key: 0,
|
|
311
1826
|
class: "astryx-list-input__control-cell astryx-list-input__control-cell--reorder"
|
|
312
1827
|
};
|
|
313
|
-
const _sfc_main$
|
|
1828
|
+
const _sfc_main$12 = /*@__PURE__*/ defineComponent({
|
|
314
1829
|
name: "AstryxListInputRow",
|
|
315
1830
|
__name: "ListInputRow",
|
|
316
1831
|
props: {
|
|
@@ -756,7 +2271,7 @@ const _hoisted_5$5 = {
|
|
|
756
2271
|
class: "astryx-list-input__action-content"
|
|
757
2272
|
};
|
|
758
2273
|
const focusableFieldSelector = "[data-list-input-cell] input, [data-list-input-cell] select, [data-list-input-cell] textarea, [data-list-input-cell] button, [data-list-input-cell] [tabindex]:not([tabindex=\"-1\"])";
|
|
759
|
-
const _sfc_main$
|
|
2274
|
+
const _sfc_main$3 = /*@__PURE__*/ defineComponent({
|
|
760
2275
|
name: "AstryxListInput",
|
|
761
2276
|
inheritAttrs: false,
|
|
762
2277
|
__name: "ListInput",
|
|
@@ -1034,7 +2549,7 @@ const _sfc_main$2 = /*@__PURE__*/ defineComponent({
|
|
|
1034
2549
|
description: unref(t)("@astryx.listInput.emptyDescription", { itemName: __props.itemName }),
|
|
1035
2550
|
"is-compact": ""
|
|
1036
2551
|
}, null, 8, ["title", "description"])])) : createCommentVNode("v-if", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(reorder).displayValue.value, (item, index) => {
|
|
1037
|
-
return openBlock(), createBlock(_sfc_main$
|
|
2552
|
+
return openBlock(), createBlock(_sfc_main$12, {
|
|
1038
2553
|
key: itemKeyOf(item),
|
|
1039
2554
|
item,
|
|
1040
2555
|
index,
|
|
@@ -1149,7 +2664,7 @@ const _hoisted_11 = {
|
|
|
1149
2664
|
key: 0,
|
|
1150
2665
|
class: "astryx-log-stream__detail"
|
|
1151
2666
|
};
|
|
1152
|
-
const _sfc_main$
|
|
2667
|
+
const _sfc_main$11 = /*@__PURE__*/ defineComponent({
|
|
1153
2668
|
name: "AstryxLogStreamRow",
|
|
1154
2669
|
__name: "LogStreamRow",
|
|
1155
2670
|
props: {
|
|
@@ -1253,7 +2768,7 @@ function useLogFollow(options) {
|
|
|
1253
2768
|
//#region src/LogStream/LogStream.vue
|
|
1254
2769
|
const _hoisted_1$6 = ["data-variant"];
|
|
1255
2770
|
const _hoisted_2$6 = ["aria-label", "aria-live"];
|
|
1256
|
-
const _sfc_main$
|
|
2771
|
+
const _sfc_main$4 = /*@__PURE__*/ defineComponent({
|
|
1257
2772
|
name: "AstryxLogStream",
|
|
1258
2773
|
inheritAttrs: false,
|
|
1259
2774
|
__name: "LogStream",
|
|
@@ -1319,7 +2834,7 @@ const _sfc_main$3 = /*@__PURE__*/ defineComponent({
|
|
|
1319
2834
|
style: normalizeStyle(scrollerStyle.value),
|
|
1320
2835
|
onScrollPassive: _cache[0] || (_cache[0] = (...args) => unref(handleScroll) && unref(handleScroll)(...args))
|
|
1321
2836
|
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.entries, (entry) => {
|
|
1322
|
-
return renderSlot(_ctx.$slots, "entry", { entry }, () => [createVNode(_sfc_main$
|
|
2837
|
+
return renderSlot(_ctx.$slots, "entry", { entry }, () => [createVNode(_sfc_main$11, {
|
|
1323
2838
|
entry,
|
|
1324
2839
|
"has-timestamps": __props.hasTimestamps,
|
|
1325
2840
|
"has-source": hasSource.value,
|
|
@@ -1485,7 +3000,7 @@ const _hoisted_10$1 = {
|
|
|
1485
3000
|
class: "astryx-mobile-tokenizer__footer"
|
|
1486
3001
|
};
|
|
1487
3002
|
const CREATABLE_ID_PREFIX = "__xds_create__";
|
|
1488
|
-
const _sfc_main$
|
|
3003
|
+
const _sfc_main$5 = /*@__PURE__*/ defineComponent({
|
|
1489
3004
|
name: "AstryxMobileTokenizer",
|
|
1490
3005
|
inheritAttrs: false,
|
|
1491
3006
|
__name: "MobileTokenizer",
|
|
@@ -1799,7 +3314,7 @@ const _hoisted_10 = {
|
|
|
1799
3314
|
key: 1,
|
|
1800
3315
|
class: "astryx-stat__media"
|
|
1801
3316
|
};
|
|
1802
|
-
const _sfc_main$
|
|
3317
|
+
const _sfc_main$7 = /*@__PURE__*/ defineComponent({
|
|
1803
3318
|
name: "AstryxStat",
|
|
1804
3319
|
inheritAttrs: false,
|
|
1805
3320
|
__name: "Stat",
|
|
@@ -2656,7 +4171,7 @@ const _hoisted_6$1 = {
|
|
|
2656
4171
|
key: 1,
|
|
2657
4172
|
class: "astryx-svg-icon__layer astryx-svg-icon__layer--secondary-stroke"
|
|
2658
4173
|
};
|
|
2659
|
-
const _sfc_main$
|
|
4174
|
+
const _sfc_main$6 = /*@__PURE__*/ defineComponent({
|
|
2660
4175
|
name: "AstryxSvgIcon",
|
|
2661
4176
|
inheritAttrs: false,
|
|
2662
4177
|
__name: "SVGIcon",
|
|
@@ -2960,7 +4475,7 @@ const _hoisted_5 = {
|
|
|
2960
4475
|
class: "astryx-transfer-list-panel__group-heading"
|
|
2961
4476
|
};
|
|
2962
4477
|
const _hoisted_6 = ["data-transfer-list-empty"];
|
|
2963
|
-
const _sfc_main$
|
|
4478
|
+
const _sfc_main$10 = /*@__PURE__*/ defineComponent({
|
|
2964
4479
|
name: "AstryxTransferListPanel",
|
|
2965
4480
|
__name: "TransferListPanel",
|
|
2966
4481
|
props: {
|
|
@@ -3432,7 +4947,7 @@ const _hoisted_3$1 = {
|
|
|
3432
4947
|
class: "astryx-transfer-list__controls"
|
|
3433
4948
|
};
|
|
3434
4949
|
const _hoisted_4 = { class: "astryx-transfer-list-collection" };
|
|
3435
|
-
const _sfc_main$
|
|
4950
|
+
const _sfc_main$8 = /*@__PURE__*/ defineComponent({
|
|
3436
4951
|
name: "AstryxTransferList",
|
|
3437
4952
|
inheritAttrs: false,
|
|
3438
4953
|
__name: "TransferList",
|
|
@@ -3706,7 +5221,7 @@ const _sfc_main$7 = /*@__PURE__*/ defineComponent({
|
|
|
3706
5221
|
default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("@astryx.transferList.reorderInstructions")), 1)]),
|
|
3707
5222
|
_: 1
|
|
3708
5223
|
}),
|
|
3709
|
-
createElementVNode("div", _hoisted_4, [createVNode(_sfc_main$
|
|
5224
|
+
createElementVNode("div", _hoisted_4, [createVNode(_sfc_main$10, {
|
|
3710
5225
|
side: "selected",
|
|
3711
5226
|
"heading-id": selectedHeadingId,
|
|
3712
5227
|
label: selectedLabel.value,
|
|
@@ -3742,7 +5257,7 @@ const _sfc_main$7 = /*@__PURE__*/ defineComponent({
|
|
|
3742
5257
|
"dragging-value",
|
|
3743
5258
|
"grabbed-value",
|
|
3744
5259
|
"drop-target"
|
|
3745
|
-
]), createVNode(_sfc_main$
|
|
5260
|
+
]), createVNode(_sfc_main$10, {
|
|
3746
5261
|
side: "available",
|
|
3747
5262
|
"heading-id": availableHeadingId,
|
|
3748
5263
|
label: availableLabel.value,
|
|
@@ -3797,7 +5312,7 @@ const _hoisted_3 = {
|
|
|
3797
5312
|
class: "astryx-transfer-list-selector__footer"
|
|
3798
5313
|
};
|
|
3799
5314
|
const DEFAULT_SELECTOR_WIDTH = "min(41rem, calc(100vw - 32px))";
|
|
3800
|
-
const _sfc_main$
|
|
5315
|
+
const _sfc_main$9 = /*@__PURE__*/ defineComponent({
|
|
3801
5316
|
name: "AstryxTransferListSelector",
|
|
3802
5317
|
__name: "TransferListSelector",
|
|
3803
5318
|
props: {
|
|
@@ -3934,7 +5449,7 @@ const _sfc_main$8 = /*@__PURE__*/ defineComponent({
|
|
|
3934
5449
|
}, [createElementVNode("fieldset", {
|
|
3935
5450
|
class: "astryx-transfer-list-selector__fieldset",
|
|
3936
5451
|
disabled: __props.isDisabled
|
|
3937
|
-
}, [createVNode(_sfc_main$
|
|
5452
|
+
}, [createVNode(_sfc_main$8, {
|
|
3938
5453
|
label: __props.label,
|
|
3939
5454
|
"is-label-hidden": "",
|
|
3940
5455
|
options: __props.options,
|
|
@@ -4013,4 +5528,4 @@ const _sfc_main$8 = /*@__PURE__*/ defineComponent({
|
|
|
4013
5528
|
}
|
|
4014
5529
|
});
|
|
4015
5530
|
//#endregion
|
|
4016
|
-
export { _sfc_main as CircularProgress, _sfc_main$1 as
|
|
5531
|
+
export { _sfc_main as CircularProgress, _sfc_main$1 as DiffViewer, _sfc_main$2 as InfoTip, _sfc_main$3 as ListInput, _sfc_main$4 as LogStream, _sfc_main$5 as MobileTokenizer, _sfc_main$6 as SVGIcon, _sfc_main$7 as Stat, _sfc_main$8 as TransferList, _sfc_main$9 as TransferListSelector, addCurvature, adjustTension, applyPersonality, bellIcon, calendarIcon, checkIcon, createDiffFile, eyeIcon, folderIcon, heartIcon, homeIcon, lockIcon, mailIcon, menuIcon, parseUnifiedDiff, roundCorners, searchIcon, settingsIcon, shieldIcon, starIcon, starterIcons, xIcon, xifBell, xifBellOverride, xifCheck, xifExamples, xifFile, xifHome, xifShield, xifStar };
|