@bitvea/feedback-toolbar 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +262 -0
- package/dist/FeedbackPanel.d.ts +117 -0
- package/dist/FeedbackPanel.d.ts.map +1 -0
- package/dist/FeedbackPanel.js +622 -0
- package/dist/FeedbackPanel.js.map +1 -0
- package/dist/FeedbackToolbar.d.ts +54 -0
- package/dist/FeedbackToolbar.d.ts.map +1 -0
- package/dist/FeedbackToolbar.js +1149 -0
- package/dist/FeedbackToolbar.js.map +1 -0
- package/dist/InboxSheet.d.ts +80 -0
- package/dist/InboxSheet.d.ts.map +1 -0
- package/dist/InboxSheet.js +580 -0
- package/dist/InboxSheet.js.map +1 -0
- package/dist/PinLayer.d.ts +59 -0
- package/dist/PinLayer.d.ts.map +1 -0
- package/dist/PinLayer.js +1702 -0
- package/dist/PinLayer.js.map +1 -0
- package/dist/RichText.d.ts +6 -0
- package/dist/RichText.d.ts.map +1 -0
- package/dist/RichText.js +135 -0
- package/dist/RichText.js.map +1 -0
- package/dist/browser/entry.d.ts +3 -0
- package/dist/browser/entry.d.ts.map +1 -0
- package/dist/browser/entry.js +13 -0
- package/dist/browser/entry.js.map +1 -0
- package/dist/browser/mount.d.ts +37 -0
- package/dist/browser/mount.d.ts.map +1 -0
- package/dist/browser/mount.js +150 -0
- package/dist/browser/mount.js.map +1 -0
- package/dist/browser/toolbar.js +38 -0
- package/dist/browser/toolbar.js.map +7 -0
- package/dist/capture.d.ts +58 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +98 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +196 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/fs.d.ts +16 -0
- package/dist/cli/fs.d.ts.map +1 -0
- package/dist/cli/fs.js +33 -0
- package/dist/cli/fs.js.map +1 -0
- package/dist/cli/init.d.ts +75 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +266 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/register.d.ts +55 -0
- package/dist/cli/register.d.ts.map +1 -0
- package/dist/cli/register.js +84 -0
- package/dist/cli/register.js.map +1 -0
- package/dist/cli/registration.d.ts +37 -0
- package/dist/cli/registration.d.ts.map +1 -0
- package/dist/cli/registration.js +69 -0
- package/dist/cli/registration.js.map +1 -0
- package/dist/cli/strategies.d.ts +98 -0
- package/dist/cli/strategies.d.ts.map +1 -0
- package/dist/cli/strategies.js +198 -0
- package/dist/cli/strategies.js.map +1 -0
- package/dist/client.d.ts +59 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +113 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/deepLink.d.ts +86 -0
- package/dist/deepLink.d.ts.map +1 -0
- package/dist/deepLink.js +185 -0
- package/dist/deepLink.js.map +1 -0
- package/dist/fontFace.d.ts +70 -0
- package/dist/fontFace.d.ts.map +1 -0
- package/dist/fontFace.js +88 -0
- package/dist/fontFace.js.map +1 -0
- package/dist/gating.d.ts +32 -0
- package/dist/gating.d.ts.map +1 -0
- package/dist/gating.js +49 -0
- package/dist/gating.js.map +1 -0
- package/dist/icons.d.ts +68 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +97 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +59 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +130 -0
- package/dist/markdown.js.map +1 -0
- package/dist/motion.d.ts +178 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +302 -0
- package/dist/motion.js.map +1 -0
- package/dist/preferences.d.ts +71 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +195 -0
- package/dist/preferences.js.map +1 -0
- package/dist/rasterise.d.ts +45 -0
- package/dist/rasterise.d.ts.map +1 -0
- package/dist/rasterise.js +490 -0
- package/dist/rasterise.js.map +1 -0
- package/dist/selector.d.ts +19 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/selector.js +142 -0
- package/dist/selector.js.map +1 -0
- package/dist/session.d.ts +19 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +36 -0
- package/dist/session.js.map +1 -0
- package/dist/shortcut.d.ts +88 -0
- package/dist/shortcut.d.ts.map +1 -0
- package/dist/shortcut.js +149 -0
- package/dist/shortcut.js.map +1 -0
- package/dist/tokens.d.ts +709 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +749 -0
- package/dist/tokens.js.map +1 -0
- package/dist/vendor/types/account.d.ts +26 -0
- package/dist/vendor/types/account.d.ts.map +1 -0
- package/dist/vendor/types/account.js +23 -0
- package/dist/vendor/types/account.js.map +1 -0
- package/dist/vendor/types/apiKey.d.ts +38 -0
- package/dist/vendor/types/apiKey.d.ts.map +1 -0
- package/dist/vendor/types/apiKey.js +36 -0
- package/dist/vendor/types/apiKey.js.map +1 -0
- package/dist/vendor/types/auth.d.ts +10 -0
- package/dist/vendor/types/auth.d.ts.map +1 -0
- package/dist/vendor/types/auth.js +15 -0
- package/dist/vendor/types/auth.js.map +1 -0
- package/dist/vendor/types/entity.d.ts +74 -0
- package/dist/vendor/types/entity.d.ts.map +1 -0
- package/dist/vendor/types/entity.js +111 -0
- package/dist/vendor/types/entity.js.map +1 -0
- package/dist/vendor/types/feedback.d.ts +285 -0
- package/dist/vendor/types/feedback.d.ts.map +1 -0
- package/dist/vendor/types/feedback.js +286 -0
- package/dist/vendor/types/feedback.js.map +1 -0
- package/dist/vendor/types/index.d.ts +14 -0
- package/dist/vendor/types/index.d.ts.map +1 -0
- package/dist/vendor/types/index.js +16 -0
- package/dist/vendor/types/index.js.map +1 -0
- package/dist/vendor/types/invite.d.ts +42 -0
- package/dist/vendor/types/invite.d.ts.map +1 -0
- package/dist/vendor/types/invite.js +30 -0
- package/dist/vendor/types/invite.js.map +1 -0
- package/dist/vendor/types/mcpToken.d.ts +38 -0
- package/dist/vendor/types/mcpToken.d.ts.map +1 -0
- package/dist/vendor/types/mcpToken.js +39 -0
- package/dist/vendor/types/mcpToken.js.map +1 -0
- package/dist/vendor/types/organization.d.ts +13 -0
- package/dist/vendor/types/organization.d.ts.map +1 -0
- package/dist/vendor/types/organization.js +12 -0
- package/dist/vendor/types/organization.js.map +1 -0
- package/dist/vendor/types/project.d.ts +73 -0
- package/dist/vendor/types/project.d.ts.map +1 -0
- package/dist/vendor/types/project.js +117 -0
- package/dist/vendor/types/project.js.map +1 -0
- package/dist/vendor/types/role.d.ts +12 -0
- package/dist/vendor/types/role.d.ts.map +1 -0
- package/dist/vendor/types/role.js +20 -0
- package/dist/vendor/types/role.js.map +1 -0
- package/dist/vendor/types/roleDefinition.d.ts +61 -0
- package/dist/vendor/types/roleDefinition.d.ts.map +1 -0
- package/dist/vendor/types/roleDefinition.js +45 -0
- package/dist/vendor/types/roleDefinition.js.map +1 -0
- package/dist/vendor/types/team.d.ts +25 -0
- package/dist/vendor/types/team.d.ts.map +1 -0
- package/dist/vendor/types/team.js +17 -0
- package/dist/vendor/types/team.js.map +1 -0
- package/dist/vendor/types/util.d.ts +39 -0
- package/dist/vendor/types/util.d.ts.map +1 -0
- package/dist/vendor/types/util.js +31 -0
- package/dist/vendor/types/util.js.map +1 -0
- package/package.json +59 -0
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import type { FeedbackStatus } from "./vendor/types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* The BitVea brand orange, as the logo draws it.
|
|
4
|
+
*
|
|
5
|
+
* It used to be the toolbar's whole action system: every primary button, the
|
|
6
|
+
* active bar tool, the count bubble, the avatars, the status chip and the
|
|
7
|
+
* checkbox were orange. That put the accent on metadata and on chrome, which
|
|
8
|
+
* is the one thing DESIGN.md's colour section forbids - ink carries the CTA,
|
|
9
|
+
* and colour is reserved. So the accent is now IDENTITY ONLY: it survives on
|
|
10
|
+
* the pin teardrop, which has to be findable over arbitrary host content and
|
|
11
|
+
* is the one object in the product that means "BitVea Feedback is here".
|
|
12
|
+
*/
|
|
13
|
+
export declare const accent: {
|
|
14
|
+
readonly start: "#DF6520";
|
|
15
|
+
readonly end: "#F97316";
|
|
16
|
+
/** 135deg, the direction the mark's own gradient runs. */
|
|
17
|
+
readonly gradient: "linear-gradient(135deg, #DF6520 0%, #F97316 100%)";
|
|
18
|
+
readonly onAccent: "#ffffff";
|
|
19
|
+
};
|
|
20
|
+
/** A status chip's ground and the ink on it, one pair per lane. */
|
|
21
|
+
export interface ToolbarStatusStyle {
|
|
22
|
+
background: string;
|
|
23
|
+
color: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The status vocabulary, mirrored from the dashboard's semantic pairs so one
|
|
27
|
+
* product fact reads the same on both surfaces. Built from DESIGN.md's
|
|
28
|
+
* soft-ground / deep-ink pairs rather than from bare dot colours, because a
|
|
29
|
+
* bare colour has to be re-chosen for the dark polarity flip and a pair does
|
|
30
|
+
* not: the ground moves with the theme and carries its ink with it.
|
|
31
|
+
*/
|
|
32
|
+
export type ToolbarStatusPalette = Record<FeedbackStatus, ToolbarStatusStyle>;
|
|
33
|
+
/**
|
|
34
|
+
* Every colour a surface here needs, named by role rather than by value so
|
|
35
|
+
* the two palettes can be swapped wholesale. `bar*` are the floating pill's
|
|
36
|
+
* own colours: it is a single opaque chip over arbitrary content, so it does
|
|
37
|
+
* not share the popovers' canvas.
|
|
38
|
+
*/
|
|
39
|
+
export interface ToolbarPalette {
|
|
40
|
+
ink: string;
|
|
41
|
+
/**
|
|
42
|
+
* What goes ON an ink fill, which is the polarity flip and not a constant:
|
|
43
|
+
* `ink` is #171717 light and #ffffff dark, so a primary button that hard-
|
|
44
|
+
* coded white text would be white-on-white for every dark-mode reviewer.
|
|
45
|
+
*/
|
|
46
|
+
onInk: string;
|
|
47
|
+
body: string;
|
|
48
|
+
mute: string;
|
|
49
|
+
/**
|
|
50
|
+
* Accent-coloured TEXT, which is not the accent itself.
|
|
51
|
+
*
|
|
52
|
+
* `accent.start` on a 14%-orange wash measures about 3.0:1, and accent text
|
|
53
|
+
* is never larger than 13px here - under the 18px where 3:1 would be
|
|
54
|
+
* allowed. So the ink on the wash is a separate value per palette: a
|
|
55
|
+
* darkened orange in light, a lightened one in dark, both above 4.5:1.
|
|
56
|
+
*
|
|
57
|
+
* Nothing consumes it since the accent was demoted to identity only. It
|
|
58
|
+
* stays because the pin teardrop is still orange and the first person to
|
|
59
|
+
* print a word on it will need this rather than `accent.start`.
|
|
60
|
+
*/
|
|
61
|
+
accentInk: string;
|
|
62
|
+
/**
|
|
63
|
+
* DESIGN.md's link blue, and it is a PAIR rather than a constant.
|
|
64
|
+
*
|
|
65
|
+
* `#0070f3` measures 4.6:1 on the light card and about 3.2:1 on the ink one,
|
|
66
|
+
* which is under AA for the 14px body copy a link sits in. The dark half is
|
|
67
|
+
* the lighter blue the status palette already uses for the same reason, so
|
|
68
|
+
* the two agree rather than each picking a blue.
|
|
69
|
+
*/
|
|
70
|
+
link: string;
|
|
71
|
+
hairline: string;
|
|
72
|
+
hairlineStrong: string;
|
|
73
|
+
canvas: string;
|
|
74
|
+
canvasSoft: string;
|
|
75
|
+
canvasSoft2: string;
|
|
76
|
+
error: string;
|
|
77
|
+
success: string;
|
|
78
|
+
status: ToolbarStatusPalette;
|
|
79
|
+
/** The floating bar. */
|
|
80
|
+
barSurface: string;
|
|
81
|
+
barBorder: string;
|
|
82
|
+
barInk: string;
|
|
83
|
+
barInkMute: string;
|
|
84
|
+
barHover: string;
|
|
85
|
+
/** The wash the picker lays over the page while an element is being chosen. */
|
|
86
|
+
pickTint: string;
|
|
87
|
+
/**
|
|
88
|
+
* An ink fill at the weight a wash wants: the tint inside the picking
|
|
89
|
+
* outline, where the accent used to be. Stated per palette because "ink at
|
|
90
|
+
* 8%" is #171717 in light and #ffffff in dark, and one literal cannot be
|
|
91
|
+
* both.
|
|
92
|
+
*/
|
|
93
|
+
inkSoft: string;
|
|
94
|
+
/** Ring drawn around a pin marker so it separates from the content beneath. */
|
|
95
|
+
markerRing: string;
|
|
96
|
+
}
|
|
97
|
+
export declare const lightPalette: ToolbarPalette;
|
|
98
|
+
/**
|
|
99
|
+
* The dark palette, taken verbatim from `packages/ui/src/styles/globals.css`'s
|
|
100
|
+
* `.dark` block.
|
|
101
|
+
*
|
|
102
|
+
* It used to run the OPPOSITE direction from the dashboard's: canvas #0f0f0f
|
|
103
|
+
* was DARKER than canvasSoft #161616, while globals.css raises canvas #1f1f1f
|
|
104
|
+
* above canvas-soft #171717. Two dark theories in one product means every rule
|
|
105
|
+
* of the form "the row is canvas on a canvasSoft sheet" inverts when a
|
|
106
|
+
* reviewer crosses from the toolbar to the dashboard looking at the same
|
|
107
|
+
* feedback. One ladder, defined once, mirrored here.
|
|
108
|
+
*/
|
|
109
|
+
export declare const darkPalette: ToolbarPalette;
|
|
110
|
+
export declare const radii: {
|
|
111
|
+
/** 4px. Concentric inner corner inside an 8px track, and the pin's point. */
|
|
112
|
+
readonly xs: "4px";
|
|
113
|
+
readonly sm: "6px";
|
|
114
|
+
readonly md: "8px";
|
|
115
|
+
readonly lg: "12px";
|
|
116
|
+
readonly pill: "100px";
|
|
117
|
+
};
|
|
118
|
+
export declare const spacing: {
|
|
119
|
+
readonly xxs: "4px";
|
|
120
|
+
readonly xs: "8px";
|
|
121
|
+
readonly sm: "12px";
|
|
122
|
+
readonly md: "16px";
|
|
123
|
+
readonly lg: "24px";
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The margin an anchored surface keeps against the viewport's edge, as a
|
|
127
|
+
* NUMBER - the shape `DRAG_THRESHOLD` below already takes, and for the same
|
|
128
|
+
* reason: every use of it is arithmetic.
|
|
129
|
+
*
|
|
130
|
+
* Here rather than beside the clamps in PinLayer.tsx, which is where it began.
|
|
131
|
+
* The gutter appears on both axes and in both spellings - a `calc()` in a
|
|
132
|
+
* `maxWidth` and a `maxHeight`, and a `Math.min`/`Math.max` pair per axis in
|
|
133
|
+
* the code that decides where a card lands - and a token file cannot import a
|
|
134
|
+
* component's constant, so a copy in PinLayer meant tokens.ts held its own. It
|
|
135
|
+
* had five spellings between them (`16`, and four `32`s inside `calc()`
|
|
136
|
+
* strings) under a comment claiming they agreed "by construction". They do
|
|
137
|
+
* now.
|
|
138
|
+
*
|
|
139
|
+
* A card whose `maxWidth` disagreed with the clamp that positions it is
|
|
140
|
+
* pushed off the very edge the clamp believes it is holding it inside, which
|
|
141
|
+
* is not a hypothetical: it is `sizes.thread`'s own story one field down.
|
|
142
|
+
*/
|
|
143
|
+
export declare const VIEWPORT_GUTTER = 16;
|
|
144
|
+
/**
|
|
145
|
+
* Box sizes that are not spacing and not radii: the handful of fixed squares
|
|
146
|
+
* and bubbles this package draws. They live here because every one of them
|
|
147
|
+
* used to be an off-grid literal spelled at its call site (13px, 14px, 9px,
|
|
148
|
+
* 3px), and a literal at a call site is how the next one gets invented.
|
|
149
|
+
*/
|
|
150
|
+
export declare const sizes: {
|
|
151
|
+
/** 16px form control, so the checkbox matches the 16px icons beside it. */
|
|
152
|
+
readonly control: "16px";
|
|
153
|
+
/** 28px: the sheet's close button and the pin marker's teardrop. */
|
|
154
|
+
readonly icon: "28px";
|
|
155
|
+
/** 18px count bubble - the smallest square that holds 12px mono legibly. */
|
|
156
|
+
readonly badge: "18px";
|
|
157
|
+
/** 8px unread dot. */
|
|
158
|
+
readonly dot: "8px";
|
|
159
|
+
/**
|
|
160
|
+
* 24px: the square a per-message action in a thread is drawn in (issue #20),
|
|
161
|
+
* and the thread's own close control, which used to be an invented 20px.
|
|
162
|
+
* A 16px glyph on DESIGN.md's `spacing.xxs` all round, so a row of them
|
|
163
|
+
* matches the 24px `Avatar` opposite it.
|
|
164
|
+
*/
|
|
165
|
+
readonly action: "24px";
|
|
166
|
+
/**
|
|
167
|
+
* The thread card's width, and the pin composer's - issue #20 says in as
|
|
168
|
+
* many words that "both the pin popover and the composer-only variant share
|
|
169
|
+
* the 280px constant, widen both consistently", so they share this one.
|
|
170
|
+
*
|
|
171
|
+
* 440px is the Vercel comment thread's content width the issue names. It was
|
|
172
|
+
* two `"280px"` string literals at two call sites, which is exactly the
|
|
173
|
+
* shape the header comment above warns about: a THIRD place - the composer's
|
|
174
|
+
* own right-edge clamp - carried `280 + padding + border` as a hard-coded
|
|
175
|
+
* `300`, so widening one literal would have left the clamp holding the
|
|
176
|
+
* composer 140px inside an edge it thought it was hugging.
|
|
177
|
+
*
|
|
178
|
+
* A `px` string rather than a number because everything else in this object
|
|
179
|
+
* is one; the two call sites that need arithmetic parse it, which is one
|
|
180
|
+
* `parseFloat` against three places to forget.
|
|
181
|
+
*/
|
|
182
|
+
readonly thread: "440px";
|
|
183
|
+
/**
|
|
184
|
+
* The ceiling a thread's message list scrolls inside, rather than growing
|
|
185
|
+
* past the viewport - issue #20's second acceptance line.
|
|
186
|
+
*
|
|
187
|
+
* Two caps, and both are needed. The `calc()` is `VIEWPORT_GUTTER` per
|
|
188
|
+
* side - the same margin the card's own `maxWidth` and the clamp that
|
|
189
|
+
* places it are built from - and is what stops a long thread running off
|
|
190
|
+
* the screen. The 480px is what stops a merely
|
|
191
|
+
* longish one filling a tall monitor: this card floats over the element it
|
|
192
|
+
* is talking about, and a review tool that covers the thing under review is
|
|
193
|
+
* the complaint the pin layer's own dismissal comment already answers once.
|
|
194
|
+
*/
|
|
195
|
+
readonly threadMaxHeight: "min(480px, calc(100vh - 32px))";
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Motion, from DESIGN.md's `## Motion` section - which this package's need is
|
|
199
|
+
* what added. See the provenance note at the head of this file: the toolbar
|
|
200
|
+
* renders through inline style inside somebody else's page, so there is no
|
|
201
|
+
* cascade to read a `--duration-fast` out of and the values are reproduced
|
|
202
|
+
* here, exactly as the colours and the type scale are.
|
|
203
|
+
*
|
|
204
|
+
* `enterScale` and `enterShift` are tokens rather than literals on purpose.
|
|
205
|
+
* The acceptance criterion for issue #24 is "no inline durations", and a bare
|
|
206
|
+
* `0.96` spelled at four call sites is the same defect one level down - the
|
|
207
|
+
* fifth surface is where somebody writes 0.95 and the product has two enters.
|
|
208
|
+
*
|
|
209
|
+
* Nothing here is a keyframe or a loop. Every value is one leg of a state
|
|
210
|
+
* transition a reviewer caused, which is the whole of the posture DESIGN.md
|
|
211
|
+
* states: small, fast, over before it is noticed.
|
|
212
|
+
*/
|
|
213
|
+
export declare const motion: {
|
|
214
|
+
readonly duration: {
|
|
215
|
+
/** 80ms - hover, press, a badge whose number changed. */
|
|
216
|
+
readonly instant: "80ms";
|
|
217
|
+
/** 120ms - the default, and every surface enter. */
|
|
218
|
+
readonly fast: "120ms";
|
|
219
|
+
/**
|
|
220
|
+
* 180ms - the ceiling, and the bar's collapse (issue #22) is its one
|
|
221
|
+
* consumer: it is the only transition here whose box changes shape
|
|
222
|
+
* underneath it, so `fast` lands before the eye has followed the pill's
|
|
223
|
+
* snap. Anything else reaching for this step needs the written reason
|
|
224
|
+
* DESIGN.md's duration table asks for.
|
|
225
|
+
*/
|
|
226
|
+
readonly settle: "180ms";
|
|
227
|
+
};
|
|
228
|
+
readonly easing: {
|
|
229
|
+
/** Ease-out, for anything arriving. */
|
|
230
|
+
readonly enter: "cubic-bezier(0, 0, 0.2, 1)";
|
|
231
|
+
/** Ease-in, for anything leaving. */
|
|
232
|
+
readonly exit: "cubic-bezier(0.4, 0, 1, 1)";
|
|
233
|
+
/** Symmetric, for a property changing in place on a stationary element. */
|
|
234
|
+
readonly inOut: "cubic-bezier(0.4, 0, 0.2, 1)";
|
|
235
|
+
};
|
|
236
|
+
/** The one scale step an entering surface takes. */
|
|
237
|
+
readonly enterScale: 0.96;
|
|
238
|
+
/**
|
|
239
|
+
* The one squash a control takes under the pointer. Shallower than
|
|
240
|
+
* `enterScale` because a button that moves as far as a popover reads as
|
|
241
|
+
* broken rather than as pressed.
|
|
242
|
+
*/
|
|
243
|
+
readonly pressScale: 0.97;
|
|
244
|
+
/** The one travel distance, for a surface that slides rather than scales. */
|
|
245
|
+
readonly enterShift: "4px";
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* One of the three steps in the table above, and nothing else.
|
|
249
|
+
*
|
|
250
|
+
* Exists so "no inline durations" - issue #24's acceptance criterion - can be
|
|
251
|
+
* a TYPE rather than a comment wherever a duration is passed as an argument.
|
|
252
|
+
* `surfaceMotion`'s `duration` option was declared `string` while its own doc
|
|
253
|
+
* comment claimed the only reachable values were `motion.duration` members;
|
|
254
|
+
* `duration: "140ms"` compiled fine, which made the claim false and the rule
|
|
255
|
+
* enforced by nothing. A call site that wants a fourth step now has to add it
|
|
256
|
+
* here first, where a reviewer sees it - which is what the comment said all
|
|
257
|
+
* along.
|
|
258
|
+
*/
|
|
259
|
+
export type MotionDuration = (typeof motion.duration)[keyof typeof motion.duration];
|
|
260
|
+
/**
|
|
261
|
+
* "An exit is one step faster than its matching enter", as a function.
|
|
262
|
+
*
|
|
263
|
+
* DESIGN.md's duration table states that rule and issue #24's helper claimed
|
|
264
|
+
* to enforce it centrally - while hard-coding `instant` for every exit. That
|
|
265
|
+
* was one step faster for the two surfaces on `fast` and TWO steps faster for
|
|
266
|
+
* anything on `settle`, which is exactly the step issue #22's collapse then
|
|
267
|
+
* took. A rule enforced only for the durations that existed when it was
|
|
268
|
+
* written is not enforced.
|
|
269
|
+
*
|
|
270
|
+
* Both halves of an exit read this: the `transition` a surface leaves on, and
|
|
271
|
+
* the timer that then takes it out of the DOM. DESIGN.md says the removal is
|
|
272
|
+
* deferred "by exactly the exit duration", and two literals that happen to
|
|
273
|
+
* agree today is how that stops being true.
|
|
274
|
+
*
|
|
275
|
+
* `instant` has nowhere faster to go and answers itself, which is the right
|
|
276
|
+
* degradation - a surface entering at 80ms is already at the floor.
|
|
277
|
+
*/
|
|
278
|
+
export declare function exitFor(enter: MotionDuration): MotionDuration;
|
|
279
|
+
/**
|
|
280
|
+
* A `transition` shorthand, composed rather than spelled.
|
|
281
|
+
*
|
|
282
|
+
* This is what makes "no inline durations" mechanically true instead of a
|
|
283
|
+
* promise: the only way to write a transition in this package is to name a
|
|
284
|
+
* duration token and an easing token, and a call site that wanted `140ms`
|
|
285
|
+
* would have to add it to `motion.duration` first, where a reviewer sees it.
|
|
286
|
+
*
|
|
287
|
+
* Deliberately not `transition: all`. `all` transitions properties nobody
|
|
288
|
+
* chose, `width` and `top` among them, and DESIGN.md forbids animating those
|
|
289
|
+
* over a host page for the reflow they cause. Every property is named.
|
|
290
|
+
*
|
|
291
|
+
* `reduced` IS REQUIRED, and that is the whole design of this signature.
|
|
292
|
+
* DESIGN.md's Reduced Motion rule is a system-layer promise, and the first
|
|
293
|
+
* draft of issue #24 kept it by writing `reduced ? [a, b] : [a, b, "transform"]`
|
|
294
|
+
* at seven call sites - seven independent restatements of one rule, none of
|
|
295
|
+
* them checked, and an eighth control added later would simply have omitted it
|
|
296
|
+
* and honoured nothing. Making the preference an argument nobody can leave out
|
|
297
|
+
* moves the rule to the token layer where the rest of this file lives: a call
|
|
298
|
+
* site now states the preference it read and this function decides what that
|
|
299
|
+
* means. Pass `false` only where there is genuinely no reviewer to ask.
|
|
300
|
+
*/
|
|
301
|
+
export declare function transition(properties: readonly string[], duration: string, easing: string, reduced: boolean): string;
|
|
302
|
+
/**
|
|
303
|
+
* The transform half of the same rule, so it cannot be honoured by one and
|
|
304
|
+
* forgotten by the other.
|
|
305
|
+
*
|
|
306
|
+
* A reduced-motion surface is already at its final position and size on its
|
|
307
|
+
* first frame - so there is no offset to animate out of, which is why this
|
|
308
|
+
* answers `none` rather than shortening anything. `transition` above drops
|
|
309
|
+
* `transform` from the property list in the same breath; a call site that used
|
|
310
|
+
* one of these and not the other would either animate a transform it promised
|
|
311
|
+
* not to, or leave a surface parked at an offset with nothing to move it.
|
|
312
|
+
*/
|
|
313
|
+
export declare function motionTransform(value: string, reduced: boolean): string;
|
|
314
|
+
/**
|
|
315
|
+
* How long an exit lasts, in milliseconds, for the deferred-unmount timer.
|
|
316
|
+
*
|
|
317
|
+
* A number as well as a string because a `setTimeout` cannot take "80ms" and
|
|
318
|
+
* hard-coding 80 beside the token is how the two drift apart. Parsed from the
|
|
319
|
+
* token rather than declared twice.
|
|
320
|
+
*/
|
|
321
|
+
export declare function durationMs(duration: string): number;
|
|
322
|
+
export declare const typeScale: {
|
|
323
|
+
/** body-sm. The default voice of every popover. */
|
|
324
|
+
readonly bodySm: {
|
|
325
|
+
readonly fontFamily: "\"BitVea Geist\", Geist, Inter, system-ui, -apple-system, sans-serif";
|
|
326
|
+
readonly fontSize: "14px";
|
|
327
|
+
readonly fontWeight: 400;
|
|
328
|
+
readonly lineHeight: "20px";
|
|
329
|
+
readonly letterSpacing: "-0.28px";
|
|
330
|
+
};
|
|
331
|
+
/** body-sm-strong. Names, button labels, the one emphasis this UI has. */
|
|
332
|
+
readonly bodySmStrong: {
|
|
333
|
+
readonly fontFamily: "\"BitVea Geist\", Geist, Inter, system-ui, -apple-system, sans-serif";
|
|
334
|
+
readonly fontSize: "14px";
|
|
335
|
+
readonly fontWeight: 500;
|
|
336
|
+
readonly lineHeight: "20px";
|
|
337
|
+
readonly letterSpacing: "-0.28px";
|
|
338
|
+
};
|
|
339
|
+
/** display-sm, 600 - the weight ceiling, and only at 20px. */
|
|
340
|
+
readonly displaySm: {
|
|
341
|
+
readonly fontFamily: "\"BitVea Geist\", Geist, Inter, system-ui, -apple-system, sans-serif";
|
|
342
|
+
readonly fontSize: "20px";
|
|
343
|
+
readonly fontWeight: 600;
|
|
344
|
+
readonly lineHeight: "28px";
|
|
345
|
+
readonly letterSpacing: "-0.6px";
|
|
346
|
+
};
|
|
347
|
+
/** caption. Footnotes and secondary lines that are not technical facts. */
|
|
348
|
+
readonly caption: {
|
|
349
|
+
readonly fontFamily: "\"BitVea Geist\", Geist, Inter, system-ui, -apple-system, sans-serif";
|
|
350
|
+
readonly fontSize: "12px";
|
|
351
|
+
readonly fontWeight: 400;
|
|
352
|
+
readonly lineHeight: "16px";
|
|
353
|
+
readonly letterSpacing: "0px";
|
|
354
|
+
};
|
|
355
|
+
/** caption-mono. Every identifier: branch, commit, path, timestamp, status. */
|
|
356
|
+
readonly captionMono: {
|
|
357
|
+
readonly fontFamily: "\"BitVea Geist Mono\", \"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace";
|
|
358
|
+
readonly fontSize: "12px";
|
|
359
|
+
readonly fontWeight: 400;
|
|
360
|
+
readonly lineHeight: "16px";
|
|
361
|
+
readonly letterSpacing: "0px";
|
|
362
|
+
};
|
|
363
|
+
/** code, 13px/20px. The captured selector, and nothing else so far. */
|
|
364
|
+
readonly code: {
|
|
365
|
+
readonly fontFamily: "\"BitVea Geist Mono\", \"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace";
|
|
366
|
+
readonly fontSize: "13px";
|
|
367
|
+
readonly fontWeight: 400;
|
|
368
|
+
readonly lineHeight: "20px";
|
|
369
|
+
readonly letterSpacing: "0px";
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* The two faces, for the places that need a family without a whole role.
|
|
374
|
+
*
|
|
375
|
+
* NOT `Geist`. No `@font-face` in this repo registers that family: `next/font`
|
|
376
|
+
* exposes a hashed name through `--font-geist` and nothing else, so naming
|
|
377
|
+
* "Geist" here has never rendered the brand face on any surface, the preview
|
|
378
|
+
* harness included - it silently fell through to Inter or to system-ui. These
|
|
379
|
+
* are DESIGN.md's own sanctioned substitutes ("Note on Font Substitutes":
|
|
380
|
+
* Inter 400/500/600 for the geometric sans, a monospace of the same voice for
|
|
381
|
+
* the technical layer). Self-hosting the real faces inside this bundle is
|
|
382
|
+
* blocked on a licence decision and on CLAUDE.md section 8's size ceiling.
|
|
383
|
+
*
|
|
384
|
+
* And NOT `var(--font-geist)` at the head of the stack either. A `var()`
|
|
385
|
+
* reference to an undefined custom property makes the whole declaration
|
|
386
|
+
* invalid at computed-value time; `font-family` is inherited, so the toolbar
|
|
387
|
+
* would then inherit the HOST page's typeface on every site that does not
|
|
388
|
+
* define that variable - while looking correct inside apps/web, which does.
|
|
389
|
+
*/
|
|
390
|
+
export declare const fonts: {
|
|
391
|
+
readonly sans: "\"BitVea Geist\", Geist, Inter, system-ui, -apple-system, sans-serif";
|
|
392
|
+
readonly mono: "\"BitVea Geist Mono\", \"Geist Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace";
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* The properties a host page's own element rules can set on markup this
|
|
396
|
+
* package renders unstyled, spread FIRST into every `form`, `label`, `header`,
|
|
397
|
+
* `section` and `p` here.
|
|
398
|
+
*
|
|
399
|
+
* The package ships no stylesheet, so nothing leaks OUT. Nothing stopped a
|
|
400
|
+
* host's `form { margin: 0 0 1rem; padding: 1rem; border: 1px solid #ccc }`
|
|
401
|
+
* from leaking IN and repainting the composer, which is the same class of bug
|
|
402
|
+
* as the `box-sizing` note in FeedbackToolbar.tsx and was invisible on the
|
|
403
|
+
* preview route only because apps/web loads Tailwind's preflight.
|
|
404
|
+
*/
|
|
405
|
+
export declare const resetBox: {
|
|
406
|
+
readonly margin: 0;
|
|
407
|
+
readonly padding: 0;
|
|
408
|
+
readonly border: 0;
|
|
409
|
+
readonly background: "transparent";
|
|
410
|
+
readonly boxSizing: "border-box";
|
|
411
|
+
};
|
|
412
|
+
/**
|
|
413
|
+
* Visually hidden, still in the accessibility tree AND still in `textContent`.
|
|
414
|
+
*
|
|
415
|
+
* An icon-only control needs a name, and `aria-label` alone would give it one
|
|
416
|
+
* only for a screen reader: `toHaveText` and `Node.textContent` - which is how
|
|
417
|
+
* both the e2e suite and the unit tests read the resolve control's label - see
|
|
418
|
+
* nothing an `aria-label` says. So the label is real text, clipped.
|
|
419
|
+
*
|
|
420
|
+
* Clipped rather than `display: none` or `visibility: hidden`, because both of
|
|
421
|
+
* those take the text OUT of the accessibility tree and out of `textContent`,
|
|
422
|
+
* which is the whole thing being preserved. The `clip` / `clipPath` pair is
|
|
423
|
+
* belt and braces: `clip` is deprecated and `clip-path` is what actually
|
|
424
|
+
* applies in a modern engine, and this package cannot rely on a host stylesheet
|
|
425
|
+
* to have shipped either.
|
|
426
|
+
*/
|
|
427
|
+
export declare const srOnly: {
|
|
428
|
+
readonly position: "absolute";
|
|
429
|
+
readonly width: "1px";
|
|
430
|
+
readonly height: "1px";
|
|
431
|
+
readonly margin: "-1px";
|
|
432
|
+
readonly padding: 0;
|
|
433
|
+
readonly border: 0;
|
|
434
|
+
readonly overflow: "hidden";
|
|
435
|
+
readonly clip: "rect(0 0 0 0)";
|
|
436
|
+
readonly clipPath: "inset(50%)";
|
|
437
|
+
readonly whiteSpace: "nowrap";
|
|
438
|
+
};
|
|
439
|
+
/** The bar's fixed geometry, from the reference's computed CSS. */
|
|
440
|
+
export declare const bar: {
|
|
441
|
+
readonly radius: "8px";
|
|
442
|
+
readonly padding: "4px";
|
|
443
|
+
readonly gap: "8px";
|
|
444
|
+
/** 36x36 icon buttons, so a 4px-padded bar lands at the reference height. */
|
|
445
|
+
readonly button: "36px";
|
|
446
|
+
/**
|
|
447
|
+
* The bar's height, in BOTH forms: a 36px button, 4px of padding either
|
|
448
|
+
* side and the 1px hairline, which is also the collapsed circle's diameter.
|
|
449
|
+
*
|
|
450
|
+
* Written down rather than left implicit because the layout now has to do
|
|
451
|
+
* arithmetic with it: a mid-edge dock centres the BAR on the viewport, and
|
|
452
|
+
* doing that without moving it when a popover opens means offsetting the
|
|
453
|
+
* dock's own padding by half this. See `dockLayout`.
|
|
454
|
+
*/
|
|
455
|
+
readonly height: "46px";
|
|
456
|
+
/**
|
|
457
|
+
* Issue #21's "standard viewport inset", on all four sides.
|
|
458
|
+
*
|
|
459
|
+
* It was `bottom`, because the bottom edge was the only dock there was. The
|
|
460
|
+
* dock is now a box inset from every edge by this one value, which is what
|
|
461
|
+
* makes "the bar always ends flush to an edge with the standard viewport
|
|
462
|
+
* inset" one number rather than four pieces of per-edge arithmetic.
|
|
463
|
+
*/
|
|
464
|
+
readonly inset: "8px";
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* How far a pointer has to travel before a press on the bar is a DRAG rather
|
|
468
|
+
* than a click. Issue #21.
|
|
469
|
+
*
|
|
470
|
+
* A hit-test distance, deliberately not `motion.enterShift`: that is how far
|
|
471
|
+
* an entering surface travels, and the two agreeing on 4px today is not a
|
|
472
|
+
* reason for one to be defined in terms of the other. 6px swallows the wobble
|
|
473
|
+
* a trackpad click or a thumb tap produces, and is well inside the collapsed
|
|
474
|
+
* dot's own 46px box - so a reviewer who means to drag the dot has crossed it
|
|
475
|
+
* long before the pointer leaves the control.
|
|
476
|
+
*/
|
|
477
|
+
export declare const DRAG_THRESHOLD = 6;
|
|
478
|
+
/**
|
|
479
|
+
* Where the bar sits. Issue #21.
|
|
480
|
+
*
|
|
481
|
+
* Eight discrete positions rather than a stored pixel coordinate, and that is
|
|
482
|
+
* the whole design: "snap targets are discrete, not free-floating - the bar
|
|
483
|
+
* always ends flush to an edge with the standard viewport inset". Two of the
|
|
484
|
+
* issue's acceptance criteria - "dragging never leaves the bar off-screen" and
|
|
485
|
+
* "a resize that would strand the bar re-docks it" - are then satisfied
|
|
486
|
+
* STRUCTURALLY. There is no coordinate to fall outside a viewport, so there is
|
|
487
|
+
* no resize listener and nothing to re-clamp; every value here is flush to an
|
|
488
|
+
* edge at every viewport size.
|
|
489
|
+
*
|
|
490
|
+
* Spelled `<vertical>-<horizontal>`, which is the vocabulary the tests written
|
|
491
|
+
* against issue #22 already pinned (`bottom-right`, `top-left`). The nine
|
|
492
|
+
* combinations less `middle-center`, which is not an edge - the issue calls the
|
|
493
|
+
* two survivors "the two vertical mid-edges".
|
|
494
|
+
*/
|
|
495
|
+
export type ToolbarDock = "top-left" | "top-center" | "top-right" | "middle-left" | "middle-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
496
|
+
/** Which horizontal band of the viewport a dock is in. */
|
|
497
|
+
export type DockVertical = "top" | "middle" | "bottom";
|
|
498
|
+
/** Which vertical band. */
|
|
499
|
+
export type DockHorizontal = "left" | "center" | "right";
|
|
500
|
+
/**
|
|
501
|
+
* The dock a reviewer who has never dragged anything gets, and the one issue
|
|
502
|
+
* #21 calls the default.
|
|
503
|
+
*/
|
|
504
|
+
export declare const DEFAULT_DOCK: ToolbarDock;
|
|
505
|
+
/**
|
|
506
|
+
* Every dock, in the reading order the keyboard control lays them out: three
|
|
507
|
+
* across the top, the two mid-edges, three across the bottom. Exported so the
|
|
508
|
+
* 3x3 grid in the account surface and the tests do not each spell the list.
|
|
509
|
+
*/
|
|
510
|
+
export declare const TOOLBAR_DOCKS: readonly ToolbarDock[];
|
|
511
|
+
/**
|
|
512
|
+
* Beside `isThemeMode`, and for the same reason: this value comes back out of
|
|
513
|
+
* the HOST's `localStorage`, so it can be anything their code, an extension or
|
|
514
|
+
* a newer build of this toolbar wrote there. `hasOwnProperty` rather than
|
|
515
|
+
* `in`, which would accept `"toString"`.
|
|
516
|
+
*/
|
|
517
|
+
export declare function isToolbarDock(value: unknown): value is ToolbarDock;
|
|
518
|
+
/** The one place a per-edge answer is decided. See `dockLayout`. */
|
|
519
|
+
export interface DockLayout {
|
|
520
|
+
vertical: DockVertical;
|
|
521
|
+
horizontal: DockHorizontal;
|
|
522
|
+
/**
|
|
523
|
+
* The dock is one flex column, and the DIRECTION is what flips for a top
|
|
524
|
+
* edge. The children are laid out switcher, popover, pill; reversed, the
|
|
525
|
+
* pill is visually first and the popover falls beneath it - which is the
|
|
526
|
+
* whole of "a top-docked bar opens its popover downwards".
|
|
527
|
+
*/
|
|
528
|
+
flexDirection: "column" | "column-reverse";
|
|
529
|
+
/**
|
|
530
|
+
* How far the dock's own bottom padding holds the stack off the viewport's
|
|
531
|
+
* bottom edge - `bar.inset` at seven of the eight docks, and at the two
|
|
532
|
+
* mid-edges the offset that centres the BAR rather than the stack.
|
|
533
|
+
*
|
|
534
|
+
* This is the whole of how a mid-edge dock avoids moving the bar when a
|
|
535
|
+
* popover opens, and it was a real defect rather than a hypothetical: the
|
|
536
|
+
* dock packed the stack with `justifyContent: center`, so opening the
|
|
537
|
+
* composer at `middle-right` slid the bar 130px down the viewport and out
|
|
538
|
+
* from under the pointer that had just clicked it. The column now always
|
|
539
|
+
* packs against the bar's own edge - the popover grows away from the pill
|
|
540
|
+
* and the pill does not move - and "middle" is expressed as a padding
|
|
541
|
+
* instead: half the viewport, less half the bar, puts the pill's CENTRE on
|
|
542
|
+
* the centre line whatever is stacked above it. `vh` because the dock is a
|
|
543
|
+
* `position: fixed` box inset to all four edges, so its box is the
|
|
544
|
+
* viewport's.
|
|
545
|
+
*/
|
|
546
|
+
paddingBottom: string;
|
|
547
|
+
/** Which side of the viewport the stack hugs. */
|
|
548
|
+
alignItems: "flex-start" | "center" | "flex-end";
|
|
549
|
+
/**
|
|
550
|
+
* `transform-origin` for the popover: DESIGN.md's rule that "a surface
|
|
551
|
+
* anchored to a control grows from the edge it is attached to", which is the
|
|
552
|
+
* edge FACING the bar and therefore flips with the dock.
|
|
553
|
+
*/
|
|
554
|
+
popoverOrigin: string;
|
|
555
|
+
/** The longhand carrying the popover's gap to the pill, on that same edge. */
|
|
556
|
+
popoverGap: "marginTop" | "marginBottom";
|
|
557
|
+
/**
|
|
558
|
+
* Where the collapsed dot's unread indicator sits: the corner facing AWAY
|
|
559
|
+
* from the docked edge, so a circle - which has no orientation of its own -
|
|
560
|
+
* still answers issue #22's "the collapsed form follows the dock edge".
|
|
561
|
+
* Nothing is ever clipped; the dock keeps `bar.inset` on every side.
|
|
562
|
+
*/
|
|
563
|
+
badge: {
|
|
564
|
+
top?: 0;
|
|
565
|
+
bottom?: 0;
|
|
566
|
+
left?: 0;
|
|
567
|
+
right?: 0;
|
|
568
|
+
};
|
|
569
|
+
/** Degrees to turn the collapse chevron by, so it points at the dock edge. */
|
|
570
|
+
chevron: number;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Everything that changes with the dock, derived in one place.
|
|
574
|
+
*
|
|
575
|
+
* Four call sites used to be four independent decisions waiting to disagree -
|
|
576
|
+
* the dock's flex triple, the popover's origin, the popover's gap and the
|
|
577
|
+
* badge's corner. They are all the same two facts (which vertical band, which
|
|
578
|
+
* horizontal band), so they are answered together.
|
|
579
|
+
*/
|
|
580
|
+
export declare function dockLayout(dock: ToolbarDock): DockLayout;
|
|
581
|
+
export interface ToolbarShadows {
|
|
582
|
+
/**
|
|
583
|
+
* The bar's elevation is an outward GLOW rather than a drop shadow: the bar
|
|
584
|
+
* floats over content it does not belong to, and a directional shadow makes
|
|
585
|
+
* it look pasted onto the page instead of hovering above it.
|
|
586
|
+
*/
|
|
587
|
+
bar: string;
|
|
588
|
+
popover: string;
|
|
589
|
+
marker: string;
|
|
590
|
+
/**
|
|
591
|
+
* DESIGN.md's Level 1: the inset hairline ring, on its own. Exported
|
|
592
|
+
* because three surfaces drew their own `0 0 0 1px <hairline>` ring by
|
|
593
|
+
* hand and one drew a raw `rgba(255,255,255,0.08)` literal.
|
|
594
|
+
*/
|
|
595
|
+
insetHairline: string;
|
|
596
|
+
/**
|
|
597
|
+
* The keyboard focus ring. There was not one `outline`, `:focus` or focus
|
|
598
|
+
* state in this entire package, so any host shipping `*:focus { outline:
|
|
599
|
+
* none }` erased keyboard focus from a review tool - WCAG 2.4.7, failed by
|
|
600
|
+
* inheritance. Ink-based rather than link-blue, mirroring `--ring`'s ROLE
|
|
601
|
+
* (the highest-contrast ring the palette has) rather than its literal, since
|
|
602
|
+
* this palette has no link blue in it.
|
|
603
|
+
*/
|
|
604
|
+
focusRing: string;
|
|
605
|
+
}
|
|
606
|
+
/** What the reviewer chose, which is not the same as what gets rendered. */
|
|
607
|
+
export type ToolbarThemeMode = "system" | "light" | "dark";
|
|
608
|
+
/** What gets rendered, after `system` has been resolved against the host. */
|
|
609
|
+
export type ResolvedTheme = "light" | "dark";
|
|
610
|
+
export interface ToolbarTheme {
|
|
611
|
+
resolved: ResolvedTheme;
|
|
612
|
+
colors: ToolbarPalette;
|
|
613
|
+
shadows: ToolbarShadows;
|
|
614
|
+
}
|
|
615
|
+
export declare const lightTheme: ToolbarTheme;
|
|
616
|
+
export declare const darkTheme: ToolbarTheme;
|
|
617
|
+
export declare function themeFor(resolved: ResolvedTheme): ToolbarTheme;
|
|
618
|
+
/**
|
|
619
|
+
* Where the reviewer's manual override is remembered. `localStorage` on the
|
|
620
|
+
* host origin, because the preference belongs to this person on this app and
|
|
621
|
+
* there is nowhere else to put it that survives a reload without a round trip
|
|
622
|
+
* the disabled-build requirement forbids.
|
|
623
|
+
*/
|
|
624
|
+
export declare const THEME_STORAGE_KEY = "bitvea.feedback-toolbar.theme";
|
|
625
|
+
/** Media query the default follows, read from the host document. */
|
|
626
|
+
export declare const DARK_SCHEME_QUERY = "(prefers-color-scheme: dark)";
|
|
627
|
+
/**
|
|
628
|
+
* The other preference the toolbar reads from the HOST document, beside it
|
|
629
|
+
* because they are answered the same way - one `matchMedia` subscription each,
|
|
630
|
+
* through `useSyncExternalStore` so there is a server snapshot.
|
|
631
|
+
*
|
|
632
|
+
* It is the host's document rather than ours because there is only one: the
|
|
633
|
+
* reviewer's own OS setting, which the page the toolbar is injected into has
|
|
634
|
+
* no say in. See `usePrefersReducedMotion` in motion.ts.
|
|
635
|
+
*/
|
|
636
|
+
export declare const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
637
|
+
export declare function isThemeMode(value: unknown): value is ToolbarThemeMode;
|
|
638
|
+
/**
|
|
639
|
+
* Above almost everything. Host apps routinely use z-indexes in the thousands
|
|
640
|
+
* for their own modals; the toolbar has to sit above those to be usable at
|
|
641
|
+
* all, and it is the reviewer's own overlay rather than part of the page.
|
|
642
|
+
*/
|
|
643
|
+
export declare const Z_INDEX = 2147483000;
|
|
644
|
+
/**
|
|
645
|
+
* The toolbar's own internal stacking order, inside the root that `Z_INDEX`
|
|
646
|
+
* lifts above the host page.
|
|
647
|
+
*
|
|
648
|
+
* This exists because every one of these surfaces used to pick its own number
|
|
649
|
+
* and the bar picked none at all - it inherited `z-index: auto`, which loses
|
|
650
|
+
* to any positive value in the same stacking context. The consequences were
|
|
651
|
+
* not cosmetic: the picking overlay painted over the bar and swallowed the
|
|
652
|
+
* click that cancels picking, leaving a reviewer with no way out of crosshair
|
|
653
|
+
* mode, and the inbox sheet covered the account button on any viewport under
|
|
654
|
+
* about 1024px.
|
|
655
|
+
*
|
|
656
|
+
* So the order is written down once, and the rule it encodes is that THE BAR
|
|
657
|
+
* IS ALWAYS REACHABLE. It is the only persistent chrome the toolbar has; every
|
|
658
|
+
* other surface is something the bar opened and the bar has to be able to
|
|
659
|
+
* close. Everything else stacks under it, most transient first.
|
|
660
|
+
*
|
|
661
|
+
* `thread` sits above `surface` rather than tying with it, and issue #23 is
|
|
662
|
+
* why. A z-index only orders the siblings that declare one; equal values fall
|
|
663
|
+
* back to DOM order, and the inbox sheet is rendered after the pin layer in
|
|
664
|
+
* FeedbackToolbar.tsx - so a thread parked at `surface` loses to the sheet on
|
|
665
|
+
* any pin under its 340px. That is the common case rather than the exotic one:
|
|
666
|
+
* `focusPin` deliberately leaves the sheet open when a reviewer clicks a row,
|
|
667
|
+
* because they are working down a list, and the thread they just asked for is
|
|
668
|
+
* then the surface they are reading. It goes above the list they asked from.
|
|
669
|
+
*
|
|
670
|
+
* The other half of #23 was that an anchored thread never reached this ladder
|
|
671
|
+
* at all. It renders inside its marker's wrapper, and that wrapper is
|
|
672
|
+
* `position: fixed` with a z-index - a stacking context whatever its opacity -
|
|
673
|
+
* so the rung a thread paints at is the one the WRAPPER declares, not one the
|
|
674
|
+
* thread could set on itself. `PinMarker` therefore moves its wrapper between
|
|
675
|
+
* `marker` and `thread` as the thread opens, and nothing inside that wrapper
|
|
676
|
+
* declares a z-index of its own. Read this list as the order that actually
|
|
677
|
+
* renders; when it stops being that, the code is what is wrong.
|
|
678
|
+
*
|
|
679
|
+
* WHAT IS DELIBERATELY NOT IN THIS LADDER: the note and account popovers.
|
|
680
|
+
* `surface` used to be documented as "threads, composers, popovers and the
|
|
681
|
+
* inbox sheet" and only the sheet was ever really there - the popovers are
|
|
682
|
+
* children of the DOCK, so they ride its `bar` rung and have never declared
|
|
683
|
+
* one of their own. Splitting `thread` out of `surface` for issue #23 made
|
|
684
|
+
* that list true for threads and composers and left the popovers unmentioned,
|
|
685
|
+
* which is worse than the wrong entry: a reader could not place them at all.
|
|
686
|
+
* They are chrome rather than surfaces. They are anchored to the bar, opened
|
|
687
|
+
* and closed by the button beside them, and clamped to the same strip, so
|
|
688
|
+
* "above everything, with the bar" is the whole of their layering and a rung
|
|
689
|
+
* would be a second place to state it.
|
|
690
|
+
*/
|
|
691
|
+
export declare const layers: {
|
|
692
|
+
/** The picking wash. Under everything, including its own markers. */
|
|
693
|
+
readonly picker: number;
|
|
694
|
+
/** Pin markers: part of the page's review state, so below any open surface. */
|
|
695
|
+
readonly marker: number;
|
|
696
|
+
/** Panels the bar opened and the bar closes again: the inbox sheet. */
|
|
697
|
+
readonly surface: number;
|
|
698
|
+
/**
|
|
699
|
+
* The one thread the reviewer is reading, and the composer they are writing
|
|
700
|
+
* the next one in - a composer is a thread that does not exist yet, so it
|
|
701
|
+
* shares this rung and wins the tie by being rendered last.
|
|
702
|
+
*/
|
|
703
|
+
readonly thread: number;
|
|
704
|
+
/** Alerts that must be seen over an open surface. */
|
|
705
|
+
readonly alert: number;
|
|
706
|
+
/** The floating bar. Nothing the toolbar renders may cover it. */
|
|
707
|
+
readonly bar: 2147483000;
|
|
708
|
+
};
|
|
709
|
+
//# sourceMappingURL=tokens.d.ts.map
|