footprint-explainable-ui 0.13.2 → 0.14.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/flowchart.d.cts +2 -0
- package/dist/flowchart.d.ts +2 -0
- package/dist/index.cjs +120 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -2
- package/dist/index.d.ts +75 -2
- package/dist/index.js +117 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -7,6 +7,8 @@ interface StageSnapshot {
|
|
|
7
7
|
stageName: string;
|
|
8
8
|
/** Human-readable label */
|
|
9
9
|
stageLabel: string;
|
|
10
|
+
/** Unique per-execution-step identifier. Format: [subflowPath/]stageId#executionIndex. Key for recorder Map lookup. */
|
|
11
|
+
runtimeStageId?: string;
|
|
10
12
|
/** Accumulated memory state after this stage ran */
|
|
11
13
|
memory: Record<string, unknown>;
|
|
12
14
|
/** Narrative text describing what happened */
|
|
@@ -26,15 +28,24 @@ interface StageSnapshot {
|
|
|
26
28
|
}
|
|
27
29
|
/** Structured narrative entry — preserves type info for semantic rendering. */
|
|
28
30
|
interface NarrativeEntry {
|
|
29
|
-
type: 'stage' | 'step' | 'condition' | 'fork' | 'selector' | 'subflow' | 'loop' | 'break' | 'error';
|
|
31
|
+
type: 'stage' | 'step' | 'condition' | 'fork' | 'selector' | 'subflow' | 'loop' | 'break' | 'error' | 'pause' | 'resume';
|
|
30
32
|
text: string;
|
|
31
33
|
depth: number;
|
|
32
34
|
stageName?: string;
|
|
33
35
|
/** Stable stage identifier (matches spec node id). Primary key for UI sync. */
|
|
34
36
|
stageId?: string;
|
|
37
|
+
/** Unique per-execution-step identifier. Format: [subflowPath/]stageId#executionIndex.
|
|
38
|
+
* Used for exact time-travel sync (preferred over stageId for progressive reveal). */
|
|
39
|
+
runtimeStageId?: string;
|
|
35
40
|
/** Subflow ID when this entry was generated inside a subflow. */
|
|
36
41
|
subflowId?: string;
|
|
42
|
+
/** Direction for subflow entries: 'entry' when entering, 'exit' when leaving. */
|
|
43
|
+
direction?: 'entry' | 'exit';
|
|
37
44
|
stepNumber?: number;
|
|
45
|
+
/** Scope key that was read or written. Only present on 'step' entries. */
|
|
46
|
+
key?: string;
|
|
47
|
+
/** Raw value from the scope event. Only present on 'step' entries. */
|
|
48
|
+
rawValue?: unknown;
|
|
38
49
|
}
|
|
39
50
|
/** Component size variants */
|
|
40
51
|
type Size = "compact" | "default" | "detailed";
|
|
@@ -373,6 +384,9 @@ interface RecorderView {
|
|
|
373
384
|
id: string;
|
|
374
385
|
/** Display label on the tab */
|
|
375
386
|
name: string;
|
|
387
|
+
/** Short description shown as tooltip and header for auto-detected views.
|
|
388
|
+
* e.g., "Per-step timing and I/O counts (KeyedRecorder)" */
|
|
389
|
+
description?: string;
|
|
376
390
|
/**
|
|
377
391
|
* Render function — receives the current snapshot index and all snapshots.
|
|
378
392
|
* Return a React node to display in the details panel.
|
|
@@ -563,4 +577,63 @@ declare function createSnapshots(stages: Array<{
|
|
|
563
577
|
subflowId?: string;
|
|
564
578
|
}>): StageSnapshot[];
|
|
565
579
|
|
|
566
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Narrative sync utilities — shared logic for mapping timeline position
|
|
582
|
+
* to narrative entries. Used by NarrativePanel and available to consumers
|
|
583
|
+
* building custom visualization shells.
|
|
584
|
+
*/
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Range index: runtimeStageId → half-open range [firstIdx, endIdx) in entries array.
|
|
588
|
+
*
|
|
589
|
+
* This is the same shape as `SequenceRecorder.getEntryRanges()` in footprintjs.
|
|
590
|
+
* When you have recorder access, pass `recorder.getEntryRanges()` directly.
|
|
591
|
+
* When you only have the flat array, use `buildEntryRangeIndex()` to build it.
|
|
592
|
+
*/
|
|
593
|
+
type EntryRangeIndex = ReadonlyMap<string, {
|
|
594
|
+
readonly firstIdx: number;
|
|
595
|
+
readonly endIdx: number;
|
|
596
|
+
}>;
|
|
597
|
+
/**
|
|
598
|
+
* Build a range index from a flat entries array for O(1) per-step lookups.
|
|
599
|
+
* Equivalent to `SequenceRecorder.getEntryRanges()` but works on detached arrays.
|
|
600
|
+
*
|
|
601
|
+
* Call once when narrativeEntries changes, then pass to `computeRevealedEntryCount`.
|
|
602
|
+
*
|
|
603
|
+
* @param entries — structured entries (from CombinedNarrativeRecorder.getEntries() or getNarrativeEntries())
|
|
604
|
+
* @returns range index for fast slider sync
|
|
605
|
+
*/
|
|
606
|
+
declare function buildEntryRangeIndex(entries: Pick<NarrativeEntry, "runtimeStageId">[]): EntryRangeIndex;
|
|
607
|
+
/**
|
|
608
|
+
* Compute how many narrative entries to reveal at a given slider position.
|
|
609
|
+
*
|
|
610
|
+
* **With range index (preferred):** O(selectedIndex) — one Map lookup per snapshot.
|
|
611
|
+
* **Without index (convenience):** O(entries) forward scan.
|
|
612
|
+
*
|
|
613
|
+
* The range index can come from:
|
|
614
|
+
* - `SequenceRecorder.getEntryRanges()` (when you have recorder access)
|
|
615
|
+
* - `buildEntryRangeIndex(entries)` (when you only have the flat array)
|
|
616
|
+
*
|
|
617
|
+
* @param narrativeEntries — structured entries from CombinedNarrativeRecorder
|
|
618
|
+
* @param snapshots — execution timeline (from adapter)
|
|
619
|
+
* @param selectedIndex — current slider position (0-based)
|
|
620
|
+
* @param rangeIndex — optional precomputed range index for O(1) lookups
|
|
621
|
+
* @returns number of entries to reveal (0 to narrativeEntries.length)
|
|
622
|
+
*/
|
|
623
|
+
declare function computeRevealedEntryCount(narrativeEntries: NarrativeEntry[], snapshots: Pick<StageSnapshot, "runtimeStageId">[], selectedIndex: number, rangeIndex?: EntryRangeIndex): number;
|
|
624
|
+
/**
|
|
625
|
+
* Extract narrative entries belonging to a specific subflow.
|
|
626
|
+
*
|
|
627
|
+
* Three-tier matching (most reliable first):
|
|
628
|
+
* 1. `stageName` prefix match (e.g., entries with `stageName` starting with `"sf-pay/"`)
|
|
629
|
+
* 2. `subflowId` field match
|
|
630
|
+
* 3. `direction` field on subflow entry/exit markers (renderer-agnostic)
|
|
631
|
+
*
|
|
632
|
+
* @param entries — all narrative entries from the execution
|
|
633
|
+
* @param subflowId — subflow identifier to extract
|
|
634
|
+
* @param subflowName — optional display name for fallback matching
|
|
635
|
+
* @returns entries belonging to the subflow
|
|
636
|
+
*/
|
|
637
|
+
declare function extractSubflowNarrative(entries: NarrativeEntry[], subflowId: string, subflowName?: string): NarrativeEntry[];
|
|
638
|
+
|
|
639
|
+
export { type NarrativeEntry as AdapterNarrativeEntry, type BaseComponentProps, type DarkModeTokensOptions, type DefaultExpanded, type DiffEntry, type EntryRangeIndex, ExplainableShell, type ExplainableShellProps, FootprintTheme, GanttTimeline, type GanttTimelineProps, type MemoryChange, MemoryInspector, type MemoryInspectorProps, MemoryPanel, type MemoryPanelProps, type NarrativeEntry, NarrativeLog, type NarrativeLogProps, NarrativePanel, type NarrativePanelProps, NarrativeTrace, type NarrativeTraceProps, type PanelLabels, type RecorderView, ResultPanel, type ResultPanelProps, type RuntimeSnapshotInput, ScopeDiff, type ScopeDiffProps, type ShellTab, type Size, SnapshotPanel, type SnapshotPanelProps, type StageDetailMode, StageDetailPanel, type StageDetailPanelProps, type StageSnapshot, StoryNarrative, type StoryNarrativeProps, SubflowTree, type SubflowTreeEntry, type SubflowTreeProps, type ThemePresetName, type ThemeTokens, TimeTravelControls, type TimeTravelControlsProps, buildEntryRangeIndex, computeRevealedEntryCount, coolDark, coolLight, createSnapshots, defaultTokens, extractSubflowNarrative, rawDefaults, subflowResultToSnapshots, themePresets, toVisualizationSnapshots, tokensToCSSVars, useDarkModeTokens, useFootprintTheme, warmDark, warmLight };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ interface StageSnapshot {
|
|
|
7
7
|
stageName: string;
|
|
8
8
|
/** Human-readable label */
|
|
9
9
|
stageLabel: string;
|
|
10
|
+
/** Unique per-execution-step identifier. Format: [subflowPath/]stageId#executionIndex. Key for recorder Map lookup. */
|
|
11
|
+
runtimeStageId?: string;
|
|
10
12
|
/** Accumulated memory state after this stage ran */
|
|
11
13
|
memory: Record<string, unknown>;
|
|
12
14
|
/** Narrative text describing what happened */
|
|
@@ -26,15 +28,24 @@ interface StageSnapshot {
|
|
|
26
28
|
}
|
|
27
29
|
/** Structured narrative entry — preserves type info for semantic rendering. */
|
|
28
30
|
interface NarrativeEntry {
|
|
29
|
-
type: 'stage' | 'step' | 'condition' | 'fork' | 'selector' | 'subflow' | 'loop' | 'break' | 'error';
|
|
31
|
+
type: 'stage' | 'step' | 'condition' | 'fork' | 'selector' | 'subflow' | 'loop' | 'break' | 'error' | 'pause' | 'resume';
|
|
30
32
|
text: string;
|
|
31
33
|
depth: number;
|
|
32
34
|
stageName?: string;
|
|
33
35
|
/** Stable stage identifier (matches spec node id). Primary key for UI sync. */
|
|
34
36
|
stageId?: string;
|
|
37
|
+
/** Unique per-execution-step identifier. Format: [subflowPath/]stageId#executionIndex.
|
|
38
|
+
* Used for exact time-travel sync (preferred over stageId for progressive reveal). */
|
|
39
|
+
runtimeStageId?: string;
|
|
35
40
|
/** Subflow ID when this entry was generated inside a subflow. */
|
|
36
41
|
subflowId?: string;
|
|
42
|
+
/** Direction for subflow entries: 'entry' when entering, 'exit' when leaving. */
|
|
43
|
+
direction?: 'entry' | 'exit';
|
|
37
44
|
stepNumber?: number;
|
|
45
|
+
/** Scope key that was read or written. Only present on 'step' entries. */
|
|
46
|
+
key?: string;
|
|
47
|
+
/** Raw value from the scope event. Only present on 'step' entries. */
|
|
48
|
+
rawValue?: unknown;
|
|
38
49
|
}
|
|
39
50
|
/** Component size variants */
|
|
40
51
|
type Size = "compact" | "default" | "detailed";
|
|
@@ -373,6 +384,9 @@ interface RecorderView {
|
|
|
373
384
|
id: string;
|
|
374
385
|
/** Display label on the tab */
|
|
375
386
|
name: string;
|
|
387
|
+
/** Short description shown as tooltip and header for auto-detected views.
|
|
388
|
+
* e.g., "Per-step timing and I/O counts (KeyedRecorder)" */
|
|
389
|
+
description?: string;
|
|
376
390
|
/**
|
|
377
391
|
* Render function — receives the current snapshot index and all snapshots.
|
|
378
392
|
* Return a React node to display in the details panel.
|
|
@@ -563,4 +577,63 @@ declare function createSnapshots(stages: Array<{
|
|
|
563
577
|
subflowId?: string;
|
|
564
578
|
}>): StageSnapshot[];
|
|
565
579
|
|
|
566
|
-
|
|
580
|
+
/**
|
|
581
|
+
* Narrative sync utilities — shared logic for mapping timeline position
|
|
582
|
+
* to narrative entries. Used by NarrativePanel and available to consumers
|
|
583
|
+
* building custom visualization shells.
|
|
584
|
+
*/
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Range index: runtimeStageId → half-open range [firstIdx, endIdx) in entries array.
|
|
588
|
+
*
|
|
589
|
+
* This is the same shape as `SequenceRecorder.getEntryRanges()` in footprintjs.
|
|
590
|
+
* When you have recorder access, pass `recorder.getEntryRanges()` directly.
|
|
591
|
+
* When you only have the flat array, use `buildEntryRangeIndex()` to build it.
|
|
592
|
+
*/
|
|
593
|
+
type EntryRangeIndex = ReadonlyMap<string, {
|
|
594
|
+
readonly firstIdx: number;
|
|
595
|
+
readonly endIdx: number;
|
|
596
|
+
}>;
|
|
597
|
+
/**
|
|
598
|
+
* Build a range index from a flat entries array for O(1) per-step lookups.
|
|
599
|
+
* Equivalent to `SequenceRecorder.getEntryRanges()` but works on detached arrays.
|
|
600
|
+
*
|
|
601
|
+
* Call once when narrativeEntries changes, then pass to `computeRevealedEntryCount`.
|
|
602
|
+
*
|
|
603
|
+
* @param entries — structured entries (from CombinedNarrativeRecorder.getEntries() or getNarrativeEntries())
|
|
604
|
+
* @returns range index for fast slider sync
|
|
605
|
+
*/
|
|
606
|
+
declare function buildEntryRangeIndex(entries: Pick<NarrativeEntry, "runtimeStageId">[]): EntryRangeIndex;
|
|
607
|
+
/**
|
|
608
|
+
* Compute how many narrative entries to reveal at a given slider position.
|
|
609
|
+
*
|
|
610
|
+
* **With range index (preferred):** O(selectedIndex) — one Map lookup per snapshot.
|
|
611
|
+
* **Without index (convenience):** O(entries) forward scan.
|
|
612
|
+
*
|
|
613
|
+
* The range index can come from:
|
|
614
|
+
* - `SequenceRecorder.getEntryRanges()` (when you have recorder access)
|
|
615
|
+
* - `buildEntryRangeIndex(entries)` (when you only have the flat array)
|
|
616
|
+
*
|
|
617
|
+
* @param narrativeEntries — structured entries from CombinedNarrativeRecorder
|
|
618
|
+
* @param snapshots — execution timeline (from adapter)
|
|
619
|
+
* @param selectedIndex — current slider position (0-based)
|
|
620
|
+
* @param rangeIndex — optional precomputed range index for O(1) lookups
|
|
621
|
+
* @returns number of entries to reveal (0 to narrativeEntries.length)
|
|
622
|
+
*/
|
|
623
|
+
declare function computeRevealedEntryCount(narrativeEntries: NarrativeEntry[], snapshots: Pick<StageSnapshot, "runtimeStageId">[], selectedIndex: number, rangeIndex?: EntryRangeIndex): number;
|
|
624
|
+
/**
|
|
625
|
+
* Extract narrative entries belonging to a specific subflow.
|
|
626
|
+
*
|
|
627
|
+
* Three-tier matching (most reliable first):
|
|
628
|
+
* 1. `stageName` prefix match (e.g., entries with `stageName` starting with `"sf-pay/"`)
|
|
629
|
+
* 2. `subflowId` field match
|
|
630
|
+
* 3. `direction` field on subflow entry/exit markers (renderer-agnostic)
|
|
631
|
+
*
|
|
632
|
+
* @param entries — all narrative entries from the execution
|
|
633
|
+
* @param subflowId — subflow identifier to extract
|
|
634
|
+
* @param subflowName — optional display name for fallback matching
|
|
635
|
+
* @returns entries belonging to the subflow
|
|
636
|
+
*/
|
|
637
|
+
declare function extractSubflowNarrative(entries: NarrativeEntry[], subflowId: string, subflowName?: string): NarrativeEntry[];
|
|
638
|
+
|
|
639
|
+
export { type NarrativeEntry as AdapterNarrativeEntry, type BaseComponentProps, type DarkModeTokensOptions, type DefaultExpanded, type DiffEntry, type EntryRangeIndex, ExplainableShell, type ExplainableShellProps, FootprintTheme, GanttTimeline, type GanttTimelineProps, type MemoryChange, MemoryInspector, type MemoryInspectorProps, MemoryPanel, type MemoryPanelProps, type NarrativeEntry, NarrativeLog, type NarrativeLogProps, NarrativePanel, type NarrativePanelProps, NarrativeTrace, type NarrativeTraceProps, type PanelLabels, type RecorderView, ResultPanel, type ResultPanelProps, type RuntimeSnapshotInput, ScopeDiff, type ScopeDiffProps, type ShellTab, type Size, SnapshotPanel, type SnapshotPanelProps, type StageDetailMode, StageDetailPanel, type StageDetailPanelProps, type StageSnapshot, StoryNarrative, type StoryNarrativeProps, SubflowTree, type SubflowTreeEntry, type SubflowTreeProps, type ThemePresetName, type ThemeTokens, TimeTravelControls, type TimeTravelControlsProps, buildEntryRangeIndex, computeRevealedEntryCount, coolDark, coolLight, createSnapshots, defaultTokens, extractSubflowNarrative, rawDefaults, subflowResultToSnapshots, themePresets, toVisualizationSnapshots, tokensToCSSVars, useDarkModeTokens, useFootprintTheme, warmDark, warmLight };
|
package/dist/index.js
CHANGED
|
@@ -2203,6 +2203,81 @@ function TimeTravelControls({
|
|
|
2203
2203
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2204
2204
|
import { memo as memo4, useState as useState9, useCallback as useCallback7, useMemo as useMemo11, useRef as useRef7, useEffect as useEffect8 } from "react";
|
|
2205
2205
|
|
|
2206
|
+
// src/utils/narrativeSync.ts
|
|
2207
|
+
function buildEntryRangeIndex(entries) {
|
|
2208
|
+
const ranges = /* @__PURE__ */ new Map();
|
|
2209
|
+
let lastId;
|
|
2210
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2211
|
+
const id = entries[i].runtimeStageId;
|
|
2212
|
+
if (id) {
|
|
2213
|
+
const existing = ranges.get(id);
|
|
2214
|
+
if (!existing) {
|
|
2215
|
+
ranges.set(id, { firstIdx: i, endIdx: i + 1 });
|
|
2216
|
+
} else {
|
|
2217
|
+
existing.endIdx = i + 1;
|
|
2218
|
+
}
|
|
2219
|
+
lastId = id;
|
|
2220
|
+
} else if (lastId) {
|
|
2221
|
+
ranges.get(lastId).endIdx = i + 1;
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
return ranges;
|
|
2225
|
+
}
|
|
2226
|
+
function computeRevealedEntryCount(narrativeEntries, snapshots, selectedIndex, rangeIndex) {
|
|
2227
|
+
if (!narrativeEntries.length || snapshots.length === 0) return 0;
|
|
2228
|
+
if (rangeIndex) {
|
|
2229
|
+
let maxEndIdx = 0;
|
|
2230
|
+
for (let si = 0; si <= selectedIndex && si < snapshots.length; si++) {
|
|
2231
|
+
const targetId = snapshots[si].runtimeStageId;
|
|
2232
|
+
if (!targetId) continue;
|
|
2233
|
+
const range = rangeIndex.get(targetId);
|
|
2234
|
+
if (range && range.endIdx > maxEndIdx) {
|
|
2235
|
+
maxEndIdx = range.endIdx;
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return maxEndIdx;
|
|
2239
|
+
}
|
|
2240
|
+
let entryIdx = 0;
|
|
2241
|
+
for (let si = 0; si <= selectedIndex && si < snapshots.length; si++) {
|
|
2242
|
+
const targetId = snapshots[si].runtimeStageId;
|
|
2243
|
+
if (!targetId) continue;
|
|
2244
|
+
let found = false;
|
|
2245
|
+
for (let j = entryIdx; j < narrativeEntries.length; j++) {
|
|
2246
|
+
if (narrativeEntries[j].runtimeStageId === targetId) {
|
|
2247
|
+
found = true;
|
|
2248
|
+
entryIdx = j;
|
|
2249
|
+
break;
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
if (!found) continue;
|
|
2253
|
+
while (entryIdx < narrativeEntries.length) {
|
|
2254
|
+
const eId = narrativeEntries[entryIdx].runtimeStageId;
|
|
2255
|
+
if (eId && eId !== targetId) break;
|
|
2256
|
+
entryIdx++;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
return entryIdx;
|
|
2260
|
+
}
|
|
2261
|
+
function extractSubflowNarrative(entries, subflowId, subflowName) {
|
|
2262
|
+
const prefix = subflowId + "/";
|
|
2263
|
+
const byPrefix = entries.filter((e) => e.stageName?.startsWith(prefix));
|
|
2264
|
+
if (byPrefix.length > 0) return byPrefix;
|
|
2265
|
+
const byId = entries.filter((e) => e.subflowId === subflowId);
|
|
2266
|
+
if (byId.length > 0) return byId;
|
|
2267
|
+
const result = [];
|
|
2268
|
+
const searchName = subflowName ?? subflowId;
|
|
2269
|
+
let inside = false;
|
|
2270
|
+
for (const entry of entries) {
|
|
2271
|
+
if (entry.type === "subflow" && entry.direction === "entry" && entry.stageName === searchName) {
|
|
2272
|
+
inside = true;
|
|
2273
|
+
continue;
|
|
2274
|
+
}
|
|
2275
|
+
if (inside && entry.type === "subflow" && entry.direction === "exit" && entry.stageName === searchName) break;
|
|
2276
|
+
if (inside) result.push(entry);
|
|
2277
|
+
}
|
|
2278
|
+
return result;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2206
2281
|
// src/adapters/fromRuntimeSnapshot.ts
|
|
2207
2282
|
function toVisualizationSnapshots(runtime, narrativeEntries) {
|
|
2208
2283
|
const stageNarrativeMap = narrativeEntries?.length ? buildStageNarrativeMap(narrativeEntries) : /* @__PURE__ */ new Map();
|
|
@@ -2278,6 +2353,7 @@ function flattenTree(node, out, sharedState, accumulatedMs = 0, subflowResults,
|
|
|
2278
2353
|
out.push({
|
|
2279
2354
|
stageName: displayName,
|
|
2280
2355
|
stageLabel: stageId,
|
|
2356
|
+
runtimeStageId: node.runtimeStageId ?? void 0,
|
|
2281
2357
|
memory,
|
|
2282
2358
|
narrative,
|
|
2283
2359
|
startMs,
|
|
@@ -2630,37 +2706,14 @@ function NarrativePanel({
|
|
|
2630
2706
|
const endIdx = groupsToShow < stageBoundaries.length ? stageBoundaries[groupsToShow] : narrative.length;
|
|
2631
2707
|
return Math.max(1, endIdx);
|
|
2632
2708
|
}, [snapshots.length, selectedIndex, narrative]);
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
if (snap.subflowId) keys.add(snap.subflowId);
|
|
2642
|
-
let found = false;
|
|
2643
|
-
for (let j = entryIdx; j < narrativeEntries.length; j++) {
|
|
2644
|
-
const e = narrativeEntries[j];
|
|
2645
|
-
const eKey = e.stageId ?? e.subflowId ?? e.stageName;
|
|
2646
|
-
if (eKey && keys.has(eKey)) {
|
|
2647
|
-
found = true;
|
|
2648
|
-
entryIdx = j;
|
|
2649
|
-
break;
|
|
2650
|
-
}
|
|
2651
|
-
if (!eKey && !found) {
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
if (!found) continue;
|
|
2655
|
-
while (entryIdx < narrativeEntries.length) {
|
|
2656
|
-
const e = narrativeEntries[entryIdx];
|
|
2657
|
-
const eKey = e.stageId ?? e.subflowId ?? e.stageName;
|
|
2658
|
-
if (eKey && !keys.has(eKey)) break;
|
|
2659
|
-
entryIdx++;
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
return entryIdx;
|
|
2663
|
-
}, [narrativeEntries, snapshots, selectedIndex]);
|
|
2709
|
+
const rangeIndex = useMemo8(
|
|
2710
|
+
() => narrativeEntries?.length ? buildEntryRangeIndex(narrativeEntries) : void 0,
|
|
2711
|
+
[narrativeEntries]
|
|
2712
|
+
);
|
|
2713
|
+
const revealedEntryCount = useMemo8(
|
|
2714
|
+
() => narrativeEntries?.length ? computeRevealedEntryCount(narrativeEntries, snapshots, selectedIndex, rangeIndex) : 0,
|
|
2715
|
+
[narrativeEntries, snapshots, selectedIndex, rangeIndex]
|
|
2716
|
+
);
|
|
2664
2717
|
const hasStructured = narrativeEntries && narrativeEntries.length > 0;
|
|
2665
2718
|
const [copied, setCopied] = useState7(false);
|
|
2666
2719
|
const buildLLMNarrative = useCallback4(() => {
|
|
@@ -2676,7 +2729,7 @@ function NarrativePanel({
|
|
|
2676
2729
|
root.push(entry);
|
|
2677
2730
|
} else {
|
|
2678
2731
|
if (entry.type === "subflow") {
|
|
2679
|
-
const isExit = entry.
|
|
2732
|
+
const isExit = entry.direction === "exit";
|
|
2680
2733
|
if (!isExit) {
|
|
2681
2734
|
root.push(entry);
|
|
2682
2735
|
}
|
|
@@ -2696,7 +2749,11 @@ function NarrativePanel({
|
|
|
2696
2749
|
if (opts?.inSubflow && e.type === "subflow") continue;
|
|
2697
2750
|
let text = e.text;
|
|
2698
2751
|
if (opts?.inSubflow) {
|
|
2699
|
-
|
|
2752
|
+
const prefix = `[${opts.inSubflow}/`;
|
|
2753
|
+
const idx = text.indexOf(prefix);
|
|
2754
|
+
if (idx !== -1) {
|
|
2755
|
+
text = text.slice(0, idx) + "[" + text.slice(idx + prefix.length);
|
|
2756
|
+
}
|
|
2700
2757
|
}
|
|
2701
2758
|
const isHeading = e.type === "stage" || e.type === "subflow" || e.type === "fork" || e.type === "selector";
|
|
2702
2759
|
if (isHeading) {
|
|
@@ -4003,6 +4060,12 @@ var DetailsContent = memo4(function DetailsContent2({
|
|
|
4003
4060
|
];
|
|
4004
4061
|
const allViews = [...builtInViews, ...extraViews ?? []];
|
|
4005
4062
|
const [activeViewId, setActiveViewId] = useState9(allViews[0]?.id ?? "memory");
|
|
4063
|
+
const viewIds = allViews.map((v2) => v2.id).join(",");
|
|
4064
|
+
useEffect8(() => {
|
|
4065
|
+
if (!allViews.find((v2) => v2.id === activeViewId)) {
|
|
4066
|
+
setActiveViewId(allViews[0]?.id ?? "memory");
|
|
4067
|
+
}
|
|
4068
|
+
}, [viewIds]);
|
|
4006
4069
|
const activeView = allViews.find((v2) => v2.id === activeViewId) ?? allViews[0];
|
|
4007
4070
|
return /* @__PURE__ */ jsxs17("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
|
|
4008
4071
|
/* @__PURE__ */ jsx18("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0, overflowX: "auto" }, children: allViews.map((view) => {
|
|
@@ -4053,25 +4116,6 @@ function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narra
|
|
|
4053
4116
|
snapshots: sfSnapshots
|
|
4054
4117
|
};
|
|
4055
4118
|
}
|
|
4056
|
-
function extractSubflowNarrative(entries, subflowId, subflowName) {
|
|
4057
|
-
const prefix = subflowId + "/";
|
|
4058
|
-
const byPrefix = entries.filter((e) => e.stageName?.startsWith(prefix));
|
|
4059
|
-
if (byPrefix.length > 0) return byPrefix;
|
|
4060
|
-
const byId = entries.filter((e) => e.subflowId === subflowId);
|
|
4061
|
-
if (byId.length > 0) return byId;
|
|
4062
|
-
const result = [];
|
|
4063
|
-
const searchName = subflowName ?? subflowId;
|
|
4064
|
-
let inside = false;
|
|
4065
|
-
for (const entry of entries) {
|
|
4066
|
-
if (entry.type === "subflow" && entry.text.includes(searchName) && entry.text.startsWith("Entering")) {
|
|
4067
|
-
inside = true;
|
|
4068
|
-
continue;
|
|
4069
|
-
}
|
|
4070
|
-
if (inside && entry.type === "subflow" && entry.text.includes(searchName) && entry.text.startsWith("Exiting")) break;
|
|
4071
|
-
if (inside) result.push(entry);
|
|
4072
|
-
}
|
|
4073
|
-
return result;
|
|
4074
|
-
}
|
|
4075
4119
|
function findSubflowSpecNode(node, name) {
|
|
4076
4120
|
if ((node.name === name || node.id === name) && node.isSubflowRoot) return node;
|
|
4077
4121
|
if (node.children) {
|
|
@@ -4160,22 +4204,22 @@ function ExplainableShell({
|
|
|
4160
4204
|
const recorders = runtimeSnapshot?.recorders;
|
|
4161
4205
|
if (!recorders?.length) return [];
|
|
4162
4206
|
const explicitIds = new Set((recorderViews ?? []).map((v2) => v2.id));
|
|
4163
|
-
return recorders.filter((r) => !explicitIds.has(r.id)).map((r) => ({ id: r.id, name: r.name, data: r.data }));
|
|
4207
|
+
return recorders.filter((r) => !explicitIds.has(r.id)).map((r) => ({ id: r.id, name: r.name, description: r.description, data: r.data }));
|
|
4164
4208
|
}, [runtimeSnapshot, recorderViews]);
|
|
4165
4209
|
const hasNarrative = !!(narrative?.length || narrativeEntries?.length);
|
|
4166
4210
|
const allTabs = useMemo11(() => {
|
|
4167
4211
|
const tabs2 = [
|
|
4168
|
-
{ id: "result", name: "Result" },
|
|
4169
|
-
{ id: "memory", name: "Memory" }
|
|
4212
|
+
{ id: "result", name: "Result", description: "Final output and console logs" },
|
|
4213
|
+
{ id: "memory", name: "Memory", description: "Accumulated shared state at each stage" }
|
|
4170
4214
|
];
|
|
4171
4215
|
if (hasNarrative) {
|
|
4172
|
-
tabs2.push({ id: "narrative", name: "Narrative" });
|
|
4216
|
+
tabs2.push({ id: "narrative", name: "Narrative", description: "What happened, what data flowed, what decisions were made" });
|
|
4173
4217
|
}
|
|
4174
4218
|
for (const v2 of recorderViews ?? []) {
|
|
4175
|
-
tabs2.push({ id: v2.id, name: v2.name });
|
|
4219
|
+
tabs2.push({ id: v2.id, name: v2.name, description: v2.description });
|
|
4176
4220
|
}
|
|
4177
4221
|
for (const v2 of autoRecorderViews) {
|
|
4178
|
-
tabs2.push({ id: v2.id, name: v2.name });
|
|
4222
|
+
tabs2.push({ id: v2.id, name: v2.name, description: v2.description });
|
|
4179
4223
|
}
|
|
4180
4224
|
const hideSet = new Set(hideTabsProp ?? []);
|
|
4181
4225
|
return hideSet.size > 0 ? tabs2.filter((t) => !hideSet.has(t.id)) : tabs2;
|
|
@@ -4337,7 +4381,17 @@ function ExplainableShell({
|
|
|
4337
4381
|
}
|
|
4338
4382
|
const autoView = autoRecorderViews.find((v2) => v2.id === activeTab);
|
|
4339
4383
|
if (autoView) {
|
|
4340
|
-
return /* @__PURE__ */
|
|
4384
|
+
return /* @__PURE__ */ jsxs17("div", { style: { overflow: "auto", height: "100%", display: "flex", flexDirection: "column" }, children: [
|
|
4385
|
+
autoView.description && /* @__PURE__ */ jsx18("div", { style: {
|
|
4386
|
+
padding: "6px 12px",
|
|
4387
|
+
fontSize: 11,
|
|
4388
|
+
color: theme.textMuted,
|
|
4389
|
+
fontStyle: "italic",
|
|
4390
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
4391
|
+
flexShrink: 0
|
|
4392
|
+
}, children: autoView.description }),
|
|
4393
|
+
/* @__PURE__ */ jsx18("div", { style: { padding: 12, fontFamily: theme.fontMono, fontSize: 11, whiteSpace: "pre-wrap", overflow: "auto", flex: 1 }, children: typeof autoView.data === "string" ? autoView.data : JSON.stringify(autoView.data, null, 2) })
|
|
4394
|
+
] });
|
|
4341
4395
|
}
|
|
4342
4396
|
return null;
|
|
4343
4397
|
}, [activeTab, resultData, logs, hideConsole, size, activeSnapshots, safeIdx, activeNarrativeEntries, activeNarrative, recorderViews, autoRecorderViews]);
|
|
@@ -4354,6 +4408,7 @@ function ExplainableShell({
|
|
|
4354
4408
|
"button",
|
|
4355
4409
|
{
|
|
4356
4410
|
onClick: () => handleTabChange(tab.id),
|
|
4411
|
+
title: tab.description,
|
|
4357
4412
|
style: {
|
|
4358
4413
|
padding: "6px 14px",
|
|
4359
4414
|
fontSize: 11,
|
|
@@ -4487,10 +4542,13 @@ export {
|
|
|
4487
4542
|
StoryNarrative,
|
|
4488
4543
|
SubflowTree,
|
|
4489
4544
|
TimeTravelControls,
|
|
4545
|
+
buildEntryRangeIndex,
|
|
4546
|
+
computeRevealedEntryCount,
|
|
4490
4547
|
coolDark,
|
|
4491
4548
|
coolLight,
|
|
4492
4549
|
createSnapshots,
|
|
4493
4550
|
defaultTokens,
|
|
4551
|
+
extractSubflowNarrative,
|
|
4494
4552
|
rawDefaults,
|
|
4495
4553
|
subflowResultToSnapshots,
|
|
4496
4554
|
themePresets,
|