@narumitw/pi-inspect 0.0.0-stage → 0.2.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -2
  3. package/dist/app.css +1 -0
  4. package/dist/app.js +128 -0
  5. package/dist/index.html +1 -0
  6. package/docs/REVIEW.md +189 -0
  7. package/docs/VERIFICATION.md +186 -0
  8. package/docs/viewer.md +74 -0
  9. package/package.json +67 -4
  10. package/src/ancestry.ts +32 -0
  11. package/src/collector.ts +143 -0
  12. package/src/context.ts +115 -0
  13. package/src/correlation.ts +27 -0
  14. package/src/entry-index.ts +17 -0
  15. package/src/entry-summary.ts +34 -0
  16. package/src/extension.ts +259 -0
  17. package/src/feed.ts +145 -0
  18. package/src/identity.ts +112 -0
  19. package/src/index.ts +1 -0
  20. package/src/model.ts +117 -0
  21. package/src/native-projection.ts +28 -0
  22. package/src/privacy.ts +136 -0
  23. package/src/projection.ts +374 -0
  24. package/src/server.ts +202 -0
  25. package/src/system-message.ts +117 -0
  26. package/src/web/api.ts +137 -0
  27. package/src/web/app.tsx +570 -0
  28. package/src/web/captured-executions.tsx +177 -0
  29. package/src/web/components.css +261 -0
  30. package/src/web/components.tsx +241 -0
  31. package/src/web/composition.css +554 -0
  32. package/src/web/context-composition.tsx +329 -0
  33. package/src/web/context-inventory.tsx +65 -0
  34. package/src/web/entry-calls.ts +14 -0
  35. package/src/web/format.ts +94 -0
  36. package/src/web/hierarchy.ts +117 -0
  37. package/src/web/history.ts +56 -0
  38. package/src/web/inline-entry.tsx +124 -0
  39. package/src/web/inspector.tsx +386 -0
  40. package/src/web/json-tree.tsx +74 -0
  41. package/src/web/overview.tsx +67 -0
  42. package/src/web/panels.css +267 -0
  43. package/src/web/panes.tsx +123 -0
  44. package/src/web/recorded-content.tsx +49 -0
  45. package/src/web/search.ts +16 -0
  46. package/src/web/session-filters.tsx +100 -0
  47. package/src/web/session.css +114 -0
  48. package/src/web/style.css +332 -0
  49. package/src/web/styles.d.ts +1 -0
  50. package/src/web/timeline.tsx +33 -0
  51. package/src/web/timing.ts +26 -0
  52. package/src/web/trace.css +411 -0
  53. package/src/web/trace.tsx +313 -0
  54. package/src/web/virtual-context.ts +138 -0
@@ -0,0 +1,332 @@
1
+ @import "./components.css";
2
+ @import "./panels.css";
3
+ @import "./trace.css";
4
+
5
+ :root,
6
+ .radix-themes {
7
+ --app-bg: #0b1220;
8
+ --panel-bg: #101b2b;
9
+ --panel-raised: #152338;
10
+ --surface-hover: #1b2d45;
11
+ --panel-border: #27384d;
12
+ --secondary: #a3b3c8;
13
+ --muted: #71849d;
14
+ --code-bg: #0d1725;
15
+ --selected-bg: #4b91f71f;
16
+ }
17
+ .radix-themes.light {
18
+ --app-bg: #f3f5f9;
19
+ --panel-bg: #fff;
20
+ --panel-raised: #edf2f9;
21
+ --surface-hover: #e5edf8;
22
+ --panel-border: #ced7e5;
23
+ --secondary: #53637a;
24
+ --muted: #687992;
25
+ --code-bg: #f7f9fd;
26
+ --selected-bg: #4b91f71a;
27
+ }
28
+ * {
29
+ box-sizing: border-box;
30
+ }
31
+ body {
32
+ margin: 0;
33
+ background: var(--app-bg);
34
+ }
35
+ button,
36
+ input {
37
+ font: inherit;
38
+ }
39
+ button {
40
+ cursor: pointer;
41
+ }
42
+ button:disabled {
43
+ cursor: default;
44
+ }
45
+ .inspector-app {
46
+ height: 100dvh;
47
+ min-height: 0;
48
+ display: flex;
49
+ flex-direction: column;
50
+ overflow: hidden;
51
+ background: var(--app-bg);
52
+ color: var(--gray-12);
53
+ font:
54
+ 13px / 1.4 "Segoe UI",
55
+ -apple-system,
56
+ BlinkMacSystemFont,
57
+ sans-serif;
58
+ }
59
+ .app-header {
60
+ height: 48px;
61
+ flex-shrink: 0;
62
+ display: flex;
63
+ align-items: center;
64
+ gap: 16px;
65
+ padding: 0 12px;
66
+ border-bottom: 1px solid var(--panel-border);
67
+ }
68
+ .brand {
69
+ display: flex;
70
+ gap: 10px;
71
+ align-items: center;
72
+ flex-shrink: 0;
73
+ }
74
+ .brand-logo {
75
+ width: 28px;
76
+ height: 28px;
77
+ display: grid;
78
+ place-items: center;
79
+ border-radius: 5px;
80
+ background: #2558a7;
81
+ color: white;
82
+ font:
83
+ bold 25px Georgia,
84
+ serif;
85
+ }
86
+ .brand .rt-Heading {
87
+ font-size: 17px;
88
+ }
89
+ .header-context {
90
+ flex: 1;
91
+ min-width: 0;
92
+ display: flex;
93
+ gap: 14px;
94
+ color: var(--secondary);
95
+ font-size: 11px;
96
+ }
97
+ .header-context span {
98
+ white-space: nowrap;
99
+ overflow: hidden;
100
+ text-overflow: ellipsis;
101
+ }
102
+ .header-context strong {
103
+ font:
104
+ 11px / 1.4 Consolas,
105
+ monospace;
106
+ color: var(--gray-12);
107
+ }
108
+ .header-actions {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 9px;
112
+ margin-left: auto;
113
+ }
114
+ .header-actions .rt-IconButton {
115
+ width: 27px;
116
+ height: 27px;
117
+ color: var(--secondary);
118
+ }
119
+ .app-notices {
120
+ padding: 5px 8px;
121
+ flex-shrink: 0;
122
+ }
123
+ .app-notices .rt-CalloutRoot {
124
+ padding: 6px 10px;
125
+ font-size: 11px;
126
+ }
127
+ .app-grid {
128
+ display: grid;
129
+ flex: 1;
130
+ min-height: 0;
131
+ min-width: 0;
132
+ overflow: hidden;
133
+ padding: 8px;
134
+ gap: 3px;
135
+ }
136
+ .side-slot {
137
+ min-width: 0;
138
+ min-height: 0;
139
+ grid-row: 1;
140
+ height: 100%;
141
+ }
142
+ .nav-slot {
143
+ grid-column: 1;
144
+ }
145
+ .inspector-slot {
146
+ grid-column: 5;
147
+ }
148
+ .side-slot[hidden] {
149
+ display: none;
150
+ }
151
+ .pane-resizer {
152
+ margin: 0;
153
+ border: 0;
154
+ min-width: 0;
155
+ height: 100%;
156
+ cursor: col-resize;
157
+ background: transparent;
158
+ touch-action: none;
159
+ grid-row: 1;
160
+ }
161
+ .pane-resizer[aria-label="Resize filters"] {
162
+ grid-column: 2;
163
+ }
164
+ .pane-resizer[aria-label="Resize details"] {
165
+ grid-column: 4;
166
+ }
167
+ .pane-resizer:hover,
168
+ .pane-resizer:focus-visible {
169
+ background: var(--blue-8);
170
+ }
171
+ .center-column {
172
+ grid-column: 3;
173
+ grid-row: 1;
174
+ display: flex;
175
+ flex-direction: column;
176
+ min-width: 0;
177
+ min-height: 0;
178
+ height: 100%;
179
+ gap: 4px;
180
+ }
181
+ .panel {
182
+ background: var(--panel-bg);
183
+ border: 1px solid var(--panel-border);
184
+ border-radius: 5px;
185
+ min-width: 0;
186
+ min-height: 0;
187
+ overflow: hidden;
188
+ }
189
+ .panel-heading {
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: space-between;
193
+ padding: 9px 10px;
194
+ gap: 6px;
195
+ }
196
+ .panel-heading .rt-Heading {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 7px;
200
+ font-size: 13px;
201
+ line-height: 18px;
202
+ }
203
+ .panel-heading svg {
204
+ width: 15px;
205
+ height: 15px;
206
+ color: var(--secondary);
207
+ }
208
+ .app-footer {
209
+ height: 24px;
210
+ flex-shrink: 0;
211
+ border-top: 1px solid var(--panel-border);
212
+ display: flex;
213
+ justify-content: space-between;
214
+ align-items: center;
215
+ gap: 10px;
216
+ padding: 0 12px;
217
+ font-size: 10px;
218
+ color: var(--muted);
219
+ }
220
+ button:focus-visible,
221
+ a:focus-visible,
222
+ [role="treeitem"]:focus-visible,
223
+ .pane-resizer:focus-visible,
224
+ summary:focus-visible {
225
+ outline: 2px solid var(--blue-9);
226
+ outline-offset: -2px;
227
+ }
228
+ .tree-scroll,
229
+ .trace-scroll,
230
+ .inspector-tabs,
231
+ .live-drawer-body {
232
+ scrollbar-width: thin;
233
+ scrollbar-color: var(--panel-border) transparent;
234
+ }
235
+ .side-drawer {
236
+ width: min(92vw, 440px);
237
+ max-width: 92vw;
238
+ height: calc(100dvh - 24px);
239
+ max-height: calc(100dvh - 24px);
240
+ margin: 12px;
241
+ padding: 10px;
242
+ display: flex;
243
+ flex-direction: column;
244
+ gap: 8px;
245
+ background: var(--panel-bg);
246
+ }
247
+ .side-drawer-left {
248
+ margin-right: auto;
249
+ }
250
+ .side-drawer-right {
251
+ margin-left: auto;
252
+ width: min(94vw, 540px);
253
+ }
254
+ .side-drawer .inspector-panel > .panel-heading {
255
+ display: none;
256
+ }
257
+ .side-drawer .sidebar,
258
+ .side-drawer .inspector-panel {
259
+ flex: 1;
260
+ min-height: 0;
261
+ }
262
+ .drawer-title {
263
+ display: flex;
264
+ justify-content: space-between;
265
+ align-items: center;
266
+ flex-shrink: 0;
267
+ }
268
+ .drawer-title .rt-DialogTitle {
269
+ margin: 0;
270
+ font-size: 16px;
271
+ }
272
+ .sr-only {
273
+ position: absolute;
274
+ width: 1px;
275
+ height: 1px;
276
+ margin: -1px;
277
+ padding: 0;
278
+ overflow: hidden;
279
+ clip: rect(0, 0, 0, 0);
280
+ white-space: nowrap;
281
+ border: 0;
282
+ }
283
+ .narrow-layout {
284
+ display: flex;
285
+ }
286
+ .narrow-layout .center-column {
287
+ flex: 1;
288
+ width: 100%;
289
+ }
290
+ @media (max-width: 1100px) {
291
+ .header-context {
292
+ display: none;
293
+ }
294
+ }
295
+ @media (max-width: 680px) {
296
+ .app-header {
297
+ gap: 6px;
298
+ padding: 0 8px;
299
+ }
300
+ .brand {
301
+ gap: 5px;
302
+ }
303
+ .brand .rt-Heading {
304
+ font-size: 14px;
305
+ }
306
+ .brand-logo {
307
+ width: 24px;
308
+ height: 24px;
309
+ font-size: 22px;
310
+ }
311
+ .header-actions {
312
+ gap: 4px;
313
+ }
314
+ .header-actions .rt-Badge:last-of-type {
315
+ display: none;
316
+ }
317
+ .app-grid {
318
+ padding: 5px;
319
+ }
320
+ .app-footer span:last-child {
321
+ display: none;
322
+ }
323
+ }
324
+ @media (prefers-reduced-motion: reduce) {
325
+ *,
326
+ *::before,
327
+ *::after {
328
+ animation: none;
329
+ transition: none;
330
+ scroll-behavior: auto;
331
+ }
332
+ }
@@ -0,0 +1 @@
1
+ declare module "*.css";
@@ -0,0 +1,33 @@
1
+ import type { Call } from "../model.js";
2
+ import { type Axis, interval, position, timestamp } from "./timing.js";
3
+
4
+ export function TimelineMark({ range, logTime, call }: { range?: Axis; logTime?: string; call?: Call }) {
5
+ if (!range) return <span className="timing-unavailable">—</span>;
6
+ const span = call ? interval(call) : undefined;
7
+ const point = logTime === undefined ? (call?.observedStartedAt ?? call?.observedEndedAt) : timestamp(logTime);
8
+ if (span && span.end > span.start)
9
+ return (
10
+ <span
11
+ className="timeline-track"
12
+ title={`Observed callback interval: ${new Date(span.start).toISOString()} → ${new Date(span.end).toISOString()} (${span.end - span.start}ms). Not provider transport timing.`}
13
+ >
14
+ <span
15
+ className="timeline-span"
16
+ style={{
17
+ left: `${position(span.start, range)}%`,
18
+ width: `${position(span.end, range) - position(span.start, range)}%`,
19
+ }}
20
+ />
21
+ </span>
22
+ );
23
+ return point === undefined ? (
24
+ <span className="timing-unavailable">—</span>
25
+ ) : (
26
+ <span
27
+ className="timeline-track"
28
+ title={`${logTime === undefined ? "Observed lifecycle point" : "Session log timestamp"}: ${new Date(point).toISOString()}`}
29
+ >
30
+ <span className="timeline-point" style={{ left: `${position(point, range)}%` }} />
31
+ </span>
32
+ );
33
+ }
@@ -0,0 +1,26 @@
1
+ import type { Call, EntrySummary } from "../model.js";
2
+ export interface Axis {
3
+ start: number;
4
+ end: number;
5
+ }
6
+ export function timestamp(value: string): number | undefined {
7
+ const parsed = Date.parse(value);
8
+ return Number.isFinite(parsed) ? parsed : undefined;
9
+ }
10
+ export function axis(entries: EntrySummary[], calls: Call[]): Axis | undefined {
11
+ const values = [
12
+ ...entries.map((entry) => timestamp(entry.timestamp)),
13
+ ...calls.flatMap((call) => [call.observedStartedAt, call.observedEndedAt]),
14
+ ].filter((value): value is number => value !== undefined && Number.isFinite(value));
15
+ return values.length ? { start: Math.min(...values), end: Math.max(...values) } : undefined;
16
+ }
17
+ export function position(value: number, range: Axis): number {
18
+ return range.end === range.start ? 50 : ((value - range.start) / (range.end - range.start)) * 100;
19
+ }
20
+ export function interval(call: Call): { start: number; end: number } | undefined {
21
+ const start = call.observedStartedAt;
22
+ const end = call.observedEndedAt;
23
+ return start !== undefined && end !== undefined && Number.isFinite(start) && Number.isFinite(end) && end >= start
24
+ ? { start, end }
25
+ : undefined;
26
+ }