@design.estate/dees-catalog 9.7.0 → 10.0.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 +4164 -3800
- package/dist_bundle/bundle.js.map +1 -1
- package/dist_bundle/third-party-notices.md +783 -10
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/elements/00actionerror.d.ts +32 -0
- package/dist_ts_web/elements/00actionerror.js +45 -0
- package/dist_ts_web/elements/00group-appui/dees-appui/dees-appui.js +6 -1
- package/dist_ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.d.ts +16 -0
- package/dist_ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.js +40 -3
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.d.ts +7 -0
- package/dist_ts_web/elements/00group-dataview/dees-table/dees-table.js +17 -17
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.d.ts +14 -3
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.demo.js +23 -4
- package/dist_ts_web/elements/00group-feedback/dees-hint/dees-hint.js +77 -5
- package/dist_ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.js +14 -4
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.d.ts +4 -0
- package/dist_ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.js +52 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.d.ts +4 -1
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.js +93 -20
- package/dist_ts_web/elements/00group-harness/dees-harness-tool-card/renderers.js +7 -21
- package/dist_ts_web/elements/00group-harness/harness.helpers.d.ts +20 -0
- package/dist_ts_web/elements/00group-harness/harness.helpers.js +58 -1
- package/dist_ts_web/elements/00group-harness/interfaces.d.ts +6 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.d.ts +8 -0
- package/dist_ts_web/elements/00group-harness/toolregistry.js +10 -2
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.d.ts +23 -3
- package/dist_ts_web/elements/00group-input/dees-input-text/dees-input-text.js +71 -11
- package/dist_ts_web/elements/00group-input/index.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/index.js +1 -2
- package/dist_ts_web/elements/00group-overlay/dees-modal/dees-modal.d.ts +7 -0
- package/dist_ts_web/elements/00group-overlay/dees-modal/dees-modal.js +13 -3
- package/dist_ts_web/elements/index.d.ts +1 -2
- package/dist_ts_web/elements/index.js +2 -4
- package/package.json +4 -4
- package/readme.md +73 -6
- package/scripts/check-third-party-notices.mjs +261 -0
- package/third-party-notices.md +783 -10
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/00actionerror.ts +66 -0
- package/ts_web/elements/00group-appui/dees-appui/dees-appui.ts +4 -0
- package/ts_web/elements/00group-appui/dees-appui-tabs/dees-appui-tabs.ts +38 -2
- package/ts_web/elements/00group-dataview/dees-table/dees-table.ts +18 -16
- package/ts_web/elements/00group-feedback/dees-hint/dees-hint.demo.ts +22 -3
- package/ts_web/elements/00group-feedback/dees-hint/dees-hint.ts +71 -3
- package/ts_web/elements/00group-harness/dees-harness-composer/dees-harness-composer.ts +12 -3
- package/ts_web/elements/00group-harness/dees-harness-message-list/dees-harness-message-list.ts +50 -0
- package/ts_web/elements/00group-harness/dees-harness-tool-card/dees-harness-tool-card.ts +95 -23
- package/ts_web/elements/00group-harness/dees-harness-tool-card/renderers.ts +7 -20
- package/ts_web/elements/00group-harness/harness.helpers.ts +63 -0
- package/ts_web/elements/00group-harness/interfaces.ts +6 -0
- package/ts_web/elements/00group-harness/toolregistry.ts +22 -1
- package/ts_web/elements/00group-input/dees-input-text/dees-input-text.ts +73 -13
- package/ts_web/elements/00group-input/index.ts +0 -1
- package/ts_web/elements/00group-overlay/dees-modal/dees-modal.ts +13 -2
- package/ts_web/elements/index.ts +1 -4
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.js +0 -2
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/index.d.ts +0 -1
- package/dist_ts_web/elements/00group-input/dees-input-searchselect/index.js +0 -2
- package/dist_ts_web/elements/dees-search/dees-search.d.ts +0 -1
- package/dist_ts_web/elements/dees-search/dees-search.js +0 -2
- package/dist_ts_web/elements/dees-search/index.d.ts +0 -1
- package/dist_ts_web/elements/dees-search/index.js +0 -2
- package/dist_ts_web/elements/dees-tooltip/dees-tooltip.d.ts +0 -1
- package/dist_ts_web/elements/dees-tooltip/dees-tooltip.js +0 -2
- package/dist_ts_web/elements/dees-tooltip/index.d.ts +0 -1
- package/dist_ts_web/elements/dees-tooltip/index.js +0 -2
- package/ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.ts +0 -0
- package/ts_web/elements/00group-input/dees-input-searchselect/index.ts +0 -1
- package/ts_web/elements/dees-search/dees-search.ts +0 -0
- package/ts_web/elements/dees-search/index.ts +0 -1
- package/ts_web/elements/dees-tooltip/dees-tooltip.ts +0 -1
- package/ts_web/elements/dees-tooltip/index.ts +0 -1
|
@@ -111,7 +111,7 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
111
111
|
private fullscreenPortal: HTMLElementTagNameMap['dees-harness-tool-fullscreen'] | null = null;
|
|
112
112
|
private disclosureFrame: number | null = null;
|
|
113
113
|
private returnScrollFrame: number | null = null;
|
|
114
|
-
private
|
|
114
|
+
private subtaskDetailElements: HTMLElement[] = [];
|
|
115
115
|
private subtaskResultResizeObserver: ResizeObserver | null = null;
|
|
116
116
|
private terminalTailFollowing = true;
|
|
117
117
|
private terminalScrollHydrated = false;
|
|
@@ -141,7 +141,11 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
141
141
|
.harness-card.subtaskPreviewOpen {
|
|
142
142
|
display: flex;
|
|
143
143
|
flex-direction: column;
|
|
144
|
-
|
|
144
|
+
/*
|
|
145
|
+
* A cap, not a fixed height: a short brief and result let the card size to their own
|
|
146
|
+
* text, and only content taller than the cap hands the overflow to the inner scrollers.
|
|
147
|
+
*/
|
|
148
|
+
max-height: var(--dees-harness-active-subtask-height, 320px);
|
|
145
149
|
box-sizing: border-box;
|
|
146
150
|
overflow: hidden;
|
|
147
151
|
}
|
|
@@ -169,7 +173,27 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
169
173
|
overflow: hidden;
|
|
170
174
|
}
|
|
171
175
|
|
|
172
|
-
|
|
176
|
+
/*
|
|
177
|
+
* The body takes its height from flex layout rather than a percentage: a capped card has no
|
|
178
|
+
* definite height for a percentage to resolve against, so a 100% height would collapse to
|
|
179
|
+
* auto and the inner scrollers would never engage. As a flex item it shrinks to the cap when
|
|
180
|
+
* the content overflows and sizes to its text when it does not.
|
|
181
|
+
*/
|
|
182
|
+
.harness-card.subtaskPreviewOpen .bodyClipFrame {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.harness-card.subtaskPreviewOpen .body {
|
|
188
|
+
flex: 1;
|
|
189
|
+
min-height: 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* A streaming child fills the cap so its transcript has a stable window to scroll in. */
|
|
193
|
+
.harness-card.subtaskPreviewOpen:has(.liveSubtaskBody) {
|
|
194
|
+
height: var(--dees-harness-active-subtask-height, 320px);
|
|
195
|
+
}
|
|
196
|
+
|
|
173
197
|
.harness-card.subtaskPreviewOpen .subtaskPreview {
|
|
174
198
|
min-height: 0;
|
|
175
199
|
height: 100%;
|
|
@@ -259,6 +283,15 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
259
283
|
font-size: 12px;
|
|
260
284
|
}
|
|
261
285
|
|
|
286
|
+
/* Stands in for the body while a finished card is collapsed. */
|
|
287
|
+
.resultSummary {
|
|
288
|
+
display: block;
|
|
289
|
+
margin-top: 2px;
|
|
290
|
+
color: var(--dees-color-text-secondary);
|
|
291
|
+
font-family: var(--dees-font-family-mono, monospace);
|
|
292
|
+
font-size: 11px;
|
|
293
|
+
}
|
|
294
|
+
|
|
262
295
|
.metaColumn {
|
|
263
296
|
display: flex;
|
|
264
297
|
align-items: center;
|
|
@@ -680,10 +713,27 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
680
713
|
&& (this.call.subtask.status.type === 'busy' || this.call.subtask.status.type === 'error');
|
|
681
714
|
}
|
|
682
715
|
if (this.call.status === 'error' || this.call.isError) return true;
|
|
716
|
+
// A card that opened while it was live closes again once the work has settled, so a finished
|
|
717
|
+
// run stops holding transcript height. 'stopped' is deliberately excluded: a killed command
|
|
718
|
+
// keeps its partial output on screen, like an error does.
|
|
719
|
+
if (this.call.status === 'completed' && this.resolvedCollapseWhenFinished) return false;
|
|
683
720
|
if (typeof descriptor.defaultExpanded === 'function') return descriptor.defaultExpanded(this.enrichedCall);
|
|
684
721
|
return descriptor.defaultExpanded ?? false;
|
|
685
722
|
}
|
|
686
723
|
|
|
724
|
+
private get resolvedCollapseWhenFinished(): boolean {
|
|
725
|
+
const collapseWhenFinished = this.descriptor.collapseWhenFinished;
|
|
726
|
+
if (typeof collapseWhenFinished === 'function') return collapseWhenFinished(this.enrichedCall);
|
|
727
|
+
return collapseWhenFinished ?? false;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/** Outcome line shown only while collapsed; the subtitle already carries the request. */
|
|
731
|
+
private get collapsedSummary(): string {
|
|
732
|
+
if (this.isOpen || !this.descriptor.summary) return '';
|
|
733
|
+
if (this.call.status !== 'completed' && this.call.status !== 'stopped') return '';
|
|
734
|
+
return this.descriptor.summary(this.enrichedCall).trim();
|
|
735
|
+
}
|
|
736
|
+
|
|
687
737
|
private get hasSubtaskPreview(): boolean {
|
|
688
738
|
return this.allowSubtaskStreams
|
|
689
739
|
&& this.descriptor.kind === 'subtask'
|
|
@@ -704,11 +754,19 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
704
754
|
const open = this.isOpen;
|
|
705
755
|
const renderBody = this.bodyMounted || open;
|
|
706
756
|
const label = typeof descriptor.label === 'function' ? descriptor.label(call) : descriptor.label;
|
|
707
|
-
const
|
|
757
|
+
const derivedSubtitle = descriptor.subtitle?.(call) ?? '';
|
|
758
|
+
// A producer-supplied title describes the call in its own words — "Read SKILL.md" rather than
|
|
759
|
+
// the sed invocation behind it — so it leads, and the derived line stays as the tooltip.
|
|
760
|
+
const producerTitle = typeof call.title === 'string' ? call.title.trim() : '';
|
|
761
|
+
const subtitle = producerTitle || derivedSubtitle;
|
|
762
|
+
const subtitleTooltip = producerTitle && derivedSubtitle && derivedSubtitle !== producerTitle
|
|
763
|
+
? `${producerTitle}\n${derivedSubtitle}`
|
|
764
|
+
: subtitle;
|
|
708
765
|
const effectiveStatus = harnessEffectiveToolStatus(call);
|
|
709
766
|
const statusTone = effectiveStatus === 'completed' ? 'success'
|
|
710
767
|
: effectiveStatus === 'stopped' ? 'warning' : effectiveStatus;
|
|
711
768
|
const duration = harnessFormatDuration(call.startedAt, call.finishedAt);
|
|
769
|
+
const collapsedSummary = this.collapsedSummary;
|
|
712
770
|
return html`
|
|
713
771
|
<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}` : ''}"
|
|
714
772
|
@mouseenter=${this.cancelTerminalTailReturn}
|
|
@@ -734,7 +792,10 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
734
792
|
${this.renderMcpBadges(call)}
|
|
735
793
|
</span>
|
|
736
794
|
${subtitle
|
|
737
|
-
? html`<dees-harness-overflow-text class="subtitle" title=${
|
|
795
|
+
? html`<dees-harness-overflow-text class="subtitle" title=${subtitleTooltip}>${subtitle}</dees-harness-overflow-text>`
|
|
796
|
+
: ''}
|
|
797
|
+
${collapsedSummary
|
|
798
|
+
? html`<dees-harness-overflow-text class="resultSummary" title=${collapsedSummary}>${collapsedSummary}</dees-harness-overflow-text>`
|
|
738
799
|
: ''}
|
|
739
800
|
</span>
|
|
740
801
|
<span class="metaColumn">
|
|
@@ -1047,30 +1108,39 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
1047
1108
|
};
|
|
1048
1109
|
|
|
1049
1110
|
private readonly syncSubtaskResultScroll = (): void => {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1111
|
+
// Both panels hide their scrollbar, so the mask is the only cue that there is more text.
|
|
1112
|
+
// Leaving the brief unwired made a long brief look truncated rather than scrollable.
|
|
1113
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1114
|
+
panel.classList.toggle('fadeTop', panel.scrollTop > 1);
|
|
1115
|
+
panel.classList.toggle(
|
|
1116
|
+
'fadeBottom',
|
|
1117
|
+
panel.scrollHeight - panel.scrollTop - panel.clientHeight > 1,
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1057
1120
|
};
|
|
1058
1121
|
|
|
1059
1122
|
private connectSubtaskResultScroll(): void {
|
|
1060
|
-
const
|
|
1061
|
-
'.harness-subtask-detail-
|
|
1062
|
-
|
|
1063
|
-
|
|
1123
|
+
const nextPanels = [...(this.shadowRoot?.querySelectorAll<HTMLElement>(
|
|
1124
|
+
'.harness-subtask-detail-brief .harness-subtask-detail-text,'
|
|
1125
|
+
+ ' .harness-subtask-detail-result .harness-subtask-detail-text',
|
|
1126
|
+
) ?? [])];
|
|
1127
|
+
const unchanged = nextPanels.length === this.subtaskDetailElements.length
|
|
1128
|
+
&& nextPanels.every((panel, index) => panel === this.subtaskDetailElements[index]);
|
|
1129
|
+
if (unchanged) {
|
|
1064
1130
|
this.syncSubtaskResultScroll();
|
|
1065
1131
|
return;
|
|
1066
1132
|
}
|
|
1067
|
-
|
|
1133
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1134
|
+
panel.removeEventListener('scroll', this.syncSubtaskResultScroll);
|
|
1135
|
+
}
|
|
1068
1136
|
this.subtaskResultResizeObserver?.disconnect();
|
|
1069
|
-
this.
|
|
1070
|
-
if (!
|
|
1071
|
-
nextResult.addEventListener('scroll', this.syncSubtaskResultScroll, { passive: true });
|
|
1137
|
+
this.subtaskDetailElements = nextPanels;
|
|
1138
|
+
if (!nextPanels.length) return;
|
|
1072
1139
|
this.subtaskResultResizeObserver ??= new ResizeObserver(this.syncSubtaskResultScroll);
|
|
1073
|
-
|
|
1140
|
+
for (const panel of nextPanels) {
|
|
1141
|
+
panel.addEventListener('scroll', this.syncSubtaskResultScroll, { passive: true });
|
|
1142
|
+
this.subtaskResultResizeObserver.observe(panel);
|
|
1143
|
+
}
|
|
1074
1144
|
this.syncSubtaskResultScroll();
|
|
1075
1145
|
}
|
|
1076
1146
|
|
|
@@ -1116,8 +1186,10 @@ export class DeesHarnessToolCard extends DeesElement {
|
|
|
1116
1186
|
this.bodyResizeObserver?.disconnect();
|
|
1117
1187
|
this.bodyResizeObserver = null;
|
|
1118
1188
|
this.observedBodyInner = null;
|
|
1119
|
-
|
|
1120
|
-
|
|
1189
|
+
for (const panel of this.subtaskDetailElements) {
|
|
1190
|
+
panel.removeEventListener('scroll', this.syncSubtaskResultScroll);
|
|
1191
|
+
}
|
|
1192
|
+
this.subtaskDetailElements = [];
|
|
1121
1193
|
this.subtaskResultResizeObserver?.disconnect();
|
|
1122
1194
|
this.subtaskResultResizeObserver = null;
|
|
1123
1195
|
await super.disconnectedCallback();
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
harnessLanguageForPath,
|
|
7
7
|
harnessParseTodos,
|
|
8
8
|
harnessStringifyValue,
|
|
9
|
+
harnessStripAnsi,
|
|
9
10
|
harnessSummarizeValue,
|
|
11
|
+
harnessTerminalOutcome,
|
|
10
12
|
} from '../harness.helpers.js';
|
|
11
13
|
import '../dees-harness-todos/dees-harness-todos.js';
|
|
12
14
|
|
|
@@ -125,35 +127,20 @@ export const applyPatchFirstPath = (call: IHarnessToolCall): string => {
|
|
|
125
127
|
return match ? match[1].trim() : '';
|
|
126
128
|
};
|
|
127
129
|
|
|
128
|
-
// eslint-disable-next-line no-control-regex
|
|
129
|
-
const ansiEscapePattern = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*(?:\x07|\x1b\\)|\x1b[=>]/g;
|
|
130
|
-
|
|
131
|
-
const stripAnsi = (text: string): string => text.replace(ansiEscapePattern, '');
|
|
132
|
-
|
|
133
130
|
const renderTerminal = (call: IHarnessToolCall, helpers: IHarnessToolRenderHelpers): TemplateResult => {
|
|
134
131
|
const input = inputRecord(call);
|
|
135
132
|
const command = String(input.command ?? '');
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
let stderr = '';
|
|
139
|
-
let nestedExitCode: number | undefined;
|
|
140
|
-
if (harnessIsRecord(output)) {
|
|
141
|
-
stdout = typeof output.stdout === 'string' ? output.stdout : harnessStringifyValue(output.stdout ?? '');
|
|
142
|
-
stderr = typeof output.stderr === 'string' ? output.stderr : '';
|
|
143
|
-
nestedExitCode = typeof output.exitCode === 'number' ? output.exitCode : undefined;
|
|
144
|
-
} else {
|
|
145
|
-
stdout = harnessStringifyValue(output);
|
|
146
|
-
}
|
|
147
|
-
const exitCode = typeof call.exitCode === 'number' ? call.exitCode : nestedExitCode;
|
|
133
|
+
// Shared with the collapsed-card summary so both read the same outcome.
|
|
134
|
+
const { stdout, stderr, exitCode } = harnessTerminalOutcome(call);
|
|
148
135
|
const running = call.status === 'pending' || call.status === 'running';
|
|
149
136
|
return html`
|
|
150
137
|
<div class="harness-terminal">
|
|
151
138
|
${command
|
|
152
139
|
? html`<div class="harness-terminal-command"><span class="prompt">$</span> ${command}</div>`
|
|
153
140
|
: ''}
|
|
154
|
-
${stdout ? html`<pre class="harness-terminal-output">${
|
|
155
|
-
${stderr ? html`<pre class="harness-terminal-output tone-error">${
|
|
156
|
-
${call.errorText ? html`<pre class="harness-terminal-output tone-error">${
|
|
141
|
+
${stdout ? html`<pre class="harness-terminal-output">${harnessStripAnsi(stdout)}</pre>` : ''}
|
|
142
|
+
${stderr ? html`<pre class="harness-terminal-output tone-error">${harnessStripAnsi(stderr)}</pre>` : ''}
|
|
143
|
+
${call.errorText ? html`<pre class="harness-terminal-output tone-error">${harnessStripAnsi(call.errorText)}</pre>` : ''}
|
|
157
144
|
${running && !stdout && !stderr
|
|
158
145
|
? html`<div class="harness-terminal-running"><span class="cursor"></span> running…</div>`
|
|
159
146
|
: ''}
|
|
@@ -313,3 +313,66 @@ export const harnessSummarizeValue = (value: unknown): string => {
|
|
|
313
313
|
export const harnessSummaryLine = (value: unknown): string => {
|
|
314
314
|
return harnessSummarizeValue(value).split('\n')[0] || '';
|
|
315
315
|
};
|
|
316
|
+
|
|
317
|
+
// eslint-disable-next-line no-control-regex
|
|
318
|
+
const ansiEscapePattern = /\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07]*(?:\x07|\x1b\\)|\x1b[=>]/g;
|
|
319
|
+
|
|
320
|
+
/** Terminal output carries raw escape sequences; they must never reach a header or a summary. */
|
|
321
|
+
export const harnessStripAnsi = (text: string): string => text.replace(ansiEscapePattern, '');
|
|
322
|
+
|
|
323
|
+
export interface IHarnessTerminalOutcome {
|
|
324
|
+
stdout: string;
|
|
325
|
+
stderr: string;
|
|
326
|
+
/** Top-level exitCode wins over a nested one, matching how producers report it. */
|
|
327
|
+
exitCode?: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Normalizes the three shapes producers use for shell output: a `{stdout, stderr, exitCode}`
|
|
332
|
+
* record, a combined string with a separate top-level `exitCode`, or an arbitrary value.
|
|
333
|
+
*/
|
|
334
|
+
export const harnessTerminalOutcome = (callArg: IHarnessToolCall): IHarnessTerminalOutcome => {
|
|
335
|
+
const output = callArg.output;
|
|
336
|
+
let stdout = '';
|
|
337
|
+
let stderr = '';
|
|
338
|
+
let nestedExitCode: number | undefined;
|
|
339
|
+
if (harnessIsRecord(output)) {
|
|
340
|
+
stdout = typeof output.stdout === 'string' ? output.stdout : harnessStringifyValue(output.stdout ?? '');
|
|
341
|
+
stderr = typeof output.stderr === 'string' ? output.stderr : '';
|
|
342
|
+
nestedExitCode = typeof output.exitCode === 'number' ? output.exitCode : undefined;
|
|
343
|
+
} else {
|
|
344
|
+
stdout = harnessStringifyValue(output);
|
|
345
|
+
}
|
|
346
|
+
const exitCode = typeof callArg.exitCode === 'number' ? callArg.exitCode : nestedExitCode;
|
|
347
|
+
return { stdout, stderr, ...(exitCode === undefined ? {} : { exitCode }) };
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const harnessLastMeaningfulLine = (text: string): string => {
|
|
351
|
+
const lines = harnessStripAnsi(text).split('\n');
|
|
352
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
353
|
+
const line = lines[index].trim();
|
|
354
|
+
if (line) return line;
|
|
355
|
+
}
|
|
356
|
+
return '';
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* One-line outcome for a finished shell call, for the collapsed card header. The command itself
|
|
361
|
+
* is already the card subtitle, so this describes the RESULT: exit code, how much came back, and
|
|
362
|
+
* the last meaningful line. Harness support for a richer summary is uneven — only OpenCode
|
|
363
|
+
* supplies a model-authored description, so this is derived from output that every harness has.
|
|
364
|
+
*/
|
|
365
|
+
export const harnessTerminalResultSummary = (callArg: IHarnessToolCall): string => {
|
|
366
|
+
const { stdout, stderr, exitCode } = harnessTerminalOutcome(callArg);
|
|
367
|
+
const parts: string[] = [];
|
|
368
|
+
if (exitCode !== undefined) parts.push(exitCode === 0 ? 'exit 0' : `exit ${exitCode}`);
|
|
369
|
+
const combined = `${stdout}${stdout && stderr ? '\n' : ''}${stderr}`;
|
|
370
|
+
const lineCount = combined ? harnessStripAnsi(combined).split('\n').filter((line) => line.trim()).length : 0;
|
|
371
|
+
if (lineCount > 0) parts.push(`${lineCount} ${lineCount === 1 ? 'line' : 'lines'}`);
|
|
372
|
+
else if (exitCode !== undefined) parts.push('no output');
|
|
373
|
+
const lastLine = harnessLastMeaningfulLine(stderr) || harnessLastMeaningfulLine(stdout);
|
|
374
|
+
const prefix = parts.join(' · ');
|
|
375
|
+
if (!lastLine) return prefix;
|
|
376
|
+
const trimmed = lastLine.length > 160 ? `${lastLine.slice(0, 160)}…` : lastLine;
|
|
377
|
+
return prefix ? `${prefix} · ${trimmed}` : trimmed;
|
|
378
|
+
};
|
|
@@ -230,6 +230,12 @@ export interface IHarnessToolCall {
|
|
|
230
230
|
subtask?: IHarnessSubtaskStream;
|
|
231
231
|
/** model the spawned session runs on, e.g. "anthropic/claude-opus-5" */
|
|
232
232
|
model?: string;
|
|
233
|
+
/**
|
|
234
|
+
* Producer-supplied one-line description of the call, e.g. OpenCode's tool title or a parsed
|
|
235
|
+
* shell action. Single line, already bounded by the producer. Preferred over any line the card
|
|
236
|
+
* derives for itself; absent whenever the producer offers nothing.
|
|
237
|
+
*/
|
|
238
|
+
title?: string;
|
|
233
239
|
/** epoch ms */
|
|
234
240
|
startedAt?: number;
|
|
235
241
|
finishedAt?: number;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { TemplateResult } from '@design.estate/dees-element';
|
|
2
2
|
import type { TDiffViewRequest } from '../00group-dataview/dees-dataview-codebox/dees-dataview-codebox.js';
|
|
3
3
|
import type { IHarnessToolCall, THarnessContentBlock } from './interfaces.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
harnessIsRecord,
|
|
6
|
+
harnessSummaryLine,
|
|
7
|
+
harnessTerminalOutcome,
|
|
8
|
+
harnessTerminalResultSummary,
|
|
9
|
+
} from './harness.helpers.js';
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* Render helpers handed to tool body renderers. Implemented by
|
|
@@ -49,6 +54,14 @@ export interface IHarnessToolDescriptor {
|
|
|
49
54
|
*/
|
|
50
55
|
renderBody?: (call: IHarnessToolCall, helpers: IHarnessToolRenderHelpers) => TemplateResult;
|
|
51
56
|
defaultExpanded?: boolean | ((call: IHarnessToolCall) => boolean);
|
|
57
|
+
/**
|
|
58
|
+
* Collapses a card that opened while it was live once its work is done, so a finished run
|
|
59
|
+
* stops occupying the transcript. Evaluated only for a settled call, and always subordinate to
|
|
60
|
+
* an explicit user toggle.
|
|
61
|
+
*/
|
|
62
|
+
collapseWhenFinished?: boolean | ((call: IHarnessToolCall) => boolean);
|
|
63
|
+
/** One-line outcome shown in the header while the card is collapsed. */
|
|
64
|
+
summary?: (call: IHarnessToolCall) => string;
|
|
52
65
|
/** Transcript grid packing; omitted descriptors use the compact layout. */
|
|
53
66
|
layout?: THarnessToolLayout;
|
|
54
67
|
/** styles the card with a destructive accent regardless of status */
|
|
@@ -101,6 +114,14 @@ const defaultDescriptors: IHarnessToolDescriptor[] = [
|
|
|
101
114
|
},
|
|
102
115
|
defaultExpanded: true,
|
|
103
116
|
layout: 'full-row',
|
|
117
|
+
/*
|
|
118
|
+
* Only a command that can be PROVEN to have succeeded collapses. An absent exit code is not
|
|
119
|
+
* evidence of success: a producer that reports a failed command as completed and encodes the
|
|
120
|
+
* failure in its output would otherwise have that failure hidden behind a one-line summary.
|
|
121
|
+
* Such a card stays open, and the producer reporting an exit code is the real fix.
|
|
122
|
+
*/
|
|
123
|
+
collapseWhenFinished: (call) => harnessTerminalOutcome(call).exitCode === 0,
|
|
124
|
+
summary: harnessTerminalResultSummary,
|
|
104
125
|
},
|
|
105
126
|
{
|
|
106
127
|
kind: 'file-read',
|
|
@@ -21,17 +21,44 @@ declare global {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** Native input types this field maps to. Anything richer has its own element. */
|
|
25
|
+
export type TTextInputType = 'text' | 'number' | 'date' | 'email' | 'url' | 'tel' | 'password';
|
|
26
|
+
|
|
27
|
+
/** Keyboard hint per type; `inputMode` on the host still wins. */
|
|
28
|
+
const defaultInputModes: Record<TTextInputType, string> = {
|
|
29
|
+
text: 'text',
|
|
30
|
+
number: 'decimal',
|
|
31
|
+
date: 'text',
|
|
32
|
+
email: 'email',
|
|
33
|
+
url: 'url',
|
|
34
|
+
tel: 'tel',
|
|
35
|
+
password: 'text',
|
|
36
|
+
};
|
|
37
|
+
|
|
24
38
|
@customElement('dees-input-text')
|
|
25
39
|
export class DeesInputText extends DeesInputBase {
|
|
26
40
|
public static demo = demoFunc;
|
|
27
41
|
public static demoGroups = ['Input'];
|
|
28
42
|
|
|
29
43
|
// INSTANCE
|
|
44
|
+
/**
|
|
45
|
+
* The native input type. `number` keeps the field's value numeric, so a form
|
|
46
|
+
* collects a number rather than a numeric string; every other type keeps the
|
|
47
|
+
* value a string. `password` masks the value; `isPasswordBool` additionally
|
|
48
|
+
* renders the reveal button.
|
|
49
|
+
*/
|
|
50
|
+
@property({
|
|
51
|
+
type: String,
|
|
52
|
+
reflect: true,
|
|
53
|
+
})
|
|
54
|
+
accessor type: TTextInputType = 'text';
|
|
55
|
+
|
|
56
|
+
/** A string for every type except `number`, which holds a number or null while empty. */
|
|
30
57
|
@property({
|
|
31
58
|
type: String,
|
|
32
59
|
reflect: true,
|
|
33
60
|
})
|
|
34
|
-
accessor value: string = '';
|
|
61
|
+
accessor value: string | number | null = '';
|
|
35
62
|
|
|
36
63
|
@property({
|
|
37
64
|
type: Boolean,
|
|
@@ -315,8 +342,8 @@ export class DeesInputText extends DeesInputBase {
|
|
|
315
342
|
<div class="maincontainer">
|
|
316
343
|
<input
|
|
317
344
|
class=${this.vintegrated ? '' : 'field-control'}
|
|
318
|
-
type="${this.
|
|
319
|
-
.value=${this.
|
|
345
|
+
type="${this.nativeInputType}"
|
|
346
|
+
.value=${this.valueAsText}
|
|
320
347
|
@input="${this.updateValue}"
|
|
321
348
|
.disabled=${this.disabled}
|
|
322
349
|
?required=${this.required}
|
|
@@ -324,7 +351,7 @@ export class DeesInputText extends DeesInputBase {
|
|
|
324
351
|
aria-invalid=${this.validationState === 'invalid' ? 'true' : 'false'}
|
|
325
352
|
aria-describedby="field-description field-validation"
|
|
326
353
|
placeholder=${this.placeholder || (this.label ? '' : 'Enter text…')}
|
|
327
|
-
inputmode=${this.inputMode || 'text'}
|
|
354
|
+
inputmode=${this.inputMode || defaultInputModes[this.type] || 'text'}
|
|
328
355
|
/>
|
|
329
356
|
${this.isPasswordBool
|
|
330
357
|
? html`
|
|
@@ -352,6 +379,39 @@ export class DeesInputText extends DeesInputBase {
|
|
|
352
379
|
`;
|
|
353
380
|
}
|
|
354
381
|
|
|
382
|
+
/** The type handed to the native input; masking wins over the declared type. */
|
|
383
|
+
private get nativeInputType(): string {
|
|
384
|
+
if (this.isPasswordBool || this.type === 'password') {
|
|
385
|
+
return this.showPasswordBool ? 'text' : 'password';
|
|
386
|
+
}
|
|
387
|
+
return this.type;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** The value as text, for validators, the context menu and the native element. */
|
|
391
|
+
private get valueAsText(): string {
|
|
392
|
+
return this.value === null || this.value === undefined ? '' : String(this.value);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Keeps `value` in the shape the current type promises, whatever a host
|
|
397
|
+
* binds: a number (or null while empty) for `number`, a string otherwise.
|
|
398
|
+
*/
|
|
399
|
+
private normalizeValue(valueArg: string | number | null | undefined): string | number | null {
|
|
400
|
+
if (this.type !== 'number') {
|
|
401
|
+
return valueArg === null || valueArg === undefined ? '' : String(valueArg);
|
|
402
|
+
}
|
|
403
|
+
if (valueArg === null || valueArg === undefined || valueArg === '') return null;
|
|
404
|
+
const parsed = typeof valueArg === 'number' ? valueArg : Number(valueArg);
|
|
405
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
|
|
409
|
+
super.willUpdate(changedProperties);
|
|
410
|
+
if (!changedProperties.has('value') && !changedProperties.has('type')) return;
|
|
411
|
+
const normalized = this.normalizeValue(this.value);
|
|
412
|
+
if (normalized !== this.value) this.value = normalized;
|
|
413
|
+
}
|
|
414
|
+
|
|
355
415
|
/**
|
|
356
416
|
* `autocomplete` is applied imperatively rather than bound in the template so that the
|
|
357
417
|
* default renders no attribute at all. Binding it would emit `autocomplete=""` on every
|
|
@@ -372,8 +432,8 @@ export class DeesInputText extends DeesInputBase {
|
|
|
372
432
|
}
|
|
373
433
|
|
|
374
434
|
firstUpdated() {
|
|
375
|
-
if (this.validationFunction && this.value) {
|
|
376
|
-
const result = this.validationFunction(this.
|
|
435
|
+
if (this.validationFunction && this.value !== null && this.value !== '') {
|
|
436
|
+
const result = this.validationFunction(this.valueAsText);
|
|
377
437
|
this.validationState = result.valid ? 'valid' : 'invalid';
|
|
378
438
|
this.validationText = result.message || '';
|
|
379
439
|
if (result.valid) {
|
|
@@ -387,10 +447,10 @@ export class DeesInputText extends DeesInputBase {
|
|
|
387
447
|
}
|
|
388
448
|
|
|
389
449
|
public async updateValue(eventArg: Event) {
|
|
390
|
-
const target
|
|
391
|
-
this.value = target.value;
|
|
450
|
+
const target = eventArg.target as HTMLInputElement;
|
|
451
|
+
this.value = this.normalizeValue(target.value);
|
|
392
452
|
if (this.validationFunction) {
|
|
393
|
-
const result = this.validationFunction(this.
|
|
453
|
+
const result = this.validationFunction(this.valueAsText);
|
|
394
454
|
this.validationState = result.valid ? 'valid' : 'invalid';
|
|
395
455
|
this.validationText = result.message || '';
|
|
396
456
|
if (result.valid) {
|
|
@@ -411,16 +471,16 @@ export class DeesInputText extends DeesInputBase {
|
|
|
411
471
|
public getContextMenuItems() {
|
|
412
472
|
return textInputContextMenuItems({
|
|
413
473
|
input: this.shadowRoot!.querySelector('input')!, disabled: this.disabled,
|
|
414
|
-
commit: value => { this.value = value; this.changeSubject.next(this); },
|
|
474
|
+
commit: value => { this.value = this.normalizeValue(value); this.changeSubject.next(this); },
|
|
415
475
|
});
|
|
416
476
|
}
|
|
417
477
|
|
|
418
|
-
public getValue(): string {
|
|
478
|
+
public getValue(): string | number | null {
|
|
419
479
|
return this.value;
|
|
420
480
|
}
|
|
421
481
|
|
|
422
|
-
public setValue(value: string): void {
|
|
423
|
-
this.value = value;
|
|
482
|
+
public setValue(value: string | number | null): void {
|
|
483
|
+
this.value = this.normalizeValue(value);
|
|
424
484
|
}
|
|
425
485
|
|
|
426
486
|
public async togglePasswordView() {
|
|
@@ -12,7 +12,6 @@ export * from './dees-input-phone/index.js';
|
|
|
12
12
|
export * from './dees-input-quantityselector/index.js';
|
|
13
13
|
export * from './dees-input-radiogroup/index.js';
|
|
14
14
|
export * from './dees-input-richtext/index.js';
|
|
15
|
-
export * from './dees-input-searchselect/index.js';
|
|
16
15
|
export * from './dees-input-tags/index.js';
|
|
17
16
|
export * from './dees-input-text/index.js';
|
|
18
17
|
export * from './dees-input-textarea/index.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../00plugins.js';
|
|
2
2
|
import { zIndexRegistry } from '../../00zindex.js';
|
|
3
|
+
import { runAction } from '../../00actionerror.js';
|
|
3
4
|
|
|
4
5
|
import { demoFunc } from './dees-modal.demo.js';
|
|
5
6
|
import {
|
|
@@ -49,7 +50,7 @@ export class DeesModal extends DeesElement {
|
|
|
49
50
|
let modal: DeesModal;
|
|
50
51
|
const confirm = async () => {
|
|
51
52
|
if (modal.promptPending) return;
|
|
52
|
-
const value = input.value.trim();
|
|
53
|
+
const value = String(input.value ?? '').trim();
|
|
53
54
|
const error = value ? options.validate?.(value) : 'Enter a name.';
|
|
54
55
|
if (error) {
|
|
55
56
|
input.validationState = 'invalid'; input.validationText = error;
|
|
@@ -332,7 +333,7 @@ export class DeesModal extends DeesElement {
|
|
|
332
333
|
<dees-button class="modal-action" .text=${action.name}
|
|
333
334
|
.disabled=${this.promptPending}
|
|
334
335
|
.type=${index === this.menuOptions.length - 1 ? 'accent' : 'secondary'}
|
|
335
|
-
@clicked=${() =>
|
|
336
|
+
@clicked=${() => this.runMenuAction(action)}></dees-button>
|
|
336
337
|
`)}
|
|
337
338
|
</div>
|
|
338
339
|
` : ''}
|
|
@@ -385,4 +386,14 @@ export class DeesModal extends DeesElement {
|
|
|
385
386
|
private async handleHelp() {
|
|
386
387
|
if (this.onHelp) await this.onHelp();
|
|
387
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* A menu action is awaited so a rejected callback surfaces as a visible
|
|
392
|
+
* failure instead of an unhandled rejection. The error is announced on the
|
|
393
|
+
* modal through the cancelable `dees-action-error` event and, unless a
|
|
394
|
+
* listener cancels it, shown as a notification inside this dialog.
|
|
395
|
+
*/
|
|
396
|
+
private async runMenuAction(actionArg: plugins.tsclass.website.IMenuItem<DeesModal>) {
|
|
397
|
+
await runAction(this, { action: actionArg, source: 'modal-menu', ownerElement: this.dialog }, () => actionArg.action(this));
|
|
398
|
+
}
|
|
388
399
|
}
|
package/ts_web/elements/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './00actionerror.js';
|
|
1
2
|
export * from './00zindex.js';
|
|
2
3
|
export * from './00theme.js';
|
|
3
4
|
|
|
@@ -17,7 +18,3 @@ export * from './00group-runtime/index.js';
|
|
|
17
18
|
export * from './00group-simple/index.js';
|
|
18
19
|
export * from './00group-utility/index.js';
|
|
19
20
|
export * from './00group-workspace/index.js';
|
|
20
|
-
|
|
21
|
-
// Standalone Components
|
|
22
|
-
export * from './dees-search/index.js';
|
|
23
|
-
export * from './dees-tooltip/index.js';
|
package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist_ts_web/elements/00group-input/dees-input-searchselect/dees-input-searchselect.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy1pbnB1dC1zZWFyY2hzZWxlY3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90c193ZWIvZWxlbWVudHMvMDBncm91cC1pbnB1dC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90c193ZWIvZWxlbWVudHMvMDBncm91cC1pbnB1dC9kZWVzLWlucHV0LXNlYXJjaHNlbGVjdC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDhCQUE4QixDQUFDIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvZGVlcy1zZWFyY2gvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxrQkFBa0IsQ0FBQyJ9
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import {} from '@design.estate/dees-element';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVlcy10b29sdGlwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL2RlZXMtdG9vbHRpcC9kZWVzLXRvb2x0aXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE1BQU0sNkJBQTZCLENBQUMifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvZGVlcy10b29sdGlwL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsbUJBQW1CLENBQUMifQ==
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-input-searchselect.js';
|
|
File without changes
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-search.js';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import {} from '@design.estate/dees-element';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './dees-tooltip.js';
|