agentfootprint-lens 0.45.0 → 0.46.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/README.md +133 -0
- package/dist/{Lens-B039-UGw.d.cts → Lens-6elRg27j.d.cts} +22 -5
- package/dist/{Lens-ey1EgN9i.d.ts → Lens-Bdz-JzVm.d.ts} +22 -5
- package/dist/{chunk-YEJJOPOZ.js → chunk-2UOQEE4U.js} +2 -2
- package/dist/{chunk-LRGT7UQL.js → chunk-A4EIPCLY.js} +52 -31
- package/dist/chunk-A4EIPCLY.js.map +1 -0
- package/dist/{chunk-QNMA25X5.js → chunk-AUGRQKRL.js} +18 -6
- package/dist/chunk-AUGRQKRL.js.map +1 -0
- package/dist/{chunk-CGR5VFJU.js → chunk-SJEKKUTI.js} +113 -3
- package/dist/chunk-SJEKKUTI.js.map +1 -0
- package/dist/{chunk-5YSPWSLW.js → chunk-WN2RJKEH.js} +2 -2
- package/dist/core.cjs +125 -11
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +6 -2
- package/dist/index.cjs +190 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -5
- package/dist/index.d.ts +34 -5
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/{scrubAxisFor-Cz214OHj.d.ts → lensStops-DAc3GB8G.d.ts} +157 -1
- package/dist/{scrubAxisFor-CNceBlz-.d.cts → lensStops-hQ10t3Lt.d.cts} +157 -1
- package/dist/skillgraph.cjs +17 -5
- package/dist/skillgraph.cjs.map +1 -1
- package/dist/skillgraph.js +2 -2
- package/dist/why.cjs +186 -40
- package/dist/why.cjs.map +1 -1
- package/dist/why.d.cts +5 -4
- package/dist/why.d.ts +5 -4
- package/dist/why.js +7 -3
- package/dist/why.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-CGR5VFJU.js.map +0 -1
- package/dist/chunk-LRGT7UQL.js.map +0 -1
- package/dist/chunk-QNMA25X5.js.map +0 -1
- /package/dist/{chunk-YEJJOPOZ.js.map → chunk-2UOQEE4U.js.map} +0 -0
- /package/dist/{chunk-5YSPWSLW.js.map → chunk-WN2RJKEH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -808,6 +808,133 @@ const href = to.ok ? `/runs/${runId}?step=${to.step}` : undefined;
|
|
|
808
808
|
|
|
809
809
|
---
|
|
810
810
|
|
|
811
|
+
## Time travel through one port
|
|
812
|
+
|
|
813
|
+
**Nothing to do — this is how the cursor already moves.** Read it if you are
|
|
814
|
+
building a second surface over the same run, or wondering why the ruler behaves
|
|
815
|
+
identically in the Why Lens, the Flow Lens and your own view.
|
|
816
|
+
|
|
817
|
+
### Why
|
|
818
|
+
|
|
819
|
+
Every consumer of a footprintjs run was writing the same six lines: clamp at
|
|
820
|
+
zero, clamp at the end, resolve an address to a stop, decide what a miss does.
|
|
821
|
+
The lens had them in `<TimeTravel>`, the flow view had its own copy, and a
|
|
822
|
+
dashboard had a third — and they disagreed at exactly the places nobody tests: a
|
|
823
|
+
▶ at the last stop, a jump to a stage that is not on this ruler, a step stored
|
|
824
|
+
from a longer run.
|
|
825
|
+
|
|
826
|
+
footprintjs 9.17 owns that arithmetic now. `timeTravel(snapshot, { strategy })`
|
|
827
|
+
is the **reader's cursor over a finished trace**: `first` / `last` / `prev` /
|
|
828
|
+
`next` / `jumpTo`, one position, and one law — **a refused move never moves the
|
|
829
|
+
cursor**, it returns the reason and the nearest stop it could name. Since
|
|
830
|
+
0.46.0 the lens's movement goes through it.
|
|
831
|
+
|
|
832
|
+
**Which stops exist did not change.** The lens still computes its own axis —
|
|
833
|
+
milestones banded by iteration for the Why reading, one stop per executed stage
|
|
834
|
+
for the Flow reading, drill-scoped either way — with the same labels, the same
|
|
835
|
+
order and the same kinds. Those positions ARE the port's stops, through a
|
|
836
|
+
lens-owned `TimeTravelStrategy`. The library decides *where a move lands*; the
|
|
837
|
+
lens decides *what there is to land on*.
|
|
838
|
+
|
|
839
|
+
**Still one cursor.** The port instance is a calculator, not a position: it is
|
|
840
|
+
re-seated on the step the lens owns before every question and remembers nothing
|
|
841
|
+
between them. `<Lens step onStepChange>` is unchanged, and so is everything a
|
|
842
|
+
panel reads.
|
|
843
|
+
|
|
844
|
+
### The port, headless
|
|
845
|
+
|
|
846
|
+
```ts
|
|
847
|
+
import { scrubAxisFor, openLensCursor } from 'agentfootprint-lens/core';
|
|
848
|
+
|
|
849
|
+
const positions = scrubAxisFor(recorder, 'group'); // the ruler, as data
|
|
850
|
+
const cursor = openLensCursor(positions);
|
|
851
|
+
|
|
852
|
+
cursor.next(4).step; // 5
|
|
853
|
+
cursor.next(positions.length - 1);
|
|
854
|
+
// { step: <last>, moved: false, reason: 'clamped', clamped: false }
|
|
855
|
+
|
|
856
|
+
cursor.toStep(2, 9999);
|
|
857
|
+
// { step: <last>, moved: true, clamped: true } ← off the axis, said so
|
|
858
|
+
|
|
859
|
+
cursor.toAddress(2, 'call-llm#18');
|
|
860
|
+
// { ok: true, step: 7 }
|
|
861
|
+
cursor.toAddress(2, 'never-ran#4');
|
|
862
|
+
// { ok: false, reason: 'miss', nearest: <the closest stop the library
|
|
863
|
+
// could name — the same stage at another execution index if this axis
|
|
864
|
+
// holds one, otherwise the last stop whose execution index is not past
|
|
865
|
+
// the one you asked for> }
|
|
866
|
+
// the cursor is still at 2 — a miss is offered, never taken
|
|
867
|
+
|
|
868
|
+
cursor.positionAt(7);
|
|
869
|
+
// the LENS position: runtimeGroupId, depth, coActiveGroupIds, milestone —
|
|
870
|
+
// everything the port's `Stop` has no slot for, unchanged and unflattened
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
`openLensCursor` answers **where**, never **what**: it has no `stateAt`, no
|
|
874
|
+
`changedSince`, no `drill`. Those are folds over the run's commit log, and a
|
|
875
|
+
fold wants the run's snapshot — which movement never reads. Want them over this
|
|
876
|
+
same ruler? Build the library's cursor directly, with the lens's stops:
|
|
877
|
+
|
|
878
|
+
```ts
|
|
879
|
+
import { timeTravel } from 'footprintjs/trace';
|
|
880
|
+
import { scrubAxisFor, lensStopsStrategy } from 'agentfootprint-lens/core';
|
|
881
|
+
|
|
882
|
+
// A LIVE run. `runner.getLastSnapshot()` IS footprintjs's own snapshot, so it
|
|
883
|
+
// is already a `TimeTravelSource` and the two libraries meet with no cast.
|
|
884
|
+
const snapshot = runner.getLastSnapshot();
|
|
885
|
+
if (snapshot) {
|
|
886
|
+
const positions = scrubAxisFor(recorder, 'group');
|
|
887
|
+
const tt = timeTravel(snapshot, { strategy: lensStopsStrategy(positions) });
|
|
888
|
+
tt.jumpTo(7);
|
|
889
|
+
tt.changedSince(); // the keys this stop's slice of the log wrote
|
|
890
|
+
tt.stateAt().state; // state as of that stop, detached
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
A **stored** recording needs one narrowing today, and the reason is honest on
|
|
895
|
+
both sides: the lens types `Recording.snapshot.commitLog` as `readonly
|
|
896
|
+
unknown[]` — a recording arrives as parsed JSON, and the lens will not claim
|
|
897
|
+
that what came back off disk is a `CommitBundle` — while `timeTravel` takes
|
|
898
|
+
`TimeTravelSource`, whose `commitLog` is `readonly CommitBundle[]`. So a
|
|
899
|
+
replayed run has to say so at the seam:
|
|
900
|
+
|
|
901
|
+
```ts
|
|
902
|
+
import type { TimeTravelSource } from 'footprintjs/trace';
|
|
903
|
+
|
|
904
|
+
const stored = recording.snapshot as unknown as TimeTravelSource;
|
|
905
|
+
const tt = timeTravel(stored, { strategy: lensStopsStrategy(positions) });
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
That cast disappears — with no change here — the day footprintjs widens
|
|
909
|
+
`TimeTravelSource.commitLog` to accept an unvalidated `readonly unknown[]` and
|
|
910
|
+
does the per-bundle narrowing where it folds, which is the only place that can
|
|
911
|
+
honestly do it. It is reported upstream as a port gap. **Movement never needs
|
|
912
|
+
this**: `openLensCursor` reads the stops and no snapshot at all, which is why
|
|
913
|
+
the lens's own cursor is unaffected either way.
|
|
914
|
+
|
|
915
|
+
### Two axes, and drilling
|
|
916
|
+
|
|
917
|
+
Rebuild the port when the positions change; its identity is that list's, which
|
|
918
|
+
is already memoised per (recording, axis, drill level):
|
|
919
|
+
|
|
920
|
+
```ts
|
|
921
|
+
const port = useMemo(() => openLensCursor(positions), [positions]);
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
Drilling rebuilds the axis — the drilled group's own stops, which is a
|
|
925
|
+
different set — so it rebuilds the port. The lens does **not** use the port's
|
|
926
|
+
`drill()` for this: `drill()` opens a subflow's own commit log, and a lens drill
|
|
927
|
+
level is not always a subflow (it is a group, and a subflow whose stages commit
|
|
928
|
+
in their own memory scope is reconstructed from the runtime overlay). Using it
|
|
929
|
+
would change which stops appear at a drill level, which is the one thing this
|
|
930
|
+
change refuses to do.
|
|
931
|
+
|
|
932
|
+
`snapSteps` also stays the lens's. Snaps narrow ◀ ▶ to a *subset* of the axis
|
|
933
|
+
while the cursor may be parked between two of them — a question `jumpTo` does
|
|
934
|
+
not answer, since it takes an exact stop.
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
811
938
|
## Rendering your own detail pane
|
|
812
939
|
|
|
813
940
|
`slots.detail` replaces the CONTENT of the shipped right column. The column
|
|
@@ -1112,6 +1239,12 @@ so instead of drawing an empty graph.
|
|
|
1112
1239
|
/ CLI view on the same primitives — see `ChangeNotifier`'s JSDoc for adapter
|
|
1113
1240
|
snippets.
|
|
1114
1241
|
|
|
1242
|
+
The cursor is headless too: `scrubAxisFor` builds the ruler, `openLensCursor`
|
|
1243
|
+
moves along it through footprintjs's `timeTravel()` port, `resolveNavigation`
|
|
1244
|
+
turns an address into a step with every rung named, and `stepForCommitIdx`
|
|
1245
|
+
carries a position between the two axes. See
|
|
1246
|
+
[Time travel through one port](#time-travel-through-one-port).
|
|
1247
|
+
|
|
1115
1248
|
---
|
|
1116
1249
|
|
|
1117
1250
|
## Why this design
|
|
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
|
|
|
4
4
|
import { CommentaryTemplates } from 'agentfootprint';
|
|
5
5
|
import React__default, { Ref } from 'react';
|
|
6
6
|
import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.cjs';
|
|
7
|
-
import { C as ChartGroupHighlight, H as Humanizer } from './
|
|
7
|
+
import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-hQ10t3Lt.cjs';
|
|
8
8
|
import { NodeTypes } from '@xyflow/react';
|
|
9
9
|
import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
|
|
10
10
|
|
|
@@ -153,6 +153,7 @@ declare const LensFlow: React__default.FC<LensFlowProps>;
|
|
|
153
153
|
* Omit `controlledStep` and nothing changes: the hook keeps today's internal
|
|
154
154
|
* state and today's auto-advance, byte for byte.
|
|
155
155
|
*/
|
|
156
|
+
|
|
156
157
|
/**
|
|
157
158
|
* Where the cursor landed, in all three units the lens knows — handed to
|
|
158
159
|
* `onStepChange` alongside the step so a host never has to reverse-engineer
|
|
@@ -178,9 +179,13 @@ interface LensCursorAt {
|
|
|
178
179
|
/** What kind of stop this is. Absent when the axis is empty. */
|
|
179
180
|
readonly kind?: string;
|
|
180
181
|
/**
|
|
181
|
-
* `true` when this call is Lens CORRECTING
|
|
182
|
-
*
|
|
183
|
-
*
|
|
182
|
+
* `true` when this call is Lens CORRECTING a step that is not a position on
|
|
183
|
+
* the axis it is now read against, not a move someone made. That is an
|
|
184
|
+
* out-of-range `step` you passed, one an internal jump produced, OR — when
|
|
185
|
+
* you passed no `step` at all — the lens's own remembered step after the
|
|
186
|
+
* axis shrank under it. Uncontrolled snaps are reported too, so this flag
|
|
187
|
+
* means the same thing in both modes. Store the corrected value and the two
|
|
188
|
+
* cursors agree again.
|
|
184
189
|
*/
|
|
185
190
|
readonly clamped: boolean;
|
|
186
191
|
}
|
|
@@ -201,6 +206,18 @@ interface UseLensCursorArgs {
|
|
|
201
206
|
readonly maxStep: number;
|
|
202
207
|
/** Resolve a step to its address. Called only when a move is reported. */
|
|
203
208
|
readonly describe: (step: number) => LensCursorPlace;
|
|
209
|
+
/**
|
|
210
|
+
* The MOVEMENT port — footprintjs 9.17's reader cursor over the Lens's own
|
|
211
|
+
* stops (`openLensCursor(positions)`). When present, every move this funnel
|
|
212
|
+
* makes is decided by the library: where a step lands, what happens at the
|
|
213
|
+
* ends of the axis, and what an out-of-range ask does. Omit it and the
|
|
214
|
+
* funnel keeps its own arithmetic, unchanged — which is what the hook's own
|
|
215
|
+
* tests drive, so the two readings can be compared.
|
|
216
|
+
*
|
|
217
|
+
* It is NOT a second cursor: the port is re-seated on the step this hook
|
|
218
|
+
* owns before every question and remembers nothing between them.
|
|
219
|
+
*/
|
|
220
|
+
readonly port?: LensCursorPort | undefined;
|
|
204
221
|
}
|
|
205
222
|
interface UseLensCursorResult {
|
|
206
223
|
/** The cursor to render. */
|
|
@@ -215,7 +232,7 @@ interface UseLensCursorResult {
|
|
|
215
232
|
* are not positions, so they clamp too — and the clamp is always reported.
|
|
216
233
|
*/
|
|
217
234
|
declare function clampStep(n: number, maxStep: number): number;
|
|
218
|
-
declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, }: UseLensCursorArgs): UseLensCursorResult;
|
|
235
|
+
declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, port, }: UseLensCursorArgs): UseLensCursorResult;
|
|
219
236
|
|
|
220
237
|
/**
|
|
221
238
|
* useLensNavigator — move the ONE cursor to a stage by its address.
|
|
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
|
|
|
4
4
|
import { CommentaryTemplates } from 'agentfootprint';
|
|
5
5
|
import React__default, { Ref } from 'react';
|
|
6
6
|
import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.js';
|
|
7
|
-
import { C as ChartGroupHighlight, H as Humanizer } from './
|
|
7
|
+
import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-DAc3GB8G.js';
|
|
8
8
|
import { NodeTypes } from '@xyflow/react';
|
|
9
9
|
import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
|
|
10
10
|
|
|
@@ -153,6 +153,7 @@ declare const LensFlow: React__default.FC<LensFlowProps>;
|
|
|
153
153
|
* Omit `controlledStep` and nothing changes: the hook keeps today's internal
|
|
154
154
|
* state and today's auto-advance, byte for byte.
|
|
155
155
|
*/
|
|
156
|
+
|
|
156
157
|
/**
|
|
157
158
|
* Where the cursor landed, in all three units the lens knows — handed to
|
|
158
159
|
* `onStepChange` alongside the step so a host never has to reverse-engineer
|
|
@@ -178,9 +179,13 @@ interface LensCursorAt {
|
|
|
178
179
|
/** What kind of stop this is. Absent when the axis is empty. */
|
|
179
180
|
readonly kind?: string;
|
|
180
181
|
/**
|
|
181
|
-
* `true` when this call is Lens CORRECTING
|
|
182
|
-
*
|
|
183
|
-
*
|
|
182
|
+
* `true` when this call is Lens CORRECTING a step that is not a position on
|
|
183
|
+
* the axis it is now read against, not a move someone made. That is an
|
|
184
|
+
* out-of-range `step` you passed, one an internal jump produced, OR — when
|
|
185
|
+
* you passed no `step` at all — the lens's own remembered step after the
|
|
186
|
+
* axis shrank under it. Uncontrolled snaps are reported too, so this flag
|
|
187
|
+
* means the same thing in both modes. Store the corrected value and the two
|
|
188
|
+
* cursors agree again.
|
|
184
189
|
*/
|
|
185
190
|
readonly clamped: boolean;
|
|
186
191
|
}
|
|
@@ -201,6 +206,18 @@ interface UseLensCursorArgs {
|
|
|
201
206
|
readonly maxStep: number;
|
|
202
207
|
/** Resolve a step to its address. Called only when a move is reported. */
|
|
203
208
|
readonly describe: (step: number) => LensCursorPlace;
|
|
209
|
+
/**
|
|
210
|
+
* The MOVEMENT port — footprintjs 9.17's reader cursor over the Lens's own
|
|
211
|
+
* stops (`openLensCursor(positions)`). When present, every move this funnel
|
|
212
|
+
* makes is decided by the library: where a step lands, what happens at the
|
|
213
|
+
* ends of the axis, and what an out-of-range ask does. Omit it and the
|
|
214
|
+
* funnel keeps its own arithmetic, unchanged — which is what the hook's own
|
|
215
|
+
* tests drive, so the two readings can be compared.
|
|
216
|
+
*
|
|
217
|
+
* It is NOT a second cursor: the port is re-seated on the step this hook
|
|
218
|
+
* owns before every question and remembers nothing between them.
|
|
219
|
+
*/
|
|
220
|
+
readonly port?: LensCursorPort | undefined;
|
|
204
221
|
}
|
|
205
222
|
interface UseLensCursorResult {
|
|
206
223
|
/** The cursor to render. */
|
|
@@ -215,7 +232,7 @@ interface UseLensCursorResult {
|
|
|
215
232
|
* are not positions, so they clamp too — and the clamp is always reported.
|
|
216
233
|
*/
|
|
217
234
|
declare function clampStep(n: number, maxStep: number): number;
|
|
218
|
-
declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, }: UseLensCursorArgs): UseLensCursorResult;
|
|
235
|
+
declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, port, }: UseLensCursorArgs): UseLensCursorResult;
|
|
219
236
|
|
|
220
237
|
/**
|
|
221
238
|
* useLensNavigator — move the ONE cursor to a stage by its address.
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
describeReceived,
|
|
7
7
|
refusalDestinationFor,
|
|
8
8
|
useNarrowRow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AUGRQKRL.js";
|
|
10
10
|
import {
|
|
11
11
|
selectSkillBeatAt,
|
|
12
12
|
selectSkillBeats,
|
|
@@ -1492,4 +1492,4 @@ export {
|
|
|
1492
1492
|
SKILL_GRAPH_READS,
|
|
1493
1493
|
SkillGraphDebugger
|
|
1494
1494
|
};
|
|
1495
|
-
//# sourceMappingURL=chunk-
|
|
1495
|
+
//# sourceMappingURL=chunk-2UOQEE4U.js.map
|
|
@@ -6,16 +6,17 @@ import {
|
|
|
6
6
|
cursorProvenance,
|
|
7
7
|
isFrameworkChartNode,
|
|
8
8
|
makeTeachingHumanizer,
|
|
9
|
+
openLensCursor,
|
|
9
10
|
selectHops,
|
|
10
11
|
selectToolChoiceCall,
|
|
11
12
|
structureGraphFromRunner
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-SJEKKUTI.js";
|
|
13
14
|
import {
|
|
14
15
|
MODE_PALETTES,
|
|
15
16
|
T,
|
|
16
17
|
TimeTravel,
|
|
17
18
|
useNarrowRow
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-AUGRQKRL.js";
|
|
19
20
|
import {
|
|
20
21
|
bandChartGroup,
|
|
21
22
|
bandIndexOf,
|
|
@@ -914,14 +915,15 @@ function useLensCursor({
|
|
|
914
915
|
controlledStep,
|
|
915
916
|
onStepChange,
|
|
916
917
|
maxStep,
|
|
917
|
-
describe
|
|
918
|
+
describe,
|
|
919
|
+
port
|
|
918
920
|
}) {
|
|
919
921
|
const isControlled = controlledStep !== void 0;
|
|
920
922
|
const [internalStep, setInternalStep] = useState(0);
|
|
921
923
|
const [autoAdvance, setAutoAdvance] = useState(
|
|
922
924
|
() => controlledStep === void 0 || clampStep(controlledStep, maxStep) >= maxStep
|
|
923
925
|
);
|
|
924
|
-
const step = isControlled ?
|
|
926
|
+
const step = clampStep(isControlled ? controlledStep : internalStep, maxStep);
|
|
925
927
|
const isLive = autoAdvance && step >= maxStep;
|
|
926
928
|
const describeRef = useRef(describe);
|
|
927
929
|
describeRef.current = describe;
|
|
@@ -931,6 +933,8 @@ function useLensCursor({
|
|
|
931
933
|
maxRef.current = maxStep;
|
|
932
934
|
const stepRef = useRef(step);
|
|
933
935
|
stepRef.current = step;
|
|
936
|
+
const portRef = useRef(port);
|
|
937
|
+
portRef.current = port;
|
|
934
938
|
const notify = useCallback2((n, clamped) => {
|
|
935
939
|
const cb = onChangeRef.current;
|
|
936
940
|
if (!cb) return;
|
|
@@ -946,10 +950,12 @@ function useLensCursor({
|
|
|
946
950
|
});
|
|
947
951
|
}, []);
|
|
948
952
|
const moveTo = useCallback2((n) => {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
if (
|
|
952
|
-
|
|
953
|
+
const from = stepRef.current;
|
|
954
|
+
const to = portRef.current !== void 0 ? portRef.current.toStep(from, n) : { step: n, moved: n !== from, clamped: false };
|
|
955
|
+
if (controlledStep === void 0) setInternalStep(to.step);
|
|
956
|
+
setAutoAdvance(to.step >= maxRef.current);
|
|
957
|
+
if (to.step === from && !to.clamped) return;
|
|
958
|
+
notify(to.step, to.clamped);
|
|
953
959
|
}, [controlledStep, notify]);
|
|
954
960
|
useEffect(() => {
|
|
955
961
|
if (controlledStep === void 0) return;
|
|
@@ -963,18 +969,20 @@ function useLensCursor({
|
|
|
963
969
|
}, [maxStep, autoAdvance, controlledStep, notify]);
|
|
964
970
|
const warnedRef = useRef(void 0);
|
|
965
971
|
useEffect(() => {
|
|
966
|
-
|
|
967
|
-
const snapped = clampStep(
|
|
968
|
-
if (snapped ===
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
warnedRef.current
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
972
|
+
const owned = controlledStep !== void 0 ? controlledStep : internalStep;
|
|
973
|
+
const snapped = clampStep(owned, maxStep);
|
|
974
|
+
if (snapped === owned) return;
|
|
975
|
+
if (controlledStep !== void 0) {
|
|
976
|
+
const key = `${controlledStep}/${maxStep}`;
|
|
977
|
+
if (warnedRef.current !== key) {
|
|
978
|
+
warnedRef.current = key;
|
|
979
|
+
console.warn(
|
|
980
|
+
`[agentfootprint-lens] <Lens step={${controlledStep}}> is not a position in this run: the cursor axis holds ${maxStep + 1} step${maxStep === 0 ? "" : "s"} (0\u2026${maxStep}). Lens moved to step ${snapped} and called onStepChange(${snapped}, { clamped: true }) so your state can follow. The axis GROWS as the run does, so store the value the callback hands you rather than a remembered number.`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
975
983
|
}
|
|
976
984
|
notify(snapped, true);
|
|
977
|
-
}, [controlledStep, maxStep, notify]);
|
|
985
|
+
}, [controlledStep, internalStep, maxStep, notify]);
|
|
978
986
|
return { step, isLive, moveTo };
|
|
979
987
|
}
|
|
980
988
|
|
|
@@ -2631,6 +2639,16 @@ var Lens = ({
|
|
|
2631
2639
|
);
|
|
2632
2640
|
const stepCount = Math.max(1, cursorPositions.length);
|
|
2633
2641
|
const maxStep = Math.max(0, stepCount - 1);
|
|
2642
|
+
const cursorPort = useMemo7(() => openLensCursor(cursorPositions), [cursorPositions]);
|
|
2643
|
+
const stepper = useMemo7(
|
|
2644
|
+
() => ({
|
|
2645
|
+
back: (from) => cursorPort.prev(from).step,
|
|
2646
|
+
forward: (from) => cursorPort.next(from).step,
|
|
2647
|
+
toFirst: (from) => cursorPort.first(from).step,
|
|
2648
|
+
toLast: (from) => cursorPort.last(from).step
|
|
2649
|
+
}),
|
|
2650
|
+
[cursorPort]
|
|
2651
|
+
);
|
|
2634
2652
|
const describeStep = useCallback5(
|
|
2635
2653
|
(n) => {
|
|
2636
2654
|
const p = cursorPositions[n];
|
|
@@ -2651,7 +2669,8 @@ var Lens = ({
|
|
|
2651
2669
|
controlledStep,
|
|
2652
2670
|
onStepChange,
|
|
2653
2671
|
maxStep,
|
|
2654
|
-
describe: describeStep
|
|
2672
|
+
describe: describeStep,
|
|
2673
|
+
port: cursorPort
|
|
2655
2674
|
});
|
|
2656
2675
|
useLensNavigator({
|
|
2657
2676
|
positions: cursorPositions,
|
|
@@ -2701,6 +2720,7 @@ var Lens = ({
|
|
|
2701
2720
|
focusSeq: focusStep,
|
|
2702
2721
|
onFocusChange: handleFocusChange,
|
|
2703
2722
|
isLive,
|
|
2723
|
+
stepper,
|
|
2704
2724
|
liveStreamLine
|
|
2705
2725
|
}
|
|
2706
2726
|
)
|
|
@@ -2722,6 +2742,8 @@ var Lens = ({
|
|
|
2722
2742
|
focusStep,
|
|
2723
2743
|
onFocusChange: handleFocusChange,
|
|
2724
2744
|
isLive,
|
|
2745
|
+
stepper,
|
|
2746
|
+
cursorPort,
|
|
2725
2747
|
stepStrip,
|
|
2726
2748
|
showSummary,
|
|
2727
2749
|
liveStreamLine,
|
|
@@ -2809,6 +2831,8 @@ var EngineerView = ({
|
|
|
2809
2831
|
cursorRuntimeStageId,
|
|
2810
2832
|
toolChoice,
|
|
2811
2833
|
granularity,
|
|
2834
|
+
stepper,
|
|
2835
|
+
cursorPort,
|
|
2812
2836
|
slots
|
|
2813
2837
|
}) => {
|
|
2814
2838
|
const bands = useMemo7(
|
|
@@ -3048,16 +3072,10 @@ var EngineerView = ({
|
|
|
3048
3072
|
const DetailSlot = slots?.detail;
|
|
3049
3073
|
const jumpToRuntimeStageId = useCallback5(
|
|
3050
3074
|
(runtimeStageId) => {
|
|
3051
|
-
const
|
|
3052
|
-
if (
|
|
3053
|
-
onFocusChange(exact);
|
|
3054
|
-
return;
|
|
3055
|
-
}
|
|
3056
|
-
const stagePart = runtimeStageId.split("#")[0];
|
|
3057
|
-
const byStage = cursorPositions.findIndex((p) => p.runtimeStageId.split("#")[0] === stagePart);
|
|
3058
|
-
if (byStage >= 0) onFocusChange(byStage);
|
|
3075
|
+
const to = cursorPort.toAddress(focusStep, runtimeStageId);
|
|
3076
|
+
if (to.ok && to.step !== void 0) onFocusChange(to.step);
|
|
3059
3077
|
},
|
|
3060
|
-
[
|
|
3078
|
+
[cursorPort, focusStep, onFocusChange]
|
|
3061
3079
|
);
|
|
3062
3080
|
return /* @__PURE__ */ jsxs8(
|
|
3063
3081
|
"div",
|
|
@@ -3109,7 +3127,8 @@ var EngineerView = ({
|
|
|
3109
3127
|
total,
|
|
3110
3128
|
focusSeq: focusStep,
|
|
3111
3129
|
onFocusChange,
|
|
3112
|
-
isLive
|
|
3130
|
+
isLive,
|
|
3131
|
+
stepper
|
|
3113
3132
|
}
|
|
3114
3133
|
),
|
|
3115
3134
|
granularity === "group" && frameworkHiddenCount > 0 && /* @__PURE__ */ jsxs8(
|
|
@@ -3899,6 +3918,7 @@ var AnalystView = ({
|
|
|
3899
3918
|
focusSeq,
|
|
3900
3919
|
onFocusChange,
|
|
3901
3920
|
isLive,
|
|
3921
|
+
stepper,
|
|
3902
3922
|
liveStreamLine
|
|
3903
3923
|
}) => {
|
|
3904
3924
|
return /* @__PURE__ */ jsxs8("div", { style: { display: "grid", gap: 16 }, children: [
|
|
@@ -3909,7 +3929,8 @@ var AnalystView = ({
|
|
|
3909
3929
|
total,
|
|
3910
3930
|
focusSeq,
|
|
3911
3931
|
onFocusChange,
|
|
3912
|
-
isLive
|
|
3932
|
+
isLive,
|
|
3933
|
+
stepper
|
|
3913
3934
|
}
|
|
3914
3935
|
),
|
|
3915
3936
|
/* @__PURE__ */ jsx9(Card, { title: "Commentary", children: /* @__PURE__ */ jsxs8("div", { style: { fontSize: 13, lineHeight: 1.6, fontFamily: T.fontSans }, children: [
|
|
@@ -4172,4 +4193,4 @@ export {
|
|
|
4172
4193
|
WhereFrom,
|
|
4173
4194
|
Lens
|
|
4174
4195
|
};
|
|
4175
|
-
//# sourceMappingURL=chunk-
|
|
4196
|
+
//# sourceMappingURL=chunk-A4EIPCLY.js.map
|