@lukekaalim/act-insight 0.0.2-alpha.6 → 1.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @lukekaalim/act-insight
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bcbd299: SSR API for @lukekaalim/act-web
8
+ - beec21c: Fixed Web text element rehydration, remove nodejs dependencies from dehydration
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies [bcbd299]
13
+ - Updated dependencies [beec21c]
14
+ - @lukekaalim/act-web@5.0.0
15
+ - @lukekaalim/act-recon@3.1.0
16
+ - @lukekaalim/act@4.1.0
17
+ - @lukekaalim/act-debug@1.0.1
18
+
19
+ ## 1.0.0
20
+
21
+ ### Major Changes
22
+
23
+ - afd247e: Another major refactor! So everything is broken. Good luck!
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [6658c01]
28
+ - Updated dependencies [bd0a076]
29
+ - Updated dependencies [fdf1557]
30
+ - Updated dependencies [7597a8f]
31
+ - Updated dependencies [ccb3900]
32
+ - Updated dependencies [afd247e]
33
+ - Updated dependencies [2984273]
34
+ - Updated dependencies [b3f6c49]
35
+ - Updated dependencies [c5e8775]
36
+ - @lukekaalim/act-web@4.0.0
37
+ - @lukekaalim/act-recon@3.0.0
38
+ - @lukekaalim/act@4.0.0
39
+ - @lukekaalim/act-debug@1.0.0
40
+
3
41
  ## 0.0.2-alpha.6
4
42
 
5
43
  ### Patch Changes
package/InsightApp.ts CHANGED
@@ -1,209 +1,404 @@
1
- import { Component, Element, h, useEffect, useMemo, useRef, useState } from '@lukekaalim/act';
2
- import { Commit, CommitID, CommitTree, DeltaSet, Reconciler, Update, WorkThread } from '@lukekaalim/act-recon';
3
- import { hs } from '@lukekaalim/act-web';
4
-
1
+ import { Component, h, useEffect, useMemo, useRef, useState } from '@lukekaalim/act';
2
+ import { CommitDetailsReport, CommitReport, DeltaReport, ReconcilerDebugController, ReconcilerDebugEventBus, ThreadReport, updateTreeReport, ValueReport, WorkTaskReport } from '@lukekaalim/act-debug';
3
+ import { CommitID } from '@lukekaalim/act-recon';
5
4
  import { CommitPreview, TreeViewer } from './TreeViewer';
6
-
7
- import { debounce } from 'lodash-es'
8
- import { getElementName } from './utils';
9
- import { CommitViewer } from './CommitViewer';
10
- import classes from './InsightApp.module.css';
11
- import { InsightMode } from './mode';
12
- import { MenuBar } from './MenuBar';
13
- import { ThreadViewer } from './ThreadViewer';
14
- import { DebuggerServer } from '@lukekaalim/act-debug';
15
- import { CommitReport, CommitStateReport, TreeReport, updateTreeReport } from '@lukekaalim/act-debug/report';
5
+ import { ScheduleControls } from './ScheduleControls';
6
+ import { CommitLookupCache, ThreadLookupCache } from './lookup';
7
+ import { Virtual1D } from './Virtual';
8
+ import { CommitAttributeTag } from './AttributeTag';
16
9
 
17
10
  export type InsightAppProps = {
18
- server: DebuggerServer,
11
+ controller: ReconcilerDebugController,
12
+ bus: ReconcilerDebugEventBus,
13
+
14
+ document: Document,
15
+
16
+ onReady(): void,
17
+ };
18
+
19
+ export type InsightAppState = {
20
+ breakOnAfterUpdate: boolean,
21
+ breakOnBeforeUpdate: boolean,
22
+
23
+ commitBreakpoints: Set<CommitID>,
24
+
25
+ paused: boolean,
19
26
  }
20
27
 
21
- export const InsightApp2: Component<InsightAppProps> = ({ server }) => {
22
- const [tree, setTree] = useState<TreeReport>({ commits: new Map(), roots: [] });
23
- const [commitId, setCommitId] = useState<CommitID | null>(null);
24
- const [commitState, setCommitState] = useState<CommitStateReport | null>(null);
28
+ const INSIGHT_SETTINGS_LOCALSTORAGE_KEY = `INSIGHT_SETTINGS`;
29
+
30
+ export const InsightApp: Component<InsightAppProps> = ({ onReady, controller, bus, document = window.document }) => {
31
+ const [c, setRenderCounter] = useState(0);
32
+
33
+ const storedState = useMemo(() => {
34
+ const settings = window.localStorage.getItem(INSIGHT_SETTINGS_LOCALSTORAGE_KEY)
35
+ if (!settings)
36
+ return;
37
+ return JSON.parse(settings) as { breakOnAfterUpdate: boolean, breakOnBeforeUpdate: boolean };
38
+ }, [])
25
39
 
40
+ const [insightState, setInsightState] = useState<InsightAppState>({
41
+ commitBreakpoints: new Set(),
42
+ breakOnAfterUpdate: storedState ? storedState.breakOnAfterUpdate : false,
43
+ breakOnBeforeUpdate: storedState ? storedState.breakOnBeforeUpdate : false,
44
+ paused: false,
45
+ });
26
46
  useEffect(() => {
27
- server.subscribe((event) => {
28
- switch (event.type) {
29
- case 'thread:finish':
30
- setTree(tree => {
31
- return updateTreeReport(tree, event.thread);
32
- });
33
- break;
34
- case 'tree:root-update':
35
- setTree(tree => {
36
- return { ...tree, roots: event.roots };
37
- });
38
- break;
39
- case 'commit-state:response':
40
- setCommitState(event.report);
41
- setCommitId(event.commitId);
42
- break;
43
- }
44
- })
45
- server.ready();
46
- return;
47
- }, [server])
47
+ window.localStorage.setItem(INSIGHT_SETTINGS_LOCALSTORAGE_KEY, JSON.stringify({
48
+ breakOnAfterUpdate: insightState.breakOnAfterUpdate,
49
+ breakOnBeforeUpdate: insightState.breakOnBeforeUpdate,
50
+ }))
51
+ }, [insightState])
52
+
53
+ const commitCache = useRef(() => new CommitLookupCache()).current;
54
+ const deltaCache = useRef(() => new ThreadLookupCache(commitCache)).current;
55
+
56
+ useMemo(() => {
57
+ commitCache.setTree(controller.getTree())
58
+ deltaCache.reset();
59
+ }, [])
48
60
 
49
61
  useEffect(() => {
50
- if (commitId)
51
- server.commitState(commitId);
52
- }, [tree, commitId])
62
+ console.log('[Insight] Populate Cache')
53
63
 
54
- const renderCommit = (commit: CommitReport) => {
55
- const onClick = () => {
56
- server.commitState(commit.id);
57
- };
64
+ bus.externalUpdate = () => {
65
+ const delta = controller.getDelta();
66
+ const thread = controller.getThread();
58
67
 
59
- return h(CommitPreview, { commit, renderCommit, tree, onClick });
60
- }
68
+ commitCache.setTree(controller.getTree())
61
69
 
62
- const selectedCommit = commitId && tree.commits.get(commitId) || null;
70
+ deltaCache.ingestDelta(delta);
71
+ deltaCache.ingestThread(thread);
72
+ setRenderCounter(c => c + 1);
73
+ }
63
74
 
64
- return h('div', { style: { display: 'flex', flexDirection: 'row' } }, [
65
- h(TreeViewer, { tree, renderCommit, }),
66
- commitState && selectedCommit &&
67
- h(CommitViewer, { commit: selectedCommit, state: commitState }),
68
- ]);
69
- }
75
+ bus.onThreadDone = (thread, delta) => {
76
+ console.log('[Insight] ThreadDone')
77
+ commitCache.ingest(delta);
78
+ deltaCache.ingestDelta(delta);
79
+ deltaCache.ingestThread(thread);
80
+ deltaCache.prevTask = null;
81
+ setRenderCounter(c => c + 1);
70
82
 
71
- export const InsightApp: Component<InsightAppProps> = () => {
72
- throw new Error();
83
+ for (const subscriber of cacheSubscribers) {
84
+ subscriber();
85
+ }
86
+ }
87
+ bus.thread.onQueue = (reason) => {
88
+ console.log('[Insight] OnQueue')
89
+ const thread = controller.getThread();
90
+
91
+ if (thread.reasons.length === 1) {
92
+ if (insightState.breakOnBeforeUpdate)
93
+ controller.scheduler.intercept = true;
73
94
 
74
- const [mode, setMode] = useState<InsightMode>('tree');
95
+ commitCache.setTree(controller.getTree())
96
+
97
+ deltaCache.reset();
98
+ deltaCache.ingestThread(thread);
99
+ setRenderCounter(c => c + 1);
75
100
 
76
- const [renderReportIndex, setRenderReportIndex] = useState(0);
77
- const [renderReports, setRenderReports] = useState<WorkThread[]>([]);
78
- const [tree, setTree] = useState<null | CommitTree>(null);
101
+ for (const subscriber of cacheSubscribers) {
102
+ subscriber();
103
+ }
104
+ }
105
+ }
106
+ bus.thread.onWork = (prevTask, nextTask, isDone) => {
79
107
 
80
- const [currentThread, setCurrentThread] = useState<WorkThread | null>(null);
108
+ if (insightState.breakOnAfterUpdate && isDone) {
109
+ controller.scheduler.intercept = true;
110
+ }
111
+ if (nextTask && insightState.commitBreakpoints.has(nextTask.id)) {
112
+ controller.scheduler.intercept = true;
113
+ }
81
114
 
82
- const currentReport = renderReports[renderReportIndex] || null;
83
- const rootCommits = tree && CommitTree.getRootCommits(tree) || [];
115
+ if (controller.scheduler.intercept) {
116
+ const thread = controller.getThread();
117
+ const delta = controller.getDelta();
118
+
119
+ deltaCache.ingestDelta(delta);
120
+ deltaCache.ingestThread(thread);
121
+ setRenderCounter(c => c + 1);
84
122
 
85
- const [pendingWork, setPendingWork] = useState(false)
86
- const [autoWork, setAutoWork] = useState(false);
123
+ for (const subscriber of cacheSubscribers) {
124
+ subscriber();
125
+ }
87
126
 
88
- const updateThread = useMemo(() => {
89
- return debounce(() => {
90
- console.log('updating thread');
91
- const currentThread = reconciler.state.thread;
92
- if (currentThread)
93
- setCurrentThread(WorkThread.clone(currentThread));
94
- else
95
- setCurrentThread(null);
96
- }, 100, { leading: true, trailing: true, maxWait: 100 });
97
- }, []);
127
+ if (prevTask)
128
+ deltaCache.prevTask = prevTask;
129
+ }
130
+ }
98
131
 
99
- //const [counter, setCounter] = useState(0);
132
+ onReady();
133
+ }, [controller, bus, insightState]);
134
+
135
+ const cacheSubscribers = useRef<Set<() => void>>(new Set()).current;
136
+
137
+ const scrollToCommitIndex = useMemo(() => {
138
+ return (index: number) => {
139
+ if (!viewportRef.current)
140
+ return;
141
+ const viewPortRect = viewportRef.current.getBoundingClientRect();
142
+ if (index) {
143
+ viewportRef.current.scrollTo({
144
+ top: (index * 33) - (viewPortRect.height / 2),
145
+ behavior: 'smooth'
146
+ })
147
+ return;
148
+ }
149
+ }
150
+ }, []);
100
151
 
101
152
  useEffect(() => {
153
+ const { nextTask, prevTask } = deltaCache;
102
154
 
103
- const renderSub = reconciler.on('on-thread-complete', (thread) => {
104
- setRenderReports(rrs => [...rrs, WorkThread.clone(thread)])
105
- setTree(reconciler.tree);
106
- updateThread();
107
- });
155
+ const task = nextTask || prevTask;
156
+ const index = task && commits.findIndex(c => (nextTask && c.id === nextTask.id) || (prevTask && prevTask.id === c.id));
108
157
 
109
- reconciler.on('on-thread-update', () => {
110
- setTree(reconciler.tree);
111
- updateThread();
112
- })
158
+ if (index && index !== -1) {
159
+ scrollToCommitIndex(index);
160
+ }
161
+ }, [deltaCache.prevTask, deltaCache.nextTask, scrollToCommitIndex])
162
+
163
+ const renderCommit = useMemo(() => (commitId: CommitID) => {
164
+ return h(CommitComponent, { commitId })
165
+ }, []);
166
+
167
+ const CommitComponent = useMemo((): Component<{ commitId: CommitID }> => ({ commitId }) => {
168
+ const [c, setRenderCounter] = useState(0);
169
+
170
+ useEffect(() => {
171
+ const subscription = () => {
172
+ const commit = deltaCache.all.get(commitId);
173
+ const originalCommit = commitCache.map.get(commitId);
174
+
175
+ const inTaskList = deltaCache.allTasks.has(commitId);
176
+
177
+ if (commit !== originalCommit || inTaskList || (deltaCache.prevTask && deltaCache.prevTask.id === commitId))
178
+ setRenderCounter(c => c + 1);
179
+ }
180
+ cacheSubscribers.add(subscription);
181
+ subscription();
182
+
183
+ return () => {
184
+ console.log(`[Commit] Cleaning up ${commitId}`)
185
+ cacheSubscribers.delete(subscription);
186
+ }
187
+ }, [commitId])
188
+
189
+ const commit = deltaCache.all.get(commitId) || null;
190
+ if (!commit)
191
+ return (console.warn(`[Commit] Commit ${commitId} not found in delta cache`), null);
192
+
193
+ const color =
194
+ (deltaCache.nextTask && deltaCache.nextTask.id === commit.id) ? '#e1d600ff'
195
+ : deltaCache.targets.has(commit.id) ? '#db55e7ff'
196
+ : deltaCache.allTasks.has(commit.id) ? '#ea931aff'
197
+ : deltaCache.created.has(commit.id) ? (deltaCache.prevTask && deltaCache.prevTask.id === commit.id ? '#4bc847ff' : '#21a51cff')
198
+ : deltaCache.removed.has(commit.id) ? '#f25252ff'
199
+ : deltaCache.updated.has(commit.id) ? '#1ab9eaff'
200
+ : deltaCache.visited.has(commit.id) ? '#6f6f97ff'
201
+ : '#cacaca';
113
202
 
203
+ if (!commit)
204
+ return (console.log(`[Commit] ${commitId} not ready yet??`), null);
114
205
 
115
- onReady();
116
- return () => {
117
- renderSub.cancel();
118
- }
206
+ return useMemo(() => h(CommitPreview, { commit, renderCommit, color, onClick: () => setSelectedCommitId(commit.id) }), [
207
+ commit.version,
208
+ color,
209
+ ])
119
210
  }, [])
120
211
 
121
- useEffect(() => {
122
- if (autoWork) {
123
- const id = setInterval(() => {
124
- scheduler.work();
125
- }, 5);
126
- return () => clearInterval(id);
127
- }
128
- }, [autoWork, reconciler])
212
+ const reload = () => {
213
+ commitCache.setTree(controller.getTree())
214
+ deltaCache.reset();
215
+ deltaCache.ingestDelta(controller.getDelta());
216
+ deltaCache.ingestThread(controller.getThread());
129
217
 
130
- const onWorkClick = () => {
131
- setPendingWork(false);
132
- scheduler.work();
133
- updateThread();
134
- }
135
- const onToggleAutoWork = (e: Event) => {
136
- setAutoWork((e.target as HTMLInputElement).checked);
218
+ setRenderCounter(c => c + 1)
137
219
  }
138
220
 
139
- const [selectedCommit, setSelectedCommit] = useState<null | CommitID>(null);
140
- const onSelectCommit = (id: CommitID) => {
141
- if (selectedCommit === id)
142
- setSelectedCommit(null)
143
- else
144
- setSelectedCommit(id)
145
- }
221
+ const viewportRef = useRef<HTMLElement | null>(null);
146
222
 
147
- const renderCommit = (depth: number) => (commit: Commit) => {
148
- if (!tree)
149
- return null;
150
- return h(CommitPreview, {
151
- commit,
152
- tree,
153
- depth,
154
- renderCommit: renderCommit(depth + 1),
155
- onSelectCommit,
156
- attributes: [
157
- ['Id', commit.id.toString()]
158
- ],
159
- selectedCommits: new Set(selectedCommit ? [selectedCommit] : [])
160
- })
161
- }
223
+ const [selectedCommitId, setSelectedCommitId] = useState<CommitID | null>(null)
224
+ const [selectedCommitDetails, setSelectedCommitDetails] = useState<CommitDetailsReport | null>(null)
162
225
 
163
- return h('div', { className: classes.insight }, [
164
- h(MenuBar, { currentMode: mode, onSelectMode: setMode }),
165
- mode === 'thread' && hs('div', {}, [
166
- hs('button', { onClick: onWorkClick }, pendingWork ? 'Do Pending Work' : 'Work'),
167
- hs('label', {}, [
168
- hs('span', {}, 'Toggle Auto-Work'),
169
- hs('input', { type: 'checkbox', onInput: onToggleAutoWork, checked: autoWork }),
170
- ])
226
+ useEffect(() => {
227
+ if (!selectedCommitId)
228
+ return;
229
+
230
+ const details = controller.getDetails(selectedCommitId);
231
+ setSelectedCommitDetails(details)
232
+ }, [selectedCommitId])
233
+
234
+ const roots = [...deltaCache.roots.keys()];
235
+ const commits = deltaCache.getFlat();
236
+
237
+ const CHUNK_SIZE = 8;
238
+
239
+ return h('div', { style: { display: 'flex', 'flex-direction': 'column', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 } }, [
240
+ h('div', { style: { flex: 0, display: 'flex' } }, [
241
+ h(ScheduleControls, {
242
+ controller: controller.scheduler,
243
+ bus: bus.scheduler,
244
+ reconciler: controller,
245
+
246
+ state: insightState,
247
+ onStateChange: setInsightState,
248
+ }),
249
+ h('button', { onClick: reload}, 'Reload'),
171
250
  ]),
172
- mode === 'thread' && currentThread && h(ThreadViewer, { thread: currentThread, tree: tree || CommitTree.new() }),
173
- //hs('pre', {}, counter),
174
- mode === 'tree' && hs('div', { className: classes.treeExplorer }, [
175
- tree && [
176
- h(TreeViewer, { tree, selectedCommits: new Set(selectedCommit ? [selectedCommit] : []), onSelectCommit,
177
- renderCommit: renderCommit(0)
178
- }),
179
- ],
180
- selectedCommit && tree && [
181
- h(CommitViewer, { tree, commitId: selectedCommit, reconciler })
182
- ]
183
- ])
184
- ]);
185
- };
251
+ h('div', { style: { flex: 1, overflow: 'hidden', background: '#c0d7ddff', display: 'flex' } }, [
252
+ h('div', { style: { flex: 1 } },
253
+ //h(TreeViewer, { roots, renderCommit }),
254
+ h(Virtual1D, { viewportRef, windowRange: 5, chunkCount: commits.length / CHUNK_SIZE, chunkSize: (33 * CHUNK_SIZE), renderChunk(index) {
255
+ if (index < 0)
256
+ return null;
186
257
 
187
- const UpdateDesc = ({ update }: { update: Update }) => {
188
- if (update.prev && update.next)
189
- return `Update ${update.ref.id} (${getElementName(update.next)})`;
190
- if (update.prev && !update.next)
191
- return `Destroy ${update.ref.id} (${getElementName(update.prev.element)})`;
192
- if (!update.prev && update.next)
193
- return `Create ${update.ref.id} (${getElementName(update.next)})`;
194
- return `???`;
195
- }
258
+ return Array.from({ length: CHUNK_SIZE }).map((_, chunkIndex) => {
259
+ const report = commits[(index * CHUNK_SIZE) + (chunkIndex)];
260
+ if (!report)
261
+ return null;
262
+
263
+ const color = getCommitColor(deltaCache, report.id);
264
+
265
+ const onClick = () => {
266
+ setSelectedCommitId(report.id);
267
+ };
268
+ const attributes: [string, string][] = [
269
+ insightState.commitBreakpoints.has(report.id) ? ['Breakpoint', 'Enabled'] as [string, string] : null
270
+ ].filter(x => !!x)
271
+
272
+ return h('div', { style: { 'margin-left': ((report.distance - 1) * 32) + 'px', height: '33px' } }, [
273
+ h(CommitPreview, { color, commit: report, onClick, attributes })
274
+ ])
275
+ });
276
+ }, })
277
+ ),
278
+ h('div', { style: { 'min-width': '300px', flex: 0, background: '#ffdeabff' } }, [
279
+ deltaCache.thread && h('div', { }, [
280
+ h('dl', {}, [
281
+ h('dt', {}, 'Thread ID'),
282
+ h('dd', {}, deltaCache.thread.id),
283
+ h('dt', {}, 'Thread Done'),
284
+ h('dd', {}, deltaCache.thread.done.toString()),
285
+ h('dt', {}, 'Thread Passes'),
286
+ h('dd', {}, deltaCache.thread.passes),
287
+ h('dt', {}, 'Tasks (count)'),
288
+ h('dd', {}, deltaCache.thread.pendingTasks.length),
289
+ h('dt', {}, 'Visited (count)'),
290
+ h('dd', {}, deltaCache.thread.visited.length),
291
+ h('dt', {}, 'Created (count)'),
292
+ h('dd', {}, deltaCache.created.size),
293
+ h('dt', {}, 'Updated (count)'),
294
+ h('dd', {}, deltaCache.updated.size),
295
+ h('dt', {}, 'Removed (count)'),
296
+ h('dd', {}, deltaCache.removed.size),
297
+ h('dt', {}, 'MustRender '),
298
+ h('dd', {}, deltaCache.thread.mustRender.map(commitId => {
299
+ const commit = deltaCache.all.get(commitId);
300
+ if (!commit)
301
+ return null;
302
+ const color = getCommitColor(deltaCache, commitId);
303
+
304
+ return h(CommitPreview, {
305
+ commit,
306
+ color,
307
+ onClick: () => (scrollToCommitIndex(commits.indexOf(commit)), setSelectedCommitId(commitId))
308
+ })
309
+ })),
310
+ h('dt', {}, 'Missed'),
311
+ h('dd', {}, deltaCache.thread.missed.map(commitId => {
312
+ const commit = deltaCache.all.get(commitId);
313
+ if (!commit)
314
+ return null;
315
+ const color = getCommitColor(deltaCache, commitId);
196
316
 
197
- export type CommitTreeLeafProps = {
198
- commit: Commit,
199
- tree: CommitTree,
317
+ return h(CommitPreview, {
318
+ commit,
319
+ color,
320
+ onClick: () => (scrollToCommitIndex(commits.indexOf(commit)), setSelectedCommitId(commitId))
321
+ })
322
+ })),
323
+ ])
324
+ ]),
325
+ h('hr'),
326
+ selectedCommitDetails && [
327
+ h(CommitPreview, {
328
+ commit: selectedCommitDetails.commit,
329
+ color: getCommitColor(deltaCache, selectedCommitDetails.commit.id),
330
+ onClick: () => (scrollToCommitIndex(commits.indexOf(selectedCommitDetails.commit)), setSelectedCommitId(selectedCommitDetails.commit.id))
331
+ }),
332
+ h('button', { onClick: () => {
333
+ setInsightState(state => {
334
+ const prev = state.commitBreakpoints;
335
+ if (prev.has(selectedCommitDetails.commit.id)) {
336
+ prev.delete(selectedCommitDetails.commit.id)
337
+ return { ...state, commitBreakpoints: new Set(prev) };
338
+ }
339
+ prev.add(selectedCommitDetails.commit.id)
340
+ return { ...state, commitBreakpoints: new Set(prev) }
341
+ })
342
+ }}, 'Toggle Breakpoint'),
343
+ h('h3', {}, 'Parent'),
344
+ (() => {
345
+ const parentId = selectedCommitDetails.commit.parent;
346
+ if (!parentId)
347
+ return 'NO PARENT';
348
+ const parent = deltaCache.all.get(parentId);
349
+ if (!parent)
350
+ return h(CommitAttributeTag, { name: 'ParentID', value: parentId.toString() });
351
+
352
+ return h(CommitPreview, {
353
+ commit: parent,
354
+ color: getCommitColor(deltaCache, parent.id),
355
+ onClick: () => (scrollToCommitIndex(commits.indexOf(parent)), setSelectedCommitId(parent.id))
356
+ });
357
+ })(),
358
+ h('h3', {}, 'Props'),
359
+ h('ul', {},
360
+ Object.entries(selectedCommitDetails.props).map(([prop, value]) => {
361
+ return h('li', {}, `${prop} = ${getTextForValue(value)}`);
362
+ })
363
+ )
364
+ ]
365
+ ])
366
+ ])
367
+ ])
200
368
  }
201
369
 
202
- const CommitTreeLeaf: Component<CommitTreeLeafProps> = ({ commit, tree }) => {
203
- return [
204
- hs('pre', {}, [getElementName(commit.element), ` id=${commit.id} v=${commit.version}`]),
205
- hs('ul', {}, commit.children
206
- .map(ref => tree.commits.get(ref.id) as Commit)
207
- .map(commit => h(CommitTreeLeaf, { commit, tree })))
208
- ];
370
+
371
+ export const getTextForValue = (value: ValueReport): string => {
372
+ switch (value.type) {
373
+ case 'primitive':
374
+ switch (typeof value.value) {
375
+ case 'object':
376
+ return `null`;
377
+ case 'string':
378
+ case 'boolean':
379
+ case 'number':
380
+ return value.value.toString();
381
+ }
382
+ case 'complex':
383
+ return value.name;
384
+ case 'undefined':
385
+ return `undefined`;
386
+ default:
387
+ return value;
388
+ }
209
389
  }
390
+
391
+ const getCommitColor = (deltaCache: ThreadLookupCache, commitId: CommitID) => {
392
+
393
+ const color =
394
+ (deltaCache.nextTask && deltaCache.nextTask.id === commitId) ? '#e1d600ff'
395
+ : deltaCache.targets.has(commitId) ? '#db55e7ff'
396
+ : deltaCache.allTasks.has(commitId) ? '#ea931aff'
397
+ : deltaCache.created.has(commitId) ? (deltaCache.prevTask && deltaCache.prevTask.id === commitId ? '#4bc847ff' : '#21a51cff')
398
+ : deltaCache.removed.has(commitId) ? '#f25252ff'
399
+ : deltaCache.updated.has(commitId) ? '#1ab9eaff'
400
+ : deltaCache.visited.has(commitId) ? '#6f6f97ff'
401
+ : '#cacaca';
402
+
403
+ return color;
404
+ }