@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,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
+ }