@kitlangton/knobs 0.3.0 → 0.4.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +350 -45
- package/dist/dev/{chunk-jx1ktap3.js → chunk-1qzffbpa.js} +497 -422
- package/dist/dev/{chunk-7xgkerd2.js → chunk-3ygqgy1x.js} +23 -1
- package/dist/dev/{chunk-pmkvb9ep.js → chunk-8mabjeyk.js} +1920 -507
- package/dist/dev/{chunk-v8ssbcbk.js → chunk-ayb292zj.js} +13 -11
- package/dist/dev/index.js +10 -5
- package/dist/index.js +25 -1
- package/dist/panel.js +2954 -1291
- package/dist/react.js +294 -6
- package/dist/solid.js +292 -4
- package/dist/themes.js +1509 -7
- package/dist/types/core/adapter.d.ts +6 -2
- package/dist/types/core/appearance.d.ts +44 -0
- package/dist/types/core/glide.d.ts +7 -0
- package/dist/types/core/motion.d.ts +66 -0
- package/dist/types/core/root.d.ts +13 -0
- package/dist/types/core/store.d.ts +18 -5
- package/dist/types/core/theme.d.ts +10 -3
- package/dist/types/index.d.ts +2 -1
- package/dist/types/panel/gpu/color.d.ts +6 -0
- package/dist/types/panel/motion.d.ts +1 -1
- package/dist/types/panel/styles.d.ts +2 -3
- package/dist/types/themes/define.d.ts +104 -0
- package/dist/types/themes/index.d.ts +11 -0
- package/dist/types/themes/knobs.d.ts +5 -0
- package/dist/types/themes/void.d.ts +8 -0
- package/dist/types/tokens/color.d.ts +42 -0
- package/dist/types/tokens/css.d.ts +13 -0
- package/dist/types/tokens/evaluate.d.ts +8 -0
- package/dist/types/tokens/index.d.ts +4 -0
- package/dist/types/tokens/schema.d.ts +1100 -0
- package/dist/types/ui/bento-css.d.ts +1 -1
- package/dist/types/ui/compare-css.d.ts +1 -0
- package/dist/types/ui/compare-frames.d.ts +41 -0
- package/dist/types/ui/compare-gpu.d.ts +58 -0
- package/dist/types/ui/compare-model.d.ts +169 -0
- package/dist/types/ui/compare-mp4.d.ts +29 -0
- package/dist/types/ui/compare-strip.d.ts +28 -0
- package/dist/types/ui/compare.d.ts +103 -0
- package/dist/types/ui/diff-css.d.ts +1 -0
- package/dist/types/ui/diff-model.d.ts +128 -0
- package/dist/types/ui/diff-scroll.d.ts +29 -0
- package/dist/types/ui/diff.d.ts +158 -0
- package/dist/types/ui/index.d.ts +10 -2
- package/dist/types/ui/look.d.ts +18 -13
- package/dist/types/ui/overlay-css.d.ts +1 -1
- package/dist/types/ui/react.d.ts +66 -0
- package/dist/types/ui/readout-css.d.ts +1 -1
- package/dist/types/ui/readout-model.d.ts +5 -1
- package/dist/types/ui/rgba.d.ts +11 -0
- package/dist/types/ui/scrollbar.d.ts +2 -2
- package/dist/types/ui/solid.d.ts +52 -0
- package/dist/types/ui/timeline.d.ts +35 -2
- package/dist/types/ui/tree.d.ts +7 -2
- package/dist/types/ui/walkthrough-css.d.ts +1 -0
- package/dist/types/ui/walkthrough-media.d.ts +29 -0
- package/dist/types/ui/walkthrough-model.d.ts +447 -0
- package/dist/types/ui/walkthrough.d.ts +63 -0
- package/dist/ui-react.js +18352 -8462
- package/dist/ui-solid.js +17947 -8148
- package/dist/ui.js +19240 -9701
- package/package.json +6 -1
package/dist/types/ui/tree.d.ts
CHANGED
|
@@ -29,8 +29,8 @@ export interface TreeOptions<T> extends UiLook {
|
|
|
29
29
|
readonly getLabel?: (item: T) => string;
|
|
30
30
|
/** A glyph, SVG markup or node before the label. */
|
|
31
31
|
readonly icon?: (item: T) => string | Node | undefined;
|
|
32
|
-
/** Dimmed text after the label
|
|
33
|
-
readonly detail?: (item: T) => string | undefined;
|
|
32
|
+
/** Dimmed text (or a node of your own) after the label: a count, a kind. */
|
|
33
|
+
readonly detail?: (item: T) => string | Node | undefined;
|
|
34
34
|
/** Your own content in place of the label. */
|
|
35
35
|
readonly render?: (item: T, row: TreeRowContext) => Node | string | undefined | void;
|
|
36
36
|
readonly selection?: Iterable<string>;
|
|
@@ -63,6 +63,11 @@ export interface TreeOptions<T> extends UiLook {
|
|
|
63
63
|
* row as a click does. While the page has hint mode, `s` goes to it rather than type-ahead.
|
|
64
64
|
*/
|
|
65
65
|
readonly hints?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* The selected row (one) shows as one highlight that glides to the next row selected, on the
|
|
68
|
+
* rows' own spring, instead of each row lighting in place. Several selected rows light in place.
|
|
69
|
+
*/
|
|
70
|
+
readonly glide?: boolean;
|
|
66
71
|
}
|
|
67
72
|
export interface Tree<T> {
|
|
68
73
|
readonly element: HTMLElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const walkthroughCss: string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type UiLook } from "./look.js";
|
|
2
|
+
import { type ContextEvent, type MediaContext, type WalkthroughMedia } from "./walkthrough-model.js";
|
|
3
|
+
export interface MediaSlotOptions extends UiLook {
|
|
4
|
+
readonly media: WalkthroughMedia;
|
|
5
|
+
/** Mount heavy kinds near the view and let them go far from it (on by default). */
|
|
6
|
+
readonly lazy?: boolean;
|
|
7
|
+
/** Kit played, paused or sought a video, or showed another diff file (for a note's log). */
|
|
8
|
+
readonly onEvent?: (event: Omit<ContextEvent, "at" | "step">) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface MediaSlot {
|
|
11
|
+
readonly element: HTMLElement;
|
|
12
|
+
/** Whether its content is mounted (heavy kinds wait until they're near the view). */
|
|
13
|
+
readonly mounted: boolean;
|
|
14
|
+
/** Pauses any video in it. */
|
|
15
|
+
pause(): void;
|
|
16
|
+
/** Moves its video (or compare) by `seconds`; false when there's none mounted. */
|
|
17
|
+
seekBy(seconds: number): boolean;
|
|
18
|
+
/** What it's showing now: a video's time, a compare's loop, zoom and mode, a diff's file. */
|
|
19
|
+
context(): MediaContext | null;
|
|
20
|
+
/** A zoomed compare back to the whole picture; false when nothing was zoomed. */
|
|
21
|
+
unzoom(): boolean;
|
|
22
|
+
/** Fills the slot's height (its step full window) or goes back: a diff switches its height. */
|
|
23
|
+
fill(on: boolean): void;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
}
|
|
26
|
+
/** Adds the walkthrough's stylesheet once per document. */
|
|
27
|
+
export declare function injectWalkthroughStyles(doc: Document): void;
|
|
28
|
+
export declare function mediaSlot(container: HTMLElement, options: MediaSlotOptions): MediaSlot;
|
|
29
|
+
export declare const CHECK: string;
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import type { CompareMode } from "./compare-model.js";
|
|
2
|
+
import type { DiffSource } from "./diff-model.js";
|
|
3
|
+
/** Where a PR or topic stands, as the agent sees it: a chip beside its title. */
|
|
4
|
+
export type WalkthroughStatus = "ready" | "needs-taste" | "draft" | "blocked" | "merged" | (string & {});
|
|
5
|
+
/** A picture or video, by URL. */
|
|
6
|
+
export interface WalkthroughSource {
|
|
7
|
+
readonly src: string;
|
|
8
|
+
readonly label?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* What a step shows. A bare string is a video or image URL (by its extension). `mount` is the one
|
|
12
|
+
* kind that isn't JSON: anything of your own, given its element, returning its cleanup.
|
|
13
|
+
*/
|
|
14
|
+
export type WalkthroughMedia = string | {
|
|
15
|
+
readonly kind: "compare";
|
|
16
|
+
readonly before: string | WalkthroughSource;
|
|
17
|
+
readonly after: string | WalkthroughSource;
|
|
18
|
+
/** `"side"` by default (`"wipe"` where the slot is narrow). */
|
|
19
|
+
readonly mode?: CompareMode;
|
|
20
|
+
/** Width over height (`16 / 9`, or `"1100/900"`); read from the video when left out. */
|
|
21
|
+
readonly aspect?: number | string;
|
|
22
|
+
/** The frame strip over the timeline. */
|
|
23
|
+
readonly strip?: boolean;
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: "video";
|
|
26
|
+
readonly src: string;
|
|
27
|
+
readonly label?: string;
|
|
28
|
+
readonly aspect?: number | string;
|
|
29
|
+
readonly poster?: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly kind: "image";
|
|
32
|
+
readonly src: string;
|
|
33
|
+
readonly label?: string;
|
|
34
|
+
readonly alt?: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: "diff";
|
|
37
|
+
/** `git diff` / `git show` output. */
|
|
38
|
+
readonly patch?: string;
|
|
39
|
+
readonly files?: readonly DiffSource[];
|
|
40
|
+
readonly file?: string;
|
|
41
|
+
readonly layout?: "split" | "unified";
|
|
42
|
+
/** With the file list beside it (`diffReview`), for many files. */
|
|
43
|
+
readonly review?: boolean;
|
|
44
|
+
/** Pixels: the diff grows to its file up to this (640), or the review's height (480). */
|
|
45
|
+
readonly height?: number;
|
|
46
|
+
} | {
|
|
47
|
+
readonly kind: "code" | "terminal";
|
|
48
|
+
readonly code: string;
|
|
49
|
+
readonly label?: string;
|
|
50
|
+
readonly lang?: string;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "metric";
|
|
53
|
+
readonly label: string;
|
|
54
|
+
readonly before: number | string;
|
|
55
|
+
readonly after: number | string;
|
|
56
|
+
/** Out of: "315 → 345 of 345". */
|
|
57
|
+
readonly of?: number | string;
|
|
58
|
+
readonly unit?: string;
|
|
59
|
+
/** Which way is better (`"up"`): colours the change. */
|
|
60
|
+
readonly better?: "up" | "down";
|
|
61
|
+
} | {
|
|
62
|
+
readonly kind: "callout";
|
|
63
|
+
readonly text: string;
|
|
64
|
+
readonly tone?: CalloutTone;
|
|
65
|
+
} | {
|
|
66
|
+
readonly kind: "text";
|
|
67
|
+
readonly text: string;
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: "mount";
|
|
70
|
+
readonly mount: (element: HTMLElement) => (() => void) | void;
|
|
71
|
+
/** Pixels held for it before it mounts. */
|
|
72
|
+
readonly height?: number;
|
|
73
|
+
};
|
|
74
|
+
export type CalloutTone = "info" | "good" | "warn" | "bad";
|
|
75
|
+
/** One option in a taste test. */
|
|
76
|
+
export interface WalkthroughOption {
|
|
77
|
+
/** "a", "b"… by position when left out. */
|
|
78
|
+
readonly id?: string;
|
|
79
|
+
readonly label: string;
|
|
80
|
+
readonly caption?: string;
|
|
81
|
+
readonly media?: WalkthroughMedia;
|
|
82
|
+
/** The agent's own pick, marked "Suggested". */
|
|
83
|
+
readonly suggested?: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface WalkthroughStep {
|
|
86
|
+
/** From the title when left out. */
|
|
87
|
+
readonly id?: string;
|
|
88
|
+
readonly title: string;
|
|
89
|
+
/** A line or two: what it shows and how it was made. `code`, **bold** and [links](…) work. */
|
|
90
|
+
readonly caption?: string;
|
|
91
|
+
/** One slot; two or more sit side by side, stacking where it's narrow. */
|
|
92
|
+
readonly media?: WalkthroughMedia | readonly WalkthroughMedia[];
|
|
93
|
+
/** A taste test: Kit picks one. */
|
|
94
|
+
readonly options?: readonly WalkthroughOption[];
|
|
95
|
+
/**
|
|
96
|
+
* What Kit is asked for: a note (the default), a pick (with options), or nothing. `"verdict"`,
|
|
97
|
+
* from older documents, reads as `"note"`.
|
|
98
|
+
*/
|
|
99
|
+
readonly ask?: "note" | "pick" | "none" | "verdict";
|
|
100
|
+
/** The question, over the step's media or options. */
|
|
101
|
+
readonly question?: string;
|
|
102
|
+
}
|
|
103
|
+
export interface WalkthroughSection {
|
|
104
|
+
readonly id?: string;
|
|
105
|
+
readonly title: string;
|
|
106
|
+
/** A PR or issue: its URL (labelled `#123` from a GitHub URL), or a label and URL. */
|
|
107
|
+
readonly link?: string | {
|
|
108
|
+
readonly href: string;
|
|
109
|
+
readonly label?: string;
|
|
110
|
+
};
|
|
111
|
+
readonly status?: WalkthroughStatus;
|
|
112
|
+
readonly summary?: string;
|
|
113
|
+
readonly steps: readonly WalkthroughStep[];
|
|
114
|
+
}
|
|
115
|
+
export interface WalkthroughMeta {
|
|
116
|
+
readonly label: string;
|
|
117
|
+
readonly value: string;
|
|
118
|
+
readonly href?: string;
|
|
119
|
+
}
|
|
120
|
+
/** Something the agent did since Kit last looked: newest first at the top of the page. */
|
|
121
|
+
export interface WalkthroughUpdate {
|
|
122
|
+
/** When: an ISO time (`2026-10-19T14:05:00-07:00`). */
|
|
123
|
+
readonly at: string;
|
|
124
|
+
readonly text: string;
|
|
125
|
+
/** The step it's about: its key, id or number ("1.2"). */
|
|
126
|
+
readonly step?: string;
|
|
127
|
+
}
|
|
128
|
+
/** A question Kit asked and the agent's answer. */
|
|
129
|
+
export interface WalkthroughQA {
|
|
130
|
+
/** When it was answered: an ISO time. */
|
|
131
|
+
readonly at?: string;
|
|
132
|
+
readonly q: string;
|
|
133
|
+
readonly a: string;
|
|
134
|
+
readonly step?: string;
|
|
135
|
+
}
|
|
136
|
+
/** The document an agent writes: plain data, JSON but for `mount` media. */
|
|
137
|
+
export interface WalkthroughDoc {
|
|
138
|
+
/** Kit's feedback is kept under this id; a new id starts fresh. */
|
|
139
|
+
readonly id: string;
|
|
140
|
+
readonly title: string;
|
|
141
|
+
readonly summary?: string;
|
|
142
|
+
/** Facts beside the title: branch, commit, date… */
|
|
143
|
+
readonly meta?: Readonly<Record<string, string>> | readonly WalkthroughMeta[];
|
|
144
|
+
/** Relative media URLs resolve against this. */
|
|
145
|
+
readonly base?: string;
|
|
146
|
+
/** Older documents' verdicts, kept in the data and the export (no buttons draw them). */
|
|
147
|
+
readonly verdicts?: readonly string[];
|
|
148
|
+
/** What the agent has done since, newest shown first. */
|
|
149
|
+
readonly updates?: readonly WalkthroughUpdate[];
|
|
150
|
+
/** Kit's questions and the agent's answers. */
|
|
151
|
+
readonly qa?: readonly WalkthroughQA[];
|
|
152
|
+
readonly sections: readonly WalkthroughSection[];
|
|
153
|
+
}
|
|
154
|
+
type CompareMedia = Extract<WalkthroughMedia, {
|
|
155
|
+
kind: "compare";
|
|
156
|
+
}>;
|
|
157
|
+
/** Media filled in: no bare URLs, and a compare's sides as sources. */
|
|
158
|
+
export type WalkMedia = Exclude<WalkthroughMedia, string | CompareMedia> | (Omit<CompareMedia, "before" | "after"> & {
|
|
159
|
+
readonly before: WalkthroughSource;
|
|
160
|
+
readonly after: WalkthroughSource;
|
|
161
|
+
});
|
|
162
|
+
export interface WalkOption {
|
|
163
|
+
readonly id: string;
|
|
164
|
+
readonly label: string;
|
|
165
|
+
/** "A", "B"… */
|
|
166
|
+
readonly letter: string;
|
|
167
|
+
readonly caption?: string;
|
|
168
|
+
readonly media?: WalkMedia;
|
|
169
|
+
readonly suggested: boolean;
|
|
170
|
+
}
|
|
171
|
+
export interface WalkStep {
|
|
172
|
+
/** `section/step`: where its feedback is kept. */
|
|
173
|
+
readonly key: string;
|
|
174
|
+
readonly id: string;
|
|
175
|
+
readonly section: string;
|
|
176
|
+
/** "1.2": section, then step. */
|
|
177
|
+
readonly number: string;
|
|
178
|
+
readonly title: string;
|
|
179
|
+
readonly caption?: string;
|
|
180
|
+
readonly question?: string;
|
|
181
|
+
readonly media: readonly WalkMedia[];
|
|
182
|
+
readonly options: readonly WalkOption[];
|
|
183
|
+
readonly ask: "note" | "pick" | "none";
|
|
184
|
+
}
|
|
185
|
+
export interface WalkSection {
|
|
186
|
+
readonly id: string;
|
|
187
|
+
readonly title: string;
|
|
188
|
+
readonly link?: {
|
|
189
|
+
readonly href: string;
|
|
190
|
+
readonly label: string;
|
|
191
|
+
};
|
|
192
|
+
readonly status?: WalkthroughStatus;
|
|
193
|
+
readonly summary?: string;
|
|
194
|
+
readonly steps: readonly WalkStep[];
|
|
195
|
+
}
|
|
196
|
+
export interface WalkVerdict {
|
|
197
|
+
readonly id: string;
|
|
198
|
+
readonly label: string;
|
|
199
|
+
}
|
|
200
|
+
/** One line of the feed: an update, or a question and its answer. */
|
|
201
|
+
export interface FeedItem {
|
|
202
|
+
readonly kind: "update" | "qa";
|
|
203
|
+
/** Epoch ms, or null when `at` isn't a time. */
|
|
204
|
+
readonly at: number | null;
|
|
205
|
+
readonly text: string;
|
|
206
|
+
/** The question, for `qa`. */
|
|
207
|
+
readonly q?: string;
|
|
208
|
+
/** The step's key, when it names one that's there. */
|
|
209
|
+
readonly step: string | null;
|
|
210
|
+
}
|
|
211
|
+
export interface WalkPage {
|
|
212
|
+
readonly id: string;
|
|
213
|
+
readonly title: string;
|
|
214
|
+
readonly summary?: string;
|
|
215
|
+
readonly meta: readonly WalkthroughMeta[];
|
|
216
|
+
readonly verdicts: readonly WalkVerdict[];
|
|
217
|
+
readonly sections: readonly WalkSection[];
|
|
218
|
+
/** Every step, in reading order. */
|
|
219
|
+
readonly steps: readonly WalkStep[];
|
|
220
|
+
/** Updates and answers, newest first. */
|
|
221
|
+
readonly feed: readonly FeedItem[];
|
|
222
|
+
}
|
|
223
|
+
export declare const DEFAULT_VERDICTS: readonly ["ship", "change", "unsure"];
|
|
224
|
+
/** A URL-safe id from a title: "Retired model (HTTP 410)" → "retired-model-http-410". */
|
|
225
|
+
export declare function slug(text: string): string;
|
|
226
|
+
/** A relative URL against `base` (none: as written). Absolute URLs, data: and blob: stay. */
|
|
227
|
+
export declare function resolveSrc(src: string, base?: string): string;
|
|
228
|
+
/** A bare URL as media: a video by its extension, else an image. */
|
|
229
|
+
export declare const mediaOf: (src: string) => WalkMedia;
|
|
230
|
+
/** Width over height from `16/9`, `"1100/900"`, `"1100x900"` or `1.5`; undefined if unreadable. */
|
|
231
|
+
export declare function parseAspect(aspect: number | string | undefined): number | undefined;
|
|
232
|
+
/** Media filled in: a bare URL as a video or image, sources resolved against `base`. */
|
|
233
|
+
export declare function fillMedia(m: WalkthroughMedia, base?: string): WalkMedia;
|
|
234
|
+
/** A GitHub PR or issue URL's number as `#123`; else the URL's last part. */
|
|
235
|
+
export declare function linkLabel(href: string): string;
|
|
236
|
+
/**
|
|
237
|
+
* The document filled in: ids from titles where left out (unique), each step's key, number and
|
|
238
|
+
* ask, options lettered, media URLs resolved against `base`, and the verdicts labelled.
|
|
239
|
+
*/
|
|
240
|
+
export declare function normalizeWalkthrough(doc: WalkthroughDoc): WalkPage;
|
|
241
|
+
/** An ISO time as epoch ms; null when it isn't one. */
|
|
242
|
+
export declare function parseAt(at: string | undefined): number | null;
|
|
243
|
+
/** Newest first; items without a time after those with one, later-written first. */
|
|
244
|
+
export declare function sortFeed(items: readonly FeedItem[]): FeedItem[];
|
|
245
|
+
/** "just now", "5m ago", "3h ago", "2d ago", else the date. */
|
|
246
|
+
export declare function ago(at: number, now: number): string;
|
|
247
|
+
/** A status chip's words and tone: "needs-taste" → "Needs taste", in the warn colour. */
|
|
248
|
+
export declare function statusLook(status: WalkthroughStatus): {
|
|
249
|
+
label: string;
|
|
250
|
+
tone: "good" | "warn" | "bad" | "dim" | "info";
|
|
251
|
+
};
|
|
252
|
+
/** A verdict's tone: the first is the good one, a "change"/"no" the warning, the rest dim. */
|
|
253
|
+
export declare function verdictTone(id: string, index: number): "good" | "warn" | "bad" | "dim";
|
|
254
|
+
/** A taste test's pick, and what older documents kept per step. */
|
|
255
|
+
export interface StepFeedback {
|
|
256
|
+
readonly pick?: string;
|
|
257
|
+
/** Older documents' verdict buttons. */
|
|
258
|
+
readonly verdict?: string;
|
|
259
|
+
/** Older documents' per-step note field. */
|
|
260
|
+
readonly note?: string;
|
|
261
|
+
}
|
|
262
|
+
/** Ink drawn over a step's media: SVG paths in 0–1 coordinates of the media's box. */
|
|
263
|
+
export interface WalkInk {
|
|
264
|
+
readonly paths: readonly string[];
|
|
265
|
+
/** The box's width over height when drawn, to map the paths back. */
|
|
266
|
+
readonly aspect: number;
|
|
267
|
+
}
|
|
268
|
+
/** A voice note, recorded in the page: a blob URL that lives as long as the page. */
|
|
269
|
+
export interface WalkVoice {
|
|
270
|
+
readonly url: string;
|
|
271
|
+
/** Seconds. */
|
|
272
|
+
readonly duration: number;
|
|
273
|
+
readonly mime?: string;
|
|
274
|
+
}
|
|
275
|
+
/** What the media on show was doing when Kit wrote. */
|
|
276
|
+
export interface MediaContext {
|
|
277
|
+
/** "compare", "video", "diff"… */
|
|
278
|
+
readonly kind: string;
|
|
279
|
+
/** Seconds. */
|
|
280
|
+
readonly time?: number;
|
|
281
|
+
readonly duration?: number;
|
|
282
|
+
readonly playing?: boolean;
|
|
283
|
+
/** The loop range, in seconds. */
|
|
284
|
+
readonly loop?: {
|
|
285
|
+
readonly start: number;
|
|
286
|
+
readonly end: number;
|
|
287
|
+
};
|
|
288
|
+
/** A compare's mode. */
|
|
289
|
+
readonly mode?: string;
|
|
290
|
+
/** The part of the picture zoomed into, 0–1 of each side (`u`, `v` its top-left). */
|
|
291
|
+
readonly zoom?: {
|
|
292
|
+
readonly u: number;
|
|
293
|
+
readonly v: number;
|
|
294
|
+
readonly w: number;
|
|
295
|
+
readonly h: number;
|
|
296
|
+
};
|
|
297
|
+
/** The diff's file on show. */
|
|
298
|
+
readonly file?: string;
|
|
299
|
+
/** It was full-window. */
|
|
300
|
+
readonly full?: boolean;
|
|
301
|
+
}
|
|
302
|
+
/** One thing Kit did, in the rolling log a note carries. */
|
|
303
|
+
export interface ContextEvent {
|
|
304
|
+
/** Epoch ms. */
|
|
305
|
+
readonly at: number;
|
|
306
|
+
readonly what: "step" | "seek" | "play" | "pause" | "file" | "mode" | "zoom" | "loop" | "full" | "pick" | "ink";
|
|
307
|
+
readonly step?: string;
|
|
308
|
+
/** Seconds, for seeks, plays and pauses. */
|
|
309
|
+
readonly time?: number;
|
|
310
|
+
readonly detail?: string;
|
|
311
|
+
}
|
|
312
|
+
export interface NoteContext {
|
|
313
|
+
/** The step in view. */
|
|
314
|
+
readonly step: string | null;
|
|
315
|
+
readonly media?: MediaContext;
|
|
316
|
+
/** What led up to it, oldest first. */
|
|
317
|
+
readonly log: readonly ContextEvent[];
|
|
318
|
+
}
|
|
319
|
+
/** A note Kit sent: words, a voice note, ink, or any mix, with what he was looking at. */
|
|
320
|
+
export interface WalkNote {
|
|
321
|
+
readonly id: string;
|
|
322
|
+
readonly step: string | null;
|
|
323
|
+
/** Epoch ms. */
|
|
324
|
+
readonly at: number;
|
|
325
|
+
readonly text?: string;
|
|
326
|
+
readonly voice?: WalkVoice;
|
|
327
|
+
readonly ink?: WalkInk;
|
|
328
|
+
readonly context: NoteContext;
|
|
329
|
+
}
|
|
330
|
+
/** Everything Kit has said: picks by step, his notes in the order sent, a note on the whole. */
|
|
331
|
+
export interface WalkthroughState {
|
|
332
|
+
readonly steps: Readonly<Record<string, StepFeedback>>;
|
|
333
|
+
readonly notes?: readonly WalkNote[];
|
|
334
|
+
readonly note?: string;
|
|
335
|
+
}
|
|
336
|
+
export declare const EMPTY_STATE: WalkthroughState;
|
|
337
|
+
/** A step's pick (or older fields) changed: blanks drop, and a step with nothing left goes. */
|
|
338
|
+
export declare function setFeedback(state: WalkthroughState, key: string, patch: Partial<Record<keyof StepFeedback, string | null>>): WalkthroughState;
|
|
339
|
+
/** The whole page's note. */
|
|
340
|
+
export declare function setNote(state: WalkthroughState, note: string): WalkthroughState;
|
|
341
|
+
/** Whether a note has anything in it. */
|
|
342
|
+
export declare const hasContent: (n: Pick<WalkNote, "text" | "voice" | "ink">) => boolean;
|
|
343
|
+
export declare function addNote(state: WalkthroughState, note: WalkNote): WalkthroughState;
|
|
344
|
+
export declare function removeNote(state: WalkthroughState, id: string): WalkthroughState;
|
|
345
|
+
/** A step's notes, in the order sent. */
|
|
346
|
+
export declare const notesOn: (state: WalkthroughState, key: string) => WalkNote[];
|
|
347
|
+
/** Whether Kit has answered a step: picked, written, or (older documents) given a verdict. */
|
|
348
|
+
export declare function isAnswered(step: WalkStep, state: WalkthroughState): boolean;
|
|
349
|
+
/**
|
|
350
|
+
* A digit's pick on a taste test (1 the first option): the id it sets, or null when it's the one
|
|
351
|
+
* already picked (pressing it again clears it); undefined when there's none.
|
|
352
|
+
*/
|
|
353
|
+
export declare function digitChoice(step: WalkStep, fb: StepFeedback | undefined, key: string): string | null | undefined;
|
|
354
|
+
/** Steps that ask for something, how many are answered, and the first still open. */
|
|
355
|
+
export declare function walkthroughProgress(w: WalkPage, state: WalkthroughState): {
|
|
356
|
+
done: number;
|
|
357
|
+
total: number;
|
|
358
|
+
next: string | null;
|
|
359
|
+
};
|
|
360
|
+
/** The step before or after `current` (stopping at the ends); the first when there's none. */
|
|
361
|
+
export declare function adjacentStep(keys: readonly string[], current: string | null, dir: -1 | 1): string | undefined;
|
|
362
|
+
/** How many events the log keeps. */
|
|
363
|
+
export declare const LOG_MAX = 40;
|
|
364
|
+
/**
|
|
365
|
+
* The log with `event` on the end: a seek straight after another on the same step replaces it
|
|
366
|
+
* (a scrub is one entry), and only the last `max` stay.
|
|
367
|
+
*/
|
|
368
|
+
export declare function pushContext(log: readonly ContextEvent[], event: ContextEvent, max?: number): ContextEvent[];
|
|
369
|
+
/** What a note carries of the log: the last `windowMs`, at least the last `min` events. */
|
|
370
|
+
export declare function recentContext(log: readonly ContextEvent[], now: number, windowMs?: number, min?: number): ContextEvent[];
|
|
371
|
+
/** Seconds as `m:ss.s`. */
|
|
372
|
+
export declare function formatTime(seconds: number): string;
|
|
373
|
+
/**
|
|
374
|
+
* A stroke as an SVG path in 0–1 coordinates of a `w`×`h` box (three decimals), dropping points
|
|
375
|
+
* closer than a pixel to the last kept one.
|
|
376
|
+
*/
|
|
377
|
+
export declare function inkPath(points: readonly {
|
|
378
|
+
x: number;
|
|
379
|
+
y: number;
|
|
380
|
+
}[], w: number, h: number): string;
|
|
381
|
+
/** A zoom region in words: "2× at 25%, 10%" (its top-left, as a share of the picture). */
|
|
382
|
+
export declare function zoomWords(z: {
|
|
383
|
+
u: number;
|
|
384
|
+
v: number;
|
|
385
|
+
w: number;
|
|
386
|
+
h: number;
|
|
387
|
+
}): string;
|
|
388
|
+
/** A media context in a few words: "compare at 0:04.2 (loop 0:02.0–0:06.0), wipe". */
|
|
389
|
+
export declare function describeMedia(m: MediaContext | undefined): string;
|
|
390
|
+
/**
|
|
391
|
+
* Saved feedback read back: anything malformed is dropped rather than trusted, and steps the
|
|
392
|
+
* document no longer has are kept (an agent's next draft may bring them back).
|
|
393
|
+
*/
|
|
394
|
+
export declare function readState(raw: string | null | undefined): WalkthroughState;
|
|
395
|
+
/** A note as the agent reads it. */
|
|
396
|
+
export interface FeedbackNote {
|
|
397
|
+
/** ISO time. */
|
|
398
|
+
readonly at: string;
|
|
399
|
+
readonly text?: string;
|
|
400
|
+
readonly voice?: WalkVoice;
|
|
401
|
+
readonly ink?: WalkInk;
|
|
402
|
+
readonly media?: MediaContext;
|
|
403
|
+
/** What led up to it: seconds before the note, oldest first. */
|
|
404
|
+
readonly log: readonly (Omit<ContextEvent, "at"> & {
|
|
405
|
+
readonly ago: number;
|
|
406
|
+
})[];
|
|
407
|
+
}
|
|
408
|
+
/** One step's line in the feedback. */
|
|
409
|
+
export interface FeedbackItem {
|
|
410
|
+
readonly key: string;
|
|
411
|
+
readonly number: string;
|
|
412
|
+
readonly section: string;
|
|
413
|
+
readonly title: string;
|
|
414
|
+
readonly ask: "note" | "pick" | "none";
|
|
415
|
+
readonly pick?: string;
|
|
416
|
+
readonly pickLabel?: string;
|
|
417
|
+
/** The option the agent suggested, when Kit picked another. */
|
|
418
|
+
readonly suggested?: string;
|
|
419
|
+
readonly notes: readonly FeedbackNote[];
|
|
420
|
+
/** Older documents' verdict and note, when there are any. */
|
|
421
|
+
readonly verdict?: string;
|
|
422
|
+
readonly note?: string;
|
|
423
|
+
}
|
|
424
|
+
/** What a page hands back to the agent: every asked step (open ones with nothing in them). */
|
|
425
|
+
export interface WalkthroughFeedback {
|
|
426
|
+
readonly id: string;
|
|
427
|
+
readonly title: string;
|
|
428
|
+
readonly reviewed: number;
|
|
429
|
+
readonly total: number;
|
|
430
|
+
readonly note?: string;
|
|
431
|
+
readonly steps: readonly FeedbackItem[];
|
|
432
|
+
/** Notes sent with no step in view. */
|
|
433
|
+
readonly general: readonly FeedbackNote[];
|
|
434
|
+
}
|
|
435
|
+
/** Kit's feedback on `w`, step by step in reading order. */
|
|
436
|
+
export declare function collectFeedback(w: WalkPage, state: WalkthroughState): WalkthroughFeedback;
|
|
437
|
+
/**
|
|
438
|
+
* The feedback as Markdown to paste back to the agent: a heading per section, a line per step
|
|
439
|
+
* (its pick in bold, open ones marked), its notes under it with what Kit was looking at.
|
|
440
|
+
*/
|
|
441
|
+
export declare function feedbackMarkdown(fb: WalkthroughFeedback, verdicts?: readonly WalkVerdict[]): string;
|
|
442
|
+
/**
|
|
443
|
+
* Inline Markdown as safe HTML: `code`, **bold**, *italic* and [links](https://…), the rest
|
|
444
|
+
* escaped. Links take http(s), relative and # URLs only; blank lines make paragraphs' breaks.
|
|
445
|
+
*/
|
|
446
|
+
export declare function inlineMarkdown(text: string): string;
|
|
447
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type StoragePort } from "../core/storage.js";
|
|
2
|
+
import { type UiLook } from "./look.js";
|
|
3
|
+
import { type MediaSlot } from "./walkthrough-media.js";
|
|
4
|
+
import { type WalkOption, type WalkPage, type WalkthroughDoc, type WalkthroughFeedback, type WalkthroughOption, type WalkthroughState } from "./walkthrough-model.js";
|
|
5
|
+
export interface TasteTestOptions extends UiLook {
|
|
6
|
+
readonly options: readonly (WalkthroughOption | WalkOption)[];
|
|
7
|
+
readonly picked?: string | null;
|
|
8
|
+
/** A click picked an option (or, on the picked one, cleared it). */
|
|
9
|
+
readonly onPick?: (id: string | null) => void;
|
|
10
|
+
/** Mount each option's media when near the view (on by default). */
|
|
11
|
+
readonly lazy?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface TasteTest {
|
|
14
|
+
readonly element: HTMLElement;
|
|
15
|
+
readonly picked: string | null;
|
|
16
|
+
/** The options' media, in order (those with any). */
|
|
17
|
+
readonly slots: readonly MediaSlot[];
|
|
18
|
+
pick(id: string | null): void;
|
|
19
|
+
/** Pauses the options' videos. */
|
|
20
|
+
pause(): void;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A vs B (or more): each option's letter, label and media side by side (stacking where it's
|
|
25
|
+
* narrow). The picked one takes a check and a wash; the others step back.
|
|
26
|
+
*/
|
|
27
|
+
export declare function tasteTest(container: HTMLElement, options: TasteTestOptions): TasteTest;
|
|
28
|
+
export interface WalkthroughOptions extends UiLook {
|
|
29
|
+
readonly doc: WalkthroughDoc;
|
|
30
|
+
/** Where Kit's answers are kept (localStorage by default; `null` keeps none). */
|
|
31
|
+
readonly storage?: StoragePort | null;
|
|
32
|
+
/** Every change to Kit's answers, as the agent reads them. */
|
|
33
|
+
readonly onFeedback?: (feedback: WalkthroughFeedback) => void;
|
|
34
|
+
/** The page's keys (on by default). */
|
|
35
|
+
readonly keys?: boolean;
|
|
36
|
+
/** The contents and progress rail (on by default). */
|
|
37
|
+
readonly rail?: boolean;
|
|
38
|
+
/** Sending a note moves on to the next step (on by default). */
|
|
39
|
+
readonly advance?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface Walkthrough {
|
|
42
|
+
readonly element: HTMLElement;
|
|
43
|
+
/** The document, filled in. */
|
|
44
|
+
readonly page: WalkPage;
|
|
45
|
+
readonly state: WalkthroughState;
|
|
46
|
+
readonly feedback: WalkthroughFeedback;
|
|
47
|
+
/** The step Kit is on (its key); null above the first. */
|
|
48
|
+
readonly current: string | null;
|
|
49
|
+
/** The feedback as Markdown, for the agent. */
|
|
50
|
+
markdown(): string;
|
|
51
|
+
/** Goes to a step (its key), the page gliding to it. */
|
|
52
|
+
go(key: string): void;
|
|
53
|
+
/** The current step full-window (`true`), back in the page (`false`), or the other way. */
|
|
54
|
+
full(on?: boolean): void;
|
|
55
|
+
/** Sends a note from the page's code, as the bar would: on the current step, with its context. */
|
|
56
|
+
send(text: string): void;
|
|
57
|
+
/** A new draft of the document: answers carry over by step key. */
|
|
58
|
+
set(doc: WalkthroughDoc): void;
|
|
59
|
+
/** Forgets every answer. */
|
|
60
|
+
reset(): void;
|
|
61
|
+
dispose(): void;
|
|
62
|
+
}
|
|
63
|
+
export declare function walkthrough(container: HTMLElement, options: WalkthroughOptions): Walkthrough;
|