footprint-explainable-ui 0.14.11 → 0.15.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/index.cjs CHANGED
@@ -20,9 +20,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
+ CompactTimeline: () => CompactTimeline,
24
+ DataTracePanel: () => DataTracePanel,
23
25
  ExplainableShell: () => ExplainableShell,
24
26
  FootprintTheme: () => FootprintTheme,
25
27
  GanttTimeline: () => GanttTimeline,
28
+ InsightPanel: () => InsightPanel,
29
+ InspectorPanel: () => InspectorPanel,
26
30
  MemoryInspector: () => MemoryInspector,
27
31
  MemoryPanel: () => MemoryPanel,
28
32
  NarrativeLog: () => NarrativeLog,
@@ -2257,7 +2261,7 @@ function TimeTravelControls({
2257
2261
  }
2258
2262
 
2259
2263
  // src/components/ExplainableShell/ExplainableShell.tsx
2260
- var import_react20 = require("react");
2264
+ var import_react24 = require("react");
2261
2265
 
2262
2266
  // src/utils/narrativeSync.ts
2263
2267
  function buildEntryRangeIndex(entries) {
@@ -3997,22 +4001,490 @@ function TracedFlowchartView({
3997
4001
  );
3998
4002
  }
3999
4003
 
4000
- // src/components/ExplainableShell/ExplainableShell.tsx
4004
+ // src/components/InspectorPanel/InspectorPanel.tsx
4005
+ var import_react21 = require("react");
4006
+
4007
+ // src/components/DataTracePanel/DataTracePanel.tsx
4008
+ var import_react20 = require("react");
4001
4009
  var import_jsx_runtime18 = require("react/jsx-runtime");
4002
- var HLinePill = (0, import_react20.memo)(function HLinePill2({
4010
+ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4011
+ frames,
4012
+ selectedStageId,
4013
+ onFrameClick,
4014
+ fromStageName
4015
+ }) {
4016
+ if (frames.length === 0) {
4017
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { padding: 12, color: theme.textMuted, fontSize: 13 }, children: "Select a stage to see its data dependency chain." });
4018
+ }
4019
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
4020
+ fromStageName && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4021
+ "div",
4022
+ {
4023
+ style: {
4024
+ padding: "4px 12px 8px",
4025
+ fontSize: 11,
4026
+ color: theme.textMuted,
4027
+ textTransform: "uppercase",
4028
+ letterSpacing: "0.5px",
4029
+ fontWeight: 600
4030
+ },
4031
+ children: [
4032
+ "Data trace from ",
4033
+ fromStageName
4034
+ ]
4035
+ }
4036
+ ),
4037
+ frames.map((frame, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4038
+ DataTraceFrame,
4039
+ {
4040
+ frame,
4041
+ isFirst: i === 0,
4042
+ isLast: i === frames.length - 1,
4043
+ isSelected: frame.runtimeStageId === selectedStageId,
4044
+ onClick: onFrameClick
4045
+ },
4046
+ frame.runtimeStageId
4047
+ ))
4048
+ ] });
4049
+ });
4050
+ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4051
+ frame,
4052
+ isFirst,
4053
+ isLast,
4054
+ isSelected,
4055
+ onClick
4056
+ }) {
4057
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4058
+ "button",
4059
+ {
4060
+ onClick: () => onClick?.(frame.runtimeStageId),
4061
+ style: {
4062
+ display: "block",
4063
+ width: "100%",
4064
+ textAlign: "left",
4065
+ border: "none",
4066
+ background: isSelected ? "var(--fp-accent-bg, rgba(99,102,241,0.12))" : "transparent",
4067
+ padding: "6px 12px 6px 16px",
4068
+ cursor: onClick ? "pointer" : "default",
4069
+ borderLeft: isSelected ? "3px solid var(--fp-accent, #6366f1)" : "3px solid transparent",
4070
+ color: "inherit",
4071
+ fontSize: 13
4072
+ },
4073
+ children: [
4074
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4075
+ !isFirst && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.textMuted, fontSize: 11 }, children: "\u2191" }),
4076
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4077
+ "span",
4078
+ {
4079
+ style: {
4080
+ fontWeight: isFirst ? 600 : 400,
4081
+ color: isFirst ? "var(--fp-accent, #6366f1)" : theme.textPrimary
4082
+ },
4083
+ children: frame.stageName
4084
+ }
4085
+ ),
4086
+ isLast && !isFirst && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4087
+ "span",
4088
+ {
4089
+ style: {
4090
+ fontSize: 10,
4091
+ color: theme.textMuted,
4092
+ fontStyle: "italic"
4093
+ },
4094
+ children: "(origin)"
4095
+ }
4096
+ )
4097
+ ] }),
4098
+ frame.keysWritten.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4099
+ "div",
4100
+ {
4101
+ style: {
4102
+ fontSize: 11,
4103
+ color: theme.textMuted,
4104
+ paddingLeft: isFirst ? 0 : 18,
4105
+ marginTop: 2
4106
+ },
4107
+ children: [
4108
+ "wrote:",
4109
+ " ",
4110
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.textSecondary }, children: frame.keysWritten.join(", ") })
4111
+ ]
4112
+ }
4113
+ ),
4114
+ frame.linkedBy && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4115
+ "div",
4116
+ {
4117
+ style: {
4118
+ fontSize: 11,
4119
+ color: "var(--fp-accent, #6366f1)",
4120
+ paddingLeft: 18,
4121
+ marginTop: 1
4122
+ },
4123
+ children: [
4124
+ "\u2190 via ",
4125
+ frame.linkedBy
4126
+ ]
4127
+ }
4128
+ )
4129
+ ]
4130
+ }
4131
+ );
4132
+ });
4133
+
4134
+ // src/components/InspectorPanel/InspectorPanel.tsx
4135
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4136
+ var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4137
+ snapshots,
4138
+ selectedIndex,
4139
+ dataTraceFrames,
4140
+ selectedStageId,
4141
+ onNavigateToStage
4142
+ }) {
4143
+ const [tab, setTab] = (0, import_react21.useState)("state");
4144
+ const currentSnapshot = snapshots[selectedIndex];
4145
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4146
+ "div",
4147
+ {
4148
+ style: {
4149
+ display: "flex",
4150
+ flexDirection: "column",
4151
+ height: "100%",
4152
+ overflow: "hidden"
4153
+ },
4154
+ children: [
4155
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4156
+ "div",
4157
+ {
4158
+ style: {
4159
+ display: "flex",
4160
+ borderBottom: `1px solid ${theme.border}`,
4161
+ flexShrink: 0
4162
+ },
4163
+ children: [
4164
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4165
+ TabButton,
4166
+ {
4167
+ active: tab === "state",
4168
+ onClick: () => setTab("state"),
4169
+ label: "State"
4170
+ }
4171
+ ),
4172
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4173
+ TabButton,
4174
+ {
4175
+ active: tab === "trace",
4176
+ onClick: () => setTab("trace"),
4177
+ label: "Data Trace",
4178
+ badge: dataTraceFrames.length > 0 ? String(dataTraceFrames.length) : void 0
4179
+ }
4180
+ )
4181
+ ]
4182
+ }
4183
+ ),
4184
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
4185
+ tab === "state" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4186
+ MemoryPanel,
4187
+ {
4188
+ snapshots,
4189
+ selectedIndex
4190
+ }
4191
+ ),
4192
+ tab === "trace" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4193
+ DataTracePanel,
4194
+ {
4195
+ frames: dataTraceFrames,
4196
+ selectedStageId,
4197
+ onFrameClick: onNavigateToStage,
4198
+ fromStageName: currentSnapshot?.stageName
4199
+ }
4200
+ )
4201
+ ] })
4202
+ ]
4203
+ }
4204
+ );
4205
+ });
4206
+ function TabButton({
4207
+ active,
4208
+ onClick,
4209
+ label,
4210
+ badge
4211
+ }) {
4212
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4213
+ "button",
4214
+ {
4215
+ onClick,
4216
+ style: {
4217
+ padding: "8px 14px",
4218
+ border: "none",
4219
+ borderBottom: active ? "2px solid var(--fp-accent, #6366f1)" : "2px solid transparent",
4220
+ background: "transparent",
4221
+ color: active ? "var(--fp-accent, #6366f1)" : theme.textMuted,
4222
+ fontWeight: active ? 600 : 400,
4223
+ fontSize: 12,
4224
+ cursor: "pointer",
4225
+ display: "flex",
4226
+ alignItems: "center",
4227
+ gap: 4
4228
+ },
4229
+ children: [
4230
+ label,
4231
+ badge && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4232
+ "span",
4233
+ {
4234
+ style: {
4235
+ fontSize: 10,
4236
+ background: active ? "var(--fp-accent, #6366f1)" : theme.textMuted,
4237
+ color: "#fff",
4238
+ borderRadius: 8,
4239
+ padding: "1px 5px",
4240
+ fontWeight: 600
4241
+ },
4242
+ children: badge
4243
+ }
4244
+ )
4245
+ ]
4246
+ }
4247
+ );
4248
+ }
4249
+
4250
+ // src/components/InsightPanel/InsightPanel.tsx
4251
+ var import_react22 = require("react");
4252
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4253
+ var InsightPanel = (0, import_react22.memo)(function InsightPanel2({
4254
+ insights,
4255
+ expandedId,
4256
+ mode
4257
+ }) {
4258
+ if (insights.length === 0) {
4259
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: 12, color: theme.textMuted, fontSize: 13 }, children: "No insights available. Attach recorders to see data." });
4260
+ }
4261
+ if (mode === "grid") {
4262
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InsightGrid, { insights });
4263
+ }
4264
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InsightTabs, { insights, defaultId: expandedId });
4265
+ });
4266
+ var InsightTabs = (0, import_react22.memo)(function InsightTabs2({
4267
+ insights,
4268
+ defaultId
4269
+ }) {
4270
+ const [activeId, setActiveId] = (0, import_react22.useState)(defaultId ?? insights[0]?.id);
4271
+ const active = insights.find((i) => i.id === activeId) ?? insights[0];
4272
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4273
+ "div",
4274
+ {
4275
+ style: {
4276
+ display: "flex",
4277
+ flexDirection: "column",
4278
+ height: "100%",
4279
+ overflow: "hidden"
4280
+ },
4281
+ children: [
4282
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4283
+ "div",
4284
+ {
4285
+ style: {
4286
+ display: "flex",
4287
+ borderBottom: `1px solid ${theme.border}`,
4288
+ flexShrink: 0,
4289
+ overflowX: "auto"
4290
+ },
4291
+ children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4292
+ "button",
4293
+ {
4294
+ onClick: () => setActiveId(insight.id),
4295
+ style: {
4296
+ padding: "8px 12px",
4297
+ border: "none",
4298
+ borderBottom: activeId === insight.id ? "2px solid var(--fp-accent, #6366f1)" : "2px solid transparent",
4299
+ background: "transparent",
4300
+ color: activeId === insight.id ? "var(--fp-accent, #6366f1)" : theme.textMuted,
4301
+ fontWeight: activeId === insight.id ? 600 : 400,
4302
+ fontSize: 12,
4303
+ cursor: "pointer",
4304
+ whiteSpace: "nowrap"
4305
+ },
4306
+ children: insight.name
4307
+ },
4308
+ insight.id
4309
+ ))
4310
+ }
4311
+ ),
4312
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: active?.render() })
4313
+ ]
4314
+ }
4315
+ );
4316
+ });
4317
+ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4318
+ insights
4319
+ }) {
4320
+ const cols = insights.length <= 2 ? 1 : 2;
4321
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4322
+ "div",
4323
+ {
4324
+ style: {
4325
+ display: "grid",
4326
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
4327
+ height: "100%",
4328
+ overflow: "auto",
4329
+ gap: 1,
4330
+ background: theme.border
4331
+ },
4332
+ children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4333
+ "div",
4334
+ {
4335
+ style: {
4336
+ background: "var(--fp-bg, #1a1b26)",
4337
+ display: "flex",
4338
+ flexDirection: "column",
4339
+ overflow: "hidden"
4340
+ },
4341
+ children: [
4342
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4343
+ "div",
4344
+ {
4345
+ style: {
4346
+ padding: "6px 10px",
4347
+ fontSize: 11,
4348
+ fontWeight: 600,
4349
+ color: theme.textMuted,
4350
+ textTransform: "uppercase",
4351
+ letterSpacing: "0.5px",
4352
+ borderBottom: `1px solid ${theme.border}`,
4353
+ flexShrink: 0
4354
+ },
4355
+ children: [
4356
+ insight.name,
4357
+ insight.summary && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4358
+ "span",
4359
+ {
4360
+ style: {
4361
+ marginLeft: 8,
4362
+ fontWeight: 400,
4363
+ fontSize: 10,
4364
+ color: theme.textMuted
4365
+ },
4366
+ children: insight.summary
4367
+ }
4368
+ )
4369
+ ]
4370
+ }
4371
+ ),
4372
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: insight.render() })
4373
+ ]
4374
+ },
4375
+ insight.id
4376
+ ))
4377
+ }
4378
+ );
4379
+ });
4380
+
4381
+ // src/components/CompactTimeline/CompactTimeline.tsx
4382
+ var import_react23 = require("react");
4383
+ var import_jsx_runtime21 = require("react/jsx-runtime");
4384
+ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4385
+ snapshots,
4386
+ selectedIndex,
4387
+ defaultExpanded = false
4388
+ }) {
4389
+ const [expanded, setExpanded] = (0, import_react23.useState)(defaultExpanded);
4390
+ if (snapshots.length === 0) return null;
4391
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: [
4392
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4393
+ "button",
4394
+ {
4395
+ onClick: () => setExpanded((e) => !e),
4396
+ style: {
4397
+ width: "100%",
4398
+ display: "flex",
4399
+ alignItems: "center",
4400
+ gap: 8,
4401
+ padding: "6px 12px",
4402
+ border: "none",
4403
+ background: "transparent",
4404
+ cursor: "pointer",
4405
+ fontSize: 11,
4406
+ color: theme.textMuted,
4407
+ fontWeight: 600,
4408
+ textTransform: "uppercase",
4409
+ letterSpacing: "0.5px"
4410
+ },
4411
+ children: [
4412
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 10 }, children: expanded ? "\u25BC" : "\u25B8" }),
4413
+ "Timeline",
4414
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { style: { fontWeight: 400, fontSize: 10 }, children: [
4415
+ snapshots.length,
4416
+ " stages"
4417
+ ] }),
4418
+ !expanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4419
+ "div",
4420
+ {
4421
+ style: {
4422
+ flex: 1,
4423
+ display: "flex",
4424
+ alignItems: "center",
4425
+ gap: 2,
4426
+ marginLeft: 8
4427
+ },
4428
+ children: [
4429
+ snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4430
+ "div",
4431
+ {
4432
+ style: {
4433
+ width: i === selectedIndex ? 8 : 5,
4434
+ height: i === selectedIndex ? 8 : 5,
4435
+ borderRadius: "50%",
4436
+ background: i < selectedIndex ? "var(--fp-success, #22c55e)" : i === selectedIndex ? "var(--fp-accent, #6366f1)" : theme.textMuted + "40",
4437
+ transition: "all 0.15s",
4438
+ flexShrink: 0
4439
+ },
4440
+ title: snap.stageName
4441
+ },
4442
+ i
4443
+ )),
4444
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4445
+ "div",
4446
+ {
4447
+ style: {
4448
+ flex: 1,
4449
+ height: 1,
4450
+ background: theme.textMuted + "30",
4451
+ marginLeft: -2,
4452
+ marginRight: 4
4453
+ }
4454
+ }
4455
+ )
4456
+ ]
4457
+ }
4458
+ )
4459
+ ]
4460
+ }
4461
+ ),
4462
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { padding: "0 12px 8px" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4463
+ GanttTimeline,
4464
+ {
4465
+ snapshots,
4466
+ selectedIndex
4467
+ }
4468
+ ) })
4469
+ ] });
4470
+ });
4471
+
4472
+ // src/components/ExplainableShell/ExplainableShell.tsx
4473
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4474
+ var HLinePill = (0, import_react24.memo)(function HLinePill2({
4003
4475
  label,
4004
4476
  detail,
4005
4477
  expanded,
4006
4478
  onClick
4007
4479
  }) {
4008
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: {
4480
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: {
4009
4481
  display: "flex",
4010
4482
  alignItems: "center",
4011
4483
  gap: 0,
4012
4484
  padding: "0"
4013
4485
  }, children: [
4014
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
4015
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4486
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
4487
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4016
4488
  "button",
4017
4489
  {
4018
4490
  onClick,
@@ -4036,31 +4508,31 @@ var HLinePill = (0, import_react20.memo)(function HLinePill2({
4036
4508
  transition: "color 0.15s ease"
4037
4509
  },
4038
4510
  children: [
4039
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
4511
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
4040
4512
  label,
4041
- detail && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
4513
+ detail && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
4042
4514
  ]
4043
4515
  }
4044
4516
  ),
4045
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
4517
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
4046
4518
  ] });
4047
4519
  });
4048
- var VLinePill = (0, import_react20.memo)(function VLinePill2({
4520
+ var VLinePill = (0, import_react24.memo)(function VLinePill2({
4049
4521
  label,
4050
4522
  expanded,
4051
4523
  side = "right",
4052
4524
  onClick
4053
4525
  }) {
4054
4526
  const arrow = side === "right" ? expanded ? "\u25B6" : "\u25C0" : expanded ? "\u25C0" : "\u25B6";
4055
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: {
4527
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: {
4056
4528
  display: "flex",
4057
4529
  flexDirection: "column",
4058
4530
  alignItems: "center",
4059
4531
  gap: 0,
4060
4532
  padding: "0"
4061
4533
  }, children: [
4062
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
4063
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4534
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
4535
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4064
4536
  "button",
4065
4537
  {
4066
4538
  onClick,
@@ -4085,12 +4557,12 @@ var VLinePill = (0, import_react20.memo)(function VLinePill2({
4085
4557
  transition: "color 0.15s ease"
4086
4558
  },
4087
4559
  children: [
4088
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
4560
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
4089
4561
  label
4090
4562
  ]
4091
4563
  }
4092
4564
  ),
4093
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
4565
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
4094
4566
  ] });
4095
4567
  });
4096
4568
  function detectKeyedSteps(data) {
@@ -4127,9 +4599,9 @@ function KeyedRecorderView({
4127
4599
  snapshots,
4128
4600
  selectedIndex
4129
4601
  }) {
4130
- const [showAggregate, setShowAggregate] = (0, import_react20.useState)(false);
4131
- const detected = (0, import_react20.useMemo)(() => detectKeyedSteps(data), [data]);
4132
- const visibleKeys = (0, import_react20.useMemo)(() => {
4602
+ const [showAggregate, setShowAggregate] = (0, import_react24.useState)(false);
4603
+ const detected = (0, import_react24.useMemo)(() => detectKeyedSteps(data), [data]);
4604
+ const visibleKeys = (0, import_react24.useMemo)(() => {
4133
4605
  const keys = /* @__PURE__ */ new Set();
4134
4606
  for (let i = 0; i <= selectedIndex && i < snapshots.length; i++) {
4135
4607
  const snap = snapshots[i];
@@ -4144,7 +4616,7 @@ function KeyedRecorderView({
4144
4616
  }, [snapshots, selectedIndex, detected?.keyType]);
4145
4617
  const isAtEnd = selectedIndex >= snapshots.length - 1;
4146
4618
  if (!detected) {
4147
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { padding: 12, fontFamily: theme.fontMono, fontSize: 11, whiteSpace: "pre-wrap", overflow: "auto", height: "100%" }, children: typeof data === "string" ? data : JSON.stringify(data, null, 2) });
4619
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: 12, fontFamily: theme.fontMono, fontSize: 11, whiteSpace: "pre-wrap", overflow: "auto", height: "100%" }, children: typeof data === "string" ? data : JSON.stringify(data, null, 2) });
4148
4620
  }
4149
4621
  const steps = detected.steps;
4150
4622
  const hints = extractRenderHints(data);
@@ -4158,13 +4630,13 @@ function KeyedRecorderView({
4158
4630
  }
4159
4631
  }
4160
4632
  const grandTotal = hints?.grandTotal ?? 0;
4161
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { overflow: "auto", height: "100%", display: "flex", flexDirection: "column" }, children: [
4162
- description && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { padding: "6px 12px", fontSize: 11, color: theme.textMuted, fontStyle: "italic", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: description }),
4163
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: 12, flex: 1, overflow: "auto" }, children: [
4633
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { overflow: "auto", height: "100%", display: "flex", flexDirection: "column" }, children: [
4634
+ description && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: "6px 12px", fontSize: 11, color: theme.textMuted, fontStyle: "italic", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: description }),
4635
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: 12, flex: 1, overflow: "auto" }, children: [
4164
4636
  preferredOperation === "aggregate" ? (
4165
4637
  /* AGGREGATE: collect silently during scrub, button at end to reveal total */
4166
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4167
- isAtEnd ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { marginBottom: 16 }, children: !showAggregate ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4638
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4639
+ isAtEnd ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { marginBottom: 16 }, children: !showAggregate ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4168
4640
  "button",
4169
4641
  {
4170
4642
  onClick: () => setShowAggregate(true),
@@ -4182,35 +4654,35 @@ function KeyedRecorderView({
4182
4654
  },
4183
4655
  children: "Aggregate \u2014 Show Grand Total"
4184
4656
  }
4185
- ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "14px 16px", background: `color-mix(in srgb, ${theme.success} 12%, transparent)`, borderRadius: 8, border: `1px solid ${theme.success}44` }, children: [
4186
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Aggregate \u2014 grand total" }),
4187
- numFieldKey && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { fontSize: 26, fontWeight: 700, color: theme.success }, children: [
4657
+ ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "14px 16px", background: `color-mix(in srgb, ${theme.success} 12%, transparent)`, borderRadius: 8, border: `1px solid ${theme.success}44` }, children: [
4658
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Aggregate \u2014 grand total" }),
4659
+ numFieldKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { fontSize: 26, fontWeight: 700, color: theme.success }, children: [
4188
4660
  grandTotal < 1 ? grandTotal.toFixed(3) : grandTotal.toFixed(1),
4189
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: { fontSize: 11, color: theme.textMuted, fontWeight: 400, marginLeft: 8 }, children: [
4661
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { style: { fontSize: 11, color: theme.textMuted, fontWeight: 400, marginLeft: 8 }, children: [
4190
4662
  numFieldKey,
4191
4663
  " \xB7 ",
4192
4664
  allKeys.length,
4193
4665
  " steps"
4194
4666
  ] })
4195
4667
  ] })
4196
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.textMuted} 6%, transparent)`, borderRadius: 6, marginBottom: 16, border: `1px dashed ${theme.border}` }, children: [
4197
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", fontWeight: 600 }, children: "Collecting data..." }),
4198
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { fontSize: 11, color: theme.textMuted, marginTop: 4 }, children: [
4668
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.textMuted} 6%, transparent)`, borderRadius: 6, marginBottom: 16, border: `1px dashed ${theme.border}` }, children: [
4669
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", fontWeight: 600 }, children: "Collecting data..." }),
4670
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { fontSize: 11, color: theme.textMuted, marginTop: 4 }, children: [
4199
4671
  visibleEntries.length,
4200
4672
  " of ",
4201
4673
  allKeys.length,
4202
4674
  " steps collected. Scrub to end to aggregate."
4203
4675
  ] })
4204
4676
  ] }),
4205
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4677
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4206
4678
  ] })
4207
4679
  ) : preferredOperation === "accumulate" ? (
4208
4680
  /* ACCUMULATE: running total grows with slider — IS the total at end, no button */
4209
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4210
- numFieldKey && visibleEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.primary} 8%, transparent)`, borderRadius: 6, marginBottom: 16 }, children: [
4211
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 4, fontWeight: 600 }, children: "Accumulate \u2014 running total up to this step" }),
4212
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontWeight: 700, fontSize: 18, color: theme.primary }, children: runningTotal < 1 ? runningTotal.toFixed(3) : runningTotal.toFixed(1) }),
4213
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { style: { color: theme.textMuted, marginLeft: 8, fontSize: 10 }, children: [
4681
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4682
+ numFieldKey && visibleEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.primary} 8%, transparent)`, borderRadius: 6, marginBottom: 16 }, children: [
4683
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 4, fontWeight: 600 }, children: "Accumulate \u2014 running total up to this step" }),
4684
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 700, fontSize: 18, color: theme.primary }, children: runningTotal < 1 ? runningTotal.toFixed(3) : runningTotal.toFixed(1) }),
4685
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { style: { color: theme.textMuted, marginLeft: 8, fontSize: 10 }, children: [
4214
4686
  numFieldKey,
4215
4687
  " \xB7 ",
4216
4688
  visibleEntries.length,
@@ -4219,27 +4691,27 @@ function KeyedRecorderView({
4219
4691
  " steps"
4220
4692
  ] })
4221
4693
  ] }),
4222
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4694
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4223
4695
  ] })
4224
4696
  ) : (
4225
4697
  /* TRANSLATE: per-step entries prominent, no totals */
4226
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Translate \u2014 per-step detail" })
4698
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Translate \u2014 per-step detail" })
4227
4699
  ),
4228
4700
  visibleEntries.map((key) => {
4229
4701
  const entry = steps[key];
4230
4702
  const label = entry.stageName ?? key;
4231
4703
  const numVal = numFieldKey ? entry[numFieldKey] : void 0;
4232
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "4px 0", fontSize: 12, fontFamily: theme.fontMono, borderBottom: `1px solid ${theme.border}22` }, children: [
4233
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.textMuted, width: 140, flexShrink: 0, fontSize: 10 }, children: key }),
4234
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { fontWeight: 600, flex: 1 }, children: label }),
4235
- numVal !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.primary, fontWeight: 700, marginLeft: 8 }, children: numVal < 1 ? numVal.toFixed(3) : numVal.toFixed(1) })
4704
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "4px 0", fontSize: 12, fontFamily: theme.fontMono, borderBottom: `1px solid ${theme.border}22` }, children: [
4705
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { color: theme.textMuted, width: 140, flexShrink: 0, fontSize: 10 }, children: key }),
4706
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 600, flex: 1 }, children: label }),
4707
+ numVal !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { color: theme.primary, fontWeight: 700, marginLeft: 8 }, children: numVal < 1 ? numVal.toFixed(3) : numVal.toFixed(1) })
4236
4708
  ] }, key);
4237
4709
  }),
4238
- visibleEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", padding: "8px 0" }, children: "Scrub the slider to reveal entries..." })
4710
+ visibleEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", padding: "8px 0" }, children: "Scrub the slider to reveal entries..." })
4239
4711
  ] })
4240
4712
  ] });
4241
4713
  }
4242
- var DetailsContent = (0, import_react20.memo)(function DetailsContent2({
4714
+ var DetailsContent = (0, import_react24.memo)(function DetailsContent2({
4243
4715
  snapshots,
4244
4716
  selectedIndex,
4245
4717
  narrativeEntries,
@@ -4252,27 +4724,27 @@ var DetailsContent = (0, import_react20.memo)(function DetailsContent2({
4252
4724
  {
4253
4725
  id: "memory",
4254
4726
  name: "Memory",
4255
- render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MemoryPanel, { snapshots: snaps, selectedIndex: idx, size, style: fillHeight ? { height: "100%" } : void 0 })
4727
+ render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: snaps, selectedIndex: idx, size, style: fillHeight ? { height: "100%" } : void 0 })
4256
4728
  },
4257
4729
  {
4258
4730
  id: "narrative",
4259
4731
  name: "Narrative",
4260
- render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NarrativePanel, { snapshots: snaps, selectedIndex: idx, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
4732
+ render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: snaps, selectedIndex: idx, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
4261
4733
  }
4262
4734
  ];
4263
4735
  const allViews = [...builtInViews, ...extraViews ?? []];
4264
- const [activeViewId, setActiveViewId] = (0, import_react20.useState)(allViews[0]?.id ?? "memory");
4736
+ const [activeViewId, setActiveViewId] = (0, import_react24.useState)(allViews[0]?.id ?? "memory");
4265
4737
  const viewIds = allViews.map((v2) => v2.id).join(",");
4266
- (0, import_react20.useEffect)(() => {
4738
+ (0, import_react24.useEffect)(() => {
4267
4739
  if (!allViews.find((v2) => v2.id === activeViewId)) {
4268
4740
  setActiveViewId(allViews[0]?.id ?? "memory");
4269
4741
  }
4270
4742
  }, [viewIds]);
4271
4743
  const activeView = allViews.find((v2) => v2.id === activeViewId) ?? allViews[0];
4272
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
4273
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0, overflowX: "auto" }, children: allViews.map((view) => {
4744
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
4745
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0, overflowX: "auto" }, children: allViews.map((view) => {
4274
4746
  const active = view.id === activeViewId;
4275
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4747
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4276
4748
  "button",
4277
4749
  {
4278
4750
  onClick: () => setActiveViewId(view.id),
@@ -4296,7 +4768,7 @@ var DetailsContent = (0, import_react20.memo)(function DetailsContent2({
4296
4768
  view.id
4297
4769
  );
4298
4770
  }) }),
4299
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: activeView?.render({ snapshots, selectedIndex }) })
4771
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: activeView?.render({ snapshots, selectedIndex }) })
4300
4772
  ] });
4301
4773
  });
4302
4774
  function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narrativeEntries) {
@@ -4336,8 +4808,52 @@ function hasSubflowNodes(node) {
4336
4808
  if (node.next && hasSubflowNodes(node.next)) return true;
4337
4809
  return false;
4338
4810
  }
4811
+ function buildDataTrace(commitLog, targetRuntimeStageId, maxDepth = 10) {
4812
+ const log = commitLog;
4813
+ if (!log?.length) return [];
4814
+ const idxMap = /* @__PURE__ */ new Map();
4815
+ for (let i = 0; i < log.length; i++) idxMap.set(log[i].runtimeStageId, i);
4816
+ const startIdx = idxMap.get(targetRuntimeStageId);
4817
+ if (startIdx === void 0) return [];
4818
+ const startCommit = log[startIdx];
4819
+ const frames = [];
4820
+ const visited = /* @__PURE__ */ new Set();
4821
+ let current = startCommit;
4822
+ let currentIdx = startIdx;
4823
+ let depth = 0;
4824
+ while (current && depth <= maxDepth) {
4825
+ if (visited.has(current.runtimeStageId)) break;
4826
+ visited.add(current.runtimeStageId);
4827
+ frames.push({
4828
+ runtimeStageId: current.runtimeStageId,
4829
+ stageId: current.stageId,
4830
+ stageName: current.stage,
4831
+ keysWritten: current.trace.map((t) => t.path),
4832
+ linkedBy: depth === 0 ? "" : current.trace[0]?.path ?? "",
4833
+ depth
4834
+ });
4835
+ if (currentIdx > 0) {
4836
+ currentIdx--;
4837
+ current = log[currentIdx];
4838
+ depth++;
4839
+ } else {
4840
+ break;
4841
+ }
4842
+ }
4843
+ return frames;
4844
+ }
4845
+ function insightName(name) {
4846
+ const map = {
4847
+ "Narrative": "Story",
4848
+ "Memory": "State",
4849
+ "Metrics": "Performance",
4850
+ "Quality": "Quality",
4851
+ "Cost": "Cost"
4852
+ };
4853
+ return map[name] ?? name;
4854
+ }
4339
4855
  function defaultRenderFlowchart({ spec: s, snapshots: snaps, selectedIndex, onNodeClick }) {
4340
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4856
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4341
4857
  TracedFlowchartView,
4342
4858
  {
4343
4859
  spec: s,
@@ -4369,7 +4885,7 @@ function ExplainableShell({
4369
4885
  className,
4370
4886
  style
4371
4887
  }) {
4372
- const derivedFromRuntime = (0, import_react20.useMemo)(() => {
4888
+ const derivedFromRuntime = (0, import_react24.useMemo)(() => {
4373
4889
  if (!runtimeSnapshot) return null;
4374
4890
  try {
4375
4891
  const snaps = toVisualizationSnapshots(runtimeSnapshot, narrativeEntries);
@@ -4390,26 +4906,29 @@ function ExplainableShell({
4390
4906
  const leftLabel = panelLabels?.topology ?? "Topology";
4391
4907
  const rightLabel = panelLabels?.details ?? "Details";
4392
4908
  const bottomLabel = panelLabels?.timeline ?? "Timeline";
4393
- const shellRef = (0, import_react20.useRef)(null);
4394
- const [isNarrow, setIsNarrow] = (0, import_react20.useState)(false);
4395
- (0, import_react20.useEffect)(() => {
4909
+ const shellRef = (0, import_react24.useRef)(null);
4910
+ const [isNarrow, setIsNarrow] = (0, import_react24.useState)(false);
4911
+ const [isMedium, setIsMedium] = (0, import_react24.useState)(false);
4912
+ (0, import_react24.useEffect)(() => {
4396
4913
  const el = shellRef.current;
4397
4914
  if (!el) return;
4398
4915
  const ro = new ResizeObserver(([entry]) => {
4399
- setIsNarrow(entry.contentRect.width < 640);
4916
+ const w = entry.contentRect.width;
4917
+ setIsNarrow(w < 640);
4918
+ setIsMedium(w >= 640 && w < 960);
4400
4919
  window.dispatchEvent(new Event("resize"));
4401
4920
  });
4402
4921
  ro.observe(el);
4403
4922
  return () => ro.disconnect();
4404
4923
  }, []);
4405
- const autoRecorderViews = (0, import_react20.useMemo)(() => {
4924
+ const autoRecorderViews = (0, import_react24.useMemo)(() => {
4406
4925
  const recorders = runtimeSnapshot?.recorders;
4407
4926
  if (!recorders?.length) return [];
4408
4927
  const explicitIds = new Set((recorderViews ?? []).map((v2) => v2.id));
4409
4928
  return recorders.filter((r) => !explicitIds.has(r.id)).map((r) => ({ id: r.id, name: r.name, description: r.description, preferredOperation: r.preferredOperation, data: r.data }));
4410
4929
  }, [runtimeSnapshot, recorderViews]);
4411
4930
  const hasNarrative = !!(narrative?.length || narrativeEntries?.length);
4412
- const allTabs = (0, import_react20.useMemo)(() => {
4931
+ const allTabs = (0, import_react24.useMemo)(() => {
4413
4932
  const tabs2 = [
4414
4933
  { id: "result", name: "Result", description: "Final output and console logs" },
4415
4934
  { id: "memory", name: "Memory", description: "Accumulator \u2014 progressive shared state at each stage" }
@@ -4428,37 +4947,37 @@ function ExplainableShell({
4428
4947
  }, [hasNarrative, recorderViews, autoRecorderViews, hideTabsProp]);
4429
4948
  const validTabIds = new Set(allTabs.map((t) => t.id));
4430
4949
  const resolvedDefault = defaultTab && validTabIds.has(defaultTab) ? defaultTab : allTabs[0]?.id ?? "result";
4431
- const [activeTab, setActiveTab] = (0, import_react20.useState)(resolvedDefault);
4432
- const [snapshotIdx, setSnapshotIdx] = (0, import_react20.useState)(0);
4433
- const [drillDownStack, setDrillDownStack] = (0, import_react20.useState)([]);
4434
- const [rightExpanded, setRightExpanded] = (0, import_react20.useState)(defaultExpanded?.details ?? true);
4435
- const [leftExpanded, setLeftExpanded] = (0, import_react20.useState)(defaultExpanded?.topology ?? false);
4436
- const [timelineExpanded, setTimelineExpanded] = (0, import_react20.useState)(defaultExpanded?.timeline ?? false);
4437
- (0, import_react20.useEffect)(() => {
4950
+ const [activeTab, setActiveTab] = (0, import_react24.useState)(resolvedDefault);
4951
+ const [snapshotIdx, setSnapshotIdx] = (0, import_react24.useState)(0);
4952
+ const [drillDownStack, setDrillDownStack] = (0, import_react24.useState)([]);
4953
+ const [rightExpanded, setRightExpanded] = (0, import_react24.useState)(defaultExpanded?.details ?? true);
4954
+ const [leftExpanded, setLeftExpanded] = (0, import_react24.useState)(defaultExpanded?.topology ?? false);
4955
+ const [timelineExpanded, setTimelineExpanded] = (0, import_react24.useState)(defaultExpanded?.timeline ?? false);
4956
+ (0, import_react24.useEffect)(() => {
4438
4957
  if (isNarrow) {
4439
4958
  setLeftExpanded(false);
4440
4959
  setRightExpanded(false);
4441
4960
  setTimelineExpanded(false);
4442
4961
  }
4443
4962
  }, [isNarrow]);
4444
- const triggerReflow = (0, import_react20.useCallback)(() => {
4963
+ const triggerReflow = (0, import_react24.useCallback)(() => {
4445
4964
  requestAnimationFrame(() => window.dispatchEvent(new Event("resize")));
4446
4965
  setTimeout(() => window.dispatchEvent(new Event("resize")), 320);
4447
4966
  }, []);
4448
- const toggleLeft = (0, import_react20.useCallback)((v2) => {
4967
+ const toggleLeft = (0, import_react24.useCallback)((v2) => {
4449
4968
  setLeftExpanded(v2);
4450
4969
  triggerReflow();
4451
4970
  }, [triggerReflow]);
4452
- const toggleRight = (0, import_react20.useCallback)((v2) => {
4971
+ const toggleRight = (0, import_react24.useCallback)((v2) => {
4453
4972
  setRightExpanded(v2);
4454
4973
  triggerReflow();
4455
4974
  }, [triggerReflow]);
4456
- const toggleTimeline = (0, import_react20.useCallback)(() => {
4975
+ const toggleTimeline = (0, import_react24.useCallback)(() => {
4457
4976
  setTimelineExpanded((p) => !p);
4458
4977
  triggerReflow();
4459
4978
  }, [triggerReflow]);
4460
4979
  const isInSubflow = drillDownStack.length > 0;
4461
- const currentLevel = (0, import_react20.useMemo)(() => {
4980
+ const currentLevel = (0, import_react24.useMemo)(() => {
4462
4981
  if (drillDownStack.length > 0) {
4463
4982
  const top = drillDownStack[drillDownStack.length - 1];
4464
4983
  return { spec: top.spec, snapshots: top.snapshots };
@@ -4468,7 +4987,7 @@ function ExplainableShell({
4468
4987
  const activeSnapshots = currentLevel.snapshots;
4469
4988
  const activeSpec = currentLevel.spec;
4470
4989
  const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
4471
- const activeNarrative = (0, import_react20.useMemo)(() => {
4990
+ const activeNarrative = (0, import_react24.useMemo)(() => {
4472
4991
  if (!isInSubflow) return narrative;
4473
4992
  const lines = [];
4474
4993
  for (const snap of activeSnapshots) {
@@ -4478,25 +4997,25 @@ function ExplainableShell({
4478
4997
  return lines.length > 0 ? lines : void 0;
4479
4998
  }, [isInSubflow, narrative, activeSnapshots]);
4480
4999
  const activeNarrativeEntries = isInSubflow ? void 0 : narrativeEntries;
4481
- const breadcrumbs = (0, import_react20.useMemo)(() => {
5000
+ const breadcrumbs = (0, import_react24.useMemo)(() => {
4482
5001
  const root = { label: title || "Flowchart", spec, description: spec?.description };
4483
5002
  return [root, ...drillDownStack.map((e) => ({ label: e.label, spec: e.spec, description: void 0 }))];
4484
5003
  }, [spec, title, drillDownStack]);
4485
- const showTreeSidebar = (0, import_react20.useMemo)(() => !!spec && hasSubflowNodes(spec), [spec]);
4486
- const rootOverlay = (0, import_react20.useMemo)(() => {
5004
+ const showTreeSidebar = (0, import_react24.useMemo)(() => !!spec && hasSubflowNodes(spec), [spec]);
5005
+ const rootOverlay = (0, import_react24.useMemo)(() => {
4487
5006
  if (isInSubflow || !snapshots.length) return { activeStage: void 0, doneStages: void 0 };
4488
5007
  const doneStages = new Set(snapshots.slice(0, safeIdx).map((s) => s.stageLabel));
4489
5008
  const activeStage = snapshots[safeIdx]?.stageLabel ?? null;
4490
5009
  return { activeStage, doneStages };
4491
5010
  }, [isInSubflow, snapshots, safeIdx]);
4492
- const handleTabChange = (0, import_react20.useCallback)((tab) => {
5011
+ const handleTabChange = (0, import_react24.useCallback)((tab) => {
4493
5012
  setActiveTab(tab);
4494
5013
  setDrillDownStack([]);
4495
5014
  }, []);
4496
- const handleSnapshotChange = (0, import_react20.useCallback)((idx) => {
5015
+ const handleSnapshotChange = (0, import_react24.useCallback)((idx) => {
4497
5016
  if (typeof idx === "number") setSnapshotIdx(idx);
4498
5017
  }, []);
4499
- const handleDrillDown = (0, import_react20.useCallback)(
5018
+ const handleDrillDown = (0, import_react24.useCallback)(
4500
5019
  (nodeName) => {
4501
5020
  if (!activeSpec) return;
4502
5021
  const entry = resolveSubflowLevel(activeSpec, activeSnapshots, nodeName, narrativeEntries);
@@ -4507,14 +5026,14 @@ function ExplainableShell({
4507
5026
  },
4508
5027
  [activeSpec, activeSnapshots, narrativeEntries, snapshotIdx]
4509
5028
  );
4510
- const handleBreadcrumbNavigate = (0, import_react20.useCallback)((level) => {
5029
+ const handleBreadcrumbNavigate = (0, import_react24.useCallback)((level) => {
4511
5030
  setDrillDownStack((prev) => {
4512
5031
  const popped = level === 0 ? prev[0] : prev[level];
4513
5032
  if (popped) setSnapshotIdx(popped.parentSnapshotIdx);
4514
5033
  return level === 0 ? [] : prev.slice(0, level);
4515
5034
  });
4516
5035
  }, []);
4517
- const handleNodeClick = (0, import_react20.useCallback)(
5036
+ const handleNodeClick = (0, import_react24.useCallback)(
4518
5037
  (indexOrId) => {
4519
5038
  if (typeof indexOrId === "number") {
4520
5039
  setSnapshotIdx(indexOrId);
@@ -4532,7 +5051,7 @@ function ExplainableShell({
4532
5051
  },
4533
5052
  [activeSpec, activeSnapshots, handleDrillDown]
4534
5053
  );
4535
- const handleTreeNodeSelect = (0, import_react20.useCallback)(
5054
+ const handleTreeNodeSelect = (0, import_react24.useCallback)(
4536
5055
  (name, isSubflow) => {
4537
5056
  if (isSubflow && spec) {
4538
5057
  setDrillDownStack([]);
@@ -4551,31 +5070,31 @@ function ExplainableShell({
4551
5070
  );
4552
5071
  const tabLabels = new Map(allTabs.map((t) => [t.id, t.name]));
4553
5072
  if (unstyled) {
4554
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
4555
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { "data-fp": "shell-tabs", children: allTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { "data-fp": "shell-tab", "data-active": tab.id === activeTab, onClick: () => handleTabChange(tab.id), children: tab.name }, tab.id)) }),
4556
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
4557
- activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
4558
- (activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4559
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
4560
- isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5073
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
5074
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-fp": "shell-tabs", children: allTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { "data-fp": "shell-tab", "data-active": tab.id === activeTab, onClick: () => handleTabChange(tab.id), children: tab.name }, tab.id)) }),
5075
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
5076
+ activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
5077
+ (activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5078
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
5079
+ isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
4561
5080
  activeSpec && effectiveRenderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick }),
4562
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
4563
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
4564
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
5081
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
5082
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
5083
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
4565
5084
  ] })
4566
5085
  ] })
4567
5086
  ] });
4568
5087
  }
4569
5088
  const showTopology = !!effectiveRenderFlowchart && !!activeSpec;
4570
- const detailsContent = (0, import_react20.useMemo)(() => {
5089
+ const detailsContent = (0, import_react24.useMemo)(() => {
4571
5090
  if (activeTab === "result") {
4572
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size });
5091
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size });
4573
5092
  }
4574
5093
  if (activeTab === "memory") {
4575
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
5094
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
4576
5095
  }
4577
5096
  if (activeTab === "narrative") {
4578
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
5097
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
4579
5098
  }
4580
5099
  const customView = recorderViews?.find((v2) => v2.id === activeTab);
4581
5100
  if (customView?.render) {
@@ -4583,7 +5102,7 @@ function ExplainableShell({
4583
5102
  }
4584
5103
  const autoView = autoRecorderViews.find((v2) => v2.id === activeTab);
4585
5104
  if (autoView) {
4586
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5105
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4587
5106
  KeyedRecorderView,
4588
5107
  {
4589
5108
  data: autoView.data,
@@ -4596,8 +5115,8 @@ function ExplainableShell({
4596
5115
  }
4597
5116
  return null;
4598
5117
  }, [activeTab, resultData, logs, hideConsole, size, activeSnapshots, safeIdx, activeNarrativeEntries, activeNarrative, recorderViews, autoRecorderViews]);
4599
- const detailsPanel = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" }, children: [
4600
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: {
5118
+ const detailsPanel = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" }, children: [
5119
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
4601
5120
  display: "flex",
4602
5121
  borderBottom: `1px solid ${theme.border}`,
4603
5122
  background: theme.bgSecondary,
@@ -4605,7 +5124,7 @@ function ExplainableShell({
4605
5124
  overflowX: "auto"
4606
5125
  }, children: allTabs.map((tab) => {
4607
5126
  const active = tab.id === activeTab;
4608
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5127
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4609
5128
  "button",
4610
5129
  {
4611
5130
  onClick: () => handleTabChange(tab.id),
@@ -4629,9 +5148,9 @@ function ExplainableShell({
4629
5148
  tab.id
4630
5149
  );
4631
5150
  }) }),
4632
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
5151
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
4633
5152
  ] });
4634
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5153
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4635
5154
  "div",
4636
5155
  {
4637
5156
  ref: shellRef,
@@ -4649,7 +5168,7 @@ function ExplainableShell({
4649
5168
  },
4650
5169
  "data-fp": "explainable-shell",
4651
5170
  children: [
4652
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5171
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4653
5172
  TimeTravelControls,
4654
5173
  {
4655
5174
  snapshots: activeSnapshots,
@@ -4658,19 +5177,19 @@ function ExplainableShell({
4658
5177
  size
4659
5178
  }
4660
5179
  ),
4661
- isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
4662
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: isNarrow ? (
5180
+ isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5181
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: isNarrow ? (
4663
5182
  /* ── Mobile: stacked vertical ── */
4664
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4665
- showTopology && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: effectiveRenderFlowchart({
5183
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5184
+ showTopology && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: effectiveRenderFlowchart({
4666
5185
  spec: activeSpec,
4667
5186
  snapshots: activeSnapshots,
4668
5187
  selectedIndex: safeIdx,
4669
5188
  onNodeClick: handleNodeClick
4670
5189
  }) }),
4671
- showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4672
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
4673
- leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5190
+ showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5191
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
5192
+ leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4674
5193
  SubflowTree,
4675
5194
  {
4676
5195
  spec,
@@ -4680,47 +5199,72 @@ function ExplainableShell({
4680
5199
  }
4681
5200
  ) })
4682
5201
  ] }),
4683
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
4684
- rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { maxHeight: 350, flexShrink: 0, overflow: "hidden" }, children: detailsPanel }),
4685
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
4686
- timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
5202
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5203
+ rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { maxHeight: 350, flexShrink: 0, overflow: "hidden" }, children: detailsPanel }),
5204
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
5205
+ timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
4687
5206
  ] })
4688
- ) : showTopology ? (
4689
- /* ── Desktop with topology: side-by-side ── */
4690
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4691
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
4692
- showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { width: 220, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
4693
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4694
- SubflowTree,
4695
- {
4696
- spec,
4697
- activeStage: rootOverlay.activeStage,
4698
- doneStages: rootOverlay.doneStages,
4699
- onNodeSelect: handleTreeNodeSelect
5207
+ ) : (
5208
+ /* ── Desktop: responsive layout ── */
5209
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5210
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
5211
+ showTopology && !isMedium && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { width: 280, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
5212
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: effectiveRenderFlowchart({
5213
+ spec: activeSpec,
5214
+ snapshots: activeSnapshots,
5215
+ selectedIndex: safeIdx,
5216
+ onNodeClick: handleNodeClick
5217
+ }) }),
5218
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
5219
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
5220
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
5221
+ width: isMedium ? "50%" : 300,
5222
+ flexShrink: isMedium ? 1 : 0,
5223
+ flex: isMedium ? 1 : void 0,
5224
+ overflow: "hidden",
5225
+ borderRight: `1px solid ${theme.border}`
5226
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5227
+ InspectorPanel,
5228
+ {
5229
+ snapshots: activeSnapshots,
5230
+ selectedIndex: safeIdx,
5231
+ dataTraceFrames: runtimeSnapshot?.commitLog ? buildDataTrace(runtimeSnapshot.commitLog, activeSnapshots[safeIdx]?.runtimeStageId ?? "") : [],
5232
+ selectedStageId: activeSnapshots[safeIdx]?.runtimeStageId,
5233
+ onNavigateToStage: (id) => {
5234
+ const idx = activeSnapshots.findIndex((s) => s.runtimeStageId === id);
5235
+ if (idx >= 0) setSnapshotIdx(idx);
4700
5236
  }
4701
- ) }),
4702
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
4703
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
4704
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: effectiveRenderFlowchart({
4705
- spec: activeSpec,
5237
+ }
5238
+ ) }),
5239
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5240
+ InsightPanel,
5241
+ {
5242
+ mode: !leftExpanded && !isMedium ? "grid" : "tabs",
5243
+ expandedId: activeTab,
5244
+ insights: allTabs.filter((t) => t.id !== "result").map((tab) => ({
5245
+ id: tab.id,
5246
+ name: insightName(tab.name),
5247
+ render: () => {
5248
+ if (tab.id === "memory") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
5249
+ if (tab.id === "narrative") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
5250
+ const customView = recorderViews?.find((v2) => v2.id === tab.id);
5251
+ if (customView?.render) return customView.render({ snapshots: activeSnapshots, selectedIndex: safeIdx });
5252
+ const autoView = autoRecorderViews.find((v2) => v2.id === tab.id);
5253
+ if (autoView) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(KeyedRecorderView, { data: autoView.data, description: autoView.description, preferredOperation: autoView.preferredOperation, snapshots: activeSnapshots, selectedIndex: safeIdx });
5254
+ return null;
5255
+ }
5256
+ }))
5257
+ }
5258
+ ) })
5259
+ ] }),
5260
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5261
+ CompactTimeline,
5262
+ {
4706
5263
  snapshots: activeSnapshots,
4707
5264
  selectedIndex: safeIdx,
4708
- onNodeClick: handleNodeClick
4709
- }) }),
4710
- rightExpanded ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { width: "38%", minWidth: 300, maxWidth: 500, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
4711
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VLinePill, { label: rightLabel, expanded: true, onClick: () => toggleRight(false) }),
4712
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: detailsPanel })
4713
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(VLinePill, { label: rightLabel, expanded: false, onClick: () => toggleRight(true) })
4714
- ] }),
4715
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
4716
- timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
4717
- ] })
4718
- ) : (
4719
- /* ── Desktop without topology: details panel takes full width ── */
4720
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4721
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: detailsPanel }),
4722
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
4723
- timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
5265
+ defaultExpanded: timelineExpanded
5266
+ }
5267
+ )
4724
5268
  ] })
4725
5269
  ) })
4726
5270
  ]
@@ -4729,9 +5273,13 @@ function ExplainableShell({
4729
5273
  }
4730
5274
  // Annotate the CommonJS export names for ESM import in node:
4731
5275
  0 && (module.exports = {
5276
+ CompactTimeline,
5277
+ DataTracePanel,
4732
5278
  ExplainableShell,
4733
5279
  FootprintTheme,
4734
5280
  GanttTimeline,
5281
+ InsightPanel,
5282
+ InspectorPanel,
4735
5283
  MemoryInspector,
4736
5284
  MemoryPanel,
4737
5285
  NarrativeLog,