@xprnvd/pi-context-view 0.0.0-stage → 0.7.2
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/LICENSE +21 -0
- package/README.md +112 -2
- package/package.json +55 -5
- package/src/capture/adjustments.ts +649 -0
- package/src/capture/attribution.ts +25 -0
- package/src/capture/builder.ts +128 -0
- package/src/capture/diff.ts +455 -0
- package/src/capture/dispatch.ts +36 -0
- package/src/capture/guard.ts +287 -0
- package/src/capture/messages.ts +246 -0
- package/src/capture/payload.ts +584 -0
- package/src/capture/redact.ts +58 -0
- package/src/capture/register.ts +129 -0
- package/src/capture/request.ts +218 -0
- package/src/capture/tools.ts +69 -0
- package/src/capture/tracker.ts +71 -0
- package/src/command.ts +164 -0
- package/src/compaction.ts +57 -0
- package/src/config.ts +408 -0
- package/src/index.ts +191 -0
- package/src/injections.ts +670 -0
- package/src/measure.ts +887 -0
- package/src/message-preview.ts +72 -0
- package/src/model-identity.ts +48 -0
- package/src/model.ts +548 -0
- package/src/pi-version.ts +29 -0
- package/src/probe/failure.ts +34 -0
- package/src/probe/filter.ts +126 -0
- package/src/probe/silent-probe.ts +349 -0
- package/src/probe/token.ts +28 -0
- package/src/probe/trigger.ts +178 -0
- package/src/probe/view.ts +26 -0
- package/src/projection.ts +122 -0
- package/src/prompt-additions.ts +226 -0
- package/src/prompt-blocks.ts +80 -0
- package/src/replay.ts +234 -0
- package/src/settings.ts +62 -0
- package/src/snapshot.ts +188 -0
- package/src/system-prompt-rows.ts +311 -0
- package/src/text.ts +46 -0
- package/src/token-diff.ts +211 -0
- package/src/transcript.ts +19 -0
- package/src/ui/color.ts +52 -0
- package/src/ui/diff-preview.ts +181 -0
- package/src/ui/injections-model.ts +378 -0
- package/src/ui/injections-view.ts +729 -0
- package/src/ui/json-preview.ts +41 -0
- package/src/ui/layout.ts +148 -0
- package/src/ui/markers.ts +253 -0
- package/src/ui/notes.ts +117 -0
- package/src/ui/section-preview.ts +267 -0
- package/src/ui/skill-preview.ts +83 -0
- package/src/ui/status-bar.ts +94 -0
- package/src/ui/usage-map.ts +157 -0
- package/src/ui/usage-preview.ts +194 -0
- package/src/ui/usage-view.ts +1255 -0
- package/src/ui/wheel.ts +65 -0
- package/src/usage.ts +461 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure block model for the Usage category preview: geometry of the flattened
|
|
3
|
+
* line stream plus the selection, paging, and in-block scrolling state shared
|
|
4
|
+
* by every preview width. No pi or TUI access — unit-testable.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/** Absolute line span of one block inside the flattened preview stream. */
|
|
8
|
+
export interface PreviewBlockExtent {
|
|
9
|
+
readonly start: number;
|
|
10
|
+
readonly height: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Owner of one flattened preview line; blank separator rows have none. */
|
|
14
|
+
export interface PreviewLineRef {
|
|
15
|
+
readonly blockIndex: number;
|
|
16
|
+
readonly lineIndex: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Flattened preview stream: block spans plus per-line ownership. */
|
|
20
|
+
export interface PreviewLayout {
|
|
21
|
+
readonly extents: readonly PreviewBlockExtent[];
|
|
22
|
+
readonly lines: ReadonlyArray<PreviewLineRef | undefined>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Stack block heights into one line stream separated by a blank row. */
|
|
26
|
+
export function layoutPreviewBlocks(heights: readonly number[]): PreviewLayout {
|
|
27
|
+
const extents: PreviewBlockExtent[] = [];
|
|
28
|
+
const lines: Array<PreviewLineRef | undefined> = [];
|
|
29
|
+
for (const [blockIndex, height] of heights.entries()) {
|
|
30
|
+
if (blockIndex > 0) lines.push(undefined);
|
|
31
|
+
extents.push({ start: lines.length, height });
|
|
32
|
+
for (let lineIndex = 0; lineIndex < height; lineIndex++) lines.push({ blockIndex, lineIndex });
|
|
33
|
+
}
|
|
34
|
+
return { extents, lines };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Selection and scroll state over stacked preview blocks. Steps move block to
|
|
39
|
+
* block, except that a block taller than the viewport scrolls line by line
|
|
40
|
+
* until its far edge is reached. Extent is re-declared each render because
|
|
41
|
+
* wrapping and terminal height both change it.
|
|
42
|
+
*/
|
|
43
|
+
export class BlockNavigator {
|
|
44
|
+
private extents: readonly PreviewBlockExtent[] = [];
|
|
45
|
+
private lineCount = 0;
|
|
46
|
+
private visibleCount = 1;
|
|
47
|
+
private selectedIndex = 0;
|
|
48
|
+
private offsetValue = 0;
|
|
49
|
+
|
|
50
|
+
/** Index of the selected block. */
|
|
51
|
+
public get selected(): number {
|
|
52
|
+
return this.selectedIndex;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Number of navigable blocks in the current stream. */
|
|
56
|
+
public get blockCount(): number {
|
|
57
|
+
return this.extents.length;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** First stream line currently visible. */
|
|
61
|
+
public get offset(): number {
|
|
62
|
+
return this.offsetValue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Highest offset that still fills the viewport. */
|
|
66
|
+
public get maxOffset(): number {
|
|
67
|
+
return Math.max(0, this.lineCount - this.visibleCount);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Adopt new geometry, keeping the scroll position unless it hides the selected block. */
|
|
71
|
+
public setExtent(layout: PreviewLayout, visibleCount: number): void {
|
|
72
|
+
this.extents = layout.extents;
|
|
73
|
+
this.lineCount = layout.lines.length;
|
|
74
|
+
this.visibleCount = Math.max(1, visibleCount);
|
|
75
|
+
this.selectedIndex = clamp(this.selectedIndex, this.extents.length - 1);
|
|
76
|
+
this.offsetValue = clamp(this.offsetValue, this.maxOffset);
|
|
77
|
+
if (!this.isSelectionAnchored()) this.revealSelected();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Step back one block, or one line while the selected block extends above the viewport. */
|
|
81
|
+
public stepBack(): boolean {
|
|
82
|
+
const block = this.extents[this.selectedIndex];
|
|
83
|
+
if (block === undefined) return false;
|
|
84
|
+
if (block.start < this.offsetValue) return this.scrollTo(this.offsetValue - 1);
|
|
85
|
+
return this.selectBlock(this.selectedIndex - 1, -1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Step forward one block, or one line while the selected block extends below the viewport. */
|
|
89
|
+
public stepForward(): boolean {
|
|
90
|
+
const block = this.extents[this.selectedIndex];
|
|
91
|
+
if (block === undefined) return false;
|
|
92
|
+
if (block.start + block.height > this.offsetValue + this.visibleCount) {
|
|
93
|
+
return this.scrollTo(this.offsetValue + 1);
|
|
94
|
+
}
|
|
95
|
+
return this.selectBlock(this.selectedIndex + 1, 1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Page by the viewport height, then finish at the stream boundary from its first or last page. */
|
|
99
|
+
public page(direction: -1 | 1): boolean {
|
|
100
|
+
if (this.extents.length === 0) return false;
|
|
101
|
+
const offset = clamp(this.offsetValue + direction * this.visibleCount, this.maxOffset);
|
|
102
|
+
if (offset === this.offsetValue) {
|
|
103
|
+
const boundaryIndex = direction < 0 ? 0 : this.extents.length - 1;
|
|
104
|
+
return this.moveTo(boundaryIndex, offset);
|
|
105
|
+
}
|
|
106
|
+
return this.moveTo(this.firstVisibleBlock(offset), offset);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Select the first block at the top of the stream. */
|
|
110
|
+
public moveToFirst(): boolean {
|
|
111
|
+
return this.moveTo(0, 0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Select the last block at the end of the stream. */
|
|
115
|
+
public moveToLast(): boolean {
|
|
116
|
+
return this.moveTo(this.extents.length - 1, this.maxOffset);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Start over at the first block after the preview target changes. */
|
|
120
|
+
public reset(): void {
|
|
121
|
+
this.selectedIndex = 0;
|
|
122
|
+
this.offsetValue = 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Move selection to an existing block and reveal the edge reached in the navigation direction. */
|
|
126
|
+
private selectBlock(index: number, direction: -1 | 1): boolean {
|
|
127
|
+
if (index < 0 || index >= this.extents.length || index === this.selectedIndex) return false;
|
|
128
|
+
this.selectedIndex = index;
|
|
129
|
+
this.revealSelected(direction);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Scroll the stream to a clamped offset. */
|
|
134
|
+
private scrollTo(offset: number): boolean {
|
|
135
|
+
const next = clamp(offset, this.maxOffset);
|
|
136
|
+
if (next === this.offsetValue) return false;
|
|
137
|
+
this.offsetValue = next;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Apply a selection and offset pair, reporting whether either changed. */
|
|
142
|
+
private moveTo(index: number, offset: number): boolean {
|
|
143
|
+
const nextIndex = clamp(index, this.extents.length - 1);
|
|
144
|
+
const nextOffset = clamp(offset, this.maxOffset);
|
|
145
|
+
if (nextIndex === this.selectedIndex && nextOffset === this.offsetValue) return false;
|
|
146
|
+
this.selectedIndex = nextIndex;
|
|
147
|
+
this.offsetValue = nextOffset;
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Whether the selected block is fully shown, or intersects the viewport when too tall to fit. */
|
|
152
|
+
private isSelectionAnchored(): boolean {
|
|
153
|
+
const block = this.extents[this.selectedIndex];
|
|
154
|
+
if (block === undefined) return true;
|
|
155
|
+
const end = block.start + block.height;
|
|
156
|
+
if (block.height <= this.visibleCount) {
|
|
157
|
+
return block.start >= this.offsetValue && end <= this.offsetValue + this.visibleCount;
|
|
158
|
+
}
|
|
159
|
+
return block.start < this.offsetValue + this.visibleCount && end > this.offsetValue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Scroll minimally to show the selected block; oversized blocks expose the approached edge. */
|
|
163
|
+
private revealSelected(direction?: -1 | 1): void {
|
|
164
|
+
const block = this.extents[this.selectedIndex];
|
|
165
|
+
if (block === undefined) return;
|
|
166
|
+
const end = block.start + block.height;
|
|
167
|
+
if (block.height > this.visibleCount) {
|
|
168
|
+
if (direction === -1) this.scrollTo(end - this.visibleCount);
|
|
169
|
+
else if (direction === 1 || !this.isSelectionAnchored()) this.scrollTo(block.start);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (block.start < this.offsetValue) this.scrollTo(block.start);
|
|
173
|
+
else if (end > this.offsetValue + this.visibleCount) this.scrollTo(end - this.visibleCount);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** First block fully inside the viewport, falling back to the first one it intersects. */
|
|
177
|
+
private firstVisibleBlock(offset: number): number {
|
|
178
|
+
const end = offset + this.visibleCount;
|
|
179
|
+
let firstIntersecting: number | undefined;
|
|
180
|
+
for (const [index, block] of this.extents.entries()) {
|
|
181
|
+
const blockEnd = block.start + block.height;
|
|
182
|
+
if (block.start >= offset && blockEnd <= end) return index;
|
|
183
|
+
if (firstIntersecting === undefined && block.start < end && blockEnd > offset) {
|
|
184
|
+
firstIntersecting = index;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return firstIntersecting ?? this.selectedIndex;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Restrict a candidate index or offset to a non-negative range. */
|
|
192
|
+
function clamp(value: number, max: number): number {
|
|
193
|
+
return Math.max(0, Math.min(max, value));
|
|
194
|
+
}
|