@design.estate/dees-catalog 6.3.0 → 6.4.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/dist_bundle/bundle.js +219 -48
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.d.ts +9 -2
- package/dist_ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js +133 -30
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +10 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +77 -9
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.d.ts +2 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +31 -4
- package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +2 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.js +1 -1
- package/package.json +1 -1
- package/readme.hints.md +2 -1
- package/readme.md +4 -2
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00group-dataview/dees-dataview-codebox/dees-dataview-codebox.ts +135 -31
- package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +74 -7
- package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +43 -4
- package/ts_web/elements/00group-harness/toolregistry.ts +7 -1
package/readme.md
CHANGED
|
@@ -1201,7 +1201,7 @@ Code display component with syntax highlighting and line numbers.
|
|
|
1201
1201
|
></dees-dataview-codebox>
|
|
1202
1202
|
```
|
|
1203
1203
|
|
|
1204
|
-
Diff rendering includes word-level intraline change highlighting, unchanged-context folding with expandable "N unchanged lines" rows, `+added −removed` stats in the footer, and a "Copy Unified Diff" context-menu action. The copy button yields the after-state text. The dependency-free engine (`computeLineDiff`, `parseUnifiedDiff`, `foldContextRows`, `buildSplitRows`, `diffStats`, `toUnifiedDiff`) is exported for programmatic use.
|
|
1204
|
+
Diff rendering includes word-level intraline change highlighting, unchanged-context folding with expandable "N unchanged lines" rows, `+added −removed` stats in the footer, and a "Copy Unified Diff" context-menu action. The app bar switches between inline and split layouts and emits the bubbling, composed `diff-view-change` event with `IDiffViewChangeDetail` (`{ diffView: TDiffView }`). The exported `TDiffView` type is `'inline' | 'split'`. Split mode keeps a fixed center divider and gives each side an independent horizontal scroll position while the containing surface owns their shared vertical movement. Set `showDiffLineNumbers` to `false` when snippet inputs do not carry trustworthy source positions. The copy button yields the after-state text. The dependency-free engine (`computeLineDiff`, `parseUnifiedDiff`, `foldContextRows`, `buildSplitRows`, `diffStats`, `toUnifiedDiff`) is exported for programmatic use.
|
|
1205
1205
|
|
|
1206
1206
|
#### `DeesDataviewStatusobject`
|
|
1207
1207
|
Status display component for complex objects with nested status indicators.
|
|
@@ -1821,7 +1821,7 @@ One message: role accent, markdown (assistant default, `message.markdown` overri
|
|
|
1821
1821
|
|
|
1822
1822
|
#### `DeesHarnessToolCard`
|
|
1823
1823
|
|
|
1824
|
-
Tool-call card: shared chrome (icon, label, subtitle, status pill, duration, MCP badges) with a per-kind body. Kind resolution goes through `DeesHarnessToolRegistry` (see below); bodies for the seeded kinds — terminal, file-read, file-write (renders a before/after diff via `dees-dataview-codebox` when the input carries previous content), file-delete, dir-list, browser (inline screenshots), http, json, search, subtask, todo, mcp, unknown — live in `renderers.ts`. Overflowing subtitles use a 40px right-edge fade and reveal the full line on hover plus once after a two-second initial delay. Expand/collapse always uses a smooth disclosure transition. Initially closed bodies mount lazily on first expansion, then retain the same DOM and become inert plus `aria-hidden` while closed. Long outputs truncate at 6,000 chars with a "Show all" expander. Terminal and error cards open by default. Terminal calls may provide `exitCode` separately when `output` is one combined process stream; the renderer retains support for `{ stdout, stderr, exitCode }` output objects.
|
|
1824
|
+
Tool-call card: shared chrome (icon, label, subtitle, status pill, duration, MCP badges) with a per-kind body. Kind resolution goes through `DeesHarnessToolRegistry` (see below); bodies for the seeded kinds — terminal, file-read, file-write (renders a before/after diff via `dees-dataview-codebox` when the input carries previous content), file-delete, dir-list, browser (inline screenshots), http, json, search, subtask, todo, mcp, unknown — live in `renderers.ts`. File-write cards retain the user's inline/split choice while the same call updates. Numeric patch hunks preserve their source line numbers; patches and replacement snippets without coordinates hide gutters rather than presenting fabricated positions. Overflowing subtitles use a 40px right-edge fade and reveal the full line on hover plus once after a two-second initial delay. Expand/collapse always uses a smooth disclosure transition. Initially closed bodies mount lazily on first expansion, then retain the same DOM and become inert plus `aria-hidden` while closed. Long outputs truncate at 6,000 chars with a "Show all" expander. Terminal and error cards open by default. Terminal output uses a black inner log surface and follows the live tail after reaching the card-height cap. Scrolling upward detaches the tail; leaving the card returns to the bottom after five seconds, while re-entering cancels that pending return. Terminal calls may provide `exitCode` separately when `output` is one combined process stream; the renderer retains support for `{ stdout, stderr, exitCode }` output objects.
|
|
1825
1825
|
|
|
1826
1826
|
Subtask calls may carry `subtask: IHarnessSubtaskStream`. While a projected child is busy, its Agent Brief is the leading item inside the same independently scrolling child transcript as its messages, permissions, questions, and status; no extra pinned or boxed brief sits above it. Dynamic 40px edge fades appear at the top only when content exists above and at the bottom only when content exists below, while wheel/touch scrolling remains available without visible scrollbars. Loading and unavailable projections reserve the same stable space. Once the child becomes idle or errored, the live transcript is removed from the card body and the disclosure cleanly renders Agent Brief followed by a scrollbar-free scrolling Result with the same position-aware edge fades; the full transcript remains available through the final bottom-positioned Open subagent chat action. `subtask.sessionId` is authoritative for drill-in when a projection exists; calls without a projection continue to use the existing `childSessionId` field. Authoritatively busy previews open by default and every expanded preview is bounded by `--dees-harness-active-subtask-height` (default `320px`), including its completion transition. The user's local expand/collapse choice wins. Completed subtask cards stay in their subtask-only chronological grid and untouched cards collapse individually without a grid-level summary. Hidden retained previews pause auto-follow observation without unmounting their DOM and catch up to the newest child content when reopened. Set `allowSubtaskStreams` false on a direct tool-card consumer to suppress the preview. The host remains responsible for hydration, transfer limits, authorization, gap recovery, and updating the shared objects; `truncated` projections visibly disclose omitted earlier messages.
|
|
1827
1827
|
|
|
@@ -1854,6 +1854,8 @@ DeesHarnessToolRegistry.default.register({
|
|
|
1854
1854
|
});
|
|
1855
1855
|
```
|
|
1856
1856
|
|
|
1857
|
+
Custom tool renderers receive `IHarnessToolRenderHelpers`. Its `diffBlock(before, after, options)` helper accepts `language`, `filename`, and `view`, plus `unifiedDiff` for a positioned patch and `showLineNumbers` to suppress untrustworthy gutters.
|
|
1858
|
+
|
|
1857
1859
|
`server__tool` names resolve automatically to the MCP kind with server + annotation badges (`readOnlyHint` → success, `destructiveHint` → error, `openWorldHint` → warning). Per-element isolation: assign a forked registry to `.toolRegistry`.
|
|
1858
1860
|
|
|
1859
1861
|
Descriptors may set `layout: 'compact' | 'full-row' | 'subtask'` to control transcript grid packing; omitted custom descriptors default to `compact`. The built-in terminal and file-write descriptors are `full-row`, while the built-in subtask descriptor uses the isolated `subtask` lane.
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@design.estate/dees-catalog',
|
|
6
|
-
version: '6.
|
|
6
|
+
version: '6.4.0',
|
|
7
7
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
|
8
8
|
}
|
|
@@ -40,6 +40,12 @@ declare global {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
export type TDiffView = 'inline' | 'split';
|
|
44
|
+
|
|
45
|
+
export interface IDiffViewChangeDetail {
|
|
46
|
+
diffView: TDiffView;
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
@customElement('dees-dataview-codebox')
|
|
44
50
|
export class DeesDataviewCodebox extends DeesElement {
|
|
45
51
|
public static demo = demoFunc;
|
|
@@ -69,7 +75,11 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
69
75
|
|
|
70
76
|
/** '' = regular code view; 'inline' = unified rows; 'split' = side by side */
|
|
71
77
|
@property({ type: String })
|
|
72
|
-
accessor diffView: '' |
|
|
78
|
+
accessor diffView: '' | TDiffView = '';
|
|
79
|
+
|
|
80
|
+
/** Hide gutters when a diff source does not provide trustworthy positions. */
|
|
81
|
+
@property({ type: Boolean })
|
|
82
|
+
accessor showDiffLineNumbers: boolean = true;
|
|
73
83
|
|
|
74
84
|
@state()
|
|
75
85
|
accessor highlightedCode: string = '';
|
|
@@ -123,9 +133,40 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
123
133
|
.appbar .actions {
|
|
124
134
|
grid-column: 3;
|
|
125
135
|
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
gap: var(--dees-spacing-sm);
|
|
126
138
|
justify-content: flex-end;
|
|
127
139
|
}
|
|
128
140
|
|
|
141
|
+
.viewToggle {
|
|
142
|
+
display: flex;
|
|
143
|
+
padding: 2px;
|
|
144
|
+
border: 1px solid var(--dees-color-border-subtle);
|
|
145
|
+
border-radius: var(--dees-radius-md);
|
|
146
|
+
background: var(--dees-color-bg-tertiary);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.viewButton {
|
|
150
|
+
padding: 3px 7px;
|
|
151
|
+
border: 0;
|
|
152
|
+
border-radius: calc(var(--dees-radius-md) - 2px);
|
|
153
|
+
background: transparent;
|
|
154
|
+
color: var(--dees-color-text-muted);
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
font: inherit;
|
|
157
|
+
font-size: var(--dees-font-caption2-size);
|
|
158
|
+
line-height: 1.2;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.viewButton:hover {
|
|
162
|
+
color: var(--dees-color-text-primary);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.viewButton.active {
|
|
166
|
+
background: var(--dees-color-active);
|
|
167
|
+
color: var(--dees-color-text-primary);
|
|
168
|
+
}
|
|
169
|
+
|
|
129
170
|
.copyButton {
|
|
130
171
|
display: flex;
|
|
131
172
|
align-items: center;
|
|
@@ -247,8 +288,37 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
247
288
|
grid-template-columns: minmax(3ch, auto) minmax(3ch, auto) 2ch minmax(0, 1fr);
|
|
248
289
|
}
|
|
249
290
|
|
|
250
|
-
.diffGrid.
|
|
251
|
-
grid-template-columns:
|
|
291
|
+
.diffGrid.inline.withoutLineNumbers {
|
|
292
|
+
grid-template-columns: 2ch minmax(0, 1fr);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.diffSplit {
|
|
296
|
+
display: grid;
|
|
297
|
+
grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr);
|
|
298
|
+
align-items: start;
|
|
299
|
+
min-width: 0;
|
|
300
|
+
padding: var(--dees-spacing-sm) 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.diffSplitDivider {
|
|
304
|
+
align-self: stretch;
|
|
305
|
+
background: var(--dees-color-border-default);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.diffSplitPane {
|
|
309
|
+
min-width: 0;
|
|
310
|
+
overflow-x: auto;
|
|
311
|
+
overflow-y: hidden;
|
|
312
|
+
scrollbar-width: thin;
|
|
313
|
+
scrollbar-color: var(--dees-color-scrollbar-thumb) transparent;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.diffGrid.splitSide {
|
|
317
|
+
grid-template-columns: minmax(3ch, auto) 2ch minmax(24ch, 1fr);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.diffGrid.splitSide.withoutLineNumbers {
|
|
321
|
+
grid-template-columns: 2ch minmax(24ch, 1fr);
|
|
252
322
|
}
|
|
253
323
|
|
|
254
324
|
.diffGrid > div {
|
|
@@ -336,10 +406,6 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
336
406
|
color: var(--dees-color-link-hover);
|
|
337
407
|
}
|
|
338
408
|
|
|
339
|
-
.diffSplitSep {
|
|
340
|
-
border-left: 1px solid var(--dees-color-border-subtle);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
409
|
.diffStat {
|
|
344
410
|
font-weight: 600;
|
|
345
411
|
}
|
|
@@ -382,6 +448,24 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
382
448
|
<div slot="header" class="appbar">
|
|
383
449
|
<div class="fileName">${this.filename}</div>
|
|
384
450
|
<div class="actions">
|
|
451
|
+
${this.isDiffActive
|
|
452
|
+
? html`
|
|
453
|
+
<div class="viewToggle" role="group" aria-label="Diff layout">
|
|
454
|
+
<button
|
|
455
|
+
class="viewButton ${this.effectiveDiffView === 'inline' ? 'active' : ''}"
|
|
456
|
+
type="button"
|
|
457
|
+
aria-pressed=${String(this.effectiveDiffView === 'inline')}
|
|
458
|
+
@click=${() => this.setDiffView('inline')}
|
|
459
|
+
>Inline</button>
|
|
460
|
+
<button
|
|
461
|
+
class="viewButton ${this.effectiveDiffView === 'split' ? 'active' : ''}"
|
|
462
|
+
type="button"
|
|
463
|
+
aria-pressed=${String(this.effectiveDiffView === 'split')}
|
|
464
|
+
@click=${() => this.setDiffView('split')}
|
|
465
|
+
>Split</button>
|
|
466
|
+
</div>
|
|
467
|
+
`
|
|
468
|
+
: ''}
|
|
385
469
|
<div
|
|
386
470
|
class="copyButton ${this.copied ? 'copied' : ''}"
|
|
387
471
|
title="Copy code"
|
|
@@ -431,18 +515,14 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
431
515
|
|
|
432
516
|
private renderDiff(): TemplateResult {
|
|
433
517
|
const displayRows = this.displayRowsWithFolds();
|
|
434
|
-
return
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
? this.renderSplitGrid(displayRows)
|
|
438
|
-
: this.renderInlineGrid(displayRows)}
|
|
439
|
-
</div>
|
|
440
|
-
`;
|
|
518
|
+
return this.effectiveDiffView === 'split'
|
|
519
|
+
? this.renderSplitGrid(displayRows)
|
|
520
|
+
: html`<div class="diffScroll">${this.renderInlineGrid(displayRows)}</div>`;
|
|
441
521
|
}
|
|
442
522
|
|
|
443
523
|
private renderInlineGrid(displayRows: TDiffDisplayRow[]): TemplateResult {
|
|
444
524
|
return html`
|
|
445
|
-
<div class="diffGrid inline">
|
|
525
|
+
<div class="diffGrid inline ${this.showDiffLineNumbers ? '' : 'withoutLineNumbers'}">
|
|
446
526
|
${displayRows.map((displayRow) => {
|
|
447
527
|
if (displayRow.kind === 'fold') {
|
|
448
528
|
return this.renderFoldRow(displayRow.rows.length, displayRow.ordinal ?? 0);
|
|
@@ -452,8 +532,12 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
452
532
|
}
|
|
453
533
|
const rowClass = displayRow.kind === 'add' ? 'row-add' : displayRow.kind === 'remove' ? 'row-remove' : '';
|
|
454
534
|
return html`
|
|
455
|
-
|
|
456
|
-
|
|
535
|
+
${this.showDiffLineNumbers
|
|
536
|
+
? html`
|
|
537
|
+
<div class="diffNum ${rowClass}">${displayRow.oldLine ?? ''}</div>
|
|
538
|
+
<div class="diffNum ${rowClass}">${displayRow.newLine ?? ''}</div>
|
|
539
|
+
`
|
|
540
|
+
: ''}
|
|
457
541
|
<div class="diffSign ${rowClass}">${displayRow.kind === 'add' ? '+' : displayRow.kind === 'remove' ? '−' : ''}</div>
|
|
458
542
|
<div class="diffText ${rowClass}">${this.renderDiffLine(displayRow.text, displayRow.segments, displayRow.kind)}</div>
|
|
459
543
|
`;
|
|
@@ -463,18 +547,17 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
463
547
|
}
|
|
464
548
|
|
|
465
549
|
private renderSplitGrid(displayRows: TDiffDisplayRow[]): TemplateResult {
|
|
466
|
-
|
|
467
|
-
const
|
|
550
|
+
const leftRendered: TemplateResult[] = [];
|
|
551
|
+
const rightRendered: TemplateResult[] = [];
|
|
468
552
|
let run: IDiffRow[] = [];
|
|
469
553
|
const flushRun = () => {
|
|
470
554
|
for (const splitRow of buildSplitRows(run)) {
|
|
471
555
|
if (splitRow.kind === 'hunk') {
|
|
472
|
-
|
|
556
|
+
leftRendered.push(html`<div class="diffHunk">${splitRow.text}</div>`);
|
|
557
|
+
rightRendered.push(html`<div class="diffHunk">${splitRow.text}</div>`);
|
|
473
558
|
} else {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
${this.renderSplitCell(splitRow.right, 'right')}
|
|
477
|
-
`);
|
|
559
|
+
leftRendered.push(this.renderSplitCell(splitRow.left));
|
|
560
|
+
rightRendered.push(this.renderSplitCell(splitRow.right));
|
|
478
561
|
}
|
|
479
562
|
}
|
|
480
563
|
run = [];
|
|
@@ -482,27 +565,38 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
482
565
|
for (const displayRow of displayRows) {
|
|
483
566
|
if (displayRow.kind === 'fold') {
|
|
484
567
|
flushRun();
|
|
485
|
-
|
|
568
|
+
leftRendered.push(this.renderFoldRow(displayRow.rows.length, displayRow.ordinal ?? 0));
|
|
569
|
+
rightRendered.push(this.renderFoldRow(displayRow.rows.length, displayRow.ordinal ?? 0));
|
|
486
570
|
} else {
|
|
487
571
|
run.push(displayRow);
|
|
488
572
|
}
|
|
489
573
|
}
|
|
490
574
|
flushRun();
|
|
491
|
-
|
|
575
|
+
const lineNumberClass = this.showDiffLineNumbers ? '' : 'withoutLineNumbers';
|
|
576
|
+
return html`
|
|
577
|
+
<div class="diffSplit">
|
|
578
|
+
<div class="diffSplitPane" data-side="left" aria-label="Before">
|
|
579
|
+
<div class="diffGrid splitSide ${lineNumberClass}">${leftRendered}</div>
|
|
580
|
+
</div>
|
|
581
|
+
<div class="diffSplitDivider" aria-hidden="true"></div>
|
|
582
|
+
<div class="diffSplitPane" data-side="right" aria-label="After">
|
|
583
|
+
<div class="diffGrid splitSide ${lineNumberClass}">${rightRendered}</div>
|
|
584
|
+
</div>
|
|
585
|
+
</div>
|
|
586
|
+
`;
|
|
492
587
|
}
|
|
493
588
|
|
|
494
|
-
private renderSplitCell(cell: ISplitCell | undefined
|
|
495
|
-
const separatorClass = side === 'right' ? 'diffSplitSep' : '';
|
|
589
|
+
private renderSplitCell(cell: ISplitCell | undefined): TemplateResult {
|
|
496
590
|
if (!cell) {
|
|
497
591
|
return html`
|
|
498
|
-
|
|
592
|
+
${this.showDiffLineNumbers ? html`<div class="diffNum row-filler"></div>` : ''}
|
|
499
593
|
<div class="diffSign row-filler"></div>
|
|
500
594
|
<div class="diffText row-filler"></div>
|
|
501
595
|
`;
|
|
502
596
|
}
|
|
503
597
|
const rowClass = cell.kind === 'add' ? 'row-add' : cell.kind === 'remove' ? 'row-remove' : '';
|
|
504
598
|
return html`
|
|
505
|
-
|
|
599
|
+
${this.showDiffLineNumbers ? html`<div class="diffNum ${rowClass}">${cell.line || ''}</div>` : ''}
|
|
506
600
|
<div class="diffSign ${rowClass}">${cell.kind === 'add' ? '+' : cell.kind === 'remove' ? '−' : ''}</div>
|
|
507
601
|
<div class="diffText ${rowClass}">${this.renderDiffLine(cell.text, cell.segments, cell.kind)}</div>
|
|
508
602
|
`;
|
|
@@ -552,10 +646,20 @@ export class DeesDataviewCodebox extends DeesElement {
|
|
|
552
646
|
return Boolean(this.unifiedDiff || this.codeBefore || this.diffView);
|
|
553
647
|
}
|
|
554
648
|
|
|
555
|
-
public get effectiveDiffView():
|
|
649
|
+
public get effectiveDiffView(): TDiffView {
|
|
556
650
|
return this.diffView === 'split' ? 'split' : 'inline';
|
|
557
651
|
}
|
|
558
652
|
|
|
653
|
+
private setDiffView(diffViewArg: TDiffView): void {
|
|
654
|
+
if (this.effectiveDiffView === diffViewArg) return;
|
|
655
|
+
this.diffView = diffViewArg;
|
|
656
|
+
this.dispatchEvent(new CustomEvent<IDiffViewChangeDetail>('diff-view-change', {
|
|
657
|
+
detail: { diffView: diffViewArg },
|
|
658
|
+
bubbles: true,
|
|
659
|
+
composed: true,
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
|
|
559
663
|
public willUpdate(changedProperties: Map<string, any>) {
|
|
560
664
|
super.willUpdate(changedProperties);
|
|
561
665
|
if (changedProperties.has('codeToDisplay')) {
|
|
@@ -31,6 +31,10 @@ import {
|
|
|
31
31
|
harnessTruncate,
|
|
32
32
|
} from '../harness.helpers.js';
|
|
33
33
|
import { DeesHarnessMessageList } from '../dees-harness-message-list/dees-harness-message-list.js';
|
|
34
|
+
import type {
|
|
35
|
+
IDiffViewChangeDetail,
|
|
36
|
+
TDiffView,
|
|
37
|
+
} from '../../00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js';
|
|
34
38
|
import '../dees-harness-content-blocks/dees-harness-content-blocks.js';
|
|
35
39
|
import '../dees-harness-overflow-text.js';
|
|
36
40
|
import '../../00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js';
|
|
@@ -91,12 +95,18 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
91
95
|
@state()
|
|
92
96
|
accessor outcomeScanTone: 'success' | 'error' | null = null;
|
|
93
97
|
|
|
98
|
+
@state()
|
|
99
|
+
accessor diffViewOverride: TDiffView | null = null;
|
|
100
|
+
|
|
94
101
|
private bodyResizeObserver: ResizeObserver | null = null;
|
|
95
102
|
private observedBodyInner: Element | null = null;
|
|
96
103
|
private fullscreenPortal: HTMLElementTagNameMap['dees-harness-tool-fullscreen'] | null = null;
|
|
97
104
|
private disclosureFrame: number | null = null;
|
|
98
105
|
private subtaskResultElement: HTMLElement | null = null;
|
|
99
106
|
private subtaskResultResizeObserver: ResizeObserver | null = null;
|
|
107
|
+
private terminalTailFollowing = true;
|
|
108
|
+
private terminalTailReturnTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
109
|
+
private terminalTailReturnDelayMs = 5000;
|
|
100
110
|
|
|
101
111
|
public static styles = [
|
|
102
112
|
themeDefaultStyles,
|
|
@@ -496,7 +506,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
496
506
|
border: 1px solid var(--dees-color-border-subtle);
|
|
497
507
|
border-radius: var(--dees-radius-md);
|
|
498
508
|
corner-shape: var(--dees-corner-shape);
|
|
499
|
-
background:
|
|
509
|
+
background: #0c0c0e;
|
|
500
510
|
overflow: hidden;
|
|
501
511
|
font-family: var(--dees-font-family-mono);
|
|
502
512
|
font-size: var(--dees-font-control-size-sm, 12px);
|
|
@@ -506,7 +516,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
506
516
|
.harness-terminal-command {
|
|
507
517
|
padding: var(--dees-spacing-xs) var(--dees-spacing-md);
|
|
508
518
|
border-bottom: 1px solid var(--dees-color-border-subtle);
|
|
509
|
-
color:
|
|
519
|
+
color: #f5f5f7;
|
|
510
520
|
font-weight: 600;
|
|
511
521
|
white-space: pre-wrap;
|
|
512
522
|
overflow-wrap: anywhere;
|
|
@@ -521,7 +531,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
521
531
|
margin: 0;
|
|
522
532
|
padding: var(--dees-spacing-xs) var(--dees-spacing-md) var(--dees-spacing-sm);
|
|
523
533
|
max-height: 100%;
|
|
524
|
-
color:
|
|
534
|
+
color: #c7c7cc;
|
|
525
535
|
white-space: pre-wrap;
|
|
526
536
|
overflow-wrap: anywhere;
|
|
527
537
|
}
|
|
@@ -535,7 +545,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
535
545
|
align-items: center;
|
|
536
546
|
gap: var(--dees-spacing-xs);
|
|
537
547
|
padding: var(--dees-spacing-xs) var(--dees-spacing-md) var(--dees-spacing-sm);
|
|
538
|
-
color:
|
|
548
|
+
color: #8e8e93;
|
|
539
549
|
}
|
|
540
550
|
|
|
541
551
|
.harness-terminal-running .cursor {
|
|
@@ -691,6 +701,8 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
691
701
|
const duration = harnessFormatDuration(call.startedAt, call.finishedAt);
|
|
692
702
|
return html`
|
|
693
703
|
<div class="harness-card status-${effectiveStatus} ${descriptor.destructive ? 'destructive' : ''} ${this.hasSubtaskPreview ? 'subtaskPreviewCard' : ''} ${this.hasSubtaskPreview && open ? 'subtaskPreviewOpen' : ''} ${this.returnFlash ? 'returnFlash' : ''} ${this.outcomeScanTone ? `outcomeScan outcomeScan-${this.outcomeScanTone}` : ''}"
|
|
704
|
+
@mouseenter=${this.cancelTerminalTailReturn}
|
|
705
|
+
@mouseleave=${this.scheduleTerminalTailReturn}
|
|
694
706
|
@animationend=${(event: AnimationEvent) => {
|
|
695
707
|
if (event.animationName === 'harness-card-return-flash') {
|
|
696
708
|
this.endReturnFlash();
|
|
@@ -735,6 +747,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
735
747
|
<div
|
|
736
748
|
class="bodyClipFrame"
|
|
737
749
|
style=${this.uncapped || this.activeSubtaskPreview ? '' : `max-height: ${this.bodyCapPx}px;`}
|
|
750
|
+
@scroll=${this.handleBodyScroll}
|
|
738
751
|
>
|
|
739
752
|
<div class=${`body bodyInner ${this.hasLiveSubtaskPreview ? 'liveSubtaskBody' : ''}`}>
|
|
740
753
|
${this.renderBody(call)}
|
|
@@ -849,7 +862,13 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
849
862
|
public willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
|
|
850
863
|
super.willUpdate(changedProperties);
|
|
851
864
|
const previousCall = changedProperties.get('call') as IHarnessToolCall | undefined;
|
|
852
|
-
if (!previousCall
|
|
865
|
+
if (!previousCall) return;
|
|
866
|
+
if (previousCall.id !== this.call.id) {
|
|
867
|
+
this.diffViewOverride = null;
|
|
868
|
+
this.terminalTailFollowing = true;
|
|
869
|
+
this.cancelTerminalTailReturn();
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
853
872
|
const previousStatus = harnessEffectiveToolStatus(previousCall);
|
|
854
873
|
const currentStatus = harnessEffectiveToolStatus(this.call);
|
|
855
874
|
const wasActive = previousStatus === 'pending' || previousStatus === 'running';
|
|
@@ -880,7 +899,15 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
880
899
|
return;
|
|
881
900
|
}
|
|
882
901
|
const bodyInner = this.shadowRoot?.querySelector('.bodyInner') ?? null;
|
|
883
|
-
if (bodyInner === this.observedBodyInner)
|
|
902
|
+
if (bodyInner === this.observedBodyInner) {
|
|
903
|
+
if (
|
|
904
|
+
(changedProperties.has('call') || changedProperties.has('bodyCapPx'))
|
|
905
|
+
&& this.terminalTailFollowing
|
|
906
|
+
) {
|
|
907
|
+
this.scrollTerminalToTail();
|
|
908
|
+
}
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
884
911
|
this.bodyResizeObserver?.disconnect();
|
|
885
912
|
this.observedBodyInner = bodyInner;
|
|
886
913
|
if (!bodyInner) {
|
|
@@ -892,10 +919,46 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
892
919
|
if (!inner) return;
|
|
893
920
|
// small slack so a few px never trigger the cap chrome
|
|
894
921
|
this.bodyOverflowing = inner.scrollHeight > this.bodyCapPx + 24;
|
|
922
|
+
if (this.terminalTailFollowing) this.scrollTerminalToTail();
|
|
895
923
|
});
|
|
896
924
|
this.bodyResizeObserver.observe(bodyInner);
|
|
925
|
+
if (this.terminalTailFollowing) this.scrollTerminalToTail();
|
|
897
926
|
}
|
|
898
927
|
|
|
928
|
+
private readonly handleBodyScroll = (eventArg: Event): void => {
|
|
929
|
+
if (this.descriptor.kind !== 'terminal' || this.uncapped) return;
|
|
930
|
+
const frame = eventArg.currentTarget as HTMLElement;
|
|
931
|
+
this.terminalTailFollowing = frame.scrollHeight - frame.scrollTop - frame.clientHeight <= 2;
|
|
932
|
+
if (this.terminalTailFollowing) this.cancelTerminalTailReturn();
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
private scrollTerminalToTail(): void {
|
|
936
|
+
if (this.descriptor.kind !== 'terminal' || this.uncapped || !this.isOpen) return;
|
|
937
|
+
const frame = this.shadowRoot?.querySelector<HTMLElement>('.bodyClipFrame');
|
|
938
|
+
if (!frame) return;
|
|
939
|
+
frame.scrollTop = frame.scrollHeight;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
private readonly scheduleTerminalTailReturn = (): void => {
|
|
943
|
+
if (this.descriptor.kind !== 'terminal' || this.terminalTailFollowing || this.uncapped) return;
|
|
944
|
+
this.cancelTerminalTailReturn();
|
|
945
|
+
this.terminalTailReturnTimeout = setTimeout(() => {
|
|
946
|
+
this.terminalTailReturnTimeout = null;
|
|
947
|
+
this.terminalTailFollowing = true;
|
|
948
|
+
this.scrollTerminalToTail();
|
|
949
|
+
}, this.terminalTailReturnDelayMs);
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
private readonly cancelTerminalTailReturn = (): void => {
|
|
953
|
+
if (!this.terminalTailReturnTimeout) return;
|
|
954
|
+
clearTimeout(this.terminalTailReturnTimeout);
|
|
955
|
+
this.terminalTailReturnTimeout = null;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
private readonly handleDiffViewChange = (eventArg: CustomEvent<IDiffViewChangeDetail>): void => {
|
|
959
|
+
this.diffViewOverride = eventArg.detail.diffView;
|
|
960
|
+
};
|
|
961
|
+
|
|
899
962
|
private readonly syncSubtaskResultScroll = (): void => {
|
|
900
963
|
const result = this.subtaskResultElement;
|
|
901
964
|
if (!result) return;
|
|
@@ -943,6 +1006,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
943
1006
|
clearTimeout(this.outcomeScanFallbackTimeout);
|
|
944
1007
|
this.outcomeScanFallbackTimeout = null;
|
|
945
1008
|
}
|
|
1009
|
+
this.cancelTerminalTailReturn();
|
|
946
1010
|
this.fullscreenPortal?.remove();
|
|
947
1011
|
this.fullscreenPortal = null;
|
|
948
1012
|
this.bodyResizeObserver?.disconnect();
|
|
@@ -1076,9 +1140,12 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
1076
1140
|
<dees-dataview-codebox
|
|
1077
1141
|
.progLang=${options?.language ?? 'plaintext'}
|
|
1078
1142
|
.filename=${options?.filename ?? ''}
|
|
1079
|
-
.diffView=${options?.view ?? 'inline'}
|
|
1143
|
+
.diffView=${this.diffViewOverride ?? options?.view ?? 'inline'}
|
|
1144
|
+
.unifiedDiff=${options?.unifiedDiff ?? ''}
|
|
1145
|
+
.showDiffLineNumbers=${options?.showLineNumbers ?? true}
|
|
1080
1146
|
.codeBefore=${before}
|
|
1081
1147
|
.codeToDisplay=${after}
|
|
1148
|
+
@diff-view-change=${this.handleDiffViewChange}
|
|
1082
1149
|
></dees-dataview-codebox>
|
|
1083
1150
|
`,
|
|
1084
1151
|
monoBlock: (text, options) => {
|
|
@@ -34,6 +34,8 @@ interface IApplyPatchFile {
|
|
|
34
34
|
path: string;
|
|
35
35
|
before: string;
|
|
36
36
|
after: string;
|
|
37
|
+
unifiedDiff?: string;
|
|
38
|
+
lineNumbersKnown: boolean;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -43,14 +45,27 @@ interface IApplyPatchFile {
|
|
|
43
45
|
*/
|
|
44
46
|
export const parseApplyPatch = (patchText: string): IApplyPatchFile[] => {
|
|
45
47
|
const files: IApplyPatchFile[] = [];
|
|
46
|
-
let current: {
|
|
48
|
+
let current: {
|
|
49
|
+
op: IApplyPatchFile['op'];
|
|
50
|
+
path: string;
|
|
51
|
+
before: string[];
|
|
52
|
+
after: string[];
|
|
53
|
+
patch: string[];
|
|
54
|
+
sawHunk: boolean;
|
|
55
|
+
lineNumbersKnown: boolean;
|
|
56
|
+
} | null = null;
|
|
47
57
|
const flush = (): void => {
|
|
48
58
|
if (!current) return;
|
|
59
|
+
const unifiedDiff = current.op === 'update' && current.sawHunk && current.lineNumbersKnown
|
|
60
|
+
? [`--- a/${current.path}`, `+++ b/${current.path}`, ...current.patch].join('\n')
|
|
61
|
+
: undefined;
|
|
49
62
|
files.push({
|
|
50
63
|
op: current.op,
|
|
51
64
|
path: current.path,
|
|
52
65
|
before: current.before.join('\n'),
|
|
53
66
|
after: current.after.join('\n'),
|
|
67
|
+
unifiedDiff,
|
|
68
|
+
lineNumbersKnown: Boolean(unifiedDiff),
|
|
54
69
|
});
|
|
55
70
|
current = null;
|
|
56
71
|
};
|
|
@@ -63,15 +78,31 @@ export const parseApplyPatch = (patchText: string): IApplyPatchFile[] => {
|
|
|
63
78
|
path: header[2].trim(),
|
|
64
79
|
before: [],
|
|
65
80
|
after: [],
|
|
81
|
+
patch: [],
|
|
82
|
+
sawHunk: false,
|
|
83
|
+
lineNumbersKnown: true,
|
|
66
84
|
};
|
|
67
85
|
continue;
|
|
68
86
|
}
|
|
69
|
-
if (
|
|
87
|
+
if (
|
|
88
|
+
/^\*\*\* (Begin|End) Patch/.test(line)
|
|
89
|
+
|| line === '*** End of File'
|
|
90
|
+
|| /^\*\*\* Move to: /.test(line)
|
|
91
|
+
) {
|
|
70
92
|
if (/End Patch/.test(line)) flush();
|
|
71
93
|
continue;
|
|
72
94
|
}
|
|
73
95
|
if (!current) continue;
|
|
74
|
-
if (line.startsWith('@@'))
|
|
96
|
+
if (line.startsWith('@@')) {
|
|
97
|
+
current.sawHunk = true;
|
|
98
|
+
if (/^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/.test(line)) {
|
|
99
|
+
current.patch.push(line);
|
|
100
|
+
} else {
|
|
101
|
+
current.lineNumbersKnown = false;
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
current.patch.push(line);
|
|
75
106
|
if (line.startsWith('+')) {
|
|
76
107
|
current.after.push(line.slice(1));
|
|
77
108
|
} else if (line.startsWith('-')) {
|
|
@@ -154,6 +185,8 @@ const renderFileWrite = (call: IHarnessToolCall, helpers: IHarnessToolRenderHelp
|
|
|
154
185
|
language: patchLanguage,
|
|
155
186
|
filename: patchFilename,
|
|
156
187
|
view: 'split',
|
|
188
|
+
unifiedDiff: file.unifiedDiff,
|
|
189
|
+
showLineNumbers: file.lineNumbersKnown,
|
|
157
190
|
});
|
|
158
191
|
}
|
|
159
192
|
if (file.op === 'add') {
|
|
@@ -180,7 +213,13 @@ const renderFileWrite = (call: IHarnessToolCall, helpers: IHarnessToolRenderHelp
|
|
|
180
213
|
const filename = path.split('/').pop() || undefined;
|
|
181
214
|
const language = harnessLanguageForPath(path);
|
|
182
215
|
if (previousContent) {
|
|
183
|
-
|
|
216
|
+
const isStringReplacement = input.oldString !== undefined || input.old_string !== undefined;
|
|
217
|
+
return helpers.diffBlock(previousContent, newContent, {
|
|
218
|
+
language,
|
|
219
|
+
filename,
|
|
220
|
+
view: 'split',
|
|
221
|
+
showLineNumbers: !isStringReplacement,
|
|
222
|
+
});
|
|
184
223
|
}
|
|
185
224
|
if (newContent) {
|
|
186
225
|
return helpers.codeBlock(newContent, language, filename);
|
|
@@ -11,7 +11,13 @@ export interface IHarnessToolRenderHelpers {
|
|
|
11
11
|
/** highlighted code block (dees-dataview-codebox) with optional filename */
|
|
12
12
|
codeBlock(code: string, language?: string, filename?: string): TemplateResult;
|
|
13
13
|
/** before/after diff (dees-dataview-codebox diff mode) */
|
|
14
|
-
diffBlock(before: string, after: string, options?: {
|
|
14
|
+
diffBlock(before: string, after: string, options?: {
|
|
15
|
+
language?: string;
|
|
16
|
+
filename?: string;
|
|
17
|
+
view?: 'inline' | 'split';
|
|
18
|
+
unifiedDiff?: string;
|
|
19
|
+
showLineNumbers?: boolean;
|
|
20
|
+
}): TemplateResult;
|
|
15
21
|
/** plain monospace block with truncation + expand */
|
|
16
22
|
monoBlock(text: string, options?: { maxChars?: number; tone?: 'default' | 'error' }): TemplateResult;
|
|
17
23
|
/** two-column key/value summary */
|