@vitrinka/web 0.1.0 → 0.1.2
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/CHANGELOG.md +34 -0
- package/README.md +21 -8
- package/build/recorder/capture/rrweb.js +5 -1
- package/build/recorder/hud/AnnotateOverlay.js +1 -1
- package/build/recorder/hud/Hud.d.ts +0 -5
- package/build/recorder/hud/Hud.js +66 -13
- package/build/recorder/hud/LinkSheet.d.ts +9 -3
- package/build/recorder/hud/LinkSheet.js +7 -3
- package/build/recorder/hud/RecorderPill.d.ts +14 -5
- package/build/recorder/hud/RecorderPill.js +164 -25
- package/build/recorder/hud/Sheet.d.ts +9 -4
- package/build/recorder/hud/Sheet.js +6 -5
- package/build/recorder/hud/layer.d.ts +52 -0
- package/build/recorder/hud/layer.js +121 -0
- package/build/recorder/hud/styles.d.ts +21 -7
- package/build/recorder/hud/styles.js +251 -96
- package/build/recorder/hud/useDock.d.ts +32 -0
- package/build/recorder/hud/useDock.js +168 -0
- package/build/recorder/link.d.ts +6 -2
- package/build/recorder/link.js +9 -4
- package/build/recorder/session.d.ts +2 -2
- package/build/recorder/session.js +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
|
-
* The composer sheet — the extension's hud.html, as a component:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* The composer sheet — the extension's hud.html, as a component: ≤ 288px
|
|
4
|
+
* anchored to the dock (a bottom sheet on phones), a 14px textarea (16px on
|
|
5
|
+
* touch, so iOS never zooms), ✕ in the title row, a three-column footer
|
|
6
|
+
* (board|task · context · ↑ Send) and the hint line (hidden on touch). Enter sends, ⇧Enter newlines, Esc
|
|
6
7
|
* cancels. The DRAFT is owned by the caller so a cancel keeps it until the
|
|
7
8
|
* next send (recorder-hud-polish D3); the board|task choice resets on every
|
|
8
9
|
* open — a destination is per observation, not a mode.
|
|
9
10
|
*/
|
|
10
11
|
import { useEffect, useRef, useState } from 'react';
|
|
11
12
|
import { ArrowUpIcon, CloseIcon } from './icons';
|
|
12
|
-
export function Sheet({ title, ctx, pick, draft, onDraft, onSend, onClose }) {
|
|
13
|
+
export function Sheet({ className, origin, title, ctx, pick, draft, onDraft, onSend, onClose }) {
|
|
13
14
|
const ta = useRef(null);
|
|
14
15
|
const [task, setTask] = useState(false);
|
|
15
16
|
useEffect(() => {
|
|
@@ -32,5 +33,5 @@ export function Sheet({ title, ctx, pick, draft, onDraft, onSend, onClose }) {
|
|
|
32
33
|
send();
|
|
33
34
|
}
|
|
34
35
|
};
|
|
35
|
-
return (_jsxs("div", { className:
|
|
36
|
+
return (_jsxs("div", { className: `pop ${className}`, "data-origin": origin, role: "dialog", "aria-labelledby": "vt-pop-title", onKeyDown: onKey, children: [_jsxs("div", { className: "pop-head", children: [_jsxs("span", { className: "title", children: [_jsx("i", {}), _jsx("span", { id: "vt-pop-title", children: title })] }), _jsx("button", { type: "button", className: "closeb", "aria-label": "Close", onClick: onClose, children: _jsx(CloseIcon, {}) })] }), _jsx("textarea", { ref: ta, placeholder: "what's wrong / what to refine\u2026", value: draft, onChange: (e) => onDraft(e.target.value), onKeyDown: onTaKey }), _jsxs("div", { className: pick ? 'row' : 'row nodest', children: [pick ? (_jsxs("span", { className: "dest", role: "group", "aria-label": "Where this snap lands", children: [_jsx("button", { type: "button", "aria-pressed": !task, onClick: () => setTask(false), children: "board" }), _jsx("button", { type: "button", "aria-pressed": task, onClick: () => setTask(true), children: "task" })] })) : null, _jsx("span", { className: "ctx", children: ctx }), _jsxs("button", { type: "button", className: "sendb", onClick: send, children: [_jsx(ArrowUpIcon, {}), _jsx("span", { children: "Send" })] })] }), _jsx("div", { className: "hints", children: "\u21A9 send \u00B7 \u21E7\u21A9 newline \u00B7 esc cancel" })] }));
|
|
36
37
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a sheet opens and how it enters and leaves (recorder-hud-subtle D5).
|
|
3
|
+
* On desktop and tablet a sheet is a popover anchored to the dock, opening
|
|
4
|
+
* toward the viewport centre from whichever spot the dock rests on; on a
|
|
5
|
+
* phone (narrow + coarse pointer) it is a bottom sheet over the visual
|
|
6
|
+
* viewport, so it rides above the on-screen keyboard. Coordinates are
|
|
7
|
+
* relative to the sheet host's own origin box, which may sit inside a
|
|
8
|
+
* transformed dialog.
|
|
9
|
+
*/
|
|
10
|
+
import { type Place } from '@vitrinka/link/dock';
|
|
11
|
+
import { type CSSProperties } from 'react';
|
|
12
|
+
export declare const PHONE_QUERY = "(max-width: 640px) and (pointer: coarse)";
|
|
13
|
+
export declare const FINE_QUERY = "(pointer: fine)";
|
|
14
|
+
/** A live media query (false where matchMedia is missing). */
|
|
15
|
+
export declare function useMedia(query: string): boolean;
|
|
16
|
+
/** Re-render while `active` whenever the visual viewport moves (keyboard, pinch, rotate). */
|
|
17
|
+
export declare function useViewportTick(active: boolean): number;
|
|
18
|
+
/**
|
|
19
|
+
* Mount-and-animate for a surface that opens and closes: `is-open` one frame
|
|
20
|
+
* after mounting (so the pre-open state paints first), `is-closing` for
|
|
21
|
+
* `closeMs` before unmounting.
|
|
22
|
+
*/
|
|
23
|
+
export declare function usePresence(open: boolean, closeMs: number): {
|
|
24
|
+
mounted: boolean;
|
|
25
|
+
cls: '' | 'is-open' | 'is-closing';
|
|
26
|
+
};
|
|
27
|
+
export interface Box {
|
|
28
|
+
left: number;
|
|
29
|
+
top: number;
|
|
30
|
+
right: number;
|
|
31
|
+
bottom: number;
|
|
32
|
+
}
|
|
33
|
+
/** The dock's resting box — layout only, so a spring in flight does not skew the anchor. */
|
|
34
|
+
export declare function restingBox(el: HTMLElement): Box;
|
|
35
|
+
/** The viewport position of the host a sheet renders in (its 0×0 origin box). */
|
|
36
|
+
export declare function hostOrigin(mount: HTMLElement): {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
};
|
|
40
|
+
/** A popover anchored to the dock, growing from the corner nearest it. */
|
|
41
|
+
export declare function anchoredLayer(dock: Box, place: Place, origin: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
}): {
|
|
45
|
+
style: CSSProperties;
|
|
46
|
+
origin: string;
|
|
47
|
+
};
|
|
48
|
+
/** A phone bottom sheet: the full visual viewport, content pinned to its bottom edge. */
|
|
49
|
+
export declare function phoneLayer(origin: {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
}): CSSProperties;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a sheet opens and how it enters and leaves (recorder-hud-subtle D5).
|
|
3
|
+
* On desktop and tablet a sheet is a popover anchored to the dock, opening
|
|
4
|
+
* toward the viewport centre from whichever spot the dock rests on; on a
|
|
5
|
+
* phone (narrow + coarse pointer) it is a bottom sheet over the visual
|
|
6
|
+
* viewport, so it rides above the on-screen keyboard. Coordinates are
|
|
7
|
+
* relative to the sheet host's own origin box, which may sit inside a
|
|
8
|
+
* transformed dialog.
|
|
9
|
+
*/
|
|
10
|
+
import { colOf, rowOf, untuck } from '@vitrinka/link/dock';
|
|
11
|
+
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
12
|
+
export const PHONE_QUERY = '(max-width: 640px) and (pointer: coarse)';
|
|
13
|
+
export const FINE_QUERY = '(pointer: fine)';
|
|
14
|
+
/** A live media query (false where matchMedia is missing). */
|
|
15
|
+
export function useMedia(query) {
|
|
16
|
+
return useSyncExternalStore((cb) => {
|
|
17
|
+
const m = globalThis.matchMedia?.(query);
|
|
18
|
+
m?.addEventListener('change', cb);
|
|
19
|
+
return () => m?.removeEventListener('change', cb);
|
|
20
|
+
}, () => globalThis.matchMedia?.(query).matches ?? false, () => false);
|
|
21
|
+
}
|
|
22
|
+
/** Re-render while `active` whenever the visual viewport moves (keyboard, pinch, rotate). */
|
|
23
|
+
export function useViewportTick(active) {
|
|
24
|
+
const [tick, setTick] = useState(0);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!active)
|
|
27
|
+
return;
|
|
28
|
+
const bump = () => setTick((n) => n + 1);
|
|
29
|
+
const vv = globalThis.visualViewport;
|
|
30
|
+
vv?.addEventListener('resize', bump);
|
|
31
|
+
vv?.addEventListener('scroll', bump);
|
|
32
|
+
addEventListener('resize', bump);
|
|
33
|
+
return () => {
|
|
34
|
+
vv?.removeEventListener('resize', bump);
|
|
35
|
+
vv?.removeEventListener('scroll', bump);
|
|
36
|
+
removeEventListener('resize', bump);
|
|
37
|
+
};
|
|
38
|
+
}, [active]);
|
|
39
|
+
return tick;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Mount-and-animate for a surface that opens and closes: `is-open` one frame
|
|
43
|
+
* after mounting (so the pre-open state paints first), `is-closing` for
|
|
44
|
+
* `closeMs` before unmounting.
|
|
45
|
+
*/
|
|
46
|
+
export function usePresence(open, closeMs) {
|
|
47
|
+
const [mounted, setMounted] = useState(false);
|
|
48
|
+
const [cls, setCls] = useState('');
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (open) {
|
|
51
|
+
setMounted(true);
|
|
52
|
+
let inner = 0;
|
|
53
|
+
const outer = requestAnimationFrame(() => {
|
|
54
|
+
inner = requestAnimationFrame(() => setCls('is-open'));
|
|
55
|
+
});
|
|
56
|
+
return () => {
|
|
57
|
+
cancelAnimationFrame(outer);
|
|
58
|
+
cancelAnimationFrame(inner);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
setCls((c) => (c === '' ? c : 'is-closing'));
|
|
62
|
+
const t = setTimeout(() => {
|
|
63
|
+
setMounted(false);
|
|
64
|
+
setCls('');
|
|
65
|
+
}, closeMs);
|
|
66
|
+
return () => clearTimeout(t);
|
|
67
|
+
}, [open, closeMs]);
|
|
68
|
+
return { mounted: open || mounted, cls };
|
|
69
|
+
}
|
|
70
|
+
/** The dock's resting box — layout only, so a spring in flight does not skew the anchor. */
|
|
71
|
+
export function restingBox(el) {
|
|
72
|
+
const left = el.offsetLeft;
|
|
73
|
+
const top = el.offsetTop;
|
|
74
|
+
return { left, top, right: left + el.offsetWidth, bottom: top + el.offsetHeight };
|
|
75
|
+
}
|
|
76
|
+
/** The viewport position of the host a sheet renders in (its 0×0 origin box). */
|
|
77
|
+
export function hostOrigin(mount) {
|
|
78
|
+
const root = mount.getRootNode();
|
|
79
|
+
const host = root instanceof ShadowRoot ? root.host : null;
|
|
80
|
+
if (!host)
|
|
81
|
+
return { x: 0, y: 0 };
|
|
82
|
+
const r = host.getBoundingClientRect();
|
|
83
|
+
return { x: r.left, y: r.top };
|
|
84
|
+
}
|
|
85
|
+
const GAP = 8;
|
|
86
|
+
/** A popover anchored to the dock, growing from the corner nearest it. */
|
|
87
|
+
export function anchoredLayer(dock, place, origin) {
|
|
88
|
+
const spot = untuck(place);
|
|
89
|
+
const row = rowOf(spot);
|
|
90
|
+
const col = colOf(spot);
|
|
91
|
+
let ax;
|
|
92
|
+
let tx;
|
|
93
|
+
let ay;
|
|
94
|
+
let ty;
|
|
95
|
+
if ('tuck' in place) {
|
|
96
|
+
ax = place.tuck === 'left' ? dock.right + GAP : dock.left - GAP;
|
|
97
|
+
tx = place.tuck === 'left' ? '0' : '-100%';
|
|
98
|
+
ay = row === 't' ? dock.top : dock.bottom;
|
|
99
|
+
ty = row === 't' ? '0' : '-100%';
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
ax = col === 'l' ? dock.left : col === 'r' ? dock.right : innerWidth / 2;
|
|
103
|
+
tx = col === 'l' ? '0' : col === 'r' ? '-100%' : '-50%';
|
|
104
|
+
ay = row === 't' ? dock.bottom + GAP : dock.top - GAP;
|
|
105
|
+
ty = row === 't' ? '0' : '-100%';
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
style: { left: ax - origin.x, top: ay - origin.y, translate: `${tx} ${ty}` },
|
|
109
|
+
origin: `${row === 't' ? 'top' : 'bottom'}-${col === 'l' ? 'left' : col === 'c' ? 'center' : 'right'}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/** A phone bottom sheet: the full visual viewport, content pinned to its bottom edge. */
|
|
113
|
+
export function phoneLayer(origin) {
|
|
114
|
+
const vv = globalThis.visualViewport;
|
|
115
|
+
return {
|
|
116
|
+
left: (vv?.offsetLeft ?? 0) - origin.x,
|
|
117
|
+
top: (vv?.offsetTop ?? 0) - origin.y,
|
|
118
|
+
width: vv?.width ?? innerWidth,
|
|
119
|
+
height: vv?.height ?? innerHeight,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The HUD's stylesheet
|
|
3
|
-
* it
|
|
4
|
-
*
|
|
2
|
+
* The HUD's stylesheet. Lives inside the shadow root (a `<style>` element),
|
|
3
|
+
* so it never touches host CSS and host CSS never touches it.
|
|
4
|
+
*
|
|
5
|
+
* Shapes (recorder-hud-subtle): the 28px glass PUCK at idle, the dot+timer
|
|
6
|
+
* CAPSULE while recording that unfolds into the TRAY on intent, the 6px edge
|
|
7
|
+
* TAB when tucked. The `.dock` rests on one of six spots (`data-row` t|b ×
|
|
8
|
+
* `data-col` l|c|r) by viewport insets, so an unfolding tray always grows
|
|
9
|
+
* toward the centre; a drag moves it by `transform` only. Motion follows
|
|
10
|
+
* the transitions.dev token scale.
|
|
11
|
+
*/
|
|
12
|
+
export declare const HUD_CSS_BASE = "\n* { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\nsvg { width:1em; height:1em; vertical-align:-.125em; }\n.hud {\n --ink: rgba(26,22,23,.72); --ink-pop: rgba(26,22,23,.88); --ink-solid: #1d1a1b;\n --ink-2: rgba(255,255,255,.08); --edge: rgba(255,255,255,.11); --rim: rgba(0,0,0,.30);\n --fg: #f4efea; --fg-2: rgba(244,239,234,.66); --fg-3: rgba(244,239,234,.44);\n --rec: #ff3b57; --warn: #f0a63a;\n --m: 16px; --h: 28px; --b: 24px;\n --duration-quick: 150ms; --duration-fast: 250ms; --duration-resize: 300ms; --duration-medium: 350ms; --duration-snap: 500ms;\n --ease-smooth-out: cubic-bezier(0.22, 1, 0.36, 1); --ease-in-out: ease-in-out; --ease-out: ease-out;\n --ease-spring: var(--ease-smooth-out);\n --scale-open: .97; --scale-closing: .99; --blur-small: 2px; --rise: 16px;\n color: var(--fg); font-size: 12px;\n}\n@supports (transition-timing-function: linear(0, 1)) {\n /* a damped spring, ~4% overshoot over 500ms \u2014 the settle after a throw */\n .hud { --ease-spring: linear(0, 0.042, 0.143, 0.274, 0.414, 0.549, 0.67, 0.773, 0.856, 0.921, 0.968, 1.001, 1.021, 1.033, 1.038, 1.038, 1.035, 1.031, 1.025, 1.02, 1.015, 1.011, 1.007, 1.004, 1); }\n}\n@media (pointer: coarse) { .hud { --m: 12px; --h: 32px; --b: 30px; } }\n/* scoped under .hud so a control's own `all:unset` reset cannot wipe the surface */\n.hud .glass, .hud .menu, .hud .pop, .hud .detail, .hud .hint {\n background: var(--ink);\n -webkit-backdrop-filter: blur(16px) saturate(1.5); backdrop-filter: blur(16px) saturate(1.5);\n box-shadow: inset 0 0 0 1px var(--edge), 0 0 0 .5px var(--rim), 0 10px 28px -10px rgba(0,0,0,.5), 0 2px 6px -2px rgba(0,0,0,.28);\n color: var(--fg);\n}\n.hud .menu, .hud .pop { background: var(--ink-pop); }\n@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {\n .hud .glass, .hud .menu, .hud .pop, .hud .detail, .hud .hint { background: var(--ink-solid); }\n}\n.sr { position:absolute; width:1px; height:1px; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }\n\n/* the dock: one of six spots, or tucked into a side edge */\n.dock { position:fixed; z-index:4; display:flex; pointer-events:auto; }\n.dock[data-row=\"b\"] { bottom: calc(var(--m) + env(safe-area-inset-bottom, 0px)); }\n.dock[data-row=\"t\"] { top: calc(var(--m) + env(safe-area-inset-top, 0px)); }\n.dock[data-col=\"l\"] { left: calc(var(--m) + env(safe-area-inset-left, 0px)); }\n.dock[data-col=\"r\"] { right: calc(var(--m) + env(safe-area-inset-right, 0px)); }\n.dock[data-col=\"c\"] { left: calc(50% - var(--hw, 36px) / 2); }\n.dock[data-tuck] { top: clamp(8px, calc(var(--ty, .5) * 100% - 28px), calc(100% - 64px)); }\n.dock[data-tuck=\"left\"] { left:0; }\n.dock[data-tuck=\"right\"] { right:0; }\n.dock.settling { transition: transform var(--duration-snap) var(--ease-spring); }\n.dock.dragging .glass { box-shadow: inset 0 0 0 1px var(--edge), 0 0 0 .5px var(--rim), 0 18px 40px -12px rgba(0,0,0,.55), 0 4px 10px -4px rgba(0,0,0,.3); }\n\n/* the recording dot: a soft ripple while recording, still otherwise */\n.dot { position:relative; width:8px; height:8px; flex:none; border-radius:50%; background:var(--rec); }\n.dot::after { content:\"\"; position:absolute; inset:0; border-radius:50%; box-shadow:0 0 0 0 rgba(255,59,87,.5); animation:ripple 2s var(--ease-out) infinite; }\n@keyframes ripple { 0% { box-shadow:0 0 0 0 rgba(255,59,87,.45); } 70%, 100% { box-shadow:0 0 0 7px rgba(255,59,87,0); } }\n.bar[data-state=\"paused\"] .dot { background:var(--fg-3); }\n.bar[data-state=\"bad\"] .dot { background:var(--warn); }\n.bar[data-state=\"paused\"] .dot::after, .bar[data-state=\"bad\"] .dot::after { animation:none; }\n\n/* idle: the puck \u2014 ring = not linked, dot = ready; the label slides out on intent */\n.puck { all:unset; box-sizing:border-box; display:inline-flex; align-items:center; height:var(--h); min-width:var(--h);\n padding:0 calc((var(--h) - 10px) / 2); border-radius:999px; cursor:pointer; touch-action:none; user-select:none; -webkit-user-select:none;\n font-weight:600; font-size:12px; color:var(--fg); }\n.dock[data-col=\"r\"] .puck { flex-direction:row-reverse; }\n.ring { width:10px; height:10px; flex:none; border-radius:50%; box-shadow:inset 0 0 0 1.5px var(--fg-2);\n transition: background-color var(--duration-quick) var(--ease-out), box-shadow var(--duration-quick) var(--ease-out); }\n.puck[data-state=\"ready\"] .ring { background:var(--fg-2); box-shadow:none; }\n.puck[data-state=\"busy\"] .ring { background:var(--fg-3); box-shadow:none; animation:breathe 1.2s var(--ease-in-out) infinite; }\n@keyframes breathe { 50% { opacity:.35; } }\n.lab { display:grid; grid-template-columns:0fr; opacity:0;\n transition: grid-template-columns var(--duration-resize) var(--ease-smooth-out), opacity var(--duration-quick) var(--ease-out); }\n/* the gap to the ring is a clipped pseudo-element: padding would keep a folded label 10px wide */\n.lab > span { min-width:0; overflow:hidden; white-space:nowrap; }\n.lab > span::before, .lab > span::after { content:\"\"; display:inline-block; }\n.lab > span::before { width:8px; }\n.lab > span::after { width:2px; }\n.dock[data-col=\"r\"] .lab > span::before { width:2px; }\n.dock[data-col=\"r\"] .lab > span::after { width:8px; }\n.puck:focus-visible .lab { grid-template-columns:1fr; opacity:1; }\n.puck:focus-visible { outline:2px solid var(--rec); outline-offset:2px; }\n@media (hover: hover) {\n .puck:hover .lab { grid-template-columns:1fr; opacity:1; }\n .puck[data-state=\"ready\"]:hover .ring { background:var(--rec); }\n .puck[data-state=\"unlinked\"]:hover .ring { box-shadow:inset 0 0 0 1.5px var(--fg); }\n}\n\n/* recording: the capsule (handle) + the tray it unfolds */\n.bar { position:relative; display:inline-flex; align-items:center; height:var(--h); border-radius:999px; }\n.dock[data-col=\"r\"] .bar, .dock[data-col=\"r\"] .tray-in { flex-direction:row-reverse; }\n.handle { all:unset; box-sizing:border-box; display:inline-flex; align-items:center; gap:7px; height:var(--h);\n padding:0 11px 0 10px; border-radius:999px; cursor:grab; touch-action:none; user-select:none; -webkit-user-select:none; }\n.dock.dragging .handle { cursor:grabbing; }\n.handle:focus-visible { outline:2px solid var(--rec); outline-offset:2px; }\n.time { font-weight:600; font-size:12px; line-height:1; font-variant-numeric:tabular-nums; letter-spacing:.01em; min-width:34px; }\n.tray { display:grid; grid-template-columns:0fr; transition: grid-template-columns var(--duration-resize) var(--ease-smooth-out); }\n.tray-in { min-width:0; overflow:hidden; display:flex; align-items:center; gap:1px; padding:34px 3px; margin:-34px 0;\n pointer-events:none; opacity:0; filter:blur(var(--blur-small));\n transition: opacity var(--duration-quick) var(--ease-in-out), filter var(--duration-quick) var(--ease-in-out); }\n.tray-in > * { pointer-events:auto; }\n.bar[data-open] .tray { grid-template-columns:1fr; }\n.bar[data-open] .tray-in { opacity:1; filter:none; transition-duration: var(--duration-fast); transition-delay: 60ms; }\n.sep { width:1px; height:14px; flex:none; margin:0 5px; background:var(--edge); }\n.sync { display:inline-grid; place-items:center; width:18px; flex:none; font-size:11px; color:var(--fg-3); }\n.sync.warn, .sync.bad { color:var(--warn); }\nbutton.tb { all:unset; pointer-events:auto; box-sizing:border-box; position:relative; flex:none; display:inline-grid; place-items:center;\n width:var(--b); height:var(--b); border-radius:999px; color:var(--fg-2); cursor:pointer; font-size:13px;\n transition: background-color var(--duration-quick) var(--ease-out), color var(--duration-quick) var(--ease-out); }\nbutton.tb.b-annotate { color:var(--rec); }\nbutton.tb.on { background:var(--rec); color:#fff; }\nbutton.tb:focus-visible { outline:2px solid var(--rec); outline-offset:-2px; }\n@media (hover: hover) { button.tb:hover { background:var(--ink-2); color:var(--fg); } button.tb.on:hover { background:var(--rec); color:#fff; } }\nkbd { position:absolute; left:50%; bottom:calc(100% + 8px); transform:translateX(-50%); padding:4px 6px; border-radius:6px;\n background:var(--ink-pop); box-shadow:inset 0 0 0 1px var(--edge); color:var(--fg-2); font:600 10px/1 -apple-system, BlinkMacSystemFont, sans-serif;\n letter-spacing:.02em; white-space:nowrap; opacity:0; pointer-events:none; transition:opacity var(--duration-quick) var(--ease-out); }\n.dock[data-row=\"t\"] kbd { bottom:auto; top:calc(100% + 8px); }\n@media (hover: hover) { button.tb:hover kbd { opacity:1; transition-delay:400ms; } }\nbutton.tb:focus-visible kbd { opacity:1; }\n.bar.composing kbd { opacity:0 !important; }\n\n/* the health line: only when something needs saying */\n.detail { position:absolute; width:max-content; max-width:260px; padding:6px 11px; border-radius:999px; color:var(--fg-2); font-size:11px; line-height:1.35;\n opacity:0; transform:translateY(4px); pointer-events:none;\n transition: opacity var(--duration-fast) var(--ease-smooth-out), transform var(--duration-fast) var(--ease-smooth-out); }\n.dock[data-row=\"b\"] .detail { bottom:calc(100% + 6px); }\n.dock[data-row=\"t\"] .detail { top:calc(100% + 6px); transform:translateY(-4px); }\n.dock[data-col=\"r\"] .detail { right:0; }\n.dock[data-col=\"l\"] .detail, .dock[data-col=\"c\"] .detail { left:0; }\n.detail.show { opacity:1; transform:none; }\n.detail.bad { color:var(--fg); box-shadow: inset 0 0 0 1px rgba(240,166,58,.55), 0 10px 28px -10px rgba(0,0,0,.5); }\n\n/* the edge tab a tucked HUD becomes */\n.tab { all:unset; box-sizing:border-box; position:relative; display:block; width:22px; height:56px; cursor:pointer; touch-action:none; }\n.tab::before { content:\"\"; position:absolute; top:0; bottom:0; width:6px; background:var(--ink);\n -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);\n box-shadow: inset 0 0 0 1px var(--edge), 0 0 0 .5px var(--rim), 0 6px 18px -6px rgba(0,0,0,.5);\n transition: width var(--duration-quick) var(--ease-out); }\n.tab::after { content:\"\"; position:absolute; top:16px; bottom:16px; width:2px; border-radius:1px; background:var(--fg-3); }\n.dock[data-tuck=\"left\"] .tab::before { left:0; border-radius:0 7px 7px 0; }\n.dock[data-tuck=\"right\"] .tab::before { right:0; border-radius:7px 0 0 7px; }\n.dock[data-tuck=\"left\"] .tab::after { left:2px; }\n.dock[data-tuck=\"right\"] .tab::after { right:2px; }\n.tab[data-state=\"rec\"]::after { background:var(--rec); }\n.tab[data-state=\"bad\"]::after { background:var(--warn); }\n.tab:focus-visible::before { width:10px; outline:2px solid var(--rec); outline-offset:1px; }\n@media (hover: hover) { .tab:hover::before { width:9px; } }\n\n/* the \u22EF menu \u2014 grows from the bar toward the viewport centre */\n.menu { position:absolute; min-width:188px; padding:5px; border-radius:12px; display:flex; flex-direction:column; gap:1px; z-index:5; }\n.dock[data-row=\"b\"] .menu { bottom:calc(100% + 8px); }\n.dock[data-row=\"t\"] .menu { top:calc(100% + 8px); }\n.dock[data-col=\"r\"] .menu { right:0; }\n.dock[data-col=\"l\"] .menu, .dock[data-col=\"c\"] .menu { left:0; }\n.menu > button { all:unset; box-sizing:border-box; cursor:pointer; padding:8px 10px; border-radius:7px; color:var(--fg);\n font-size:12.5px; line-height:1; display:flex; gap:9px; align-items:center; }\n.menu > button svg { color:var(--fg-2); }\n.menu > button:focus-visible { outline:2px solid var(--rec); outline-offset:-2px; }\n@media (hover: hover) { .menu > button:hover { background:var(--ink-2); } .menu > button.danger:hover, .menu > button.danger:hover svg { color:var(--rec); } }\n.moveto { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:6px 6px 6px 10px; margin-top:3px;\n border-top:1px solid var(--edge); color:var(--fg-2); font-size:12px; }\n.spots { display:grid; grid-template-columns:repeat(3, 24px); grid-template-rows:repeat(2, 24px); gap:0 2px; }\n.spots button { all:unset; box-sizing:border-box; position:relative; cursor:pointer; border-radius:5px; }\n.spots button::before { content:\"\"; position:absolute; inset:7px 5px; border-radius:3px; background:var(--ink-2); box-shadow:inset 0 0 0 1px var(--edge); }\n.spots button[aria-checked=\"true\"]::before { background:var(--rec); box-shadow:none; }\n.spots button:focus-visible { outline:2px solid var(--rec); outline-offset:-2px; }\n@media (hover: hover) { .spots button:hover::before { background:var(--fg-3); } .spots button[aria-checked=\"true\"]:hover::before { background:var(--rec); } }\n\n/* presence: dropdown-style grow from the anchor on open, a softer shrink on close */\n.grow { transform:scale(var(--scale-open)); opacity:0;\n transition: transform var(--duration-fast) var(--ease-smooth-out), opacity var(--duration-fast) var(--ease-smooth-out); }\n.grow[data-origin=\"bottom-right\"] { transform-origin:bottom right; }\n.grow[data-origin=\"bottom-left\"] { transform-origin:bottom left; }\n.grow[data-origin=\"bottom-center\"] { transform-origin:bottom center; }\n.grow[data-origin=\"top-right\"] { transform-origin:top right; }\n.grow[data-origin=\"top-left\"] { transform-origin:top left; }\n.grow[data-origin=\"top-center\"] { transform-origin:top center; }\n.grow.is-open { transform:scale(1); opacity:1; }\n.grow.is-closing { transform:scale(var(--scale-closing)); opacity:0; pointer-events:none;\n transition-duration: var(--duration-quick); }\n/* phone: the sheet rises from the bottom edge (toast motion) */\n.rise { transform:translateY(var(--rise)) scale(var(--scale-open)); opacity:0; filter:blur(var(--blur-small));\n transition: transform var(--duration-fast) var(--ease-smooth-out), opacity var(--duration-fast) var(--ease-smooth-out), filter var(--duration-fast) var(--ease-smooth-out); }\n.rise.is-open { transform:none; opacity:1; filter:none; transition-duration: var(--duration-medium); }\n.rise.is-closing { pointer-events:none; }\n\n/* pick chrome (annotate mode): dim, outline, hint \u2014 shadow-root children */\n.dim { position:fixed; inset:0; z-index:1; pointer-events:none; background:rgba(0,0,0,.18); }\n.outline { position:fixed; z-index:2; pointer-events:none; border:2px solid var(--rec); border-radius:6px;\n box-shadow:0 0 0 4px rgba(255,59,87,.15); }\n.hint { position:fixed; top:14px; left:50%; transform:translateX(-50%); z-index:3; padding:8px 14px;\n border-radius:999px; font-size:12px; font-weight:600; line-height:1; white-space:nowrap; display:flex; gap:8px; align-items:center; }\n.hint svg { color:var(--rec); }\n.hint .keys { color:var(--fg-3); font-weight:500; margin-left:-4px; }\n@media (pointer: coarse) { .hint .keys { display:none; } }\n.hud[data-spot=\"tc\"] .hint { top:auto; bottom:14px; }\n\n/* sheets: a popover anchored to the dock, or a phone bottom sheet */\n.anchor { position:fixed; z-index:6; pointer-events:none; }\n.anchor > * { pointer-events:auto; }\n.phone { position:fixed; z-index:6; display:flex; flex-direction:column; justify-content:flex-end;\n padding:0 8px calc(8px + env(safe-area-inset-bottom, 0px)); pointer-events:none; }\n.phone > * { pointer-events:auto; }\n.pop { width:min(288px, calc(100vw - 24px)); padding:12px; border-radius:14px; }\n.phone .pop { width:100%; border-radius:18px; padding:14px; }\n.pop-head { display:flex; align-items:center; justify-content:space-between; gap:8px; }\n.title { display:flex; align-items:center; gap:7px; font-size:12.5px; font-weight:600; color:var(--fg); }\n.title i { width:3px; height:11px; border-radius:1px; background:var(--rec); }\n.closeb { all:unset; box-sizing:border-box; cursor:pointer; width:24px; height:24px; border-radius:7px; color:var(--fg-3);\n display:inline-grid; place-items:center; font-size:12px; }\n.closeb:focus-visible, .sendb:focus-visible, .retry:focus-visible, .dest button:focus-visible { outline:2px solid var(--rec); outline-offset:2px; }\n@media (hover: hover) { .closeb:hover { color:var(--fg); background:var(--ink-2); } }\ntextarea { width:100%; margin-top:10px; padding:9px 11px; min-height:64px; resize:vertical; background:rgba(0,0,0,.28);\n border:0; box-shadow:inset 0 0 0 1px var(--edge); border-radius:9px; color:var(--fg);\n font:400 14px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\n@media (pointer: coarse) { textarea { font-size:16px; } .hints { display:none; } }\ntextarea::placeholder { color:var(--fg-3); }\ntextarea:focus { outline:none; box-shadow:inset 0 0 0 1px var(--fg-3); }\n.row { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:10px; margin-top:10px; }\n.row.nodest { grid-template-columns:minmax(0,1fr) auto; }\n.ctx { font-size:11px; line-height:1.35; color:var(--fg-3); text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.row.nodest .ctx { text-align:left; }\n.dest { display:inline-flex; padding:2px; border-radius:8px; background:rgba(0,0,0,.28); box-shadow:inset 0 0 0 1px var(--edge); }\n.dest button { all:unset; box-sizing:border-box; cursor:pointer; padding:5px 8px; min-height:24px; border-radius:6px; color:var(--fg-3);\n font-size:11.5px; font-weight:600; line-height:14px; transition: background-color var(--duration-quick) var(--ease-out), color var(--duration-quick) var(--ease-out); }\n.dest button[aria-pressed=\"true\"] { background:var(--ink-2); color:var(--fg); }\n.sendb { all:unset; box-sizing:border-box; cursor:pointer; display:inline-flex; align-items:center; gap:6px; height:28px; padding:0 12px;\n border-radius:8px; background:var(--rec); color:#fff; font-size:12.5px; font-weight:600; line-height:1; text-decoration:none; }\n@media (hover: hover) { .sendb:hover { background:#ff5670; } }\n.hints { margin-top:9px; font-size:11px; line-height:1; color:var(--fg-3); }\n";
|
|
13
|
+
/**
|
|
14
|
+
* Inline style of the HUD host: a 0×0 fixed box at the viewport origin (the
|
|
15
|
+
* dock and sheets are fixed children of its shadow root). Zero-size keeps
|
|
16
|
+
* rrweb's placeholder for this blocked node empty in every replay, and
|
|
17
|
+
* `pointer-events:none` lets the page under the HUD's gaps take every
|
|
18
|
+
* click. Overrides the [popover] UA sheet.
|
|
5
19
|
*/
|
|
6
|
-
export declare const HUD_CSS_BASE = "\n* { box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\nsvg { width:1em; height:1em; vertical-align:-.125em; }\n.stack { position:relative; z-index:4; display:flex; flex-direction:column; align-items:flex-end; gap:6px; }\n.pill { display:flex; align-items:center; gap:10px; padding:8px 10px 8px 14px;\n background:#1d1a1b; border:1px solid #292526; border-radius:999px;\n box-shadow:0 8px 30px rgba(0,0,0,.35); color:#f0eae4; }\n.dot { width:10px; height:10px; border-radius:50%; background:#ff3b57; animation:p 1.4s ease infinite; }\n.paused .dot { animation:none; background:#756e68; }\n@keyframes p { 50% { opacity:.35; } }\n.sync { font:600 11px/1 ui-monospace, Menlo, monospace; color:#5f7a5f; }\n.sync.warn { color:#e8a33d; }\n.sync.bad { color:#ff3b57; }\n.sync.busy { color:#a8a099; }\n.detail { max-width:320px; padding:6px 12px; border-radius:999px;\n background:#1d1a1b; border:1px solid #292526; color:#a8a099;\n font:500 10px/1.4 ui-monospace, Menlo, monospace;\n opacity:0; transform:translateY(-3px); transition:opacity .24s ease, transform .24s ease;\n pointer-events:none; }\n.detail.show { opacity:1; transform:none; }\n.detail.bad { border-color:#ff3b57; color:#f0eae4; }\n.time { font:600 11px/1 ui-monospace, Menlo, monospace; }\n.name { font:500 10px/1 ui-monospace, Menlo, monospace; color:#756e68;\n border-left:1px solid #292526; padding-left:10px; max-width:140px;\n overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\nbutton { all:unset; cursor:pointer; position:relative; width:28px; height:28px; border-radius:50%;\n border:1px solid #363132; color:#a8a099; font-size:12px; text-align:center; line-height:28px; }\nbutton:hover { color:#f0eae4; border-color:#756e68; }\nbutton:focus-visible, .sendb:focus-visible, .closeb:focus-visible { outline:2px solid #ff3b57; outline-offset:2px; }\nbutton.snap { background:#ff3b57; border-color:#ff3b57; color:#fff; }\nbutton.snap.on { background:#f0eae4; border-color:#f0eae4; color:#1d1a1b; }\nkbd { position:absolute; left:50%; bottom:calc(100% + 8px); transform:translateX(-50%);\n padding:4px 6px; border-radius:5px; background:#252122; border:1px solid #363132;\n color:#a8a099; font:600 9.5px/1 ui-monospace, Menlo, monospace; letter-spacing:.06em;\n white-space:nowrap; opacity:0; transition:opacity .16s ease; pointer-events:none; }\n.b-snap kbd, .b-more kbd { left:auto; right:0; transform:none; }\nbutton:hover kbd, button:focus-visible kbd { opacity:1; }\n.pill.composing kbd { opacity:0; }\n/* idle grip: a quiet dot until a recording starts */\n.grip { display:flex; align-items:center; justify-content:center; width:34px; height:34px; border-radius:50%;\n background:#1d1a1b; border:1px solid #292526; box-shadow:0 8px 30px rgba(0,0,0,.35); }\n.grip .dot { animation:none; opacity:.9; }\n.grip:hover .dot { animation:p 1.4s ease infinite; }\n.grip.busy .dot { background:#756e68; }\n/* menu (Open board \u00B7 Stop) */\n.menu { position:absolute; right:0; bottom:44px; min-width:160px; padding:6px;\n background:#1d1a1b; border:1px solid #292526; border-radius:10px;\n box-shadow:0 8px 30px rgba(0,0,0,.35); display:flex; flex-direction:column; gap:2px; }\n.menu button { all:unset; cursor:pointer; padding:7px 10px; border-radius:6px; color:#d8d2cc;\n font:500 12px/1 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; display:flex; gap:8px; align-items:center; }\n.menu button:hover { background:#252122; color:#f0eae4; }\n.menu button.danger:hover { color:#ff3b57; }\n.menuwrap { position:relative; }\n/* pick chrome (annotate mode): dim, outline, hint \u2014 shadow-root children */\n.dim { position:fixed; inset:0; z-index:1; pointer-events:none; background:rgba(0,0,0,.22); }\n.outline { position:fixed; z-index:2; pointer-events:none; border:2px solid #ff3b57; border-radius:6px;\n box-shadow:0 0 0 4px rgba(255,59,87,.15); }\n.hint { position:fixed; top:16px; left:50%; transform:translateX(-50%); z-index:3; padding:8px 16px;\n border-radius:999px; background:#1d1a1b; border:1px solid #ff3b57; box-shadow:0 8px 30px rgba(0,0,0,.4);\n font:600 12px/1 -apple-system,BlinkMacSystemFont,sans-serif; color:#f0eae4; white-space:nowrap; }\n/* the composer sheet (360px) */\n.pop { width:360px; padding:14px; background:#1d1a1b; border:1px solid #292526; border-radius:14px;\n box-shadow:0 8px 30px rgba(0,0,0,.35); color:#f0eae4; }\n.pop-head { display:flex; align-items:center; justify-content:space-between; gap:8px; }\nlabel { display:flex; align-items:center; gap:7px; font:700 9.5px/1 ui-monospace, Menlo, monospace;\n letter-spacing:.18em; text-transform:uppercase; color:#756e68; }\nlabel i { width:3px; height:10px; background:#ff3b57; }\n.closeb { all:unset; cursor:pointer; width:24px; height:24px; border-radius:6px; color:#756e68; font-size:13px; text-align:center; line-height:24px; }\n.closeb:hover { color:#f0eae4; background:#252122; }\ntextarea { width:100%; margin-top:10px; padding:10px 12px; min-height:72px; resize:vertical;\n background:#141213; border:1px solid #363132; border-radius:8px; color:#f0eae4;\n font:400 16px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\ntextarea:focus { outline:none; border-color:#756e68; }\n.row { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:12px; margin-top:10px; }\n.row.nodest { grid-template-columns:minmax(0,1fr) auto; }\n.ctx { font:500 10px/1.35 ui-monospace, Menlo, monospace; color:#756e68; text-align:center;\n overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.row.nodest .ctx { text-align:left; }\n.dest { display:flex; align-items:center; gap:9px; }\n.dest button { all:unset; cursor:pointer; padding:2px 0; color:#756e68; font:600 9px/1 ui-monospace, Menlo, monospace;\n letter-spacing:.12em; text-transform:uppercase; border-bottom:1px solid transparent; }\n.dest button[aria-pressed=\"true\"] { color:#f0eae4; border-bottom-color:#ff3b57; }\n.sendb { all:unset; cursor:pointer; display:inline-flex; align-items:center; gap:6px; padding:7px 14px; border-radius:8px;\n background:#ff3b57; color:#fff; font-size:13px; font-weight:600; line-height:1; }\n.sendb:hover { background:#ff5670; }\n.hints { margin-top:8px; font:500 9.5px/1 ui-monospace, Menlo, monospace; letter-spacing:.06em; color:#756e68; }\n";
|
|
7
|
-
/** Inline style of the HUD host — fixed bottom-right, above everything; overrides the [popover] UA sheet. */
|
|
8
20
|
export declare const HOST_STYLE: string;
|
|
9
|
-
/** Inline style of a sheet host portaled into a dialog
|
|
21
|
+
/** Inline style of a sheet host portaled into a dialog — the same 0×0 origin box. */
|
|
10
22
|
export declare const SHEET_HOST_STYLE: string;
|
|
11
23
|
/** Link-sheet additions (appended to HUD_CSS). */
|
|
12
|
-
export declare const LINK_CSS = "\n.
|
|
24
|
+
export declare const LINK_CSS = "\n.linkgrid { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:4px 12px; align-items:center; margin-top:10px; }\n.linkgrid.noqr { grid-template-columns:minmax(0,1fr); }\n.code { font-size:22px; font-weight:700; line-height:1.2; letter-spacing:.08em; font-variant-numeric:tabular-nums; color:var(--fg); }\n.linkgrid .sendb { justify-self:start; margin-top:6px; }\n.qr { grid-column:2; grid-row:1 / span 3; width:76px; height:76px; border-radius:8px; background:#fff; padding:4px; }\n.linkline { margin-top:8px; font-size:11px; line-height:1.4; color:var(--fg-2); display:flex; gap:10px; align-items:center; }\n.linkline.bad { color:var(--warn); }\n.retry { all:unset; box-sizing:border-box; cursor:pointer; min-height:24px; padding:4px 9px; border-radius:7px; box-shadow:inset 0 0 0 1px var(--edge);\n color:var(--fg); font-size:11.5px; font-weight:600; line-height:1; }\n@media (hover: hover) { .retry:hover { background:var(--ink-2); } }\n";
|
|
25
|
+
/** Reduced motion: no ripple, no springs, no slides — every state change is instant. */
|
|
26
|
+
export declare const MOTION_CSS = "\n@media (prefers-reduced-motion: reduce) {\n .hud *, .hud *::before, .hud *::after { transition-duration:0ms !important; transition-delay:0ms !important; animation:none !important; }\n}\n";
|
|
13
27
|
export declare const HUD_CSS: string;
|