@weatherboard/annotation 0.4.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.
Files changed (75) hide show
  1. package/ADOPTING.md +147 -10
  2. package/COMPATIBILITY.md +29 -0
  3. package/README.md +84 -10
  4. package/dist/anchor.cjs +1 -1
  5. package/dist/anchor.cjs.map +2 -2
  6. package/dist/anchor.mjs.map +2 -2
  7. package/dist/arming.d.cts +18 -21
  8. package/dist/arming.d.mts +18 -21
  9. package/dist/arming.d.ts +18 -21
  10. package/dist/bootstrap-runtime.d.cts +30 -0
  11. package/dist/bootstrap-runtime.d.mts +30 -0
  12. package/dist/bootstrap-runtime.d.ts +30 -0
  13. package/dist/bootstrap.cjs +2 -0
  14. package/dist/bootstrap.cjs.map +7 -0
  15. package/dist/bootstrap.d.cts +8 -0
  16. package/dist/bootstrap.d.mts +8 -0
  17. package/dist/bootstrap.d.ts +8 -0
  18. package/dist/bootstrap.mjs +2 -0
  19. package/dist/bootstrap.mjs.map +7 -0
  20. package/dist/host.cjs +1 -1
  21. package/dist/host.cjs.map +3 -3
  22. package/dist/host.mjs +1 -1
  23. package/dist/host.mjs.map +3 -3
  24. package/dist/index.cjs +4 -4
  25. package/dist/index.cjs.map +4 -4
  26. package/dist/index.d.cts +10 -4
  27. package/dist/index.d.mts +10 -4
  28. package/dist/index.d.ts +10 -4
  29. package/dist/index.mjs +4 -4
  30. package/dist/index.mjs.map +4 -4
  31. package/dist/markers.cjs +2 -2
  32. package/dist/markers.cjs.map +2 -2
  33. package/dist/markers.mjs +2 -2
  34. package/dist/markers.mjs.map +2 -2
  35. package/dist/page-capture.cjs +2 -0
  36. package/dist/page-capture.cjs.map +7 -0
  37. package/dist/page-capture.d.cts +115 -0
  38. package/dist/page-capture.d.mts +115 -0
  39. package/dist/page-capture.d.ts +115 -0
  40. package/dist/page-capture.mjs +2 -0
  41. package/dist/page-capture.mjs.map +7 -0
  42. package/dist/protocol.cjs +1 -1
  43. package/dist/protocol.cjs.map +3 -3
  44. package/dist/protocol.d.cts +19 -0
  45. package/dist/protocol.d.mts +19 -0
  46. package/dist/protocol.d.ts +19 -0
  47. package/dist/protocol.mjs +1 -1
  48. package/dist/protocol.mjs.map +3 -3
  49. package/dist/read.cjs +1 -1
  50. package/dist/read.cjs.map +2 -2
  51. package/dist/read.mjs +1 -1
  52. package/dist/read.mjs.map +2 -2
  53. package/dist/screenshot.d.cts +56 -7
  54. package/dist/screenshot.d.mts +56 -7
  55. package/dist/screenshot.d.ts +56 -7
  56. package/dist/selection.cjs +5 -0
  57. package/dist/selection.cjs.map +7 -0
  58. package/dist/selection.d.cts +27 -0
  59. package/dist/selection.d.mts +27 -0
  60. package/dist/selection.d.ts +27 -0
  61. package/dist/selection.mjs +5 -0
  62. package/dist/selection.mjs.map +7 -0
  63. package/dist/session.d.cts +17 -0
  64. package/dist/session.d.mts +17 -0
  65. package/dist/session.d.ts +17 -0
  66. package/dist/shortcut.d.cts +52 -0
  67. package/dist/shortcut.d.mts +52 -0
  68. package/dist/shortcut.d.ts +52 -0
  69. package/dist/toolbar.d.cts +13 -0
  70. package/dist/toolbar.d.mts +13 -0
  71. package/dist/toolbar.d.ts +13 -0
  72. package/dist/transport.d.cts +10 -0
  73. package/dist/transport.d.mts +10 -0
  74. package/dist/transport.d.ts +10 -0
  75. package/package.json +45 -10
@@ -0,0 +1,27 @@
1
+ /** Host-neutral authored-content selection. No capture token, transport or production arming. */
2
+ export interface RelativePoint {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface TextSelectionAnchor {
9
+ path: string;
10
+ start: number;
11
+ end: number;
12
+ quote: string;
13
+ prefix: string;
14
+ suffix: string;
15
+ }
16
+ export type TextPlacement = {
17
+ state: "exact";
18
+ range: Range;
19
+ } | {
20
+ state: "missing" | "ambiguous" | "stale";
21
+ reason: string;
22
+ };
23
+ export declare function relativePoint(box: Pick<DOMRect, "left" | "top" | "width" | "height">, x: number, y: number): RelativePoint | null;
24
+ export declare function captureTextSelection(scope: Element, selection: Selection | null): TextSelectionAnchor | null;
25
+ /** A snapshot resolver batches DOM traversal for all annotations in one frame. */
26
+ export declare function prepareTextSelections(scope: Element): (anchor: TextSelectionAnchor, unchanged: boolean) => TextPlacement;
27
+ export declare function resolveTextSelection(scope: Element, anchor: TextSelectionAnchor, unchanged: boolean): TextPlacement;
@@ -0,0 +1,27 @@
1
+ /** Host-neutral authored-content selection. No capture token, transport or production arming. */
2
+ export interface RelativePoint {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface TextSelectionAnchor {
9
+ path: string;
10
+ start: number;
11
+ end: number;
12
+ quote: string;
13
+ prefix: string;
14
+ suffix: string;
15
+ }
16
+ export type TextPlacement = {
17
+ state: "exact";
18
+ range: Range;
19
+ } | {
20
+ state: "missing" | "ambiguous" | "stale";
21
+ reason: string;
22
+ };
23
+ export declare function relativePoint(box: Pick<DOMRect, "left" | "top" | "width" | "height">, x: number, y: number): RelativePoint | null;
24
+ export declare function captureTextSelection(scope: Element, selection: Selection | null): TextSelectionAnchor | null;
25
+ /** A snapshot resolver batches DOM traversal for all annotations in one frame. */
26
+ export declare function prepareTextSelections(scope: Element): (anchor: TextSelectionAnchor, unchanged: boolean) => TextPlacement;
27
+ export declare function resolveTextSelection(scope: Element, anchor: TextSelectionAnchor, unchanged: boolean): TextPlacement;
@@ -0,0 +1,27 @@
1
+ /** Host-neutral authored-content selection. No capture token, transport or production arming. */
2
+ export interface RelativePoint {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export interface TextSelectionAnchor {
9
+ path: string;
10
+ start: number;
11
+ end: number;
12
+ quote: string;
13
+ prefix: string;
14
+ suffix: string;
15
+ }
16
+ export type TextPlacement = {
17
+ state: "exact";
18
+ range: Range;
19
+ } | {
20
+ state: "missing" | "ambiguous" | "stale";
21
+ reason: string;
22
+ };
23
+ export declare function relativePoint(box: Pick<DOMRect, "left" | "top" | "width" | "height">, x: number, y: number): RelativePoint | null;
24
+ export declare function captureTextSelection(scope: Element, selection: Selection | null): TextSelectionAnchor | null;
25
+ /** A snapshot resolver batches DOM traversal for all annotations in one frame. */
26
+ export declare function prepareTextSelections(scope: Element): (anchor: TextSelectionAnchor, unchanged: boolean) => TextPlacement;
27
+ export declare function resolveTextSelection(scope: Element, anchor: TextSelectionAnchor, unchanged: boolean): TextPlacement;
@@ -0,0 +1,5 @@
1
+ function A(e,t,i){return![e.left,e.top,e.width,e.height,t,i].every(Number.isFinite)||e.width<=0||e.height<=0||t<e.left||t>e.left+e.width||i<e.top||i>e.top+e.height?null:{x:(t-e.left)/e.width,y:(i-e.top)/e.height,width:e.width,height:e.height}}function M(e,t){let i=Array.from(e.querySelectorAll("[data-annotation-text]")).filter(u=>t==="slide"||u.dataset.annotationText===t);if(t!=="slide"&&i.length!==1)return null;let o=[],n="",a=null,f=0;for(let u of i){let d=e.ownerDocument.createTreeWalker(u,4),r=[],l=d.nextNode();for(;l;){if(++f>1e4)return null;l.parentElement?.closest("[data-annotation-text]")===u&&r.push(l),l=d.nextNode()}n.length&&r.length&&t==="slide"&&(n+=`
2
+ `,o.push({node:r[0],offset:0}));for(let s of r){for(let c=0;c<s.data.length;c++){let x=s.data[c];if(o.push({node:s,offset:c}),n+=x==="\r"?`
3
+ `:x,x==="\r"&&s.data[c+1]===`
4
+ `&&c++,n.length>25e4)return null}a={node:s,offset:s.data.length}}}return{text:n,positions:o,end:a}}function T(e,t,i){if(e.end?.node===t&&e.end.offset===i)return e.text.length;let o=e.positions.findIndex(n=>n.node===t&&n.offset===i);if(o>=0)return o;if(t.nodeType===3&&i===t.textContent?.length){for(let n=e.positions.length-1;n>=0;n--)if(e.positions[n]?.node===t)return n+1}return null}function P(e,t){if(!t||t.isCollapsed||t.rangeCount!==1)return null;let i=t.getRangeAt(0);if(!e.contains(i.startContainer)||!e.contains(i.endContainer))return null;let o=(c,x,g)=>{if(c.nodeType===3)return{node:c,offset:x};let m=c.childNodes[g?x-1:x];if(!m)return null;let h=m;for(;g?h.lastChild:h.firstChild;)h=g?h.lastChild:h.firstChild;return h.nodeType===3?{node:h,offset:g?h.textContent.length:0}:null},n=o(i.startContainer,i.startOffset,!1),a=o(i.endContainer,i.endOffset,!0);if(!n||!a)return null;let f=n.node.parentElement?.closest("[data-annotation-text]"),u=a.node.parentElement?.closest("[data-annotation-text]");if(!f||!u)return null;let d=f===u?f.dataset.annotationText:"slide",r=M(e,d);if(!r)return null;let l=T(r,n.node,n.offset),s=T(r,a.node,a.offset);return l===null||s===null||l>=s||s-l>1024?null:{path:d,start:l,end:s,quote:r.text.slice(l,s),prefix:r.text.slice(Math.max(0,l-128),l),suffix:r.text.slice(s,s+128)}}function p(e,t,i,o){let n=t.positions[i],a=o===t.text.length?t.end:t.positions[o];if(!n||!a)return null;let f=e.ownerDocument.createRange();return f.setStart(n.node,n.offset),f.setEnd(a.node,a.offset),f}function E(e){let t=new Map,i=n=>(t.has(n)||t.set(n,M(e,n)),t.get(n)??null),o=Array.from(new Set(Array.from(e.querySelectorAll("[data-annotation-text]"),n=>n.dataset.annotationText)));return(n,a)=>w(e,n,a,i,o)}function S(e,t,i){return E(e)(t,i)}function w(e,t,i,o,n){if(!t.quote.length)return{state:"missing",reason:"No selected text was recorded."};let a=o(t.path);if(i&&a?.text.slice(t.start,t.end)===t.quote){let d=p(e,a,t.start,t.end);if(d)return{state:"exact",range:d}}let f=t.path==="slide"?["slide"]:n,u=[];for(let d of f){let r=o(d);if(!r)continue;let l=r.text.indexOf(t.quote);for(;l>=0;){if(r.text.slice(Math.max(0,l-t.prefix.length),l)===t.prefix&&r.text.slice(l+t.quote.length,l+t.quote.length+t.suffix.length)===t.suffix){let s=p(e,r,l,l+t.quote.length);if(s&&u.push(s),u.length>1)return{state:"ambiguous",reason:"This text matches more than one location."}}l=r.text.indexOf(t.quote,l+1)}}return u[0]?{state:"exact",range:u[0]}:{state:"stale",reason:"The selected text has changed. The original quote is retained."}}export{P as captureTextSelection,E as prepareTextSelections,A as relativePoint,S as resolveTextSelection};
5
+ //# sourceMappingURL=selection.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/selection.ts"],
4
+ "sourcesContent": ["/** Host-neutral authored-content selection. No capture token, transport or production arming. */\nexport interface RelativePoint { x: number; y: number; width: number; height: number }\nexport interface TextSelectionAnchor {\n path: string; start: number; end: number; quote: string; prefix: string; suffix: string;\n}\nexport type TextPlacement =\n | { state: \"exact\"; range: Range }\n | { state: \"missing\" | \"ambiguous\" | \"stale\"; reason: string };\ninterface Position { node: Text; offset: number }\ninterface TextMap { text: string; positions: Position[]; end: Position | null }\nconst MAX_NODES = 10000;\nconst MAX_TEXT = 250000;\n\nexport function relativePoint(box: Pick<DOMRect, \"left\" | \"top\" | \"width\" | \"height\">, x: number, y: number): RelativePoint | null {\n if (![box.left, box.top, box.width, box.height, x, y].every(Number.isFinite) || box.width <= 0 || box.height <= 0 ||\n x < box.left || x > box.left + box.width || y < box.top || y > box.top + box.height) return null;\n return { x: (x - box.left) / box.width, y: (y - box.top) / box.height, width: box.width, height: box.height };\n}\n\n/** Maps original UTF-16 offsets, folding CRLF as HTML does. Newlines separate authored fields. */\nfunction textMap(scope: Element, path: string): TextMap | null {\n const fields = Array.from(scope.querySelectorAll<HTMLElement>(\"[data-annotation-text]\"))\n .filter((field) => path === \"slide\" || field.dataset.annotationText === path);\n if (path !== \"slide\" && fields.length !== 1) return null;\n const positions: Position[] = [];\n let text = \"\";\n let end: Position | null = null;\n let nodes = 0;\n for (const field of fields) {\n const walker = scope.ownerDocument.createTreeWalker(field, 4);\n const fieldNodes: Text[] = [];\n let next = walker.nextNode();\n while (next) {\n if (++nodes > MAX_NODES) return null;\n if (next.parentElement?.closest(\"[data-annotation-text]\") === field) fieldNodes.push(next as Text);\n next = walker.nextNode();\n }\n if (text.length && fieldNodes.length && path === \"slide\") {\n text += \"\\n\";\n positions.push({ node: fieldNodes[0]!, offset: 0 });\n }\n for (const node of fieldNodes) {\n for (let offset = 0; offset < node.data.length; offset++) {\n const character = node.data[offset]!;\n positions.push({ node, offset });\n text += character === \"\\r\" ? \"\\n\" : character;\n if (character === \"\\r\" && node.data[offset + 1] === \"\\n\") offset++;\n if (text.length > MAX_TEXT) return null;\n }\n end = { node, offset: node.data.length };\n }\n }\n return { text, positions, end };\n}\nfunction offsetAt(map: TextMap, node: Node, offset: number): number | null {\n if (map.end?.node === node && map.end.offset === offset) return map.text.length;\n const found = map.positions.findIndex((position) => position.node === node && position.offset === offset);\n if (found >= 0) return found;\n // Selection can end exactly after a text node before the next field.\n if (node.nodeType === 3 && offset === node.textContent?.length) {\n for (let i = map.positions.length - 1; i >= 0; i--) if (map.positions[i]?.node === node) return i + 1;\n }\n return null;\n}\n\nexport function captureTextSelection(scope: Element, selection: Selection | null): TextSelectionAnchor | null {\n if (!selection || selection.isCollapsed || selection.rangeCount !== 1) return null;\n const range = selection.getRangeAt(0);\n if (!scope.contains(range.startContainer) || !scope.contains(range.endContainer)) return null;\n // Browser selection may use element boundaries (select-all, keyboard), not\n // just text nodes. Resolve those boundaries to the authored text they enclose.\n const boundary = (node: Node, offset: number, ending: boolean): Position | null => {\n if (node.nodeType === 3) return { node: node as Text, offset };\n const child = node.childNodes[ending ? offset - 1 : offset];\n if (!child) return null;\n let leaf: Node = child;\n while (ending ? leaf.lastChild : leaf.firstChild) leaf = (ending ? leaf.lastChild : leaf.firstChild)!;\n return leaf.nodeType === 3 ? { node: leaf as Text, offset: ending ? leaf.textContent!.length : 0 } : null;\n };\n const a = boundary(range.startContainer, range.startOffset, false);\n const b = boundary(range.endContainer, range.endOffset, true);\n if (!a || !b) return null;\n const first = a.node.parentElement?.closest<HTMLElement>(\"[data-annotation-text]\");\n const last = b.node.parentElement?.closest<HTMLElement>(\"[data-annotation-text]\");\n if (!first || !last) return null;\n const path = first === last ? first.dataset.annotationText! : \"slide\";\n const map = textMap(scope, path);\n if (!map) return null;\n const start = offsetAt(map, a.node, a.offset);\n const end = offsetAt(map, b.node, b.offset);\n if (start === null || end === null || start >= end || end - start > 1024) return null;\n return { path, start, end, quote: map.text.slice(start, end),\n prefix: map.text.slice(Math.max(0, start - 128), start), suffix: map.text.slice(end, end + 128) };\n}\n\nfunction rangeAt(scope: Element, map: TextMap, start: number, end: number): Range | null {\n const a = map.positions[start];\n const b = end === map.text.length ? map.end : map.positions[end];\n if (!a || !b) return null;\n const range = scope.ownerDocument.createRange();\n range.setStart(a.node, a.offset);\n range.setEnd(b.node, b.offset);\n return range;\n}\n/** A snapshot resolver batches DOM traversal for all annotations in one frame. */\nexport function prepareTextSelections(scope: Element): (anchor: TextSelectionAnchor, unchanged: boolean) => TextPlacement {\n const maps = new Map<string, TextMap | null>();\n const getMap = (path: string) => {\n if (!maps.has(path)) maps.set(path, textMap(scope, path));\n return maps.get(path) ?? null;\n };\n const authoredPaths = Array.from(new Set(Array.from(scope.querySelectorAll<HTMLElement>(\"[data-annotation-text]\"), field => field.dataset.annotationText!)));\n return (anchor, unchanged) => resolvePrepared(scope, anchor, unchanged, getMap, authoredPaths);\n}\nexport function resolveTextSelection(scope: Element, anchor: TextSelectionAnchor, unchanged: boolean): TextPlacement {\n return prepareTextSelections(scope)(anchor, unchanged);\n}\nfunction resolvePrepared(scope: Element, anchor: TextSelectionAnchor, unchanged: boolean, getMap: (path: string) => TextMap | null, authoredPaths: string[]): TextPlacement {\n if (!anchor.quote.length) return { state: \"missing\", reason: \"No selected text was recorded.\" };\n const original = getMap(anchor.path);\n if (unchanged && original?.text.slice(anchor.start, anchor.end) === anchor.quote) {\n const range = rangeAt(scope, original, anchor.start, anchor.end);\n if (range) return { state: \"exact\", range };\n }\n // Positional field paths are valid only at the captured revision. Search all\n // authored fields after edits; two identical matches must remain ambiguous.\n const paths = anchor.path === \"slide\" ? [\"slide\"] : authoredPaths;\n const matches: Range[] = [];\n for (const path of paths) {\n const map = getMap(path);\n if (!map) continue;\n let index = map.text.indexOf(anchor.quote);\n while (index >= 0) {\n if (map.text.slice(Math.max(0, index - anchor.prefix.length), index) === anchor.prefix &&\n map.text.slice(index + anchor.quote.length, index + anchor.quote.length + anchor.suffix.length) === anchor.suffix) {\n const range = rangeAt(scope, map, index, index + anchor.quote.length);\n if (range) matches.push(range);\n if (matches.length > 1) return { state: \"ambiguous\", reason: \"This text matches more than one location.\" };\n }\n index = map.text.indexOf(anchor.quote, index + 1);\n }\n }\n if (matches[0]) return { state: \"exact\", range: matches[0] };\n return { state: \"stale\", reason: \"The selected text has changed. The original quote is retained.\" };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["relativePoint", "box", "x", "y", "textMap", "scope", "path", "fields", "field", "positions", "text", "end", "nodes", "walker", "fieldNodes", "next", "node", "offset", "character", "offsetAt", "map", "found", "position", "i", "captureTextSelection", "selection", "range", "boundary", "ending", "child", "leaf", "a", "b", "first", "last", "start", "rangeAt", "prepareTextSelections", "maps", "getMap", "authoredPaths", "anchor", "unchanged", "resolvePrepared", "resolveTextSelection", "original", "paths", "matches", "index"]
7
+ }
@@ -0,0 +1,17 @@
1
+ /** Per-tab state. None of these values are cookies or sent by the browser. */
2
+ export declare const CAPTURE_TOKEN_STORAGE_KEY = "ai.annotation.capture";
3
+ export declare const READ_TOKEN_STORAGE_KEY = "ai.annotation.read";
4
+ export declare const REVIEW_RUN_STORAGE_KEY = "ai.annotation.run";
5
+ /** A credential-free record that an annotation deep link was opened. */
6
+ export declare const ANNOTATION_ATTEMPT_STORAGE_KEY = "ai.annotation.attempt";
7
+ export declare const ANNOTATION_ATTEMPT_GLOBAL = "__weatherboardAnnotationAttemptV1";
8
+ export declare const ANNOTATION_STARTUP_STATUS_ID = "ai-annotation-startup-status";
9
+ /** Small enough to remain in the root entry point for Done's full teardown. */
10
+ export declare function clearAnnotationBootstrap(win: Window | null, storage: Pick<Storage, "removeItem"> | null, doc?: Document): void;
11
+ /**
12
+ * A credential-free note that the shortcut asked to open the toolbar but had
13
+ * to re-arm first (A-2384). The value is the request time in epoch ms; the
14
+ * next armed mount in this tab opens the toolbar once if it is fresh.
15
+ */
16
+ export declare const ANNOTATION_OPEN_INTENT_STORAGE_KEY = "ai.annotation.open";
17
+ export declare const OPEN_INTENT_TTL_MS: number;
@@ -0,0 +1,17 @@
1
+ /** Per-tab state. None of these values are cookies or sent by the browser. */
2
+ export declare const CAPTURE_TOKEN_STORAGE_KEY = "ai.annotation.capture";
3
+ export declare const READ_TOKEN_STORAGE_KEY = "ai.annotation.read";
4
+ export declare const REVIEW_RUN_STORAGE_KEY = "ai.annotation.run";
5
+ /** A credential-free record that an annotation deep link was opened. */
6
+ export declare const ANNOTATION_ATTEMPT_STORAGE_KEY = "ai.annotation.attempt";
7
+ export declare const ANNOTATION_ATTEMPT_GLOBAL = "__weatherboardAnnotationAttemptV1";
8
+ export declare const ANNOTATION_STARTUP_STATUS_ID = "ai-annotation-startup-status";
9
+ /** Small enough to remain in the root entry point for Done's full teardown. */
10
+ export declare function clearAnnotationBootstrap(win: Window | null, storage: Pick<Storage, "removeItem"> | null, doc?: Document): void;
11
+ /**
12
+ * A credential-free note that the shortcut asked to open the toolbar but had
13
+ * to re-arm first (A-2384). The value is the request time in epoch ms; the
14
+ * next armed mount in this tab opens the toolbar once if it is fresh.
15
+ */
16
+ export declare const ANNOTATION_OPEN_INTENT_STORAGE_KEY = "ai.annotation.open";
17
+ export declare const OPEN_INTENT_TTL_MS: number;
@@ -0,0 +1,17 @@
1
+ /** Per-tab state. None of these values are cookies or sent by the browser. */
2
+ export declare const CAPTURE_TOKEN_STORAGE_KEY = "ai.annotation.capture";
3
+ export declare const READ_TOKEN_STORAGE_KEY = "ai.annotation.read";
4
+ export declare const REVIEW_RUN_STORAGE_KEY = "ai.annotation.run";
5
+ /** A credential-free record that an annotation deep link was opened. */
6
+ export declare const ANNOTATION_ATTEMPT_STORAGE_KEY = "ai.annotation.attempt";
7
+ export declare const ANNOTATION_ATTEMPT_GLOBAL = "__weatherboardAnnotationAttemptV1";
8
+ export declare const ANNOTATION_STARTUP_STATUS_ID = "ai-annotation-startup-status";
9
+ /** Small enough to remain in the root entry point for Done's full teardown. */
10
+ export declare function clearAnnotationBootstrap(win: Window | null, storage: Pick<Storage, "removeItem"> | null, doc?: Document): void;
11
+ /**
12
+ * A credential-free note that the shortcut asked to open the toolbar but had
13
+ * to re-arm first (A-2384). The value is the request time in epoch ms; the
14
+ * next armed mount in this tab opens the toolbar once if it is fresh.
15
+ */
16
+ export declare const ANNOTATION_OPEN_INTENT_STORAGE_KEY = "ai.annotation.open";
17
+ export declare const OPEN_INTENT_TTL_MS: number;
@@ -0,0 +1,52 @@
1
+ import type { BuildInfo } from "./protocol.cjs";
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.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;
@@ -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
  *
@@ -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
  *
@@ -49,6 +49,11 @@ export type FrameMessageKind = FrameToParent["type"];
49
49
  * successful send and never a failed one.
50
50
  */
51
51
  export interface SendReceipt {
52
+ /**
53
+ * Durable console status for this capture's workflow. This is an opaque URL
54
+ * supplied by the console, not a client-derived run identifier.
55
+ */
56
+ progressUrl?: string;
52
57
  /**
53
58
  * The issue this capture became, when the far end names one.
54
59
  *
@@ -57,6 +62,11 @@ export interface SendReceipt {
57
62
  * crossing the network boundary is put through before it gets to be one.
58
63
  */
59
64
  issueUrl?: string;
65
+ saved?: {
66
+ feedbackId: string;
67
+ recoveryUrl?: string;
68
+ message: string;
69
+ };
60
70
  }
61
71
  /**
62
72
  * A URL from the network that is safe to put in somebody else's page (A-240).
@@ -49,6 +49,11 @@ export type FrameMessageKind = FrameToParent["type"];
49
49
  * successful send and never a failed one.
50
50
  */
51
51
  export interface SendReceipt {
52
+ /**
53
+ * Durable console status for this capture's workflow. This is an opaque URL
54
+ * supplied by the console, not a client-derived run identifier.
55
+ */
56
+ progressUrl?: string;
52
57
  /**
53
58
  * The issue this capture became, when the far end names one.
54
59
  *
@@ -57,6 +62,11 @@ export interface SendReceipt {
57
62
  * crossing the network boundary is put through before it gets to be one.
58
63
  */
59
64
  issueUrl?: string;
65
+ saved?: {
66
+ feedbackId: string;
67
+ recoveryUrl?: string;
68
+ message: string;
69
+ };
60
70
  }
61
71
  /**
62
72
  * A URL from the network that is safe to put in somebody else's page (A-240).
@@ -49,6 +49,11 @@ export type FrameMessageKind = FrameToParent["type"];
49
49
  * successful send and never a failed one.
50
50
  */
51
51
  export interface SendReceipt {
52
+ /**
53
+ * Durable console status for this capture's workflow. This is an opaque URL
54
+ * supplied by the console, not a client-derived run identifier.
55
+ */
56
+ progressUrl?: string;
52
57
  /**
53
58
  * The issue this capture became, when the far end names one.
54
59
  *
@@ -57,6 +62,11 @@ export interface SendReceipt {
57
62
  * crossing the network boundary is put through before it gets to be one.
58
63
  */
59
64
  issueUrl?: string;
65
+ saved?: {
66
+ feedbackId: string;
67
+ recoveryUrl?: string;
68
+ message: string;
69
+ };
60
70
  }
61
71
  /**
62
72
  * A URL from the network that is safe to put in somebody else's page (A-240).