@kgdiff/viewer 0.3.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/README.md +9 -8
- 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 +30 -173
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +94 -1099
- package/dist/controller.js.map +1 -1
- package/dist/element.d.ts +12 -6
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +105 -236
- package/dist/element.js.map +1 -1
- package/dist/hash-sync.d.ts +4 -2
- package/dist/hash-sync.d.ts.map +1 -1
- package/dist/hash-sync.js +21 -5
- package/dist/hash-sync.js.map +1 -1
- package/dist/i18n.d.ts +7 -7
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +14 -14
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts +24 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keyboard.d.ts +6 -4
- package/dist/keyboard.d.ts.map +1 -1
- package/dist/keyboard.js +6 -5
- package/dist/keyboard.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/prefs.d.ts +7 -1
- package/dist/prefs.d.ts.map +1 -1
- package/dist/prefs.js +15 -3
- package/dist/prefs.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 +270 -295
- 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 +21 -5
- package/dist/ui/context.d.ts.map +1 -1
- package/dist/ui/context.js.map +1 -1
- package/dist/ui/dock.d.ts +17 -0
- package/dist/ui/dock.d.ts.map +1 -0
- package/dist/ui/dock.js +151 -0
- package/dist/ui/dock.js.map +1 -0
- 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.d.ts +7 -2
- package/dist/ui/header.d.ts.map +1 -1
- package/dist/ui/header.js +26 -135
- package/dist/ui/header.js.map +1 -1
- package/dist/ui/popovers.d.ts.map +1 -1
- package/dist/ui/popovers.js +3 -2
- package/dist/ui/popovers.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 -538
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/stage.d.ts +4 -0
- package/dist/ui/stage.d.ts.map +1 -1
- package/dist/ui/stage.js +42 -95
- package/dist/ui/stage.js.map +1 -1
- package/dist/ui/swatch.d.ts +7 -0
- package/dist/ui/swatch.d.ts.map +1 -0
- package/dist/ui/swatch.js +26 -0
- package/dist/ui/swatch.js.map +1 -0
- package/package.json +6 -6
- package/src/context.ts +106 -0
- package/src/controller.ts +120 -1196
- package/src/element.ts +114 -254
- package/src/hash-sync.ts +20 -6
- package/src/i18n.ts +15 -14
- package/src/index.ts +24 -10
- package/src/keyboard.ts +12 -6
- package/src/pipeline.ts +4 -1
- package/src/prefs.ts +19 -4
- package/src/root.ts +308 -0
- package/src/schedule.ts +43 -0
- package/src/session.ts +1386 -0
- package/src/styles.ts +277 -299
- package/src/theme.ts +167 -0
- package/src/ui/change-list.ts +401 -0
- package/src/ui/context.ts +23 -5
- package/src/ui/dock.ts +198 -0
- package/src/ui/filters.ts +79 -0
- package/src/ui/header.ts +40 -170
- package/src/ui/popovers.ts +3 -2
- package/src/ui/search.ts +220 -0
- package/src/ui/sidebar.ts +28 -667
- package/src/ui/stage.ts +50 -115
- package/src/ui/swatch.ts +31 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @kgdiff/viewer
|
|
2
2
|
|
|
3
|
-
`<kgdiff-viewer>`, a framework-agnostic web component for reviewing graph diffs.
|
|
4
|
-
(Overlay, Swipe, Replay
|
|
3
|
+
`<kgdiff-viewer>`, a framework-agnostic web component for reviewing graph diffs. Three ways to
|
|
4
|
+
compare (Overlay, Swipe, Replay), each with or without Changes only, a searchable change list, a detail panel with
|
|
5
5
|
character-level diffs, review progress, deep links, keyboard shortcuts, light and dark themes, and
|
|
6
6
|
an English and Chinese UI.
|
|
7
7
|
|
|
@@ -37,9 +37,10 @@ ESM only. Runs in evergreen browsers.
|
|
|
37
37
|
self-contained script that defines the element and shows embedded payloads. The optional
|
|
38
38
|
`@kgdiff/viewer/standalone/compute` add-on brings the diff engine and force layout for `load()`.
|
|
39
39
|
|
|
40
|
-
Attributes: `mode` (`overlay` | `swipe` | `replay` | `changes`), `theme`,
|
|
41
|
-
`sidebar`, `sync-hash`, `global-shortcuts`, `header`, `chrome`, `legend`,
|
|
42
|
-
`pause-on-hover`.
|
|
40
|
+
Attributes: `mode` (`overlay` | `swipe` | `replay`), `scope` (`all` | `changes`), `theme`,
|
|
41
|
+
`palette`, `locale`, `sidebar`, `sync-hash`, `global-shortcuts`, `header`, `chrome`, `legend`,
|
|
42
|
+
`loop`, `loop-delay`, `pause-on-hover`. The legacy `mode="changes"` still works: it means
|
|
43
|
+
`overlay` with `scope="changes"`. Methods: `load`, `selectHunk`, `next`, `prev`, `play`, `pause`, `fit`,
|
|
43
44
|
`exportDelta`, `reviewState`, `setReviewed`. Events: `kgdiff-ready`, `kgdiff-select`,
|
|
44
45
|
`kgdiff-mode`, `kgdiff-review`, `kgdiff-error`, `kgdiff-replay-end`.
|
|
45
46
|
|
|
@@ -60,9 +61,9 @@ replay loop:
|
|
|
60
61
|
| `loop-delay` (`loopDelay`) | ms between the end and restart | `1200` |
|
|
61
62
|
| `pause-on-hover` (`pauseOnHover`) | `false` to keep playing | on: a looping replay pauses under a mouse or pen |
|
|
62
63
|
|
|
63
|
-
- `chrome="none"` shows the graph alone: no
|
|
64
|
-
|
|
65
|
-
or legend card. The hover tooltip stays, and the legend becomes one line with the families this
|
|
64
|
+
- `chrome="none"` shows the graph alone: no title, actions (theme, help, sidebar button), view
|
|
65
|
+
dock (with its step caption, playback bar and finished card), change list, zoom buttons,
|
|
66
|
+
minimap or legend card. The hover tooltip stays, and the legend becomes one line with the families this
|
|
66
67
|
diff has, then unchanged. Clicks and keyboard shortcuts do nothing. The minimum height drops
|
|
67
68
|
from 420px to 120px. The view stays fitted: when the canvas is resized (unless the user
|
|
68
69
|
panned or zoomed since) and at the start of every loop. Labels are never cut at the edge: one
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Web Components community context protocol
|
|
3
|
+
* (https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md),
|
|
4
|
+
* without dependencies: how the viewer's regions find the root whose session they show
|
|
5
|
+
* (RFC 0001 §2).
|
|
6
|
+
*
|
|
7
|
+
* A consumer dispatches a `context-request` event (bubbling, composed: it crosses shadow roots
|
|
8
|
+
* on its way up); the nearest provider of that context answers through the callback and stops
|
|
9
|
+
* the event. Requests nobody answered reach `document`, where they wait; a provider that
|
|
10
|
+
* connects later announces itself with `context-provider` and the waiting requests are sent
|
|
11
|
+
* again, so definition and connection order do not matter. A consumer outside the provider's
|
|
12
|
+
* subtree sends its request through an element inside it (`via`: the root named by `for`).
|
|
13
|
+
*/
|
|
14
|
+
export interface Context<T> {
|
|
15
|
+
/**
|
|
16
|
+
* `Symbol.for(name)`: equal across copies of the library on one page (an IIFE report bundle
|
|
17
|
+
* next to an ESM build), so a component of one copy finds the root of another. The name is
|
|
18
|
+
* versioned when the provided value changes incompatibly.
|
|
19
|
+
*/
|
|
20
|
+
readonly key: symbol;
|
|
21
|
+
/** Carries the value type only. */
|
|
22
|
+
readonly __value?: T;
|
|
23
|
+
}
|
|
24
|
+
export declare const createContext: <T>(name: string) => Context<T>;
|
|
25
|
+
export declare class ContextRequestEvent<T> extends Event {
|
|
26
|
+
readonly context: Context<T>;
|
|
27
|
+
/** The consumer, which may differ from the element the event is dispatched on (`via`). */
|
|
28
|
+
readonly consumer: Element;
|
|
29
|
+
readonly callback: (value: T) => void;
|
|
30
|
+
constructor(context: Context<T>,
|
|
31
|
+
/** The consumer, which may differ from the element the event is dispatched on (`via`). */
|
|
32
|
+
consumer: Element, callback: (value: T) => void);
|
|
33
|
+
}
|
|
34
|
+
export declare class ContextProviderEvent<T> extends Event {
|
|
35
|
+
readonly context: Context<T>;
|
|
36
|
+
constructor(context: Context<T>);
|
|
37
|
+
}
|
|
38
|
+
/** Ask for `context` for `consumer`, through `via` (an element of the provider's subtree). */
|
|
39
|
+
export declare function requestContext<T>(consumer: Element, context: Context<T>, callback: (value: T) => void, via?: Element | null): void;
|
|
40
|
+
/** Answer requests for `context` reaching `host`; returns the function that stops answering. */
|
|
41
|
+
export declare function provideContext<T>(host: Element, context: Context<T>, value: () => T): () => void;
|
|
42
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,OAAO,CAAC,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,GAAI,CAAC,QAAQ,MAAM,KAAG,OAAO,CAAC,CAAC,CAAgC,CAAC;AAE1F,qBAAa,mBAAmB,CAAC,CAAC,CAAE,SAAQ,KAAK;IAE7C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5B,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,EAAE,OAAO;IAC1B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI;IAJvC,YACW,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5B,0FAA0F;IACjF,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAGtC;CACF;AAED,qBAAa,oBAAoB,CAAC,CAAC,CAAE,SAAQ,KAAK;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IAAxC,YAAqB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAEvC;CACF;AAwCD,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,CAAC,EAC9B,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,EAC5B,GAAG,GAAE,OAAO,GAAG,IAAW,GACzB,IAAI,CAGN;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,IAAI,CAWhG"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Web Components community context protocol
|
|
3
|
+
* (https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md),
|
|
4
|
+
* without dependencies: how the viewer's regions find the root whose session they show
|
|
5
|
+
* (RFC 0001 §2).
|
|
6
|
+
*
|
|
7
|
+
* A consumer dispatches a `context-request` event (bubbling, composed: it crosses shadow roots
|
|
8
|
+
* on its way up); the nearest provider of that context answers through the callback and stops
|
|
9
|
+
* the event. Requests nobody answered reach `document`, where they wait; a provider that
|
|
10
|
+
* connects later announces itself with `context-provider` and the waiting requests are sent
|
|
11
|
+
* again, so definition and connection order do not matter. A consumer outside the provider's
|
|
12
|
+
* subtree sends its request through an element inside it (`via`: the root named by `for`).
|
|
13
|
+
*/
|
|
14
|
+
export const createContext = (name) => ({ key: Symbol.for(name) });
|
|
15
|
+
export class ContextRequestEvent extends Event {
|
|
16
|
+
context;
|
|
17
|
+
consumer;
|
|
18
|
+
callback;
|
|
19
|
+
constructor(context,
|
|
20
|
+
/** The consumer, which may differ from the element the event is dispatched on (`via`). */
|
|
21
|
+
consumer, callback) {
|
|
22
|
+
super('context-request', { bubbles: true, composed: true });
|
|
23
|
+
this.context = context;
|
|
24
|
+
this.consumer = consumer;
|
|
25
|
+
this.callback = callback;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class ContextProviderEvent extends Event {
|
|
29
|
+
context;
|
|
30
|
+
constructor(context) {
|
|
31
|
+
super('context-provider', { bubbles: true, composed: true });
|
|
32
|
+
this.context = context;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const waiting = new Set();
|
|
36
|
+
let collecting = false;
|
|
37
|
+
/** Keep the requests nobody answered, and send them again when a provider connects. */
|
|
38
|
+
function collect() {
|
|
39
|
+
if (collecting || typeof document === 'undefined')
|
|
40
|
+
return;
|
|
41
|
+
collecting = true;
|
|
42
|
+
document.addEventListener('context-request', (e) => {
|
|
43
|
+
// Consumers that left the document since they asked are forgotten, closures and all.
|
|
44
|
+
for (const w of waiting)
|
|
45
|
+
if (!w.consumer.isConnected)
|
|
46
|
+
waiting.delete(w);
|
|
47
|
+
const r = e;
|
|
48
|
+
waiting.add({
|
|
49
|
+
context: r.context,
|
|
50
|
+
consumer: r.consumer,
|
|
51
|
+
via: e.composedPath()[0] ?? r.consumer,
|
|
52
|
+
callback: r.callback,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
document.addEventListener('context-provider', (e) => {
|
|
56
|
+
const key = e.context.key;
|
|
57
|
+
for (const w of [...waiting]) {
|
|
58
|
+
if (w.context.key !== key)
|
|
59
|
+
continue;
|
|
60
|
+
waiting.delete(w);
|
|
61
|
+
// Unanswered again: it waits again on its way through `document`.
|
|
62
|
+
if (w.consumer.isConnected) {
|
|
63
|
+
w.via.dispatchEvent(new ContextRequestEvent(w.context, w.consumer, w.callback));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Ask for `context` for `consumer`, through `via` (an element of the provider's subtree). */
|
|
69
|
+
export function requestContext(consumer, context, callback, via = null) {
|
|
70
|
+
collect();
|
|
71
|
+
(via ?? consumer).dispatchEvent(new ContextRequestEvent(context, consumer, callback));
|
|
72
|
+
}
|
|
73
|
+
/** Answer requests for `context` reaching `host`; returns the function that stops answering. */
|
|
74
|
+
export function provideContext(host, context, value) {
|
|
75
|
+
collect();
|
|
76
|
+
const answer = (e) => {
|
|
77
|
+
const r = e;
|
|
78
|
+
if (r.context?.key !== context.key)
|
|
79
|
+
return;
|
|
80
|
+
e.stopPropagation();
|
|
81
|
+
r.callback(value());
|
|
82
|
+
};
|
|
83
|
+
host.addEventListener('context-request', answer);
|
|
84
|
+
host.dispatchEvent(new ContextProviderEvent(context));
|
|
85
|
+
return () => host.removeEventListener('context-request', answer);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/controller.d.ts
CHANGED
|
@@ -1,52 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The canvas side of the viewer: a `DiffSession` (state, commands, events; `session.ts`) plus
|
|
3
|
+
* the one view that draws it (RFC 0001 §1: renderer, camera, framing, minimap), behind the
|
|
4
|
+
* `SessionView` boundary. Phase 3 of the RFC moves this view out into a `StageView` of its own,
|
|
5
|
+
* so one session can drive several canvases.
|
|
3
6
|
*
|
|
4
|
-
* -
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
*
|
|
11
|
-
* The UI subscribes to topic notifications (batched per microtask) and reads public fields.
|
|
7
|
+
* - draws the session's view state into the renderer (and the minimap, throttled while
|
|
8
|
+
* replaying);
|
|
9
|
+
* - frames what the session asks for in the free part of the canvas, clear of the chrome over
|
|
10
|
+
* it (`framing.ts`); the replay camera follows the playing hunk / chapter unless the user
|
|
11
|
+
* moved it in the last 2 s;
|
|
12
|
+
* - fits a new diff once the canvas is laid out, and again on resize with `autoFit`;
|
|
13
|
+
* - turns canvas hovers and clicks into session commands.
|
|
12
14
|
*/
|
|
13
|
-
import type {
|
|
14
|
-
import type { LayoutResult } from '@kgdiff/layout';
|
|
15
|
+
import type { Rect } from '@kgdiff/core';
|
|
15
16
|
import { type Camera } from '@kgdiff/renderer';
|
|
16
|
-
import { type ChangeFamily, type FocusState, type PaletteName, type Scene, type Theme, type ThemeName, type VisualResolver } from '@kgdiff/scene';
|
|
17
17
|
import { type ChromeLayout } from './framing.js';
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import { type StorageLike } from './review-store.js';
|
|
23
|
-
import { type SearchHit } from './search.js';
|
|
24
|
-
export type Status = 'idle' | 'loading' | 'ready' | 'error';
|
|
25
|
-
export type Topic = 'status' | 'data' | 'mode' | 'selection' | 'filters' | 'review' | 'replay' | 'hover' | 'theme' | 'locale' | 'focus' | 'swipe';
|
|
26
|
-
export interface ControllerHost {
|
|
27
|
-
ready(stats: DiffStats): void;
|
|
28
|
-
select(hunk: string | null, node: string | null): void;
|
|
29
|
-
mode(mode: ViewerMode): void;
|
|
30
|
-
review(hunk: string, reviewed: boolean, progress: ReviewState): void;
|
|
31
|
-
error(error: Error): void;
|
|
32
|
-
announce(text: string): void;
|
|
33
|
-
/** Playback reached the end of the replay; `loop`: it starts again after the loop delay. */
|
|
34
|
-
replayEnd(loop: boolean): void;
|
|
35
|
-
}
|
|
18
|
+
import { DiffSession, type SessionDeps, type SessionHost, type SessionView } from './session.js';
|
|
19
|
+
export type { Preference, Status, Topic } from './session.js';
|
|
20
|
+
export type ControllerHost = SessionHost;
|
|
21
|
+
export type ControllerDeps = SessionDeps;
|
|
36
22
|
export interface FitOptions {
|
|
37
23
|
readonly animate?: boolean;
|
|
38
24
|
/** Room around the content, px (default `FRAME_PADDING`); kept for later automatic fits. */
|
|
39
25
|
readonly padding?: number;
|
|
40
26
|
}
|
|
41
|
-
export interface ControllerDeps {
|
|
42
|
-
readonly now: () => number;
|
|
43
|
-
readonly requestFrame: (cb: (time: number) => void) => number;
|
|
44
|
-
readonly cancelFrame: (id: number) => void;
|
|
45
|
-
/** Resolves once the browser had a chance to paint (between loading steps). */
|
|
46
|
-
readonly yieldToPaint: () => Promise<void>;
|
|
47
|
-
readonly storage: StorageLike | null;
|
|
48
|
-
}
|
|
49
|
-
export type Preference = 'mode' | 'theme' | 'palette' | 'locale';
|
|
50
27
|
/** Camera follow is suppressed this long after the user moved the camera (ms). */
|
|
51
28
|
export declare const USER_CAMERA_GRACE = 2000;
|
|
52
29
|
/** The camera follows when less than this share of the target bounds is in view. */
|
|
@@ -55,42 +32,12 @@ export declare const FOLLOW_COVERAGE = 0.7;
|
|
|
55
32
|
export declare const FLY_MAX_ZOOM = 1.8;
|
|
56
33
|
/** Fitting everything never zooms in further (the renderer's default). */
|
|
57
34
|
export declare const FIT_MAX_ZOOM = 2.5;
|
|
35
|
+
/** Everything counts as in view (the minimap hides) from this share of the content bounds on. */
|
|
36
|
+
export declare const OVERVIEW_COVERAGE = 0.98;
|
|
58
37
|
/** Share of `bounds` inside `view`, in [0, 1]. */
|
|
59
38
|
export declare function coverage(bounds: Rect, view: Rect): number;
|
|
60
|
-
export declare class ViewerController {
|
|
39
|
+
export declare class ViewerController extends DiffSession implements SessionView {
|
|
61
40
|
#private;
|
|
62
|
-
status: Status;
|
|
63
|
-
/** Steps of the current load, in order; `step` is the running one. */
|
|
64
|
-
steps: LoadStep[];
|
|
65
|
-
step: LoadStep | null;
|
|
66
|
-
error: Error | null;
|
|
67
|
-
data: LoadedDiff | null;
|
|
68
|
-
layout: LayoutResult | null;
|
|
69
|
-
scene: Scene | null;
|
|
70
|
-
resolver: VisualResolver | null;
|
|
71
|
-
list: ListModel;
|
|
72
|
-
mode: ViewerMode;
|
|
73
|
-
themePreference: ThemePreference;
|
|
74
|
-
palette: PaletteName;
|
|
75
|
-
locale: Locale;
|
|
76
|
-
systemDark: boolean;
|
|
77
|
-
reducedMotion: boolean;
|
|
78
|
-
/** Preferences set explicitly by the embedder (a report's `view` does not override them). */
|
|
79
|
-
readonly explicit: Set<Preference>;
|
|
80
|
-
selectedHunk: number;
|
|
81
|
-
selectedNode: number;
|
|
82
|
-
/** An edge emphasized from the detail panel (drawn as hovered). */
|
|
83
|
-
pinnedEdge: number;
|
|
84
|
-
hoverNode: number;
|
|
85
|
-
hoverEdge: number;
|
|
86
|
-
hoverGroup: number;
|
|
87
|
-
readonly families: Set<ChangeFamily>;
|
|
88
|
-
hops: number;
|
|
89
|
-
readonly expanded: Set<string>;
|
|
90
|
-
focus: FocusState | null;
|
|
91
|
-
clock: ReplayClock | null;
|
|
92
|
-
split: number;
|
|
93
|
-
emphasis: number;
|
|
94
41
|
/**
|
|
95
42
|
* Keep the view fitted (embeds without controls): fit again when the canvas is resized unless
|
|
96
43
|
* the user moved the camera since, and at the start of every loop.
|
|
@@ -98,21 +45,9 @@ export declare class ViewerController {
|
|
|
98
45
|
autoFit: boolean;
|
|
99
46
|
/** Canvas clicks select (and double clicks fly); off for embeds without controls. */
|
|
100
47
|
selectOnClick: boolean;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
flush(): void;
|
|
105
|
-
get themeName(): ThemeName;
|
|
106
|
-
get theme(): Theme;
|
|
107
|
-
get diff(): DiffResult | null;
|
|
108
|
-
get hunkCount(): number;
|
|
109
|
-
isReviewed(hunkId: string): boolean;
|
|
110
|
-
reviewState(): ReviewState;
|
|
111
|
-
search(query: string, limit?: number): SearchHit[];
|
|
112
|
-
load(options: LoadOptions): Promise<void>;
|
|
113
|
-
loadPayload(payload: ReportPayload): Promise<void>;
|
|
114
|
-
/** Show an error state (bad payload, failed load). */
|
|
115
|
-
fail(error: Error): void;
|
|
48
|
+
/** The whole content is in view (the minimap has nothing to add). */
|
|
49
|
+
overview: boolean;
|
|
50
|
+
constructor(host: SessionHost, deps: SessionDeps);
|
|
116
51
|
attach(stage: HTMLElement, minimapHost: HTMLElement | null): void;
|
|
117
52
|
detach(): void;
|
|
118
53
|
destroy(): void;
|
|
@@ -134,99 +69,21 @@ export declare class ViewerController {
|
|
|
134
69
|
layoutChanged(): void;
|
|
135
70
|
/** The user panned / zoomed: suspend replay camera follow for `USER_CAMERA_GRACE`. */
|
|
136
71
|
noteUserCamera(): void;
|
|
137
|
-
/** Redraw on the next animation frame. */
|
|
138
|
-
invalidate(): void;
|
|
139
72
|
/** Resolve the current view and hand it to the renderer (and the minimap). */
|
|
140
73
|
draw(now?: number): void;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
selectHunk(h: number, options?: {
|
|
150
|
-
fly?: boolean;
|
|
151
|
-
node?: number;
|
|
152
|
-
}): void;
|
|
153
|
-
/** Select a node: its hunk when it has one, else the node itself. */
|
|
154
|
-
selectNode(i: number, options?: {
|
|
155
|
-
fly?: boolean;
|
|
156
|
-
}): void;
|
|
157
|
-
clearSelection(): void;
|
|
158
|
-
next(): void;
|
|
159
|
-
prev(): void;
|
|
160
|
-
first(): void;
|
|
161
|
-
last(): void;
|
|
162
|
-
/** Emphasize an edge (from the detail panel) and fly to it. */
|
|
163
|
-
pinEdge(j: number): void;
|
|
164
|
-
flyToHunk(h: number): void;
|
|
165
|
-
flyToNode(i: number): void;
|
|
166
|
-
flyToSelection(): void;
|
|
74
|
+
sceneChanged(): void;
|
|
75
|
+
installed(): void;
|
|
76
|
+
drawnChanged(): void;
|
|
77
|
+
show(bounds: Rect): void;
|
|
78
|
+
/** Follow `bounds` (replay): fly unless the user steered recently or it is mostly visible. */
|
|
79
|
+
follow(bounds: Rect): void;
|
|
80
|
+
looped(): void;
|
|
81
|
+
viewport(): Rect | null;
|
|
167
82
|
/**
|
|
168
83
|
* Fit everything visible (the focus in changes-only mode), labels included, into the free part
|
|
169
84
|
* of the canvas. Before the canvas is laid out it falls back to the renderer's own fit.
|
|
170
85
|
*/
|
|
171
86
|
fit(options?: FitOptions): Promise<void>;
|
|
172
87
|
zoom(direction: 1 | -1): void;
|
|
173
|
-
/** Clear hover state (pointer left the canvas). */
|
|
174
|
-
clearHover(): void;
|
|
175
|
-
toggleFamily(family: ChangeFamily): void;
|
|
176
|
-
/** Replace the family filter (empty = all). Setting the current set again clears it. */
|
|
177
|
-
setFamilies(families: readonly ChangeFamily[]): void;
|
|
178
|
-
setHops(hops: number): void;
|
|
179
|
-
/**
|
|
180
|
-
* Expand collapsed group `index` of the current focus: its in-scene members become visible
|
|
181
|
-
* and, for a `changes`-scoped layout, the neighbours left out of the layout are placed with
|
|
182
|
-
* `extendLayout` (nothing already placed moves) and the scene is rebuilt.
|
|
183
|
-
*/
|
|
184
|
-
expandGroup(index: number): void;
|
|
185
|
-
/**
|
|
186
|
-
* The collapsed group nearest the selection (else the view center), or -1. Groups that can
|
|
187
|
-
* be expanded at least partly come first.
|
|
188
|
-
*/
|
|
189
|
-
nearestGroup(): number;
|
|
190
|
-
/** Expand the group nearest the selection (keyboard `e`); announces what happened. */
|
|
191
|
-
expandNearest(): void;
|
|
192
|
-
/**
|
|
193
|
-
* Whether collapsed group `index` can be expanded completely: always, unless some members lie
|
|
194
|
-
* outside a `changes`-scoped layout and the layout engine is known to be unavailable
|
|
195
|
-
* (standalone report without the compute add-on).
|
|
196
|
-
*/
|
|
197
|
-
canExpand(index: number): boolean;
|
|
198
|
-
/** Bring a union node into the scene and the focus (search results), then select it. */
|
|
199
|
-
revealNode(id: UnionId): Promise<void>;
|
|
200
|
-
play(): void;
|
|
201
|
-
/** Pause; a looping replay stays paused (until `play`, a new diff or loop turned on again). */
|
|
202
|
-
pause(): void;
|
|
203
|
-
togglePlay(): void;
|
|
204
|
-
/**
|
|
205
|
-
* Play the replay over and over: `delay` ms after it ends it starts again; null plays it
|
|
206
|
-
* once. Turning the loop on starts a replay that is shown but not playing.
|
|
207
|
-
*/
|
|
208
|
-
setLoop(delay: number | null): void;
|
|
209
|
-
get loopDelay(): number | null;
|
|
210
|
-
/**
|
|
211
|
-
* Hold playback while the pointer rests on the graph (`pause-on-hover`): a playing replay
|
|
212
|
-
* pauses and continues on release; a pending loop restart waits for the release, then the
|
|
213
|
-
* full loop delay.
|
|
214
|
-
*/
|
|
215
|
-
hold(held: boolean): void;
|
|
216
|
-
seek(t: number): void;
|
|
217
|
-
stepReplay(direction: 1 | -1): void;
|
|
218
|
-
restart(): void;
|
|
219
|
-
cycleSpeed(): void;
|
|
220
|
-
setSpeed(speed: number): void;
|
|
221
|
-
setSplit(split: number): void;
|
|
222
|
-
setReviewed(hunkId: string, reviewed: boolean): void;
|
|
223
|
-
/** Another tab changed review progress (`storage` event): re-read it. */
|
|
224
|
-
syncReview(key: string | null): void;
|
|
225
|
-
toggleReviewed(hunkId?: string): void;
|
|
226
|
-
setThemePreference(pref: ThemePreference): void;
|
|
227
|
-
setPalette(palette: PaletteName): void;
|
|
228
|
-
setLocale(locale: Locale): void;
|
|
229
|
-
setSystemDark(dark: boolean): void;
|
|
230
|
-
setReducedMotion(reduced: boolean): void;
|
|
231
88
|
}
|
|
232
89
|
//# sourceMappingURL=controller.d.ts.map
|
package/dist/controller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,KAAK,MAAM,EAMZ,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EACL,KAAK,YAAY,EAOlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EAEX,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC;AACzC,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC;AAEzC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,oFAAoF;AACpF,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,sFAAsF;AACtF,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,0EAA0E;AAC1E,eAAO,MAAM,YAAY,MAAM,CAAC;AAGhC,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAQtC,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAazD;AAED,qBAAa,gBAAiB,SAAQ,WAAY,YAAW,WAAW;;IACtE;;;OAGG;IACH,OAAO,UAAS;IAChB,qFAAqF;IACrF,aAAa,UAAQ;IACrB,qEAAqE;IACrE,QAAQ,UAAQ;IAyBhB,YAAY,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAI/C;IAID,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAsBhE;IAED,MAAM,IAAI,IAAI,CASb;IAEQ,OAAO,IAAI,IAAI,CAGvB;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,IAAI,CAE1B;IAED;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,YAAY,CAAC,GAAG,IAAI,GAAG,IAAI,CAErD;IAED;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAGrC;IAED;;;OAGG;IACH,aAAa,IAAI,IAAI,CAmBpB;IAED,sFAAsF;IACtF,cAAc,IAAI,IAAI,CAGrB;IAID,8EAA8E;IAC9E,IAAI,CAAC,GAAG,SAAc,GAAG,IAAI,CA0B5B;IAED,YAAY,IAAI,IAAI,CAKnB;IAED,SAAS,IAAI,IAAI,CAMhB;IAED,YAAY,IAAI,IAAI,CAGnB;IAED,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAEvB;IAED,8FAA8F;IAC9F,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAKzB;IAED,MAAM,IAAI,IAAI,CAEb;IAED,QAAQ,IAAI,IAAI,GAAG,IAAI,CAEtB;IA8BD;;;OAGG;IACH,GAAG,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAU3C;IAqED,IAAI,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAG5B;CA4CF"}
|