@kgdiff/viewer 0.4.0 → 0.5.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/dist/context.d.ts +42 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +87 -0
- package/dist/context.js.map +1 -0
- package/dist/controller.d.ts +26 -185
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +45 -1155
- package/dist/controller.js.map +1 -1
- package/dist/element.d.ts +6 -3
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +49 -229
- package/dist/element.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +3 -2
- package/dist/pipeline.js.map +1 -1
- package/dist/root.d.ts +54 -0
- package/dist/root.d.ts.map +1 -0
- package/dist/root.js +265 -0
- package/dist/root.js.map +1 -0
- package/dist/schedule.d.ts +22 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +41 -0
- package/dist/schedule.js.map +1 -0
- package/dist/session.d.ts +253 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +1239 -0
- package/dist/session.js.map +1 -0
- package/dist/standalone/kgdiff-viewer-compute.js +1 -1
- package/dist/standalone/kgdiff-viewer.js +4 -4
- package/dist/styles.d.ts +13 -6
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +217 -248
- package/dist/styles.js.map +1 -1
- package/dist/theme.d.ts +25 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +149 -0
- package/dist/theme.js.map +1 -0
- package/dist/ui/change-list.d.ts +18 -0
- package/dist/ui/change-list.d.ts.map +1 -0
- package/dist/ui/change-list.js +331 -0
- package/dist/ui/change-list.js.map +1 -0
- package/dist/ui/context.d.ts +19 -5
- package/dist/ui/context.d.ts.map +1 -1
- package/dist/ui/context.js.map +1 -1
- package/dist/ui/filters.d.ts +9 -0
- package/dist/ui/filters.d.ts.map +1 -0
- package/dist/ui/filters.js +68 -0
- package/dist/ui/filters.js.map +1 -0
- package/dist/ui/header.js +2 -2
- package/dist/ui/header.js.map +1 -1
- package/dist/ui/search.d.ts +13 -0
- package/dist/ui/search.d.ts.map +1 -0
- package/dist/ui/search.js +191 -0
- package/dist/ui/search.js.map +1 -0
- package/dist/ui/sidebar.d.ts +8 -16
- package/dist/ui/sidebar.d.ts.map +1 -1
- package/dist/ui/sidebar.js +15 -539
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/stage.js +1 -1
- package/dist/ui/stage.js.map +1 -1
- package/dist/ui/swatch.js +1 -1
- package/dist/ui/swatch.js.map +1 -1
- package/package.json +6 -6
- package/src/context.ts +106 -0
- package/src/controller.ts +70 -1257
- package/src/element.ts +52 -243
- package/src/index.ts +3 -1
- package/src/pipeline.ts +4 -1
- package/src/root.ts +308 -0
- package/src/schedule.ts +43 -0
- package/src/session.ts +1386 -0
- package/src/styles.ts +225 -253
- package/src/theme.ts +167 -0
- package/src/ui/change-list.ts +401 -0
- package/src/ui/context.ts +21 -5
- package/src/ui/filters.ts +79 -0
- package/src/ui/header.ts +2 -2
- package/src/ui/search.ts +220 -0
- package/src/ui/sidebar.ts +28 -668
- package/src/ui/stage.ts +1 -1
- package/src/ui/swatch.ts +1 -1
package/src/element.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `<kgdiff-viewer
|
|
3
|
-
*
|
|
2
|
+
* `<kgdiff-viewer>`, the default layout (RFC 0001 §4): a root (the session, keyboard map, live
|
|
3
|
+
* region, events and theme tokens; `root.ts`) and the regions bound to it, the stage with its
|
|
4
|
+
* chrome, the change list, the search, the family filters and the detail panel, placed as they
|
|
5
|
+
* always were (a 360 px sidebar; a bottom sheet below 760 px). Around them: attribute /
|
|
6
|
+
* property reflection, lifecycle, the public methods and deep links.
|
|
4
7
|
*/
|
|
5
8
|
import { type Delta, KgdiffError, type Locale } from '@kgdiff/core';
|
|
6
9
|
import type { PaletteName } from '@kgdiff/scene';
|
|
7
|
-
import
|
|
10
|
+
import type { ViewerController } from './controller.js';
|
|
8
11
|
import { h } from './dom.js';
|
|
9
12
|
import { formatHash, parseHash } from './hash-sync.js';
|
|
10
13
|
import { detectLocale, translate, translator } from './i18n.js';
|
|
@@ -20,12 +23,10 @@ import type {
|
|
|
20
23
|
ViewerMode,
|
|
21
24
|
ViewerScope,
|
|
22
25
|
} from './index.js';
|
|
23
|
-
import { isTypingTarget, keyAction } from './keyboard.js';
|
|
24
26
|
import { assertPayload } from './payload.js';
|
|
25
27
|
import {
|
|
26
28
|
DEFAULT_LOOP_DELAY,
|
|
27
29
|
LEGACY_CHANGES_MODE,
|
|
28
|
-
THEME_PREFERENCES,
|
|
29
30
|
toChrome,
|
|
30
31
|
toDelay,
|
|
31
32
|
toLegend,
|
|
@@ -35,11 +36,15 @@ import {
|
|
|
35
36
|
toScope,
|
|
36
37
|
toTheme,
|
|
37
38
|
} from './prefs.js';
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import
|
|
39
|
+
import { bindRegion, uiContext, ViewerRoot } from './root.js';
|
|
40
|
+
import { ALL_TOPICS, type Topic } from './session.js';
|
|
41
|
+
import { ChangeList } from './ui/change-list.js';
|
|
42
|
+
import type { UiActions } from './ui/context.js';
|
|
43
|
+
import { Detail } from './ui/detail.js';
|
|
44
|
+
import { Filters } from './ui/filters.js';
|
|
41
45
|
import { Header } from './ui/header.js';
|
|
42
46
|
import { HelpDialog, Menu } from './ui/popovers.js';
|
|
47
|
+
import { Search } from './ui/search.js';
|
|
43
48
|
import { Sidebar } from './ui/sidebar.js';
|
|
44
49
|
import { Stage } from './ui/stage.js';
|
|
45
50
|
|
|
@@ -101,31 +106,6 @@ function deepFreeze<T>(value: T): T {
|
|
|
101
106
|
return value;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
function media(query: string): MediaQueryList | null {
|
|
105
|
-
return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
|
|
106
|
-
? window.matchMedia(query)
|
|
107
|
-
: null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Resolve once the browser had a chance to paint the loading step. Hidden tabs never run
|
|
112
|
-
* animation frames, so a short timer resolves it there (loading must never stall).
|
|
113
|
-
*/
|
|
114
|
-
function nextPaint(): Promise<void> {
|
|
115
|
-
return new Promise((resolve) => {
|
|
116
|
-
let done = false;
|
|
117
|
-
const finish = (): void => {
|
|
118
|
-
if (done) return;
|
|
119
|
-
done = true;
|
|
120
|
-
resolve();
|
|
121
|
-
};
|
|
122
|
-
if (typeof requestAnimationFrame === 'function') {
|
|
123
|
-
requestAnimationFrame(() => setTimeout(finish, 0));
|
|
124
|
-
}
|
|
125
|
-
setTimeout(finish, 50);
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
109
|
export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
130
110
|
static get observedAttributes(): readonly string[] {
|
|
131
111
|
return OBSERVED;
|
|
@@ -134,18 +114,17 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
134
114
|
/** @internal */
|
|
135
115
|
readonly controller: ViewerController;
|
|
136
116
|
readonly #shadow: ShadowRoot;
|
|
117
|
+
readonly #root: ViewerRoot;
|
|
137
118
|
#ui: {
|
|
138
119
|
root: HTMLElement;
|
|
139
|
-
vars: HTMLStyleElement;
|
|
140
120
|
header: Header;
|
|
141
121
|
stage: Stage;
|
|
122
|
+
list: ChangeList;
|
|
142
123
|
sidebar: Sidebar;
|
|
143
124
|
menu: Menu;
|
|
144
125
|
help: HelpDialog;
|
|
145
|
-
live: HTMLElement;
|
|
146
126
|
} | null = null;
|
|
147
127
|
#currentPayload: ReportPayload | null = null;
|
|
148
|
-
#renderFailed = false;
|
|
149
128
|
#reflecting = false;
|
|
150
129
|
#compact = false;
|
|
151
130
|
readonly #cleanup: (() => void)[] = [];
|
|
@@ -157,34 +136,13 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
157
136
|
constructor() {
|
|
158
137
|
super();
|
|
159
138
|
this.#shadow = this.attachShadow({ mode: 'open' });
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
select: (hunk, node) => {
|
|
163
|
-
this.#emit('kgdiff-select', { hunk, node });
|
|
164
|
-
this.#writeHash();
|
|
165
|
-
},
|
|
166
|
-
mode: (mode, scope) => {
|
|
167
|
-
this.#emit('kgdiff-mode', { mode, scope });
|
|
168
|
-
this.#writeHash();
|
|
169
|
-
},
|
|
170
|
-
review: (hunk, reviewed, progress) =>
|
|
171
|
-
this.#emit('kgdiff-review', { hunk, reviewed, progress }),
|
|
172
|
-
error: (error) => this.#emit('kgdiff-error', { error }),
|
|
173
|
-
announce: (text) => this.#announce(text),
|
|
174
|
-
replayEnd: (loop) => this.#emit('kgdiff-replay-end', { loop }),
|
|
175
|
-
};
|
|
176
|
-
this.controller = new ViewerController(host, {
|
|
177
|
-
now: () => (typeof performance !== 'undefined' ? performance.now() : Date.now()),
|
|
178
|
-
requestFrame: (cb) =>
|
|
179
|
-
typeof requestAnimationFrame === 'function' ? requestAnimationFrame(cb) : 0,
|
|
180
|
-
cancelFrame: (id) => {
|
|
181
|
-
if (typeof cancelAnimationFrame === 'function') cancelAnimationFrame(id);
|
|
182
|
-
},
|
|
183
|
-
yieldToPaint: nextPaint,
|
|
184
|
-
storage: defaultStorage(),
|
|
139
|
+
this.#root = new ViewerRoot(this, this.#shadow, this.#actions(), (type) => {
|
|
140
|
+
if (type === 'kgdiff-select' || type === 'kgdiff-mode') this.#writeHash();
|
|
185
141
|
});
|
|
142
|
+
this.controller = this.#root.controller;
|
|
186
143
|
this.controller.locale = toLocale(this.getAttribute('locale')) ?? detectLocale();
|
|
187
|
-
|
|
144
|
+
// The layout renders first, then the regions in the order they bound to the root.
|
|
145
|
+
this.#root.attach({ update: (topics) => this.#update(topics) });
|
|
188
146
|
}
|
|
189
147
|
|
|
190
148
|
// ─── Lifecycle ────────────────────────────────────────────────────────────
|
|
@@ -201,6 +159,8 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
201
159
|
}
|
|
202
160
|
}
|
|
203
161
|
}
|
|
162
|
+
// Provide the session (the regions bind to it) and follow the system's preferences.
|
|
163
|
+
this.#cleanup.push(this.#root.connect());
|
|
204
164
|
if (!this.#ui) this.#build();
|
|
205
165
|
connected.push(this);
|
|
206
166
|
const ui = this.#ui;
|
|
@@ -216,36 +176,17 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
216
176
|
canvas.addEventListener('pointerdown', focusCanvas);
|
|
217
177
|
this.#cleanup.push(() => canvas.removeEventListener('pointerdown', focusCanvas));
|
|
218
178
|
}
|
|
219
|
-
this.#watchMedia();
|
|
220
179
|
this.#watchSize();
|
|
221
180
|
const onKey = (e: KeyboardEvent): void => this.#onKeyDown(e);
|
|
222
181
|
document.addEventListener('keydown', onKey);
|
|
223
182
|
this.#cleanup.push(() => document.removeEventListener('keydown', onKey));
|
|
224
|
-
const onStorage = (e: StorageEvent): void => this.controller.syncReview(e.key);
|
|
225
|
-
window.addEventListener('storage', onStorage);
|
|
226
|
-
this.#cleanup.push(() => window.removeEventListener('storage', onStorage));
|
|
227
183
|
const onHash = (): void => this.#readHash();
|
|
228
184
|
window.addEventListener('hashchange', onHash);
|
|
229
185
|
this.#cleanup.push(() => window.removeEventListener('hashchange', onHash));
|
|
230
186
|
this.controller.setChrome(() => ui.stage.chromeLayout());
|
|
231
187
|
document.fonts?.ready?.then(() => this.#ui?.stage.layout()).catch(() => undefined);
|
|
232
188
|
this.#readHash();
|
|
233
|
-
this.#render(
|
|
234
|
-
new Set<Topic>([
|
|
235
|
-
'status',
|
|
236
|
-
'data',
|
|
237
|
-
'mode',
|
|
238
|
-
'theme',
|
|
239
|
-
'locale',
|
|
240
|
-
'filters',
|
|
241
|
-
'review',
|
|
242
|
-
'selection',
|
|
243
|
-
'focus',
|
|
244
|
-
'swipe',
|
|
245
|
-
'replay',
|
|
246
|
-
'overview',
|
|
247
|
-
]),
|
|
248
|
-
);
|
|
189
|
+
this.#root.render(ALL_TOPICS);
|
|
249
190
|
}
|
|
250
191
|
|
|
251
192
|
disconnectedCallback(): void {
|
|
@@ -513,10 +454,6 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
513
454
|
|
|
514
455
|
// ─── Internals ────────────────────────────────────────────────────────────
|
|
515
456
|
|
|
516
|
-
#emit(type: string, detail: unknown): void {
|
|
517
|
-
this.dispatchEvent(new CustomEvent(type, { detail, bubbles: true, composed: true }));
|
|
518
|
-
}
|
|
519
|
-
|
|
520
457
|
#reflect(name: string, value: string | null): void {
|
|
521
458
|
if (this.getAttribute(name) === value) return;
|
|
522
459
|
this.#reflecting = true;
|
|
@@ -528,16 +465,6 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
528
465
|
}
|
|
529
466
|
}
|
|
530
467
|
|
|
531
|
-
#announce(text: string): void {
|
|
532
|
-
const live = this.#ui?.live;
|
|
533
|
-
if (!live) return;
|
|
534
|
-
live.textContent = '';
|
|
535
|
-
// A fresh text node in a later task makes screen readers announce repeats too.
|
|
536
|
-
setTimeout(() => {
|
|
537
|
-
live.textContent = text;
|
|
538
|
-
}, 30);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
468
|
get #sidebarOpen(): boolean {
|
|
542
469
|
return this.getAttribute('sidebar') !== 'closed';
|
|
543
470
|
}
|
|
@@ -577,19 +504,12 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
577
504
|
this.controller.hold(this.#pointerOver && this.loop && this.pauseOnHover);
|
|
578
505
|
}
|
|
579
506
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const ctx: UiContext = {
|
|
584
|
-
controller: this.controller,
|
|
507
|
+
/** What the layout does for the regions: preferences reflect as attributes, panels open. */
|
|
508
|
+
#actions(): UiActions {
|
|
509
|
+
return {
|
|
585
510
|
tr: () => translator(this.controller.locale),
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
this.controller.setMode(mode);
|
|
589
|
-
},
|
|
590
|
-
toggleScope: () => {
|
|
591
|
-
this.controller.toggleScope();
|
|
592
|
-
},
|
|
511
|
+
setMode: (mode) => this.controller.setMode(mode),
|
|
512
|
+
toggleScope: () => this.controller.toggleScope(),
|
|
593
513
|
setThemePreference: (pref) => {
|
|
594
514
|
this.theme = pref;
|
|
595
515
|
},
|
|
@@ -605,24 +525,27 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
605
525
|
openHelp: () => this.#ui?.help.show(),
|
|
606
526
|
focusSearch: () => this.#focusSearch(),
|
|
607
527
|
};
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
#build(): void {
|
|
531
|
+
const root = h('div', { class: 'root' });
|
|
532
|
+
this.#shadow.append(root);
|
|
533
|
+
// Each region binds to the root on its own (context protocol); the layout only places them.
|
|
534
|
+
const header = bindRegion(root, (ui) => new Header(ui));
|
|
535
|
+
const list = bindRegion(root, (ui) => new ChangeList(ui));
|
|
536
|
+
const search = bindRegion(root, (ui) => new Search(ui));
|
|
537
|
+
const filters = bindRegion(root, (ui) => new Filters(ui));
|
|
538
|
+
const detail = bindRegion(root, (ui) => new Detail(ui));
|
|
539
|
+
const stage = bindRegion(root, (ui) => new Stage(ui));
|
|
540
|
+
const ui = uiContext(this.#root, this.#shadow);
|
|
541
|
+
const menu = new Menu(ui);
|
|
542
|
+
const help = new HelpDialog(ui);
|
|
543
|
+
const sidebar = new Sidebar(ui.tr, list, search, filters, detail);
|
|
614
544
|
// The title and the actions float over the canvas; the menu drops from the actions.
|
|
615
545
|
header.attachMenu(menu.el);
|
|
616
546
|
stage.el.appendChild(header.el);
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
{ class: 'root' },
|
|
620
|
-
h('div', { class: 'body' }, stage.el, sidebar.el),
|
|
621
|
-
help.el,
|
|
622
|
-
live,
|
|
623
|
-
);
|
|
624
|
-
this.#shadow.append(style, vars, root);
|
|
625
|
-
this.#ui = { root, vars, header, stage, sidebar, menu, help, live };
|
|
547
|
+
root.append(h('div', { class: 'body' }, stage.el, sidebar.el), help.el, this.#root.live);
|
|
548
|
+
this.#ui = { root, header, stage, list, sidebar, menu, help };
|
|
626
549
|
// Close the menu on outside clicks.
|
|
627
550
|
root.addEventListener('pointerdown', (e) => {
|
|
628
551
|
if (!menu.open) return;
|
|
@@ -640,24 +563,8 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
640
563
|
this.#applyChrome();
|
|
641
564
|
}
|
|
642
565
|
|
|
643
|
-
/**
|
|
644
|
-
#
|
|
645
|
-
try {
|
|
646
|
-
this.#renderTopics(topics);
|
|
647
|
-
this.#renderFailed = false;
|
|
648
|
-
} catch (error) {
|
|
649
|
-
console.error('[kgdiff] render failed:', error);
|
|
650
|
-
if (this.#renderFailed) return;
|
|
651
|
-
this.#renderFailed = true;
|
|
652
|
-
const c = this.controller;
|
|
653
|
-
c.explicit.clear();
|
|
654
|
-
c.setThemePreference('auto');
|
|
655
|
-
c.setPalette('default');
|
|
656
|
-
c.fail(error instanceof Error ? error : new Error(String(error)));
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
#renderTopics(topics: ReadonlySet<Topic>): void {
|
|
566
|
+
/** The layout's part of a render (the root renders the regions after it). */
|
|
567
|
+
#update(topics: ReadonlySet<Topic>): void {
|
|
661
568
|
const ui = this.#ui;
|
|
662
569
|
const c = this.controller;
|
|
663
570
|
if (topics.has('mode')) {
|
|
@@ -671,19 +578,14 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
671
578
|
);
|
|
672
579
|
}
|
|
673
580
|
if (!ui) return;
|
|
674
|
-
if (topics.has('theme') || topics.has('status'))
|
|
675
|
-
ui.vars.textContent = `:host { ${themeVariables(c.theme)} }`;
|
|
676
|
-
ui.root.dataset.theme = c.themeName;
|
|
677
|
-
}
|
|
581
|
+
if (topics.has('theme') || topics.has('status')) ui.root.dataset.theme = c.themeName;
|
|
678
582
|
if (topics.has('locale') || topics.has('status')) {
|
|
679
583
|
ui.root.setAttribute('lang', c.locale);
|
|
680
584
|
if (ui.menu.open) ui.menu.render();
|
|
681
585
|
if (ui.help.open) ui.help.render();
|
|
682
586
|
}
|
|
683
587
|
if (topics.has('theme') && ui.menu.open) ui.menu.render();
|
|
684
|
-
ui.header.update(topics);
|
|
685
588
|
ui.sidebar.update(topics);
|
|
686
|
-
ui.stage.update(topics);
|
|
687
589
|
if (topics.has('mode') || topics.has('status')) {
|
|
688
590
|
ui.root.dataset.mode = c.mode;
|
|
689
591
|
ui.root.dataset.scope = c.scope;
|
|
@@ -707,22 +609,7 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
707
609
|
|
|
708
610
|
#focusSearch(): void {
|
|
709
611
|
if (!this.#sidebarOpen) this.#toggleSidebar(true);
|
|
710
|
-
this.#
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
#watchMedia(): void {
|
|
714
|
-
const dark = media('(prefers-color-scheme: light)');
|
|
715
|
-
const motion = media('(prefers-reduced-motion: reduce)');
|
|
716
|
-
const apply = (): void => {
|
|
717
|
-
this.controller.setSystemDark(!(dark?.matches ?? false));
|
|
718
|
-
this.controller.setReducedMotion(motion?.matches ?? false);
|
|
719
|
-
};
|
|
720
|
-
apply();
|
|
721
|
-
for (const q of [dark, motion]) {
|
|
722
|
-
if (!q) continue;
|
|
723
|
-
q.addEventListener?.('change', apply);
|
|
724
|
-
this.#cleanup.push(() => q.removeEventListener?.('change', apply));
|
|
725
|
-
}
|
|
612
|
+
this.#root.focus('search');
|
|
726
613
|
}
|
|
727
614
|
|
|
728
615
|
#watchSize(): void {
|
|
@@ -738,7 +625,7 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
738
625
|
ui.root.dataset.compact = String(compact);
|
|
739
626
|
}
|
|
740
627
|
ui.stage.layout();
|
|
741
|
-
ui.
|
|
628
|
+
ui.list.renderWindow();
|
|
742
629
|
if (compact) ui.sidebar.syncSheet();
|
|
743
630
|
this.controller.layoutChanged();
|
|
744
631
|
});
|
|
@@ -769,85 +656,7 @@ export class KgdiffViewer extends HTMLElement implements KgdiffViewerElement {
|
|
|
769
656
|
}
|
|
770
657
|
return;
|
|
771
658
|
}
|
|
772
|
-
|
|
773
|
-
if (isTypingTarget(target)) return;
|
|
774
|
-
const c = this.controller;
|
|
775
|
-
const action = keyAction(e, c.mode, c.scope);
|
|
776
|
-
if (!action) return;
|
|
777
|
-
// Enter / Space on a focused control activate that control.
|
|
778
|
-
const el = target as Element | null;
|
|
779
|
-
if (
|
|
780
|
-
(e.key === 'Enter' || e.key === ' ') &&
|
|
781
|
-
el?.closest?.('button, a, summary, [role="radio"], [role="menuitemradio"]')
|
|
782
|
-
) {
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
if (c.status !== 'ready' && !['help', 'theme', 'sidebar', 'escape'].includes(action.type))
|
|
786
|
-
return;
|
|
787
|
-
e.preventDefault();
|
|
788
|
-
switch (action.type) {
|
|
789
|
-
case 'mode':
|
|
790
|
-
c.setMode(action.mode);
|
|
791
|
-
break;
|
|
792
|
-
case 'scope':
|
|
793
|
-
c.toggleScope();
|
|
794
|
-
break;
|
|
795
|
-
case 'next':
|
|
796
|
-
c.next();
|
|
797
|
-
break;
|
|
798
|
-
case 'prev':
|
|
799
|
-
c.prev();
|
|
800
|
-
break;
|
|
801
|
-
case 'first':
|
|
802
|
-
c.first();
|
|
803
|
-
break;
|
|
804
|
-
case 'last':
|
|
805
|
-
c.last();
|
|
806
|
-
break;
|
|
807
|
-
case 'fly':
|
|
808
|
-
c.flyToSelection();
|
|
809
|
-
break;
|
|
810
|
-
case 'escape':
|
|
811
|
-
if (!ui.sidebar.escape()) c.clearSelection();
|
|
812
|
-
break;
|
|
813
|
-
case 'play-pause':
|
|
814
|
-
c.togglePlay();
|
|
815
|
-
break;
|
|
816
|
-
case 'step':
|
|
817
|
-
c.stepReplay(action.direction);
|
|
818
|
-
break;
|
|
819
|
-
case 'divider':
|
|
820
|
-
c.setSplit(c.split + action.delta);
|
|
821
|
-
break;
|
|
822
|
-
case 'fit':
|
|
823
|
-
void c.fit();
|
|
824
|
-
break;
|
|
825
|
-
case 'zoom':
|
|
826
|
-
c.zoom(action.direction);
|
|
827
|
-
break;
|
|
828
|
-
case 'search':
|
|
829
|
-
this.#focusSearch();
|
|
830
|
-
break;
|
|
831
|
-
case 'review':
|
|
832
|
-
c.toggleReviewed();
|
|
833
|
-
break;
|
|
834
|
-
case 'sidebar':
|
|
835
|
-
this.#toggleSidebar();
|
|
836
|
-
break;
|
|
837
|
-
case 'theme': {
|
|
838
|
-
const i = THEME_PREFERENCES.indexOf(c.themePreference);
|
|
839
|
-
this.theme = THEME_PREFERENCES[(i + 1) % THEME_PREFERENCES.length] as ThemePreference;
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
case 'help':
|
|
843
|
-
ui.help.show();
|
|
844
|
-
break;
|
|
845
|
-
case 'expand':
|
|
846
|
-
c.expandNearest();
|
|
847
|
-
break;
|
|
848
|
-
default:
|
|
849
|
-
action satisfies never;
|
|
850
|
-
}
|
|
659
|
+
this.#root.handleKey(e);
|
|
851
660
|
}
|
|
852
661
|
|
|
853
662
|
// ─── Deep links ───────────────────────────────────────────────────────────
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
GraphInput,
|
|
18
18
|
KGraph,
|
|
19
19
|
Locale,
|
|
20
|
+
PackedDiffJSON,
|
|
20
21
|
} from '@kgdiff/core';
|
|
21
22
|
import type { ParseOptions } from '@kgdiff/formats';
|
|
22
23
|
import type { LayoutJSON, LayoutOptions } from '@kgdiff/layout';
|
|
@@ -39,7 +40,8 @@ export type ViewerLegend = 'auto' | 'full' | 'mini' | 'none';
|
|
|
39
40
|
export interface ReportPayload {
|
|
40
41
|
readonly format: 'kgdiff.report';
|
|
41
42
|
readonly version: 1;
|
|
42
|
-
|
|
43
|
+
/** The diff, packed (`packDiffJSON`, what the CLI writes) or not. */
|
|
44
|
+
readonly diff: DiffJSON | PackedDiffJSON;
|
|
43
45
|
readonly layout: LayoutJSON;
|
|
44
46
|
readonly meta: {
|
|
45
47
|
readonly title?: string;
|
package/src/pipeline.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
isKGraph,
|
|
12
12
|
type KGraph,
|
|
13
13
|
KgdiffError,
|
|
14
|
+
unpackDiffJSON,
|
|
14
15
|
} from '@kgdiff/core';
|
|
15
16
|
import { type LayoutOptions, type LayoutResult, layoutFromJSON } from '@kgdiff/layout';
|
|
16
17
|
import { loadCompute } from './compute-loader.js';
|
|
@@ -157,7 +158,9 @@ export async function readPayload(
|
|
|
157
158
|
onStep: (step: LoadStep) => Promise<void>,
|
|
158
159
|
): Promise<LoadedDiff> {
|
|
159
160
|
await onStep('match');
|
|
160
|
-
const
|
|
161
|
+
const json =
|
|
162
|
+
payload.diff.format === 'kgdiff.diff.packed' ? unpackDiffJSON(payload.diff) : payload.diff;
|
|
163
|
+
const diff = diffFromJSON(json);
|
|
161
164
|
await onStep('layout');
|
|
162
165
|
const layout = layoutFromJSON(payload.layout);
|
|
163
166
|
const vb = str(diff.before.meta.version);
|