@weatherboard/annotation 0.6.0 → 0.7.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/ADOPTING.md +50 -4
- package/COMPATIBILITY.md +14 -0
- package/README.md +51 -2
- package/dist/anchor.cjs +1 -1
- package/dist/anchor.cjs.map +1 -1
- package/dist/anchor.mjs +1 -1
- package/dist/anchor.mjs.map +1 -1
- package/dist/arming.d.cts +17 -18
- package/dist/arming.d.mts +17 -18
- package/dist/arming.d.ts +17 -18
- package/dist/bootstrap.cjs +1 -1
- package/dist/bootstrap.cjs.map +4 -4
- package/dist/bootstrap.d.cts +1 -0
- package/dist/bootstrap.d.mts +1 -0
- package/dist/bootstrap.d.ts +1 -0
- package/dist/bootstrap.mjs +1 -1
- package/dist/bootstrap.mjs.map +4 -4
- package/dist/host.cjs +1 -1
- package/dist/host.cjs.map +2 -2
- package/dist/host.mjs +1 -1
- package/dist/host.mjs.map +2 -2
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.cts +9 -3
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +4 -4
- package/dist/markers.cjs.map +1 -1
- package/dist/markers.mjs.map +1 -1
- package/dist/page-capture.cjs +2 -0
- package/dist/page-capture.cjs.map +7 -0
- package/dist/page-capture.d.cts +115 -0
- package/dist/page-capture.d.mts +115 -0
- package/dist/page-capture.d.ts +115 -0
- package/dist/page-capture.mjs +2 -0
- package/dist/page-capture.mjs.map +7 -0
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.cjs.map +2 -2
- package/dist/protocol.d.cts +12 -0
- package/dist/protocol.d.mts +12 -0
- package/dist/protocol.d.ts +12 -0
- package/dist/protocol.mjs +1 -1
- package/dist/protocol.mjs.map +2 -2
- package/dist/screenshot.d.cts +56 -7
- package/dist/screenshot.d.mts +56 -7
- package/dist/screenshot.d.ts +56 -7
- package/dist/session.d.cts +7 -0
- package/dist/session.d.mts +7 -0
- package/dist/session.d.ts +7 -0
- package/dist/shortcut.d.cts +52 -0
- package/dist/shortcut.d.mts +52 -0
- package/dist/shortcut.d.ts +52 -0
- package/dist/toolbar.d.cts +13 -0
- package/dist/toolbar.d.mts +13 -0
- package/dist/toolbar.d.ts +13 -0
- package/package.json +13 -3
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { BuildInfo } from "./protocol.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* The in-app shortcut's pre-arm half (A-2384).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS LIVES IN `./bootstrap` AND NOT IN `mountAnnotation`
|
|
6
|
+
* A host loads the toolbar behind a dynamic `import()` that only an armed page
|
|
7
|
+
* reaches (ADOPTING.md, step 6). On an ordinary staging page there is no
|
|
8
|
+
* marker, so nothing from the index entry point is loaded, and a listener
|
|
9
|
+
* there cannot hear the chord. This one is installed by the small, statically
|
|
10
|
+
* imported entry point on every annotatable page, armed or not.
|
|
11
|
+
*
|
|
12
|
+
* ONE DECISION, MADE BY WHOEVER IS MOUNTED
|
|
13
|
+
* The mounted toolbar's own listener toggles the toolbar when the page is armed
|
|
14
|
+
* with a fresh token, and calls `preventDefault()`. This listener waits for the
|
|
15
|
+
* dispatch to finish and re-arms only if nothing claimed the chord: no toolbar
|
|
16
|
+
* loaded, a toolbar whose token is missing or within five minutes of expiry,
|
|
17
|
+
* or a toolbar the reviewer ended with Done. So the two listeners cannot both
|
|
18
|
+
* act, and freshness is judged in exactly one place.
|
|
19
|
+
*
|
|
20
|
+
* WHAT RE-ARMING IS
|
|
21
|
+
* A top-level navigation to Studio's `/annotate/arm`, which checks the signed-in
|
|
22
|
+
* reviewer, mints the tokens and redirects back to the return path with them in
|
|
23
|
+
* the query; the host's middleware arms on that request and the bootstrap
|
|
24
|
+
* script strips the query. The only thing this side carries is a return path
|
|
25
|
+
* with every annotation parameter removed, plus a credential-free open intent
|
|
26
|
+
* in session storage so the armed mount on return opens the toolbar.
|
|
27
|
+
*
|
|
28
|
+
* PRODUCTION
|
|
29
|
+
* `isAnnotatableBuild` refuses production at install: no listener is added.
|
|
30
|
+
*/
|
|
31
|
+
/** The default chord: Command or Control, Shift and the full stop. */
|
|
32
|
+
export declare const DEFAULT_HOTKEY = "Mod+Shift+Period";
|
|
33
|
+
/** Exact modifiers and a KeyboardEvent code or key, such as Mod+Shift+Period. */
|
|
34
|
+
export declare function matchesHotkey(event: Pick<KeyboardEvent, "key" | "code" | "metaKey" | "ctrlKey" | "shiftKey" | "altKey" | "repeat">, chord: string): boolean;
|
|
35
|
+
export interface AnnotationShortcutOptions {
|
|
36
|
+
/** The same build the mount is given. Production installs nothing. */
|
|
37
|
+
build: Pick<BuildInfo, "env">;
|
|
38
|
+
/**
|
|
39
|
+
* Studio's arm endpoint for this product's staging target, for example
|
|
40
|
+
* `https://studio.example/annotate/arm?product=<product-id>&target=staging`. Absolute
|
|
41
|
+
* http(s), and carrying no `return`: the shortcut adds it.
|
|
42
|
+
*/
|
|
43
|
+
armUrl: string;
|
|
44
|
+
/** The chord. The mount must be given the same one. */
|
|
45
|
+
hotkey: string;
|
|
46
|
+
/** Defaults to the ambient `document`. */
|
|
47
|
+
doc?: Document;
|
|
48
|
+
}
|
|
49
|
+
/** The URL the chord navigates to: `armUrl` plus a token-free return path. */
|
|
50
|
+
export declare function annotationArmUrl(armUrl: string, current: string): string;
|
|
51
|
+
/** Install the pre-arm listener. Returns its removal. */
|
|
52
|
+
export declare function installAnnotationShortcut(options: AnnotationShortcutOptions): () => void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { BuildInfo } from "./protocol.js";
|
|
2
|
+
/**
|
|
3
|
+
* The in-app shortcut's pre-arm half (A-2384).
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS LIVES IN `./bootstrap` AND NOT IN `mountAnnotation`
|
|
6
|
+
* A host loads the toolbar behind a dynamic `import()` that only an armed page
|
|
7
|
+
* reaches (ADOPTING.md, step 6). On an ordinary staging page there is no
|
|
8
|
+
* marker, so nothing from the index entry point is loaded, and a listener
|
|
9
|
+
* there cannot hear the chord. This one is installed by the small, statically
|
|
10
|
+
* imported entry point on every annotatable page, armed or not.
|
|
11
|
+
*
|
|
12
|
+
* ONE DECISION, MADE BY WHOEVER IS MOUNTED
|
|
13
|
+
* The mounted toolbar's own listener toggles the toolbar when the page is armed
|
|
14
|
+
* with a fresh token, and calls `preventDefault()`. This listener waits for the
|
|
15
|
+
* dispatch to finish and re-arms only if nothing claimed the chord: no toolbar
|
|
16
|
+
* loaded, a toolbar whose token is missing or within five minutes of expiry,
|
|
17
|
+
* or a toolbar the reviewer ended with Done. So the two listeners cannot both
|
|
18
|
+
* act, and freshness is judged in exactly one place.
|
|
19
|
+
*
|
|
20
|
+
* WHAT RE-ARMING IS
|
|
21
|
+
* A top-level navigation to Studio's `/annotate/arm`, which checks the signed-in
|
|
22
|
+
* reviewer, mints the tokens and redirects back to the return path with them in
|
|
23
|
+
* the query; the host's middleware arms on that request and the bootstrap
|
|
24
|
+
* script strips the query. The only thing this side carries is a return path
|
|
25
|
+
* with every annotation parameter removed, plus a credential-free open intent
|
|
26
|
+
* in session storage so the armed mount on return opens the toolbar.
|
|
27
|
+
*
|
|
28
|
+
* PRODUCTION
|
|
29
|
+
* `isAnnotatableBuild` refuses production at install: no listener is added.
|
|
30
|
+
*/
|
|
31
|
+
/** The default chord: Command or Control, Shift and the full stop. */
|
|
32
|
+
export declare const DEFAULT_HOTKEY = "Mod+Shift+Period";
|
|
33
|
+
/** Exact modifiers and a KeyboardEvent code or key, such as Mod+Shift+Period. */
|
|
34
|
+
export declare function matchesHotkey(event: Pick<KeyboardEvent, "key" | "code" | "metaKey" | "ctrlKey" | "shiftKey" | "altKey" | "repeat">, chord: string): boolean;
|
|
35
|
+
export interface AnnotationShortcutOptions {
|
|
36
|
+
/** The same build the mount is given. Production installs nothing. */
|
|
37
|
+
build: Pick<BuildInfo, "env">;
|
|
38
|
+
/**
|
|
39
|
+
* Studio's arm endpoint for this product's staging target, for example
|
|
40
|
+
* `https://studio.example/annotate/arm?product=<product-id>&target=staging`. Absolute
|
|
41
|
+
* http(s), and carrying no `return`: the shortcut adds it.
|
|
42
|
+
*/
|
|
43
|
+
armUrl: string;
|
|
44
|
+
/** The chord. The mount must be given the same one. */
|
|
45
|
+
hotkey: string;
|
|
46
|
+
/** Defaults to the ambient `document`. */
|
|
47
|
+
doc?: Document;
|
|
48
|
+
}
|
|
49
|
+
/** The URL the chord navigates to: `armUrl` plus a token-free return path. */
|
|
50
|
+
export declare function annotationArmUrl(armUrl: string, current: string): string;
|
|
51
|
+
/** Install the pre-arm listener. Returns its removal. */
|
|
52
|
+
export declare function installAnnotationShortcut(options: AnnotationShortcutOptions): () => void;
|
package/dist/toolbar.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AnnotationSdk, AnnotationSdkOptions, CommentCollector } from "./sdk.cjs";
|
|
2
2
|
import { type Arming } from "./arming.cjs";
|
|
3
3
|
import type { EgressOutcome } from "./transport.cjs";
|
|
4
|
+
export { DEFAULT_HOTKEY, matchesHotkey } from "./shortcut.cjs";
|
|
4
5
|
/**
|
|
5
6
|
* The toolbar and the post-it (A-240).
|
|
6
7
|
*
|
|
@@ -131,6 +132,8 @@ export interface AnnotationToolbarOptions {
|
|
|
131
132
|
* Unarmed, NOTHING IS APPENDED TO THE DOCUMENT. Not hidden: absent.
|
|
132
133
|
*/
|
|
133
134
|
armed?: boolean;
|
|
135
|
+
/** Keep the toolbar detached until the host's chord is pressed. */
|
|
136
|
+
dormant?: boolean;
|
|
134
137
|
doc?: Document;
|
|
135
138
|
/** Only for the viewport clamp, so a fake one is two numbers. */
|
|
136
139
|
win?: Pick<Window, "innerWidth" | "innerHeight">;
|
|
@@ -149,11 +152,21 @@ export interface AnnotationToolbar {
|
|
|
149
152
|
showResult: (outcome: EgressOutcome, retry: () => void) => void;
|
|
150
153
|
/** Whether the panel is open. For tests and for the mounting app. */
|
|
151
154
|
isExpanded: () => boolean;
|
|
155
|
+
setVisible: (visible: boolean) => void;
|
|
156
|
+
isVisible: () => boolean;
|
|
152
157
|
destroy: () => void;
|
|
153
158
|
}
|
|
154
159
|
export declare function createAnnotationToolbar(options?: AnnotationToolbarOptions): AnnotationToolbar;
|
|
155
160
|
export interface MountAnnotationOptions extends AnnotationSdkOptions {
|
|
156
161
|
toolbar?: AnnotationToolbarOptions;
|
|
162
|
+
/**
|
|
163
|
+
* Opt-in (A-2384). A chord such as `DEFAULT_HOTKEY` starts an armed toolbar
|
|
164
|
+
* hidden until it is pressed; absent or null keeps the immediate toolbar every
|
|
165
|
+
* mount before 0.7.0 had. Mod means Meta on macOS and Control elsewhere.
|
|
166
|
+
* Re-arming is not this listener's: with no fresh token it leaves the chord
|
|
167
|
+
* unclaimed, and `installAnnotationShortcut` (from `./bootstrap`) re-arms.
|
|
168
|
+
*/
|
|
169
|
+
hotkey?: string | null;
|
|
157
170
|
/**
|
|
158
171
|
* The arming decision, from `readArming()` (A-241).
|
|
159
172
|
*
|
package/dist/toolbar.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AnnotationSdk, AnnotationSdkOptions, CommentCollector } from "./sdk.mjs";
|
|
2
2
|
import { type Arming } from "./arming.mjs";
|
|
3
3
|
import type { EgressOutcome } from "./transport.mjs";
|
|
4
|
+
export { DEFAULT_HOTKEY, matchesHotkey } from "./shortcut.mjs";
|
|
4
5
|
/**
|
|
5
6
|
* The toolbar and the post-it (A-240).
|
|
6
7
|
*
|
|
@@ -131,6 +132,8 @@ export interface AnnotationToolbarOptions {
|
|
|
131
132
|
* Unarmed, NOTHING IS APPENDED TO THE DOCUMENT. Not hidden: absent.
|
|
132
133
|
*/
|
|
133
134
|
armed?: boolean;
|
|
135
|
+
/** Keep the toolbar detached until the host's chord is pressed. */
|
|
136
|
+
dormant?: boolean;
|
|
134
137
|
doc?: Document;
|
|
135
138
|
/** Only for the viewport clamp, so a fake one is two numbers. */
|
|
136
139
|
win?: Pick<Window, "innerWidth" | "innerHeight">;
|
|
@@ -149,11 +152,21 @@ export interface AnnotationToolbar {
|
|
|
149
152
|
showResult: (outcome: EgressOutcome, retry: () => void) => void;
|
|
150
153
|
/** Whether the panel is open. For tests and for the mounting app. */
|
|
151
154
|
isExpanded: () => boolean;
|
|
155
|
+
setVisible: (visible: boolean) => void;
|
|
156
|
+
isVisible: () => boolean;
|
|
152
157
|
destroy: () => void;
|
|
153
158
|
}
|
|
154
159
|
export declare function createAnnotationToolbar(options?: AnnotationToolbarOptions): AnnotationToolbar;
|
|
155
160
|
export interface MountAnnotationOptions extends AnnotationSdkOptions {
|
|
156
161
|
toolbar?: AnnotationToolbarOptions;
|
|
162
|
+
/**
|
|
163
|
+
* Opt-in (A-2384). A chord such as `DEFAULT_HOTKEY` starts an armed toolbar
|
|
164
|
+
* hidden until it is pressed; absent or null keeps the immediate toolbar every
|
|
165
|
+
* mount before 0.7.0 had. Mod means Meta on macOS and Control elsewhere.
|
|
166
|
+
* Re-arming is not this listener's: with no fresh token it leaves the chord
|
|
167
|
+
* unclaimed, and `installAnnotationShortcut` (from `./bootstrap`) re-arms.
|
|
168
|
+
*/
|
|
169
|
+
hotkey?: string | null;
|
|
157
170
|
/**
|
|
158
171
|
* The arming decision, from `readArming()` (A-241).
|
|
159
172
|
*
|
package/dist/toolbar.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AnnotationSdk, AnnotationSdkOptions, CommentCollector } from "./sdk.js";
|
|
2
2
|
import { type Arming } from "./arming.js";
|
|
3
3
|
import type { EgressOutcome } from "./transport.js";
|
|
4
|
+
export { DEFAULT_HOTKEY, matchesHotkey } from "./shortcut.js";
|
|
4
5
|
/**
|
|
5
6
|
* The toolbar and the post-it (A-240).
|
|
6
7
|
*
|
|
@@ -131,6 +132,8 @@ export interface AnnotationToolbarOptions {
|
|
|
131
132
|
* Unarmed, NOTHING IS APPENDED TO THE DOCUMENT. Not hidden: absent.
|
|
132
133
|
*/
|
|
133
134
|
armed?: boolean;
|
|
135
|
+
/** Keep the toolbar detached until the host's chord is pressed. */
|
|
136
|
+
dormant?: boolean;
|
|
134
137
|
doc?: Document;
|
|
135
138
|
/** Only for the viewport clamp, so a fake one is two numbers. */
|
|
136
139
|
win?: Pick<Window, "innerWidth" | "innerHeight">;
|
|
@@ -149,11 +152,21 @@ export interface AnnotationToolbar {
|
|
|
149
152
|
showResult: (outcome: EgressOutcome, retry: () => void) => void;
|
|
150
153
|
/** Whether the panel is open. For tests and for the mounting app. */
|
|
151
154
|
isExpanded: () => boolean;
|
|
155
|
+
setVisible: (visible: boolean) => void;
|
|
156
|
+
isVisible: () => boolean;
|
|
152
157
|
destroy: () => void;
|
|
153
158
|
}
|
|
154
159
|
export declare function createAnnotationToolbar(options?: AnnotationToolbarOptions): AnnotationToolbar;
|
|
155
160
|
export interface MountAnnotationOptions extends AnnotationSdkOptions {
|
|
156
161
|
toolbar?: AnnotationToolbarOptions;
|
|
162
|
+
/**
|
|
163
|
+
* Opt-in (A-2384). A chord such as `DEFAULT_HOTKEY` starts an armed toolbar
|
|
164
|
+
* hidden until it is pressed; absent or null keeps the immediate toolbar every
|
|
165
|
+
* mount before 0.7.0 had. Mod means Meta on macOS and Control elsewhere.
|
|
166
|
+
* Re-arming is not this listener's: with no fresh token it leaves the chord
|
|
167
|
+
* unclaimed, and `installAnnotationShortcut` (from `./bootstrap`) re-arms.
|
|
168
|
+
*/
|
|
169
|
+
hotkey?: string | null;
|
|
157
170
|
/**
|
|
158
171
|
* The arming decision, from `readArming()` (A-241).
|
|
159
172
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weatherboard/annotation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Click an element in a running app, get the source location that rendered it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotation",
|
|
@@ -122,16 +122,26 @@
|
|
|
122
122
|
"//sizeBudget-host": "A-359, and the first note this entry point has had. MEASURED BY RUNNING `node build.mjs` AND READING ITS OUTPUT, three entry points, before and after, on this branch rather than trusted from the last note: host 566 -> 649 (+83), protocol 800 -> 869 (+69), index 9531 -> 9599 (+68). WHAT THE BYTES ARE: `unhandledMessage` in protocol.ts, which is the whole of protocol's 69 and is re-exported into index for index's 68; host pays that 69 plus 14 for its own `default` branch. The `const unhandled: never` assertions cost NOTHING - they are erased at build, which is the good half of this trade. WHAT THEY BUY: host.ts and sdk.ts each dispatched on a discriminated union with no `default`, so a message type added to `ParentToFrame` or `FrameToParent` was DROPPED by whichever half had not been taught about it, with no error anywhere. Measured before the fix: adding a member to each union and running `tsc --noEmit` gave exit 0 and zero errors. These two files are the only things in the world that can disagree about what a message on this wire means, and they deploy from different repositories. THE FIRST DRAFT OF THE SENTENCE COST 230 CHARACTERS AND PUT ALL THREE ENTRY POINTS OVER AT ONCE - index 9717/9650, protocol 991/870, host 771/660 - which is the gate working, and is why the shipped string is one line: which half, what arrived, and that nothing was done with it. Nothing beyond that survived the budget, and nothing beyond that is load-bearing. BUDGETS RAISED: protocol 870 -> 890, index 9650 -> 9700, host 660 -> 680. Every one of the three was ALREADY UNDER its old budget after the string was trimmed - 869/870, 9599/9650 and 649/660 - so this is not a raise to make a failure go away, and the numbers above are the gate's own output at both ends. It is a raise because 1, 51 and 11 bytes of headroom is the state the A-277 note names as the thing to avoid: 'a budget that fails on the next sentence somebody writes rather than on the next decision somebody makes'. Protocol at one byte would have failed the next contributor over a comma. New headroom: protocol 890/869 = 2.4%, host 680/649 = 4.8%, index 9700/9599 = 1.05% - index tightest in proportion for the reason every note here gives, that a percentage of index is a lot of bytes, and the two small entry points looser for the reason protocol has always run looser, that 20 bytes is 2% there and would be 0.2% of index.",
|
|
123
123
|
"//sizeBudget-host-a359-cycle2": "The guard on `unhandledMessage`'s stringify, and this raise IS forced by the gate rather than headroom-tidying like the one above it. MEASURED BY RUNNING `node build.mjs`, every figure its own output: protocol 869 -> 908 (+39), host 649 -> 686 (+37), index 9599 -> 9615 (+16). protocol and host both went OVER their A-359 budgets (908/890, 686/680) and are raised to 940 and 720; index needs nothing and is NOT raised, so it stays at 9700 with 9615 against it. WHAT THE BYTES BUY: `unhandledMessage` interpolated `JSON.stringify(message)` unguarded, and postMessage serialises with STRUCTURED CLONE, which PRESERVES CYCLES where JSON cannot represent them - so a circular `event.data` made the function throw `Converting circular structure to JSON` from inside a `message` listener on a page this SDK is a guest on, once per message, at the sender's discretion. That is the exact outcome the forty-line comment on that function argues against; the implementation defeated its own argument. Both the circular case and a throwing-getter Proxy were confirmed RED against the previous implementation before the fix and are pinned in exhaustive.test.ts. THE EXPENSIVE ALTERNATIVE WAS TRIED AND MEASURED RATHER THAN REASONED ABOUT: reusing `describeArg` from state.ts - which solves the same problem and whose own comment names the same hazard - costs protocol 869 -> 1008 (+139) and host 649 -> 793 (+144), a hundred bytes per entry point more than the local version, because describeArg is built for console ARGUMENTS (strings, Errors, a prototype walk) and almost none of it is reachable for a wire message. It was reverted for a second reason that would stand even at equal cost: `protocol.ts` currently has ZERO imports, `state.ts` imports FROM it, and importing back would make the pure wire contract depend on the console-patching state collector and close a module cycle that is invisible only because state.ts's side is `import type`. The cost of that choice is two safe-stringify helpers in this package instead of one; they cannot drift in a way that matters, each being a total function with one behaviour and its own test, and the note on `safeJson` says what to do if a third ever appears. New headroom: protocol 940/908 = 3.5%, host 720/686 = 5.0%, index 9700/9615 = 0.88%.",
|
|
124
124
|
"//sizeBudget-a1375": "A-1375 adds an eighth, deliberately small ./bootstrap entry point for the synchronous pre-hydration script, arming reads and token-free startup status. It is separate so an ordinary visitor does not download the toolbar, state collector or transport before the page knows it is armed. REPRODUCED WITH NODE 22.19.0 by running `pnpm --filter @weatherboard/annotation build` on the unmodified origin/main baseline 4d1df98e and on this source, then reading build.mjs's ESM gzip rows: index 9919 -> 10082 (+163), protocol 908 -> 967 (+59), and bootstrap absent (0 emitted bytes) -> 2321 (+2321). The index increase pays for the hosted-mode constants re-export and Done clearing shared bootstrap state; protocol pays for the two token-free capability constants; bootstrap contains the new early runtime itself. The index budget moves 9950 -> 10150 (+200, 2.01%) and leaves 68 bytes, 0.67% of its current artifact. The protocol budget moves 940 -> 1000 (+60, 6.38%) and leaves 33 bytes, 3.41% of its current artifact, effectively preserving its previous 32-byte allowance. The new bootstrap budget is 2350 and leaves 29 bytes, 1.25% of its current artifact. Bootstrap is deliberately tighter than protocol because it runs synchronously in every adopting document; all three limits remain close enough that the next material feature requires an explicit increase rather than spending unrecorded slack.",
|
|
125
|
+
"//sizeBudget-a2385": "A-2385, the viewport screenshot. MEASURED BY RUNNING `node build.mjs` under Node 22.23.3, before (an archive of origin/main) and after: index 10082 -> 10441 (+359): the viewport caps, the option resolution, the timeout wrapper and the lazy loader in screenshot.ts. Budget 10150 -> 10500. NINTH ENTRY POINT `page-capture`, 1308 as built, budget 1400: it is an entry ONLY so it can be lazy - screenshot.ts loads it with import() on the first capture, so its 1.3KB and the modern-screenshot library behind it (~14KB, external, not counted here) are downloaded by a page that is clicked and by no other. Folding it into index was measured too: 11385, which is the cost this split avoids.",
|
|
126
|
+
"//sizeBudget-a2384": "A-2384 adds cookie fallback and expiry decisions to readArming, exported by both index and bootstrap, plus shortcut wiring in index. The Node 22.23.3 package build measured index 11035 and bootstrap 2822 gzip bytes on this branch; both exceeded the previous 10150 and 2350 limits. Raise only those entry points to 11100 and 2850, leaving 65 and 28 bytes of headroom. Other entry points remain at their previous limits. No runtime dependency was added.",
|
|
127
|
+
"//sizeBudget-a2384-cycle2": "Cycle 2 of A-2384 makes the shortcut opt-in, reveals the toolbar when a result is shown, and carries an open intent through re-arming in session storage; bootstrap re-exports the intent's storage key so a host pre-arm listener can write it. MEASURED with Node 22.23.3 by `pnpm build`: index 11035 -> 11183 (+148) and bootstrap 2822 -> 2859 (+37), over the 11100 and 2850 limits. Raise only those two to 11250 and 2900, leaving 67 and 41 bytes of headroom. Other entry points are unchanged.",
|
|
128
|
+
"//sizeBudget-a2384-cycle3": "Cycle 3 of A-2384 moves the shortcut's re-arming out of mountAnnotation into ./bootstrap (installAnnotationShortcut, matchesHotkey, DEFAULT_HOTKEY, annotationArmUrl), because a host loads the index entry only on an armed page and an unarmed page must still hear the chord. MEASURED with Node 22.23.3 by `pnpm build`: bootstrap 2859 -> 3383 (+524), index 11183 -> 11098 (-85, onNeedArming and the intent write left it). Raise bootstrap only, 2900 -> 3450, leaving 67 bytes. Index stays at 11250. Other entry points are unchanged.",
|
|
129
|
+
"//sizeBudget-a2385-merge": "After merging the shortcut source from main with A-2385's lazy screenshot loader, Node 22.23.3 `pnpm --filter @weatherboard/annotation build` measured index 11452 gzip bytes, 202 above the shortcut branch's 11250 limit and 354 above its measured 11098. Raise index to 11500, leaving 48 bytes; page-capture remains 1301/1400 and the other entry points remain below their limits.",
|
|
125
130
|
"sizeBudget": {
|
|
126
|
-
"bootstrap":
|
|
131
|
+
"bootstrap": 3450,
|
|
127
132
|
"protocol": 1000,
|
|
128
|
-
"index":
|
|
133
|
+
"index": 11500,
|
|
134
|
+
"page-capture": 1400,
|
|
129
135
|
"host": 720,
|
|
130
136
|
"anchor": 1100,
|
|
131
137
|
"read": 1700,
|
|
132
138
|
"markers": 4250,
|
|
133
139
|
"selection": 3500
|
|
134
140
|
},
|
|
141
|
+
"//dependencies": "ONE, by approved exception (A-2385, owner decision 2026-10-17). `modern-screenshot` renders the viewport for the capture's screenshot: MIT, maintained, zero dependencies of its own, ~14KB gzip. It is imported dynamically in src/page-capture.ts only and kept external by build.mjs, so it is not in any entry point's bundle or size budget and an armed page that is never clicked never loads it. The argument is in the header of build.mjs; any further dependency needs its own there.",
|
|
142
|
+
"dependencies": {
|
|
143
|
+
"modern-screenshot": "^4.7.0"
|
|
144
|
+
},
|
|
135
145
|
"engines": {
|
|
136
146
|
"node": ">=18"
|
|
137
147
|
},
|