@lukekaalim/act-insight 1.0.0 → 2.0.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 (123) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/InsightApp.module.css +36 -0
  3. package/InsightApp2.ts +82 -0
  4. package/TreeViewer.module.css +3 -8
  5. package/TreeViewer.ts +16 -12
  6. package/Virtual.ts +29 -11
  7. package/assets/icons/ancestor_column.png +0 -0
  8. package/assets/icons/ancestor_column.svg +26 -0
  9. package/assets/icons/ancestor_skip_row_column.png +0 -0
  10. package/assets/icons/ancestor_skip_row_column.svg +52 -0
  11. package/assets/icons/breakpoint.png +0 -0
  12. package/assets/icons/breakpoint.svg +33 -0
  13. package/assets/icons/breakpoint_arrow.png +0 -0
  14. package/assets/icons/breakpoint_arrow.svg +22 -0
  15. package/assets/icons/breakpoint_arrow_paused.png +0 -0
  16. package/assets/icons/breakpoint_arrow_paused.svg +38 -0
  17. package/assets/icons/breakpoint_panel.png +0 -0
  18. package/assets/icons/breakpoint_panel.svg +121 -0
  19. package/assets/icons/breakpoint_unset.png +0 -0
  20. package/assets/icons/breakpoint_unset.svg +26 -0
  21. package/assets/icons/bug.png +0 -0
  22. package/assets/icons/bug.svg +65 -0
  23. package/assets/icons/children.png +0 -0
  24. package/assets/icons/children.svg +38 -0
  25. package/assets/icons/collapse.png +0 -0
  26. package/assets/icons/collapse.svg +38 -0
  27. package/assets/icons/commit.png +0 -0
  28. package/assets/icons/commit.svg +30 -0
  29. package/assets/icons/commit_tree.png +0 -0
  30. package/assets/icons/commit_tree.svg +44 -0
  31. package/assets/icons/effect.png +0 -0
  32. package/assets/icons/effect.svg +28 -0
  33. package/assets/icons/expand.png +0 -0
  34. package/assets/icons/expand.svg +47 -0
  35. package/assets/icons/eye.png +0 -0
  36. package/assets/icons/eye.svg +36 -0
  37. package/assets/icons/eye_closed.png +0 -0
  38. package/assets/icons/eye_closed.svg +32 -0
  39. package/assets/icons/filter.png +0 -0
  40. package/assets/icons/filter.svg +22 -0
  41. package/assets/icons/graph.png +0 -0
  42. package/assets/icons/graph.svg +129 -0
  43. package/assets/icons/history_list.png +0 -0
  44. package/assets/icons/history_list.svg +60 -0
  45. package/assets/icons/hook.png +0 -0
  46. package/assets/icons/hook.svg +22 -0
  47. package/assets/icons/index.ts +76 -0
  48. package/assets/icons/inspector_panel.png +0 -0
  49. package/assets/icons/inspector_panel.svg +135 -0
  50. package/assets/icons/magnifying_glass.png +0 -0
  51. package/assets/icons/magnifying_glass.svg +39 -0
  52. package/assets/icons/pause.png +0 -0
  53. package/assets/icons/pause.svg +34 -0
  54. package/assets/icons/play.png +0 -0
  55. package/assets/icons/play.svg +23 -0
  56. package/assets/icons/reload.png +0 -0
  57. package/assets/icons/reload.svg +39 -0
  58. package/assets/icons/selection.png +0 -0
  59. package/assets/icons/selection.svg +22 -0
  60. package/assets/icons/side_effects.png +0 -0
  61. package/assets/icons/side_effects.svg +48 -0
  62. package/assets/icons/skip.png +0 -0
  63. package/assets/icons/skip.svg +22 -0
  64. package/assets/icons/source.svg +1366 -0
  65. package/assets/icons/step.png +0 -0
  66. package/assets/icons/step.svg +51 -0
  67. package/assets/icons/step_over_children.png +0 -0
  68. package/assets/icons/step_over_children.svg +57 -0
  69. package/assets/icons/thread.png +0 -0
  70. package/assets/icons/thread.svg +32 -0
  71. package/assets/icons/tree_column.png +0 -0
  72. package/assets/icons/tree_column.svg +26 -0
  73. package/assets/icons/tree_end.png +0 -0
  74. package/assets/icons/tree_end.svg +35 -0
  75. package/assets/icons/tree_junction.png +0 -0
  76. package/assets/icons/tree_junction.svg +35 -0
  77. package/assets/icons/vertical_grab_handle.png +0 -0
  78. package/assets/icons/vertical_grab_handle.svg +36 -0
  79. package/components/BreakpointPanel.ts +110 -0
  80. package/components/BreakpointToggle.ts +14 -0
  81. package/components/Button.ts +53 -0
  82. package/components/Checkbox.ts +17 -0
  83. package/components/CommitInspector.ts +161 -0
  84. package/components/CommitTree.ts +295 -0
  85. package/components/ControlBar.ts +60 -0
  86. package/components/EffectTable.ts +125 -0
  87. package/components/FiltersPanel.ts +45 -0
  88. package/components/HistoryList.ts +0 -0
  89. package/components/InspectorPanel.ts +54 -0
  90. package/components/PlaybackBar.ts +42 -0
  91. package/components/ThreadInspector.ts +26 -0
  92. package/components/index.module.css +414 -0
  93. package/lib/controller.ts +222 -0
  94. package/lib/list.ts +82 -0
  95. package/lib/selection.ts +32 -0
  96. package/package.json +4 -4
  97. package/public/icons/icons.svg +520 -0
  98. package/public/icons/icons__breakpoint.png +0 -0
  99. package/public/icons/icons__breakpoint.svg +30 -0
  100. package/public/icons/icons__breakpoint_panel.png +0 -0
  101. package/public/icons/icons__breakpoint_panel.svg +121 -0
  102. package/public/icons/icons__children.png +0 -0
  103. package/public/icons/icons__children.svg +38 -0
  104. package/public/icons/icons__eye.png +0 -0
  105. package/public/icons/icons__eye.svg +36 -0
  106. package/public/icons/icons__inspector_panel.png +0 -0
  107. package/public/icons/icons__inspector_panel.svg +131 -0
  108. package/public/icons/icons__magnifying_glass.png +0 -0
  109. package/public/icons/icons__magnifying_glass.svg +39 -0
  110. package/public/icons/icons__pause.png +0 -0
  111. package/public/icons/icons__pause.svg +34 -0
  112. package/public/icons/icons__play.png +0 -0
  113. package/public/icons/icons__play.svg +23 -0
  114. package/public/icons/icons__skip.png +0 -0
  115. package/public/icons/icons__skip.svg +22 -0
  116. package/public/icons/icons__step.png +0 -0
  117. package/public/icons/icons__step.svg +51 -0
  118. package/utils.ts +78 -11
  119. package/InsightApp.ts +0 -365
  120. package/MenuBar.module.css +0 -18
  121. package/MenuBar.ts +0 -39
  122. package/ScheduleControls.ts +0 -61
  123. package/lookup.ts +0 -185
@@ -0,0 +1,414 @@
1
+ .controlBar {
2
+ display: flex;
3
+ flex-direction: row;
4
+
5
+ background-color: rgb(236, 236, 236);
6
+ border-bottom: 1px solid rgba(110, 110, 110, 0.473);
7
+ }
8
+
9
+ .toggle {
10
+ display: flex;
11
+ flex-direction: row;
12
+
13
+ background: rgba(240, 240, 240, 0);
14
+ border: none;
15
+ border-radius: 4px;
16
+ padding: 2px;
17
+ margin: 4px;
18
+ transition: background 0.1s, opacity 0.1s;
19
+ }
20
+ .toggle.off {
21
+ opacity: 0.5;
22
+ }
23
+ .toggle:hover {
24
+ opacity: 1;
25
+ background: rgba(255, 255, 255, 0.466);
26
+ }
27
+ .toggle:disabled {
28
+ opacity: 1;
29
+ background: rgb(255, 255, 255);
30
+ }
31
+
32
+ .controlBar > .breakpointToggle {
33
+ position: absolute;
34
+ left: 0;
35
+
36
+ }
37
+
38
+ .controlBar > .inspectorToggle {
39
+ position: absolute;
40
+ right: 0;
41
+ }
42
+
43
+ .controlBar > .windowToggle {
44
+ display: flex;
45
+ flex-direction: row;
46
+ margin: 0 auto;
47
+ }
48
+
49
+ .controlBar > .windowToggle > button {
50
+
51
+ }
52
+
53
+ .panel {
54
+ padding: 4px;
55
+
56
+ overflow: auto;
57
+
58
+ display: flex;
59
+ flex-direction: column;
60
+ }
61
+
62
+ .panelContainer {
63
+ width: 300px;
64
+ position: relative;
65
+
66
+ border-right: 1px solid rgba(110, 110, 110, 0.473);
67
+ border-left: 1px solid rgba(110, 110, 110, 0.473);
68
+
69
+ display: flex;
70
+ flex-direction: column;
71
+ }
72
+
73
+ .namedBreakpointList {
74
+ display: flex;
75
+ flex-direction: column;
76
+ }
77
+
78
+ .hit {
79
+ background: red;
80
+ }
81
+
82
+ .playbackBar {
83
+ position: absolute;
84
+ bottom: 120px;
85
+ left: 50%;
86
+ transform: translateX(-50%);
87
+ }
88
+
89
+ .playbackBar > menu {
90
+ margin: 0;
91
+
92
+ padding: 4px;
93
+ background-color: rgb(134, 78, 66);
94
+ border-radius: 6px;
95
+
96
+ gap: 4px;
97
+ display: flex;
98
+ flex-direction: row;
99
+ }
100
+ .breakLocation {
101
+ position: absolute;
102
+ bottom: -58px;
103
+
104
+ color: white;
105
+ background-color: rgb(200, 61, 29);
106
+ padding: 4px;
107
+ border-radius: 6px;
108
+
109
+ white-space: nowrap;
110
+ display: flex;
111
+ flex-direction: row;
112
+ gap: 8px;
113
+
114
+ transform: translateX(-50%);
115
+ left: 50%;
116
+ align-items: center;
117
+ }
118
+
119
+ .playbackButton {
120
+ background: white;
121
+ border: none;
122
+ border-radius: 4px;
123
+
124
+ cursor: pointer;
125
+ transition: transform 0.1s;
126
+ }
127
+
128
+ .playbackButton img {
129
+ width: 24px;
130
+ height: 24px;
131
+ }
132
+
133
+ .playbackButton:hover {
134
+ transform: scale(1.1);
135
+ }
136
+ .playbackButton:active {
137
+ transform: scale(0.9);
138
+ }
139
+
140
+ .commitRowBackground {
141
+ position: absolute;
142
+ height: 33px;
143
+ background-color: #ffffff;
144
+ }
145
+
146
+ .commitRow:nth-child(odd) > .commitRowBackground {
147
+ background-color: #f4f4f4;
148
+ }
149
+
150
+ .commitRow > img {
151
+ position: absolute;
152
+ height: 33px;
153
+ width: 33px;
154
+ }
155
+
156
+ .commitRowPreviewContainer {
157
+ height: 33px;
158
+ position: relative;
159
+ display: flex;
160
+ align-items: center;
161
+ }
162
+
163
+ .commitRowControls {
164
+ display: inline-flex;
165
+ height: 33px;
166
+ align-items: center;
167
+ }
168
+
169
+ .commitRowBreakpointToggle {
170
+ border: none;
171
+ padding: 0;
172
+ margin: 0;
173
+ background: none;
174
+
175
+ cursor: pointer;
176
+ transition: transform 0.1s, opacity 0.2s;
177
+ opacity: 1;
178
+ }
179
+ .commitRowBreakpointToggle:hover {
180
+ transform: scale(1.2);
181
+ opacity: 1;
182
+ }
183
+
184
+ .commitRowBreakpointToggle > img {
185
+ height: 22px;
186
+ }
187
+
188
+ .iconButton {
189
+ border: 0;
190
+ border-radius: 4px;
191
+ background: white;
192
+ border: 1px solid rgba(44, 44, 44, 0.26);
193
+ cursor: pointer;
194
+ padding: 4px 6px;
195
+
196
+ display: flex;
197
+ flex-direction: row;
198
+ gap: 2px;
199
+ align-items: center;
200
+ }
201
+ .iconButton:hover {
202
+ opacity: 1;
203
+ border: 1px solid rgba(34, 34, 34, 0.61);
204
+ background: rgb(243, 243, 243);
205
+ }
206
+ .iconButton:active {
207
+ opacity: 1;
208
+ border: 1px solid black;
209
+ }
210
+
211
+ .iconButton > img {
212
+ height: 20px;
213
+ }
214
+
215
+ .commitControlBar {
216
+ display: flex;
217
+ flex-direction: row;
218
+
219
+ margin: 0 auto;
220
+ gap: 4px;
221
+ padding: 8px 16px;
222
+ border-radius: 8px;
223
+ background-color: rgb(231, 231, 231);
224
+ }
225
+
226
+ .commitRowCollapseButton {
227
+ position: absolute;
228
+ left: 0;
229
+ transform: translateX(-100%);
230
+
231
+ background: none;
232
+ }
233
+ .commitRowCollapseButton:hover {
234
+ background: none;
235
+ }
236
+
237
+ .commitInspectorPreviewRow {
238
+ display: flex;
239
+ flex-direction: row;
240
+ justify-content: center;
241
+ }
242
+
243
+ .commitInspector {
244
+ display: flex;
245
+ flex-direction: column;
246
+
247
+ gap: 8px;
248
+ }
249
+
250
+ .commitTree {
251
+ position: relative;
252
+ display: flex;
253
+ flex-direction: column;
254
+ overflow: hidden;
255
+
256
+ margin: 8px;
257
+ }
258
+ .effectTable {
259
+ overflow: auto;
260
+ display: flex;
261
+ flex-direction: column;
262
+ }
263
+
264
+ .selected {
265
+ font-weight: bold;
266
+ }
267
+
268
+ .filterButton {
269
+ position: absolute;
270
+
271
+ right: 8px;
272
+ top: 8px;
273
+ }
274
+ .filterPanel {
275
+ position: absolute;
276
+ top: 42px;
277
+ right: 8px;
278
+
279
+ padding: 8px;
280
+ border-radius: 8px;
281
+
282
+ background: rgb(235, 235, 238);
283
+ backdrop-filter: blur(4px);
284
+
285
+ display: flex;
286
+ flex-direction: column;
287
+ }
288
+
289
+ .commitList, .effectList {
290
+ padding: 0;
291
+ margin: 4px;
292
+ gap: 4px;
293
+ display: flex;
294
+ flex-direction: column;
295
+
296
+ list-style: none;
297
+ }
298
+
299
+ .commitList > li, .effectList > li {
300
+ display: flex;
301
+ }
302
+
303
+ .threadInspector {
304
+ padding: 8px;
305
+ margin: 16px;
306
+ border-radius: 8px;
307
+
308
+ background-color: rgb(243, 205, 183);
309
+ border: 1px solid #f08b7b;
310
+ box-shadow: 0 4px 4px #00000024;
311
+
312
+ display: flex;
313
+ flex-direction: column;
314
+ }
315
+
316
+ .lineageColumn {
317
+ margin: 16px;
318
+ padding: 8px;
319
+ list-style: none;
320
+
321
+ display: flex;
322
+ flex-direction: column;
323
+ align-items: center;
324
+ background-color: #c6f094;
325
+ border: 1px solid #a1d0a1;
326
+ border-radius: 8px;
327
+ box-shadow: 0 4px 4px #00000024;
328
+ }
329
+
330
+ .lineageColumn > li {
331
+
332
+ }
333
+
334
+ .lineageColumn > img {
335
+ height: 20px;
336
+ }
337
+
338
+ .grabHandle {
339
+ position: absolute;
340
+ background: #ddd;
341
+ border: 1px solid #bdbdbd;
342
+ border-radius: 6px;
343
+ cursor: grab;
344
+
345
+ padding: 0;
346
+ user-select: none;
347
+ }
348
+ .grabHandle.left {
349
+ left: 0;
350
+ top: 50%;
351
+ transform: translate(-50%, 50%);
352
+ }
353
+ .grabHandle.right {
354
+ right: -1px;
355
+ top: 50%;
356
+ transform: translate(50%, 50%);
357
+ }
358
+
359
+ .grabHandle > img {
360
+ width: 10px;
361
+ padding: 2px 0px;
362
+ height: 32px;
363
+
364
+ pointer-events: none;
365
+ }
366
+
367
+ .breakpointToggle {
368
+ border: 1px solid transparent;
369
+ padding: 0;
370
+ margin: 0;
371
+ background: none;
372
+
373
+ cursor: pointer;
374
+ transition: transform 0.1s, opacity 0.2s;
375
+ opacity: 1;
376
+ }
377
+ .breakpointToggle:hover {
378
+
379
+ }
380
+
381
+ .effectCleanupTable {
382
+ border-collapse: collapse;
383
+ background: rgb(241, 241, 241);
384
+ }
385
+
386
+ .effectCleanupHeading.effectCleanupHeading {
387
+ margin: 0;
388
+ padding-left: 8px;
389
+ background-color: rgb(59, 59, 59);
390
+ color: white;
391
+ }
392
+
393
+ .effectCleanupTable td {
394
+ border: 1px solid black;
395
+ }
396
+ .effectCleanupTable td > div {
397
+ display: flex;
398
+ flex-direction: row;
399
+
400
+ align-items: center;
401
+ gap: 4px;
402
+ }
403
+ .effectCleanupTable th {
404
+ border: 1px solid black;
405
+ }
406
+ .effectCleanupTable td:first-child {
407
+ border-left: 0;
408
+ }
409
+ .effectCleanupTable td:last-child {
410
+ border-right: 0;
411
+ }
412
+ .effectCleanupTable tr:nth-child(2) {
413
+ background: rgb(224, 224, 224);
414
+ }
@@ -0,0 +1,222 @@
1
+ import { Breakpoints, BreakPosition2, CommitReport, DebugClient, DEFAULT_BREAKPOINTS, EffectCleanupReport, EffectReport, ThreadReport } from "@lukekaalim/act-debug";
2
+ import { CommitListEntry, createCommitList } from "./list";
3
+ import { SelectionManager, SelectionTarget, useSelectionManager } from "./selection";
4
+ import { CommitID } from "@lukekaalim/act-recon";
5
+ import { createContext, useEffect, useMemo, useState } from "@lukekaalim/act";
6
+
7
+ export type InsightWindow =
8
+ | 'commits'
9
+ | 'effects'
10
+ | 'history'
11
+ | 'performance'
12
+
13
+ export type Filters = {
14
+ collapsed: Set<CommitID>,
15
+
16
+ skipComponents: boolean,
17
+ skipPrimitives: boolean,
18
+ skipSpecial: boolean,
19
+ skipNamed: boolean,
20
+ }
21
+
22
+ const DEFAULT_FILTERS: Filters = {
23
+ collapsed: new Set(),
24
+
25
+ skipComponents: false,
26
+ skipPrimitives: false,
27
+ skipSpecial: false,
28
+ skipNamed: false,
29
+ }
30
+
31
+ export const toggleCollapsedCommit = (filters: Filters, collapseToggledCommit: CommitID) => {
32
+ const isCollapsed = filters.collapsed.has(collapseToggledCommit);
33
+
34
+ const nextFilters = { ...filters, collapsed: new Set(filters.collapsed) };
35
+ if (isCollapsed) {
36
+ nextFilters.collapsed.delete(collapseToggledCommit);
37
+ } else {
38
+ nextFilters.collapsed.add(collapseToggledCommit);
39
+ }
40
+ return nextFilters;
41
+ }
42
+
43
+ export type InsightState = {
44
+ paused: boolean,
45
+ breakPosition: BreakPosition2 | null,
46
+
47
+ breakpoints: Breakpoints,
48
+ thread: ThreadReport | null,
49
+ filters: Filters,
50
+ commits: CommitListEntry[],
51
+ effects: EffectReport[],
52
+ cleanups: EffectCleanupReport[],
53
+ client: DebugClient,
54
+
55
+ selection: SelectionManager,
56
+
57
+ activeWindow: InsightWindow,
58
+
59
+ panels: {
60
+ inspector: boolean,
61
+ breakpoints: boolean,
62
+ },
63
+ commitScrollTarget: CommitID | null,
64
+ }
65
+
66
+ export type InsightController = {
67
+ select(newTarget: SelectionTarget): void,
68
+ focus(focusTarget: SelectionTarget): void,
69
+
70
+ changeWindow(nextWindow: InsightWindow): void,
71
+ changeFilters(newFilters: Filters): void,
72
+
73
+ setShowInspectorPanel(showPanel: boolean): void,
74
+ setShowBreakpointPanel(showPanel: boolean): void,
75
+
76
+ onConsumeCommitScrollTarget(): void,
77
+ };
78
+
79
+ export const useInsightManager = (client: DebugClient) => {
80
+ const [commits, setCommits] = useState<CommitListEntry[]>([]);
81
+ const [effects, setEffects] = useState<EffectReport[]>([]);
82
+ const [cleanups, setCleanups] = useState<EffectCleanupReport[]>([]);
83
+ const [thread, setThread] = useState<ThreadReport | null>(null);
84
+
85
+ const selection = useSelectionManager();
86
+
87
+ const [activeWindow, setActiveWindow] = useState<InsightWindow>('commits');
88
+
89
+ const [showBreakpointPanel, setShowBreakpointPanel] = useState(true);
90
+ const [showInspectorPanel, setShowInspectorPanel] = useState(true);
91
+
92
+ const [breakpoints, setBreakpoints] = useState(DEFAULT_BREAKPOINTS);
93
+ const [filters, setFilters] = useState(DEFAULT_FILTERS);
94
+ const [paused, setPaused] = useState(false);
95
+
96
+ const [commitScrollTarget, setScrollTarget] = useState<CommitID | null>(null);
97
+
98
+ const [breakPosition, setBreakPosition] = useState<BreakPosition2 | null>(null);
99
+
100
+ useEffect(() => {
101
+ const skip = (c: CommitReport) => {
102
+ if (filters.skipPrimitives && (c.element.type.type === 'primitive' || c.element.type.type === 'array'))
103
+ return true;
104
+ if (filters.skipComponents && c.element.type.type === 'component')
105
+ return true;
106
+ if (filters.skipNamed && (c.element.type.type === 'string' || c.element.type.type === 'symbol'))
107
+ return true;
108
+ if (filters.skipSpecial && (c.element.type.type === 'special' || c.element.type.type === 'render'))
109
+ return true;
110
+
111
+ //return c.element.type.type !== 'component'
112
+ //return c.element.type.type !== 'string' && c.element.type.type !== 'primitive';
113
+ return false;
114
+ }
115
+ const hide = (c: CommitReport) => {
116
+ if (c.parent && filters.collapsed.has(c.parent)) {
117
+ return true;
118
+ }
119
+ return false;
120
+ //return c.element.type.type === 'primitive';
121
+ }
122
+ const thread = client.getThread();
123
+
124
+ setCommits(createCommitList(client.cache, { skip, hide }));
125
+ setThread(thread);
126
+ setEffects(thread.delta.effects);
127
+ setCleanups(client.cache.getAllCleanups());
128
+ setPaused(thread.paused);
129
+
130
+ const subs = [
131
+ client.onBreak(([thread, position]) => {
132
+ setCommits(createCommitList(client.cache, { skip, hide }));
133
+ setEffects(thread.delta.effects);
134
+ setCleanups([...client.cache.liveCleanups.values()]);
135
+ setThread(thread)
136
+ setBreakPosition(position);
137
+
138
+ const pendingTask = thread.pendingTasks[thread.pendingTasks.length - 1];
139
+ const pendingEffect = thread.effects.tasks[thread.effects.taskIndex];
140
+ if (pendingTask)
141
+ controller.focus({ type: 'commit', id: pendingTask.id });
142
+ else if (pendingEffect)
143
+ controller.focus({ type: 'effect', id: pendingEffect.id });
144
+
145
+ setPaused(true)
146
+ }),
147
+ client.onBreakpointsChange((newBreakpoints) => {
148
+ setBreakpoints(newBreakpoints)
149
+ }),
150
+ client.onFinish((thread) => {
151
+ setCommits(createCommitList(client.cache, { skip, hide }));
152
+ setEffects(thread.delta.effects);
153
+ setPaused(false);
154
+ setThread(thread);
155
+ setBreakPosition(null);
156
+ })
157
+ ];
158
+ () => {
159
+ subs.forEach(sub => sub.cancel());
160
+ }
161
+ }, [client, filters]);
162
+
163
+ const state: InsightState = {
164
+ paused,
165
+ breakPosition,
166
+
167
+ thread,
168
+ breakpoints,
169
+ selection,
170
+ activeWindow,
171
+
172
+ client,
173
+ commits,
174
+ effects,
175
+ cleanups,
176
+
177
+ filters,
178
+ panels: {
179
+ breakpoints: showBreakpointPanel,
180
+ inspector: showInspectorPanel,
181
+ },
182
+ commitScrollTarget
183
+ };
184
+
185
+ // KLUDGE
186
+ useEffect(() => {
187
+ if (selection.target.type === 'commit')
188
+ controller.focus(selection.target);
189
+ }, [selection.target])
190
+
191
+ const controller: InsightController = useMemo(() => ({
192
+ setShowBreakpointPanel,
193
+ setShowInspectorPanel,
194
+ select(newTarget) {
195
+ selection.select(newTarget)
196
+ },
197
+ focus(focusTarget) {
198
+ switch (focusTarget.type) {
199
+ case 'commit':
200
+ setActiveWindow('commits')
201
+ setScrollTarget(focusTarget.id);
202
+ break;
203
+ case 'effect':
204
+ setActiveWindow('effects')
205
+ break;
206
+ }
207
+ },
208
+ onConsumeCommitScrollTarget() {
209
+ setScrollTarget(null);
210
+ },
211
+ changeFilters(newFilters) {
212
+ setFilters(newFilters);
213
+ },
214
+ changeWindow(nextWindow) {
215
+ setActiveWindow(nextWindow);
216
+ },
217
+ }), [client]);
218
+
219
+ return [state, controller] as const;
220
+ };
221
+
222
+ export const InsightControllerContext = createContext(null);
package/lib/list.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { CommitReport, DebugCache } from "@lukekaalim/act-debug";
2
+ import { CommitID } from "@lukekaalim/act-recon";
3
+
4
+ export type CommitTestFunction = (commit: CommitReport) => boolean;
5
+
6
+ export type CommitListEntry = {
7
+ parent: number,
8
+ distance: number,
9
+ id: CommitID,
10
+ children: number[],
11
+ }
12
+
13
+ /**
14
+ * A CommitList is a (possibly transformed) representation
15
+ * of a tree of commits into a linear tree, noting:
16
+ * - "distance" (aka how many parents from root)
17
+ * - "parent" (the index of the parent in this list)
18
+ * - "children" (the indices of the children in this list)
19
+ * - "id" The actual CommitID that this entry represents.
20
+ *
21
+ * Due to transformation rules, commits "parents" and "children"
22
+ * may not map to their real (as CommitReport might describe)
23
+ * relationships.
24
+ *
25
+ * @param cache
26
+ * @param options
27
+ * @returns
28
+ */
29
+ export const createCommitList = (
30
+ cache: DebugCache,
31
+ transform: { skip?: CommitTestFunction, hide?: CommitTestFunction }
32
+ ): CommitListEntry[] => {
33
+ type StackEntry = [id: CommitID, parent: number];
34
+ // We push commits into this stack
35
+ // to pop them later in the `while` loop.
36
+ const stack: StackEntry[] = [...cache.roots].map(id => [id, -1]);
37
+
38
+ const all: CommitListEntry[] = [];
39
+
40
+ while (stack.length > 0) {
41
+ const stack_entry = stack.pop() as StackEntry;
42
+
43
+ const commit = cache.getCommit(stack_entry[0])
44
+ if (!commit)
45
+ continue;
46
+
47
+ const shouldSkip = transform.skip && transform.skip(commit);
48
+ const shouldHide = transform.hide && transform.hide(commit);
49
+
50
+ if (shouldHide)
51
+ continue;
52
+
53
+ let parent = all[stack_entry[1]];
54
+
55
+ if (shouldSkip) {
56
+ // reassign parent
57
+ for (const childId of commit.children.toReversed()) {
58
+ stack.push([childId, stack_entry[1]]);
59
+ }
60
+ continue;
61
+ }
62
+
63
+ const index = all.length;
64
+ all[index] = {
65
+ distance: parent ? parent.distance + 1 : 1,
66
+ id: commit.id,
67
+ parent: stack_entry[1],
68
+ children: [],
69
+ };
70
+
71
+ if (parent) {
72
+ parent.children.push(index);
73
+ }
74
+
75
+ for (const childId of commit.children.toReversed()) {
76
+ stack.push([childId, index]);
77
+ }
78
+
79
+ }
80
+
81
+ return all;
82
+ }