@stigmer/react 3.12.9 → 3.14.0-dev.20260910084630
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/cursor-accounts/CursorAccountsConsole.d.ts +3 -2
- package/cursor-accounts/CursorAccountsConsole.d.ts.map +1 -1
- package/cursor-accounts/CursorAccountsConsole.js +3 -2
- package/cursor-accounts/CursorAccountsConsole.js.map +1 -1
- package/cursor-accounts/MemberCoverageTable.d.ts +11 -3
- package/cursor-accounts/MemberCoverageTable.d.ts.map +1 -1
- package/cursor-accounts/MemberCoverageTable.js +70 -42
- package/cursor-accounts/MemberCoverageTable.js.map +1 -1
- package/cursor-accounts/MemberKeysPanel.d.ts.map +1 -1
- package/cursor-accounts/MemberKeysPanel.js +4 -1
- package/cursor-accounts/MemberKeysPanel.js.map +1 -1
- package/internal/ResizableSplit.d.ts +1 -1
- package/package.json +4 -4
- package/provider-standing/useProviderStanding.d.ts +1 -1
- package/provider-standing/useProviderStanding.js +1 -1
- package/sidebar/SettingsSidebar.d.ts.map +1 -1
- package/sidebar/SettingsSidebar.js +3 -3
- package/sidebar/SettingsSidebar.js.map +1 -1
- package/sidebar/WorkspaceSidebar.d.ts +1 -1
- package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
- package/sidebar/WorkspaceSidebar.js +19 -15
- package/sidebar/WorkspaceSidebar.js.map +1 -1
- package/sidebar/chrome.d.ts +21 -7
- package/sidebar/chrome.d.ts.map +1 -1
- package/sidebar/chrome.js +25 -7
- package/sidebar/chrome.js.map +1 -1
- package/src/cursor-accounts/CursorAccountsConsole.tsx +3 -2
- package/src/cursor-accounts/MemberCoverageTable.tsx +222 -200
- package/src/cursor-accounts/MemberKeysPanel.tsx +25 -1
- package/src/cursor-accounts/__tests__/CursorAccountsConsole.test.tsx +64 -8
- package/src/cursor-accounts/__tests__/MemberCoverageTable.layout.test.tsx +212 -0
- package/src/internal/ResizableSplit.tsx +1 -1
- package/src/provider-standing/useProviderStanding.ts +1 -1
- package/src/sidebar/SettingsSidebar.tsx +11 -5
- package/src/sidebar/WorkspaceSidebar.tsx +75 -67
- package/src/sidebar/__tests__/SettingsSidebar.test.tsx +6 -2
- package/src/sidebar/chrome.tsx +29 -15
- package/src/workflow/__tests__/WorkflowExecutionViewer.reconciliation.test.tsx +11 -9
- package/src/workflow/__tests__/WorkflowTaskThread.test.tsx +54 -2
- package/src/workflow/thread/WorkflowTaskThread.tsx +47 -22
- package/styles.css +1 -1
- package/workflow/thread/WorkflowTaskThread.js +34 -11
- package/workflow/thread/WorkflowTaskThread.js.map +1 -1
package/src/sidebar/chrome.tsx
CHANGED
|
@@ -9,8 +9,9 @@ import { OrgSwitcher } from "../organization/OrgSwitcher.js";
|
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
// Shared sidebar chrome — the pieces WorkspaceSidebar and SettingsSidebar
|
|
11
11
|
// have in common: the nav container, the top row (collapse toggle + org
|
|
12
|
-
// switcher), the hairline separator, the footer band, and the
|
|
13
|
-
//
|
|
12
|
+
// switcher), the hairline separator, the footer band, and the shared class
|
|
13
|
+
// recipes (nav rows, section labels). Internal to the sidebar domain; not
|
|
14
|
+
// exported from the SDK.
|
|
14
15
|
//
|
|
15
16
|
// Every class here is transcribed from the console's sidebars — this file
|
|
16
17
|
// IS the console's chrome now, so a change here changes the console, the
|
|
@@ -107,23 +108,36 @@ export function SidebarSeparator() {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/**
|
|
110
|
-
* The one nav-row class recipe both sidebars share:
|
|
111
|
-
* 16px icons (sized by callers), 8px/
|
|
111
|
+
* The one nav-row class recipe both sidebars share: 13px/16px regular
|
|
112
|
+
* labels, 16px icons (sized by callers), 8px/4px padding — 24px rows.
|
|
113
|
+
* The explicit `leading-4` is load-bearing: without it the inherited
|
|
114
|
+
* line-height (~1.5) pads every row to ~28px, and across a full column
|
|
115
|
+
* of rows that difference alone decides whether the sidebar breathes.
|
|
116
|
+
* 24px also stays exactly at the WCAG 2.5.8 minimum pointer-target size.
|
|
112
117
|
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
118
|
+
* Resting rows are muted so the navigation recedes and the content leads;
|
|
119
|
+
* the active row carries the accent fill, hover brightens to the accent
|
|
120
|
+
* foreground. Font weight is constant across states — state is expressed
|
|
121
|
+
* through color and fill only, so activating a row never shifts its text
|
|
122
|
+
* width. The muted-on-sidebar pairing is held to WCAG AA by the theme's
|
|
123
|
+
* contrast contract (sdk/theme/src/contract/pairs.ts) in every preset.
|
|
115
124
|
*/
|
|
116
|
-
export function navRowClassName(
|
|
117
|
-
active: boolean,
|
|
118
|
-
{ muted = false }: { muted?: boolean } = {},
|
|
119
|
-
): string {
|
|
125
|
+
export function navRowClassName(active: boolean): string {
|
|
120
126
|
return cn(
|
|
121
|
-
"stg:flex stg:items-center stg:gap-2 stg:rounded-
|
|
127
|
+
"stg:flex stg:items-center stg:gap-2 stg:rounded-md stg:px-2 stg:py-1 stg:text-[13px] stg:leading-4 stg:transition-colors",
|
|
122
128
|
active
|
|
123
129
|
? "stg:bg-sidebar-accent stg:text-sidebar-accent-foreground"
|
|
124
|
-
:
|
|
125
|
-
muted ? "stg:text-sidebar-muted-foreground" : "stg:text-sidebar-foreground",
|
|
126
|
-
"stg:hover:bg-sidebar-accent stg:hover:text-sidebar-accent-foreground",
|
|
127
|
-
),
|
|
130
|
+
: "stg:text-sidebar-muted-foreground stg:hover:bg-sidebar-accent stg:hover:text-sidebar-accent-foreground",
|
|
128
131
|
);
|
|
129
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The section-label recipe both sidebars share (settings group labels,
|
|
136
|
+
* the Recents heading): 11px/16px medium uppercase in the muted sidebar
|
|
137
|
+
* tone. Spacing around a label stays with the caller — it is a layout
|
|
138
|
+
* concern; this recipe owns only the typography, so the two sidebars
|
|
139
|
+
* cannot drift.
|
|
140
|
+
*/
|
|
141
|
+
export function sectionLabelClassName(): string {
|
|
142
|
+
return "stg:text-sidebar-muted-foreground stg:px-2 stg:text-[11px] stg:leading-4 stg:font-medium stg:tracking-wide stg:uppercase";
|
|
143
|
+
}
|
|
@@ -383,19 +383,21 @@ describe("WorkflowExecutionViewer (center-column Thread|Graph toggle, S9)", () =
|
|
|
383
383
|
it("a thread card offers no selection or drill-down gesture — the card IS the surface (T06)", () => {
|
|
384
384
|
renderViewer();
|
|
385
385
|
|
|
386
|
-
// build-report
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
386
|
+
// build-report renders from the snapshot fallback (no events): kind 0
|
|
387
|
+
// (unspecified → summary disclosure) with nothing beyond what its
|
|
388
|
+
// header row already carries — so it is a PLAIN row (stigmer#886): no
|
|
389
|
+
// expand chevron, no selection, no Inspect drill-down. (Expansion
|
|
390
|
+
// being card-local for content-bearing summary cards is pinned in
|
|
391
|
+
// WorkflowTaskThread.test.tsx.)
|
|
392
|
+
expect(
|
|
393
|
+
screen.queryByRole("button", { name: /^build-report/ }),
|
|
394
|
+
).toBeNull();
|
|
391
395
|
expect(
|
|
392
396
|
screen.queryByRole("button", { name: "Inspect build-report" }),
|
|
393
397
|
).toBeNull();
|
|
394
398
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
// Expansion is card-local; the panel never opens on a card gesture.
|
|
398
|
-
expect(header.getAttribute("aria-expanded")).toBe("true");
|
|
399
|
+
// Clicking the row is inert: the panel never opens on a card gesture.
|
|
400
|
+
fireEvent.click(screen.getByText("build-report"));
|
|
399
401
|
expect(screen.queryByRole("radio", { name: "Inspect" })).toBeNull();
|
|
400
402
|
});
|
|
401
403
|
|
|
@@ -451,6 +451,48 @@ describe("WorkflowTaskThread", () => {
|
|
|
451
451
|
expect(screen.getByText(/at dial tcp 10\.0\.0\.1:443/)).toBeTruthy();
|
|
452
452
|
});
|
|
453
453
|
|
|
454
|
+
it("a summary-kind card with a body-less detail renders no expand gesture (stigmer#886)", () => {
|
|
455
|
+
// A settled wait card with nothing the header cannot carry — no retry,
|
|
456
|
+
// no usage, no error, no I/O. Since R6-6 moved Status/Duration onto
|
|
457
|
+
// the header, its detail body would be empty, so the header is a plain
|
|
458
|
+
// row: offering a chevron that reveals nothing is worse than none.
|
|
459
|
+
render(
|
|
460
|
+
<WorkflowTaskThread
|
|
461
|
+
taskStates={statesOf(
|
|
462
|
+
taskState({ taskName: "blocking_wait", taskKind: WorkflowTaskKind.wait }),
|
|
463
|
+
)}
|
|
464
|
+
totalTasks={1}
|
|
465
|
+
isRunning={false}
|
|
466
|
+
/>,
|
|
467
|
+
);
|
|
468
|
+
|
|
469
|
+
expect(screen.getByText("blocking_wait")).toBeTruthy();
|
|
470
|
+
expect(screen.queryByRole("button", { name: /^blocking_wait/ })).toBeNull();
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it("the expand gesture returns the moment the detail body has content (stigmer#886)", () => {
|
|
474
|
+
// The same wait card with a retry behind it: the Attempt row gives the
|
|
475
|
+
// body content, so the header is the expand gesture again.
|
|
476
|
+
render(
|
|
477
|
+
<WorkflowTaskThread
|
|
478
|
+
taskStates={statesOf(
|
|
479
|
+
taskState({
|
|
480
|
+
taskName: "blocking_wait",
|
|
481
|
+
taskKind: WorkflowTaskKind.wait,
|
|
482
|
+
attemptNumber: 2,
|
|
483
|
+
}),
|
|
484
|
+
)}
|
|
485
|
+
totalTasks={1}
|
|
486
|
+
isRunning={false}
|
|
487
|
+
/>,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
const header = screen.getByRole("button", { name: /^blocking_wait/ });
|
|
491
|
+
fireEvent.click(header);
|
|
492
|
+
expect(screen.getByText("Attempt")).toBeTruthy();
|
|
493
|
+
expect(screen.getByText("2")).toBeTruthy();
|
|
494
|
+
});
|
|
495
|
+
|
|
454
496
|
it("shows the full error in a summary-kind card's expanded body", () => {
|
|
455
497
|
render(
|
|
456
498
|
<WorkflowTaskThread
|
|
@@ -471,9 +513,15 @@ describe("WorkflowTaskThread", () => {
|
|
|
471
513
|
});
|
|
472
514
|
|
|
473
515
|
it("keeps a card's expanded state across streaming updates (no remount)", () => {
|
|
516
|
+
// The wait card carries input so its detail body has content — a
|
|
517
|
+
// body-less summary card has no expand gesture at all (stigmer#886).
|
|
474
518
|
const running = () =>
|
|
475
519
|
statesOf(
|
|
476
|
-
taskState({
|
|
520
|
+
taskState({
|
|
521
|
+
taskName: "settled",
|
|
522
|
+
taskKind: WorkflowTaskKind.wait,
|
|
523
|
+
inputSummary: { duration: "10s" },
|
|
524
|
+
}),
|
|
477
525
|
taskState({ taskName: "live", status: "running", messagesCount: 1 }),
|
|
478
526
|
);
|
|
479
527
|
const { rerender } = render(
|
|
@@ -495,7 +543,11 @@ describe("WorkflowTaskThread", () => {
|
|
|
495
543
|
rerender(
|
|
496
544
|
<WorkflowTaskThread
|
|
497
545
|
taskStates={statesOf(
|
|
498
|
-
taskState({
|
|
546
|
+
taskState({
|
|
547
|
+
taskName: "settled",
|
|
548
|
+
taskKind: WorkflowTaskKind.wait,
|
|
549
|
+
inputSummary: { duration: "10s" },
|
|
550
|
+
}),
|
|
499
551
|
taskState({ taskName: "live", status: "running", messagesCount: 2 }),
|
|
500
552
|
)}
|
|
501
553
|
totalTasks={2}
|
|
@@ -288,7 +288,12 @@ function ThreadEmptyState({ isRunning }: { readonly isRunning: boolean }) {
|
|
|
288
288
|
* card chevron — `BoundedContent` owns its own in-place reveal, so the
|
|
289
289
|
* old "expand, then Show more" double control never comes back).
|
|
290
290
|
* - `"summary"` kinds expand from the header — the session card's own
|
|
291
|
-
* gesture, the chevron appended by the shell (T06)
|
|
291
|
+
* gesture, the chevron appended by the shell (T06) — but ONLY when the
|
|
292
|
+
* detail body would carry content (stigmer#886): since R6-6 moved
|
|
293
|
+
* Status/Duration onto the header, a settled wait card often has
|
|
294
|
+
* nothing left to reveal, and a chevron that opens an empty body is
|
|
295
|
+
* worse than no chevron. The gate is the summary twin of
|
|
296
|
+
* `showPreviewBody` below.
|
|
292
297
|
* - AGENT_CALL cards with a spawned child render the child's inline
|
|
293
298
|
* transcript as the body (T07) — keyed on the VARIANT, not the
|
|
294
299
|
* disclosure, so a platform builder's presenter override can never route
|
|
@@ -362,6 +367,17 @@ const ThreadTaskCard = memo(function ThreadTaskCard({
|
|
|
362
367
|
!showTranscript &&
|
|
363
368
|
isPreview &&
|
|
364
369
|
(outputIO !== null || !!item.error || showApprovalSummary);
|
|
370
|
+
// The summary twin of that gate (stigmer#886): the expand gesture exists
|
|
371
|
+
// only when `ThreadTaskDetail` would render something. Short-circuits
|
|
372
|
+
// before the row build for preview/transcript cards, which never render
|
|
373
|
+
// the detail body.
|
|
374
|
+
const summaryHasDetail =
|
|
375
|
+
!isPreview &&
|
|
376
|
+
!showTranscript &&
|
|
377
|
+
(taskDetailRows(item).length > 0 ||
|
|
378
|
+
!!item.error ||
|
|
379
|
+
inputIO !== null ||
|
|
380
|
+
outputIO !== null);
|
|
365
381
|
|
|
366
382
|
const meta = formatMetaChips({
|
|
367
383
|
durationMs: item.durationMs,
|
|
@@ -378,19 +394,19 @@ const ThreadTaskCard = memo(function ThreadTaskCard({
|
|
|
378
394
|
accent={item.status === "waiting_approval" ? "warning" : null}
|
|
379
395
|
cursorTarget="workflow-task-row"
|
|
380
396
|
>
|
|
381
|
-
{/* The session card's gestures exactly (T06): summary rows
|
|
382
|
-
from the header (chevron appended
|
|
383
|
-
|
|
384
|
-
plain layout
|
|
397
|
+
{/* The session card's gestures exactly (T06): summary rows with a
|
|
398
|
+
content-bearing detail expand from the header (chevron appended
|
|
399
|
+
by the shell); preview, transcript, and body-less summary rows
|
|
400
|
+
are plain layout rows. */}
|
|
385
401
|
<ThreadCardHeader
|
|
386
402
|
gesture={
|
|
387
|
-
|
|
388
|
-
? {
|
|
389
|
-
: {
|
|
403
|
+
summaryHasDetail
|
|
404
|
+
? {
|
|
390
405
|
kind: "expand",
|
|
391
406
|
expanded,
|
|
392
407
|
onToggle: () => setExpanded((v) => !v),
|
|
393
408
|
}
|
|
409
|
+
: { kind: "none" }
|
|
394
410
|
}
|
|
395
411
|
>
|
|
396
412
|
<StatusGlyph status={item.status} />
|
|
@@ -486,7 +502,7 @@ const ThreadTaskCard = memo(function ThreadTaskCard({
|
|
|
486
502
|
</ThreadCardBody>
|
|
487
503
|
)}
|
|
488
504
|
|
|
489
|
-
{
|
|
505
|
+
{summaryHasDetail && expanded && (
|
|
490
506
|
<ThreadCardBody>
|
|
491
507
|
<ThreadTaskDetail item={item} inputIO={inputIO} outputIO={outputIO} />
|
|
492
508
|
</ThreadCardBody>
|
|
@@ -630,19 +646,15 @@ function ThreadTaskPreviewBody({
|
|
|
630
646
|
);
|
|
631
647
|
}
|
|
632
648
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
// No Status/Duration rows (R6-6): the card header is the single source
|
|
643
|
-
// for both — the status glyph and the duration meta chip. The detail
|
|
644
|
-
// body carries only what the header cannot: attempt count, usage, the
|
|
645
|
-
// agent slug.
|
|
649
|
+
/**
|
|
650
|
+
* The detail body's label/value rows. No Status/Duration rows (R6-6): the
|
|
651
|
+
* card header is the single source for both — the status glyph and the
|
|
652
|
+
* duration meta chip. The detail carries only what the header cannot:
|
|
653
|
+
* attempt count, usage, the agent slug. Shared with the card's
|
|
654
|
+
* `summaryHasDetail` gate so "would the body render anything?" can never
|
|
655
|
+
* drift from what the body actually renders (stigmer#886).
|
|
656
|
+
*/
|
|
657
|
+
function taskDetailRows(item: WorkflowThreadItem): Array<[string, string]> {
|
|
646
658
|
const rows: Array<[string, string]> = [];
|
|
647
659
|
if (item.attemptNumber > 1) rows.push(["Attempt", String(item.attemptNumber)]);
|
|
648
660
|
const costChip = formatMetaChips({
|
|
@@ -653,6 +665,19 @@ function ThreadTaskDetail({
|
|
|
653
665
|
if (item.variant === "agent-call" && item.agentSlug) {
|
|
654
666
|
rows.push(["Agent", item.agentSlug]);
|
|
655
667
|
}
|
|
668
|
+
return rows;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function ThreadTaskDetail({
|
|
672
|
+
item,
|
|
673
|
+
inputIO,
|
|
674
|
+
outputIO,
|
|
675
|
+
}: {
|
|
676
|
+
readonly item: WorkflowThreadItem;
|
|
677
|
+
readonly inputIO: TaskDetailIO | null;
|
|
678
|
+
readonly outputIO: TaskDetailIO | null;
|
|
679
|
+
}) {
|
|
680
|
+
const rows = taskDetailRows(item);
|
|
656
681
|
|
|
657
682
|
return (
|
|
658
683
|
<div className="stg:flex stg:flex-col stg:gap-2">
|