@lukekaalim/act-insight 1.1.0 → 2.0.1
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/CHANGELOG.md +21 -0
- package/InsightApp.module.css +36 -0
- package/InsightApp2.ts +82 -0
- package/TreeViewer.module.css +3 -8
- package/TreeViewer.ts +16 -12
- package/Virtual.ts +29 -11
- package/assets/icons/ancestor_column.png +0 -0
- package/assets/icons/ancestor_column.svg +26 -0
- package/assets/icons/ancestor_skip_row_column.png +0 -0
- package/assets/icons/ancestor_skip_row_column.svg +52 -0
- package/assets/icons/breakpoint.png +0 -0
- package/assets/icons/breakpoint.svg +33 -0
- package/assets/icons/breakpoint_arrow.png +0 -0
- package/assets/icons/breakpoint_arrow.svg +22 -0
- package/assets/icons/breakpoint_arrow_paused.png +0 -0
- package/assets/icons/breakpoint_arrow_paused.svg +38 -0
- package/assets/icons/breakpoint_panel.png +0 -0
- package/assets/icons/breakpoint_panel.svg +121 -0
- package/assets/icons/breakpoint_unset.png +0 -0
- package/assets/icons/breakpoint_unset.svg +26 -0
- package/assets/icons/bug.png +0 -0
- package/assets/icons/bug.svg +65 -0
- package/assets/icons/children.png +0 -0
- package/assets/icons/children.svg +38 -0
- package/assets/icons/collapse.png +0 -0
- package/assets/icons/collapse.svg +38 -0
- package/assets/icons/commit.png +0 -0
- package/assets/icons/commit.svg +30 -0
- package/assets/icons/commit_tree.png +0 -0
- package/assets/icons/commit_tree.svg +44 -0
- package/assets/icons/effect.png +0 -0
- package/assets/icons/effect.svg +28 -0
- package/assets/icons/expand.png +0 -0
- package/assets/icons/expand.svg +47 -0
- package/assets/icons/eye.png +0 -0
- package/assets/icons/eye.svg +36 -0
- package/assets/icons/eye_closed.png +0 -0
- package/assets/icons/eye_closed.svg +32 -0
- package/assets/icons/filter.png +0 -0
- package/assets/icons/filter.svg +22 -0
- package/assets/icons/graph.png +0 -0
- package/assets/icons/graph.svg +129 -0
- package/assets/icons/history_list.png +0 -0
- package/assets/icons/history_list.svg +60 -0
- package/assets/icons/hook.png +0 -0
- package/assets/icons/hook.svg +22 -0
- package/assets/icons/index.ts +76 -0
- package/assets/icons/inspector_panel.png +0 -0
- package/assets/icons/inspector_panel.svg +135 -0
- package/assets/icons/magnifying_glass.png +0 -0
- package/assets/icons/magnifying_glass.svg +39 -0
- package/assets/icons/pause.png +0 -0
- package/assets/icons/pause.svg +34 -0
- package/assets/icons/play.png +0 -0
- package/assets/icons/play.svg +23 -0
- package/assets/icons/reload.png +0 -0
- package/assets/icons/reload.svg +39 -0
- package/assets/icons/selection.png +0 -0
- package/assets/icons/selection.svg +22 -0
- package/assets/icons/side_effects.png +0 -0
- package/assets/icons/side_effects.svg +48 -0
- package/assets/icons/skip.png +0 -0
- package/assets/icons/skip.svg +22 -0
- package/assets/icons/source.svg +1366 -0
- package/assets/icons/step.png +0 -0
- package/assets/icons/step.svg +51 -0
- package/assets/icons/step_over_children.png +0 -0
- package/assets/icons/step_over_children.svg +57 -0
- package/assets/icons/thread.png +0 -0
- package/assets/icons/thread.svg +32 -0
- package/assets/icons/tree_column.png +0 -0
- package/assets/icons/tree_column.svg +26 -0
- package/assets/icons/tree_end.png +0 -0
- package/assets/icons/tree_end.svg +35 -0
- package/assets/icons/tree_junction.png +0 -0
- package/assets/icons/tree_junction.svg +35 -0
- package/assets/icons/vertical_grab_handle.png +0 -0
- package/assets/icons/vertical_grab_handle.svg +36 -0
- package/components/BreakpointPanel.ts +110 -0
- package/components/BreakpointToggle.ts +14 -0
- package/components/Button.ts +53 -0
- package/components/Checkbox.ts +17 -0
- package/components/CommitInspector.ts +161 -0
- package/components/CommitTree.ts +295 -0
- package/components/ControlBar.ts +60 -0
- package/components/EffectTable.ts +125 -0
- package/components/FiltersPanel.ts +45 -0
- package/components/HistoryList.ts +0 -0
- package/components/InspectorPanel.ts +54 -0
- package/components/PlaybackBar.ts +42 -0
- package/components/ThreadInspector.ts +26 -0
- package/components/index.module.css +414 -0
- package/lib/controller.ts +222 -0
- package/lib/list.ts +82 -0
- package/lib/selection.ts +32 -0
- package/package.json +4 -4
- package/public/icons/icons.svg +520 -0
- package/public/icons/icons__breakpoint.png +0 -0
- package/public/icons/icons__breakpoint.svg +30 -0
- package/public/icons/icons__breakpoint_panel.png +0 -0
- package/public/icons/icons__breakpoint_panel.svg +121 -0
- package/public/icons/icons__children.png +0 -0
- package/public/icons/icons__children.svg +38 -0
- package/public/icons/icons__eye.png +0 -0
- package/public/icons/icons__eye.svg +36 -0
- package/public/icons/icons__inspector_panel.png +0 -0
- package/public/icons/icons__inspector_panel.svg +131 -0
- package/public/icons/icons__magnifying_glass.png +0 -0
- package/public/icons/icons__magnifying_glass.svg +39 -0
- package/public/icons/icons__pause.png +0 -0
- package/public/icons/icons__pause.svg +34 -0
- package/public/icons/icons__play.png +0 -0
- package/public/icons/icons__play.svg +23 -0
- package/public/icons/icons__skip.png +0 -0
- package/public/icons/icons__skip.svg +22 -0
- package/public/icons/icons__step.png +0 -0
- package/public/icons/icons__step.svg +51 -0
- package/utils.ts +71 -7
- package/InsightApp.ts +0 -404
- package/MenuBar.module.css +0 -18
- package/MenuBar.ts +0 -39
- package/ScheduleControls.ts +0 -61
- package/lookup.ts +0 -185
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { Component, h, useEffect, useMemo, useRef, useState } from "@lukekaalim/act";
|
|
2
|
+
import { CommitReport, DebugCache, DebugClient, ThreadReport } from "@lukekaalim/act-debug";
|
|
3
|
+
|
|
4
|
+
import { CommitPreview } from "../TreeViewer";
|
|
5
|
+
import { Virtual1D } from "../Virtual";
|
|
6
|
+
import { useSelection } from "../lib/selection";
|
|
7
|
+
import { CommitListEntry } from "../lib/list";
|
|
8
|
+
|
|
9
|
+
import classes from './index.module.css';
|
|
10
|
+
|
|
11
|
+
import treeColumnURL from '../assets/icons/tree_column.svg'
|
|
12
|
+
import treeJunctionURL from '../assets/icons/tree_junction.svg'
|
|
13
|
+
import treeEndURL from '../assets/icons/tree_end.svg'
|
|
14
|
+
|
|
15
|
+
import breakpointURL from '../assets/icons/breakpoint.svg'
|
|
16
|
+
import breakpointUnsetURL from '../assets/icons/breakpoint_unset.svg'
|
|
17
|
+
import { IconButton } from "./Button";
|
|
18
|
+
import { InsightController, InsightState, toggleCollapsedCommit } from "../lib/controller";
|
|
19
|
+
import { FiltersPanel } from "./FiltersPanel";
|
|
20
|
+
import { CommitID } from "@lukekaalim/act-recon";
|
|
21
|
+
import { icons } from "../assets/icons";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export type CommitTreeProps = {
|
|
25
|
+
state: InsightState,
|
|
26
|
+
controller: InsightController,
|
|
27
|
+
|
|
28
|
+
commits: CommitListEntry[],
|
|
29
|
+
client: DebugClient,
|
|
30
|
+
thread: ThreadReport | null,
|
|
31
|
+
|
|
32
|
+
scrollTarget: CommitID | null,
|
|
33
|
+
onScrollTargetComplete(): void,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const COMMIT_VIEW_HEIGHT_PX = 33;
|
|
37
|
+
const CHUNK_COMMIT_COUNT = 8;
|
|
38
|
+
const CHUNK_HEIGHT_PX = COMMIT_VIEW_HEIGHT_PX * CHUNK_COMMIT_COUNT;
|
|
39
|
+
|
|
40
|
+
type CommitRowProps = {
|
|
41
|
+
state: InsightState,
|
|
42
|
+
controller: InsightController,
|
|
43
|
+
|
|
44
|
+
commit: CommitReport,
|
|
45
|
+
|
|
46
|
+
index: number,
|
|
47
|
+
list: CommitListEntry[],
|
|
48
|
+
|
|
49
|
+
client: DebugClient,
|
|
50
|
+
thread: null | ThreadReport,
|
|
51
|
+
|
|
52
|
+
width: number,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const CommitRow: Component<CommitRowProps> = ({ width, thread, commit, client, index, list, state, controller }) => {
|
|
56
|
+
const ancestors = buildAncestorList(index, list);
|
|
57
|
+
|
|
58
|
+
const entry = list[index]
|
|
59
|
+
const distance = entry.distance;
|
|
60
|
+
|
|
61
|
+
const selection = useSelection()
|
|
62
|
+
|
|
63
|
+
const onClick = () => {
|
|
64
|
+
selection.select({ type: 'commit', id: commit.id })
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const [focused, setFocused] = useState(false);
|
|
68
|
+
|
|
69
|
+
const collapsed = state.filters.collapsed.has(commit.id);
|
|
70
|
+
const onToggleCollapse = () => {
|
|
71
|
+
controller.changeFilters(toggleCollapsedCommit(state.filters, commit.id))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const onMouseEnter = () => {
|
|
75
|
+
setFocused(true)
|
|
76
|
+
};
|
|
77
|
+
const onMouseLeave = () => {
|
|
78
|
+
setFocused(false)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const selected = selection.target.type === 'commit' && selection.target.id === commit.id;
|
|
82
|
+
|
|
83
|
+
const border = getCommitBorder(commit, client.cache, thread);
|
|
84
|
+
const color = getCommitColor(commit, client.cache, thread);
|
|
85
|
+
|
|
86
|
+
const breakpointSet = client.breakpoints.commits.has(commit.id)
|
|
87
|
+
const onClickBreakpointToggle = () => {
|
|
88
|
+
const next = { ...client.breakpoints, commits: new Set(client.breakpoints.commits) };
|
|
89
|
+
if (breakpointSet) {
|
|
90
|
+
next.commits.delete(commit.id)
|
|
91
|
+
} else {
|
|
92
|
+
next.commits.add(commit.id)
|
|
93
|
+
}
|
|
94
|
+
client.setBreakpoints(next);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const hasEffect = thread && thread.delta.effects.some(effect => effect.commit === commit.id);
|
|
98
|
+
|
|
99
|
+
return [
|
|
100
|
+
h('div', { onMouseEnter, onMouseLeave, className: classes.commitRow }, [
|
|
101
|
+
h('div', { className: classes.commitRowBackground, style: { width: width + 'px' } }),
|
|
102
|
+
ancestors.map((ancestorCommitIndex, ancestorIndex) => {
|
|
103
|
+
const childCommitIndex = ancestors[ancestorIndex + 1];
|
|
104
|
+
const ancestor = list[ancestorCommitIndex];
|
|
105
|
+
const child = list[childCommitIndex]
|
|
106
|
+
|
|
107
|
+
const left = ancestorIndex * 32 + 'px';
|
|
108
|
+
const style = { left };
|
|
109
|
+
|
|
110
|
+
if (!child) {
|
|
111
|
+
if (ancestor.children[ancestor.children.length - 1] === index) {
|
|
112
|
+
return h('img', { src: treeEndURL, style });
|
|
113
|
+
} else {
|
|
114
|
+
return h('img', { src: treeJunctionURL, style });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (ancestor.children[ancestor.children.length - 1] === childCommitIndex) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return h('img', { src: treeColumnURL, style });
|
|
122
|
+
}),
|
|
123
|
+
h('div', { className: classes.commitRowPreviewContainer, style: {
|
|
124
|
+
'margin-left': ((distance - 1) * 32) + 'px',
|
|
125
|
+
} }, [
|
|
126
|
+
(focused || collapsed) && commit.children.length > 0 && h(IconButton, {
|
|
127
|
+
icon: collapsed ? 'expand' : 'collapse',
|
|
128
|
+
onClick: onToggleCollapse,
|
|
129
|
+
className: classes.commitRowCollapseButton
|
|
130
|
+
}),
|
|
131
|
+
h(CommitPreview, { color, commit, onClick, border, attributes: [], className: selected && classes.selected || undefined }),
|
|
132
|
+
h('div', { className: classes.commitRowControls }, [
|
|
133
|
+
(focused || breakpointSet) ? h('button', {
|
|
134
|
+
classList: [classes.commitRowBreakpointToggle, !breakpointSet && classes.off],
|
|
135
|
+
onClick: onClickBreakpointToggle
|
|
136
|
+
},
|
|
137
|
+
h('img', { src: breakpointSet ? breakpointURL : breakpointUnsetURL, title: 'Set Breakpoint' }))
|
|
138
|
+
: h('div', { style: { width: '22px' } }),
|
|
139
|
+
hasEffect && h('img', { src: icons.effect, height: 16 }),
|
|
140
|
+
]),
|
|
141
|
+
])
|
|
142
|
+
]),
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
export const getCommitBorder = (commit: CommitReport, cache: DebugCache, thread: ThreadReport | null) => {
|
|
148
|
+
const nextTask = thread && thread.pendingTasks[thread.pendingTasks.length - 1];
|
|
149
|
+
if (nextTask && nextTask.id === commit.id)
|
|
150
|
+
return '2px solid rgb(255, 145, 0)';
|
|
151
|
+
|
|
152
|
+
const state = cache.getCommitState(commit.id);
|
|
153
|
+
if (state === 'mount-task')
|
|
154
|
+
return '2px dashed rgb(26, 123, 234)'
|
|
155
|
+
|
|
156
|
+
if (thread) {
|
|
157
|
+
if (thread.mustRender.includes(commit.id))
|
|
158
|
+
return '2px solid rgb(19, 33, 231)';
|
|
159
|
+
const pendingTask = thread.pendingTasks.find(c => c.id === commit.id)
|
|
160
|
+
if (pendingTask) {
|
|
161
|
+
if (!pendingTask.element)
|
|
162
|
+
return '2px dashed #f25252ff'
|
|
163
|
+
return '2px dashed rgb(255, 145, 0)'
|
|
164
|
+
}
|
|
165
|
+
if (state !== 'live' && thread.visited.includes(commit.id)) {
|
|
166
|
+
return '1px dashed rgb(160, 160, 160)';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return '1px solid #b1b1b1';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const getCommitColor = (commit: CommitReport, cache: DebugCache, thread: ThreadReport | null) => {
|
|
174
|
+
const state = cache.getCommitState(commit.id);
|
|
175
|
+
switch (state) {
|
|
176
|
+
case 'created':
|
|
177
|
+
return '#4bc847ff';
|
|
178
|
+
case 'live':
|
|
179
|
+
if (thread && thread.visited.includes(commit.id)) {
|
|
180
|
+
return '#8b8b8b';
|
|
181
|
+
}
|
|
182
|
+
return '#d8d8d8';
|
|
183
|
+
case 'removed':
|
|
184
|
+
return '#f25252ff';
|
|
185
|
+
case 'updated':
|
|
186
|
+
return '#1ab9eaff';
|
|
187
|
+
case 'mount-task':
|
|
188
|
+
return 'rgb(221, 247, 255)';
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const buildAncestorList = (index: number, list: CommitListEntry[]) => {
|
|
193
|
+
const ancestors: number[] = [];
|
|
194
|
+
let current_index: number = list[index].parent;
|
|
195
|
+
|
|
196
|
+
while (current_index !== -1) {
|
|
197
|
+
ancestors.unshift(current_index);
|
|
198
|
+
const entry = list[current_index];
|
|
199
|
+
current_index = entry.parent;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return ancestors;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const CommitTree: Component<CommitTreeProps> = ({ commits, client, thread, state, controller, scrollTarget, onScrollTargetComplete }) => {
|
|
206
|
+
const viewportRef = useRef<HTMLElement | null>(null);
|
|
207
|
+
|
|
208
|
+
const nextTask = thread && thread.pendingTasks[thread.pendingTasks.length - 1];
|
|
209
|
+
|
|
210
|
+
const renderChunk = (index: number, width: number) => {
|
|
211
|
+
if (index < 0)
|
|
212
|
+
return null;
|
|
213
|
+
|
|
214
|
+
return Array.from({ length: CHUNK_COMMIT_COUNT }).map((_, chunkIndex) => {
|
|
215
|
+
const commitIndex = (index * CHUNK_COMMIT_COUNT) + (chunkIndex);
|
|
216
|
+
const entry = commits[commitIndex];
|
|
217
|
+
const commit = entry && client.cache.getCommit(entry.id);
|
|
218
|
+
|
|
219
|
+
if (!commit)
|
|
220
|
+
return null;
|
|
221
|
+
|
|
222
|
+
return h(CommitRow, {
|
|
223
|
+
width,
|
|
224
|
+
|
|
225
|
+
client,
|
|
226
|
+
commit,
|
|
227
|
+
list: commits,
|
|
228
|
+
index: commitIndex,
|
|
229
|
+
thread,
|
|
230
|
+
state,
|
|
231
|
+
controller,
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/*
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
const viewport = viewportRef.current;
|
|
239
|
+
|
|
240
|
+
if (!nextTask || !viewport)
|
|
241
|
+
return;
|
|
242
|
+
|
|
243
|
+
const rect = viewport.getBoundingClientRect()
|
|
244
|
+
|
|
245
|
+
const index = commits.findIndex(c => c.id === nextTask.id);
|
|
246
|
+
const commit = commits[index];
|
|
247
|
+
if (!commit)
|
|
248
|
+
return console.info(`Failed to scroll to ${index} or ${nextTask.id}`);
|
|
249
|
+
|
|
250
|
+
viewport.scrollTo({
|
|
251
|
+
top: (index * COMMIT_VIEW_HEIGHT_PX) - (rect.height / 2),
|
|
252
|
+
left: ((commit.distance - 1) * 32) - (rect.width / 2),
|
|
253
|
+
behavior: 'smooth'
|
|
254
|
+
});
|
|
255
|
+
}, [nextTask])
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
useEffect(() => {
|
|
259
|
+
const viewport = viewportRef.current;
|
|
260
|
+
if (!scrollTarget || !viewport)
|
|
261
|
+
return;
|
|
262
|
+
|
|
263
|
+
const commit = client.cache.getCommit(scrollTarget);
|
|
264
|
+
if (!commit)
|
|
265
|
+
return;
|
|
266
|
+
|
|
267
|
+
const rect = viewport.getBoundingClientRect()
|
|
268
|
+
const index = commits.findIndex(c => c.id === scrollTarget);
|
|
269
|
+
if (index === -1)
|
|
270
|
+
return;
|
|
271
|
+
const entry = commits[index];
|
|
272
|
+
|
|
273
|
+
viewport.scrollTo({
|
|
274
|
+
top: (index * COMMIT_VIEW_HEIGHT_PX) - (rect.height / 2),
|
|
275
|
+
left: ((entry.distance - 1) * 32) - (rect.width / 2),
|
|
276
|
+
behavior: 'smooth'
|
|
277
|
+
});
|
|
278
|
+
onScrollTargetComplete();
|
|
279
|
+
}, [scrollTarget, onScrollTargetComplete])
|
|
280
|
+
|
|
281
|
+
const [showFilter, setShowFilter] = useState(false);
|
|
282
|
+
|
|
283
|
+
return h('div', { className: classes.commitTree }, [
|
|
284
|
+
h(Virtual1D, {
|
|
285
|
+
viewportRef,
|
|
286
|
+
chunkCount: commits.length / CHUNK_COMMIT_COUNT,
|
|
287
|
+
chunkSize: CHUNK_HEIGHT_PX,
|
|
288
|
+
renderChunk
|
|
289
|
+
}),
|
|
290
|
+
h(IconButton, { className: classes.filterButton, icon: 'filter', title: 'Filters', onClick() {
|
|
291
|
+
setShowFilter(!showFilter)
|
|
292
|
+
}, }),
|
|
293
|
+
showFilter && h(FiltersPanel, { controller, state }),
|
|
294
|
+
])
|
|
295
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Component, h } from "@lukekaalim/act";
|
|
2
|
+
|
|
3
|
+
import classes from './index.module.css';
|
|
4
|
+
import breakpointPanelURL from '../assets/icons/breakpoint_panel.svg';
|
|
5
|
+
import inspectorPanelURL from '../assets/icons/inspector_panel.svg';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import commitTreeURL from '../assets/icons/commit_tree.svg';
|
|
9
|
+
import sideEffectsURL from '../assets/icons/side_effects.svg';
|
|
10
|
+
import historyListURL from '../assets/icons/history_list.svg';
|
|
11
|
+
import graphURL from '../assets/icons/graph.svg';
|
|
12
|
+
import { InsightWindow } from "../lib/controller";
|
|
13
|
+
|
|
14
|
+
let breakpointPanelURL2 = new URL(breakpointPanelURL, document.location.href);
|
|
15
|
+
let inspectorPanelURL2 = new URL(inspectorPanelURL, document.location.href);
|
|
16
|
+
let graphURL2 = new URL(graphURL, document.location.href);
|
|
17
|
+
|
|
18
|
+
export type ControlBarProps = {
|
|
19
|
+
showBreakpointPanel: boolean,
|
|
20
|
+
showInspectorPanel: boolean,
|
|
21
|
+
|
|
22
|
+
activeWindow: InsightWindow,
|
|
23
|
+
onChangeWindow(window: InsightWindow): void,
|
|
24
|
+
|
|
25
|
+
onShowBreakpointPanelChange(show: boolean): void,
|
|
26
|
+
onShowInspectorPanelChange(show: boolean): void,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const ControlBar: Component<ControlBarProps> = ({
|
|
30
|
+
showBreakpointPanel,
|
|
31
|
+
showInspectorPanel,
|
|
32
|
+
activeWindow,
|
|
33
|
+
|
|
34
|
+
onChangeWindow,
|
|
35
|
+
onShowInspectorPanelChange,
|
|
36
|
+
onShowBreakpointPanelChange,
|
|
37
|
+
}) => {
|
|
38
|
+
return h('div', { className: classes.controlBar }, [
|
|
39
|
+
h('button', {
|
|
40
|
+
classList: [classes.toggle, !showBreakpointPanel && classes.off],
|
|
41
|
+
onClick: () => onShowBreakpointPanelChange(!showBreakpointPanel)
|
|
42
|
+
}, h('img', { height: '32', src: breakpointPanelURL2, title: "Toggle Breakpoints" })),
|
|
43
|
+
|
|
44
|
+
h('div', { className: classes.windowToggle }, [
|
|
45
|
+
h('button', { className: classes.toggle, disabled: activeWindow === 'commits', onClick: () => onChangeWindow('commits') },
|
|
46
|
+
h('img', { height: '32', src: commitTreeURL, title: "Commits" })),
|
|
47
|
+
h('button', { className: classes.toggle, disabled: activeWindow === 'effects', onClick: () => onChangeWindow('effects') },
|
|
48
|
+
h('img', { height: '32', src: sideEffectsURL, title: "Effects" })),
|
|
49
|
+
h('button', { className: classes.toggle, disabled: activeWindow === 'history', onClick: () => onChangeWindow('history') },
|
|
50
|
+
h('img', { height: '32', src: historyListURL, title: "History" })),
|
|
51
|
+
h('button', { className: classes.toggle, disabled: activeWindow === 'performance', onClick: () => onChangeWindow('performance') },
|
|
52
|
+
h('img', { height: '32', src: graphURL2, title: "Graph" })),
|
|
53
|
+
]),
|
|
54
|
+
|
|
55
|
+
h('button', {
|
|
56
|
+
classList: [classes.toggle, !showInspectorPanel && classes.off],
|
|
57
|
+
onClick: () => onShowInspectorPanelChange(!showInspectorPanel)
|
|
58
|
+
}, h('img', { height: '32', src: inspectorPanelURL2, title: "Toggle Inspector" })),
|
|
59
|
+
])
|
|
60
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Component, h } from "@lukekaalim/act"
|
|
2
|
+
import { InsightController, InsightState } from "../lib/controller"
|
|
3
|
+
import { CommitPreview } from "../TreeViewer"
|
|
4
|
+
import { BreakpointToggle } from "./BreakpointToggle"
|
|
5
|
+
import { toggleCommitBreakpoint, toggleEffectBreakpoint } from "@lukekaalim/act-debug"
|
|
6
|
+
|
|
7
|
+
import classes from './index.module.css';
|
|
8
|
+
import { CommitButton, EffectButton, HookButton } from "./Button"
|
|
9
|
+
|
|
10
|
+
export type EffectTableProps = {
|
|
11
|
+
state: InsightState
|
|
12
|
+
controller: InsightController,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const EffectTable: Component<EffectTableProps> = ({ state, controller }) => {
|
|
16
|
+
return h('div', { className: classes.effectTable }, [
|
|
17
|
+
h('h4', { className: classes.effectCleanupHeading }, "Live Cleanups"),
|
|
18
|
+
h('table', { className: classes.effectCleanupTable }, [
|
|
19
|
+
h('thead', {}, [
|
|
20
|
+
h('tr', {}, [
|
|
21
|
+
h('th', {}, ''),
|
|
22
|
+
h('th', {}, 'EffectID'),
|
|
23
|
+
h('th', {}, 'Component'),
|
|
24
|
+
h('th', {}, 'Hook'),
|
|
25
|
+
h('th', {}, 'Name'),
|
|
26
|
+
])
|
|
27
|
+
]),
|
|
28
|
+
h('tbody', {}, state.cleanups.map(cleanup => {
|
|
29
|
+
const commit = state.client.cache.getCommit(cleanup.commit);
|
|
30
|
+
if (!commit)
|
|
31
|
+
return h('tr', { row: 4 }, `Error: Missing commit (${cleanup.commit}) in cache`);
|
|
32
|
+
|
|
33
|
+
const details = state.client.getDetails(commit.id);
|
|
34
|
+
const hookIndex = details && details.component && details.component.effects.findIndex(e => e.effect === cleanup.id);
|
|
35
|
+
|
|
36
|
+
const onClick = () => {
|
|
37
|
+
controller.select({ type: 'commit', id: commit.id });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return h('tr', {}, [
|
|
41
|
+
h('td', {}, [
|
|
42
|
+
h(BreakpointToggle, { toggled: state.breakpoints.effects.has(cleanup.id), onToggle() {
|
|
43
|
+
state.client.setBreakpoints(toggleEffectBreakpoint(state.breakpoints, cleanup.id))
|
|
44
|
+
}, }),
|
|
45
|
+
]),
|
|
46
|
+
h('td', {}, [
|
|
47
|
+
h('div', {}, [
|
|
48
|
+
h(EffectButton, { onClick() {}, effectId: cleanup.id })
|
|
49
|
+
])
|
|
50
|
+
]),
|
|
51
|
+
h('td', {}, h(CommitButton, { commitId: commit.id, onClick })),
|
|
52
|
+
h('td', {}, (hookIndex !== null) && h(HookButton, { onClick() {}, hookIndex })),
|
|
53
|
+
h('td', {}, cleanup.functionName || 'Anonymous'),
|
|
54
|
+
])
|
|
55
|
+
}))
|
|
56
|
+
]),
|
|
57
|
+
h('div', { style: { height: '24px' } }, ''),
|
|
58
|
+
state.effects.length > 0 && [
|
|
59
|
+
h('h4', { className: classes.effectCleanupHeading }, "Pending Effects"),
|
|
60
|
+
h('table', { className: classes.effectCleanupTable }, [
|
|
61
|
+
h('thead', {}, [
|
|
62
|
+
h('tr', {}, [
|
|
63
|
+
h('th', {}, ''),
|
|
64
|
+
h('th', {}, 'EffectID'),
|
|
65
|
+
h('th', {}, 'Cleanup'),
|
|
66
|
+
h('th', {}, 'Run'),
|
|
67
|
+
h('th', {}, 'Component'),
|
|
68
|
+
h('th', {}, 'Name'),
|
|
69
|
+
h('th', {}, 'Teardown?'),
|
|
70
|
+
])
|
|
71
|
+
]),
|
|
72
|
+
h('tbody', {}, state.effects.map(effect => {
|
|
73
|
+
const commit = state.client.cache.getCommit(effect.commit);
|
|
74
|
+
if (!commit)
|
|
75
|
+
return h('tr', { row: 4 }, `Error: Missing commit (${effect.commit}) in cache`);
|
|
76
|
+
|
|
77
|
+
if (!effect.effect) {
|
|
78
|
+
// lets see if we can find the original cleanup task
|
|
79
|
+
const cleanup = state.cleanups.find(c => c.id === effect.id)
|
|
80
|
+
if (cleanup)
|
|
81
|
+
effect.functionName = cleanup.functionName;
|
|
82
|
+
}
|
|
83
|
+
const onClick = () => {
|
|
84
|
+
controller.select({ type: 'commit', id: commit.id });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (state.thread) {
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const renderCleanupAndRun = () => {
|
|
91
|
+
if (!state.thread) {
|
|
92
|
+
return [
|
|
93
|
+
h('td'),
|
|
94
|
+
h('td'),
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
const { tasks, taskIndex } = state.thread.effects;
|
|
98
|
+
const cleanupIndex = tasks.findIndex(e => e.type === 'cleanup' && e.id === effect.id)
|
|
99
|
+
const runIndex = tasks.findIndex(e => e.type === 'constructor' && e.id === effect.id)
|
|
100
|
+
|
|
101
|
+
return [
|
|
102
|
+
h('td', { style: { background: taskIndex === cleanupIndex ? '#ce4545' : '' } },
|
|
103
|
+
cleanupIndex !== -1 && h('input', { type: 'checkbox', checked: taskIndex > cleanupIndex, disabled: true })),
|
|
104
|
+
h('td', { style: { background: taskIndex === runIndex ? '#ce4545' : '' } },
|
|
105
|
+
runIndex !== -1 && h('input', { type: 'checkbox', checked: taskIndex > runIndex, disabled: true })),
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return h('tr', {}, [
|
|
110
|
+
h('td', {}, [
|
|
111
|
+
h(BreakpointToggle, { toggled: state.breakpoints.effects.has(effect.id), onToggle() {
|
|
112
|
+
state.client.setBreakpoints(toggleEffectBreakpoint(state.breakpoints, effect.id))
|
|
113
|
+
}, }),
|
|
114
|
+
]),
|
|
115
|
+
h('td', {}, h(EffectButton, { effectId: effect.id, onClick() {} })),
|
|
116
|
+
renderCleanupAndRun(),
|
|
117
|
+
h('td', {}, h(CommitButton, { onClick() { controller.select({ type: 'commit', id: commit.id }) }, commitId: commit.id })),
|
|
118
|
+
h('td', {}, effect.functionName || 'Anonymous'),
|
|
119
|
+
h('td', {}, (!effect.effect).toString()),
|
|
120
|
+
])
|
|
121
|
+
}))
|
|
122
|
+
]),
|
|
123
|
+
]
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Component, h } from "@lukekaalim/act";
|
|
2
|
+
import classes from './index.module.css';
|
|
3
|
+
import { Checkbox } from "./Checkbox";
|
|
4
|
+
import { InsightController, InsightState, toggleCollapsedCommit } from "../lib/controller";
|
|
5
|
+
import { CommitPreview } from "../TreeViewer";
|
|
6
|
+
import { IconButton } from "./Button";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export type FiltersPanelProps = {
|
|
10
|
+
state: InsightState,
|
|
11
|
+
controller: InsightController,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const FiltersPanel: Component<FiltersPanelProps> = ({ state, controller }) => {
|
|
15
|
+
const collapsedList = [...state.filters.collapsed.values()];
|
|
16
|
+
|
|
17
|
+
return h('div', { className: classes.filterPanel }, [
|
|
18
|
+
h(Checkbox, { label: 'Skip Primitive Elements', checked: state.filters.skipPrimitives, onCheckedChange(nextChecked) {
|
|
19
|
+
controller.changeFilters({ ...state.filters, skipPrimitives: nextChecked })
|
|
20
|
+
}, }),
|
|
21
|
+
h(Checkbox, { label: 'Skip Components', checked: state.filters.skipComponents, onCheckedChange(nextChecked) {
|
|
22
|
+
controller.changeFilters({ ...state.filters, skipComponents: nextChecked })
|
|
23
|
+
}, }),
|
|
24
|
+
h(Checkbox, { label: 'Skip Special Elements', checked: state.filters.skipSpecial, onCheckedChange(nextChecked) {
|
|
25
|
+
controller.changeFilters({ ...state.filters, skipSpecial: nextChecked })
|
|
26
|
+
}, }),
|
|
27
|
+
h(Checkbox, { label: 'Skip Named Elements', checked: state.filters.skipNamed, onCheckedChange(nextChecked) {
|
|
28
|
+
controller.changeFilters({ ...state.filters, skipNamed: nextChecked })
|
|
29
|
+
}, }),
|
|
30
|
+
collapsedList.length > 0 && [
|
|
31
|
+
h('h4', {}, 'Collapsed Commits'),
|
|
32
|
+
h('ul', { className: classes.commitList }, collapsedList.map(collapsed => {
|
|
33
|
+
const commit = state.client.cache.getCommit(collapsed);
|
|
34
|
+
if (!commit)
|
|
35
|
+
return null;
|
|
36
|
+
const onExpandClick = () => {
|
|
37
|
+
controller.changeFilters(toggleCollapsedCommit(state.filters, collapsed))
|
|
38
|
+
}
|
|
39
|
+
return h('li', {}, [h(IconButton, { icon: 'expand', title: 'Expand', onClick: onExpandClick }), h(CommitPreview, { commit, onClick: () => {
|
|
40
|
+
controller.select({ type: 'commit', id: collapsed })
|
|
41
|
+
} })])
|
|
42
|
+
}))
|
|
43
|
+
]
|
|
44
|
+
]);
|
|
45
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Component, h, useState } from '@lukekaalim/act';
|
|
2
|
+
import classes from './index.module.css';
|
|
3
|
+
import { Breakpoints, DebugClient } from '@lukekaalim/act-debug';
|
|
4
|
+
import { CommitPreview } from '../TreeViewer';
|
|
5
|
+
import { useSelection } from '../lib/selection';
|
|
6
|
+
import { CommitInspector } from './CommitInspector';
|
|
7
|
+
import { InsightController, InsightState } from '../lib/controller';
|
|
8
|
+
import { ThreadInspector } from './ThreadInspector';
|
|
9
|
+
import { icons } from '../assets/icons';
|
|
10
|
+
|
|
11
|
+
export type InspectorPanelProps = {
|
|
12
|
+
client: DebugClient,
|
|
13
|
+
breakpoints: Breakpoints,
|
|
14
|
+
|
|
15
|
+
state: InsightState,
|
|
16
|
+
controller: InsightController,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const InspectorPanel: Component<InspectorPanelProps> = ({ client, breakpoints, state, controller }) => {
|
|
20
|
+
const { target } = useSelection();
|
|
21
|
+
|
|
22
|
+
const commit = target.type === 'commit' && client.cache.getCommit(target.id)
|
|
23
|
+
|
|
24
|
+
const [width, setWidth] = useState(300);
|
|
25
|
+
const [dragging, setDragging] = useState(false);
|
|
26
|
+
const onPointerDown = (event: PointerEvent) => {
|
|
27
|
+
setDragging(true);
|
|
28
|
+
(event.target as HTMLButtonElement).setPointerCapture(event.pointerId);
|
|
29
|
+
}
|
|
30
|
+
const onPointerMove = (event: PointerEvent) => {
|
|
31
|
+
if (dragging) {
|
|
32
|
+
setWidth(w => w - event.movementX)
|
|
33
|
+
// do something about moving it
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const onPointerUp = (event: PointerEvent) => {
|
|
37
|
+
setDragging(false);
|
|
38
|
+
(event.target as HTMLButtonElement).releasePointerCapture(event.pointerId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return h('div', { className: classes.panelContainer, style: { width: `${width}px` } }, [
|
|
42
|
+
h('button', { classList: [classes.grabHandle, classes.left], onPointerDown, onPointerMove, onPointerUp }, h('img', { src: icons.vertical_grab_handle })),
|
|
43
|
+
h('div', { className: classes.panel }, [
|
|
44
|
+
commit && state.thread && h(ThreadInspector, { thread: state.thread, commit }),
|
|
45
|
+
commit && h(CommitInspector, {
|
|
46
|
+
commit,
|
|
47
|
+
breakpoints,
|
|
48
|
+
client,
|
|
49
|
+
state,
|
|
50
|
+
controller,
|
|
51
|
+
})
|
|
52
|
+
])
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Component, h, Node } from "@lukekaalim/act"
|
|
2
|
+
import classes from './index.module.css';
|
|
3
|
+
|
|
4
|
+
import stepURL from '../assets/icons/step.svg';
|
|
5
|
+
import playURL from '../assets/icons/play.svg';
|
|
6
|
+
import breakpointActiveURL from '../assets/icons/breakpoint_arrow.svg';
|
|
7
|
+
import breakpointInactiveURL from '../assets/icons/breakpoint_arrow_paused.svg';
|
|
8
|
+
import reloadURL from '../assets/icons/reload.svg';
|
|
9
|
+
import { IconButton } from "./Button";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
export type PlaybackBarProps = {
|
|
13
|
+
onStepClick(): void,
|
|
14
|
+
onResumeClick(): void,
|
|
15
|
+
onReloadClick(): void,
|
|
16
|
+
|
|
17
|
+
breakpointsEnabled: boolean,
|
|
18
|
+
onToggleBreakpointsEnabled(breakpointsEnabled: boolean): void,
|
|
19
|
+
|
|
20
|
+
currentBreakLocation: null | Node,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const PlaybackBar: Component<PlaybackBarProps> = ({
|
|
24
|
+
onStepClick, onReloadClick, onResumeClick,
|
|
25
|
+
|
|
26
|
+
currentBreakLocation,
|
|
27
|
+
onToggleBreakpointsEnabled,
|
|
28
|
+
|
|
29
|
+
breakpointsEnabled
|
|
30
|
+
}) => {
|
|
31
|
+
return h('div', { className: classes.playbackBar }, [
|
|
32
|
+
h('menu', {}, [
|
|
33
|
+
h(IconButton, { icon: 'step', onClick: onStepClick }),
|
|
34
|
+
//h(IconButton, { icon: 'step_over_children', onClick() {} }),
|
|
35
|
+
h(IconButton, { icon: 'play', onClick: onResumeClick }),
|
|
36
|
+
|
|
37
|
+
]),
|
|
38
|
+
currentBreakLocation && h('div', { className: classes.breakLocation },
|
|
39
|
+
currentBreakLocation
|
|
40
|
+
)
|
|
41
|
+
])
|
|
42
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Component, h } from "@lukekaalim/act"
|
|
2
|
+
import { CommitReport, ThreadReport } from "@lukekaalim/act-debug"
|
|
3
|
+
|
|
4
|
+
import classes from './index.module.css';
|
|
5
|
+
|
|
6
|
+
export type ThreadInspectorProps = {
|
|
7
|
+
thread: ThreadReport,
|
|
8
|
+
commit: CommitReport,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ThreadInspector: Component<ThreadInspectorProps> = ({ thread, commit }) => {
|
|
12
|
+
const task = thread.pendingTasks.find(t => t.id === commit.id)
|
|
13
|
+
|
|
14
|
+
return h('div', { className: classes.threadInspector }, [
|
|
15
|
+
h('div', {}, `Thread#${thread.id}`),
|
|
16
|
+
!!task && (
|
|
17
|
+
task.element ? (task.prev ? h('div', {}, `Schedule for update.`)
|
|
18
|
+
: h('div', {}, `Scheduled for creation`))
|
|
19
|
+
: h('div', {}, `Scheduled for deletion`)
|
|
20
|
+
),
|
|
21
|
+
thread.visited.includes(commit.id) && h('div', {}, `Was visited in this Pass`),
|
|
22
|
+
thread.mustRender.includes(commit.id) && h('div', {}, `Is a Render Target`),
|
|
23
|
+
thread.mustVisit.includes(commit.id) && h('div', {}, `Is a Visit Target`),
|
|
24
|
+
thread.missed.includes(commit.id) && h('div', {}, `Is missed on the current Pass`),
|
|
25
|
+
])
|
|
26
|
+
}
|