@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/deepLink.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Deep-linking into one pin on the live page, from the dashboard's feedback
|
|
2
|
+
// list or from the toolbar's own inbox sheet.
|
|
3
|
+
//
|
|
4
|
+
// The carrier is a namespaced hash fragment - `#bitvea-feedback=<id>`. A hash
|
|
5
|
+
// never reaches the host's server, so it cannot land in their request logs,
|
|
6
|
+
// their analytics or an edge cache key, which a query parameter would. The
|
|
7
|
+
// price is that hashes are somebody else's namespace too: host apps use them
|
|
8
|
+
// for hash routing and for in-page anchors, and breaking either is not a
|
|
9
|
+
// trade we are allowed to make. Hence the two rules this module exists to
|
|
10
|
+
// enforce.
|
|
11
|
+
//
|
|
12
|
+
// 1. Act only on a hash that starts with EXACTLY our prefix. Everything else
|
|
13
|
+
// is read and left completely alone - no rewrite, no normalisation, no
|
|
14
|
+
// "helpful" clearing.
|
|
15
|
+
// 2. Read it once, as early as a React tree can, and consume it immediately.
|
|
16
|
+
// A host framework that owns the hash rewrites it during hydration, so a
|
|
17
|
+
// later read finds nothing; and a link that keeps its fragment gets pasted
|
|
18
|
+
// onward, reopening a stranger's thread on somebody else's screen.
|
|
19
|
+
// 3. Never let the address bar be the source of truth for "which page is
|
|
20
|
+
// this". Reading it once is under our control; keeping it clean afterwards
|
|
21
|
+
// is not - Next's App Router restores the URL it considered canonical at
|
|
22
|
+
// load, fragment and all, from an effect that runs after every effect the
|
|
23
|
+
// toolbar can register. So the clear is retried (`clearDeepLinkHash`) and,
|
|
24
|
+
// more importantly, every page-identity question goes through
|
|
25
|
+
// `currentPageUrl`, which derives the answer instead of trusting it.
|
|
26
|
+
/** The whole namespace. Nothing else in a hash is ours. */
|
|
27
|
+
export const DEEP_LINK_PREFIX = "#bitvea-feedback=";
|
|
28
|
+
/**
|
|
29
|
+
* Ids we could have written. Feedback ids are cuids, so this is deliberately
|
|
30
|
+
* narrower than "any string": the value ends up in a `data-testid`, in an
|
|
31
|
+
* equality check against server rows, and in a URL the sheet builds, and a
|
|
32
|
+
* crafted hash must not be able to become any of those. A hash that is ours
|
|
33
|
+
* by prefix but carries something else is treated as no deep link at all.
|
|
34
|
+
*/
|
|
35
|
+
const ID_SHAPE = /^[A-Za-z0-9_-]{1,64}$/;
|
|
36
|
+
/**
|
|
37
|
+
* The feedback id a hash names, or null for every other hash there is -
|
|
38
|
+
* including `#pricing`, `#/route/23` and a bare `#`.
|
|
39
|
+
*/
|
|
40
|
+
export function parseDeepLink(hash) {
|
|
41
|
+
if (!hash.startsWith(DEEP_LINK_PREFIX))
|
|
42
|
+
return null;
|
|
43
|
+
const raw = hash.slice(DEEP_LINK_PREFIX.length);
|
|
44
|
+
let decoded = raw;
|
|
45
|
+
try {
|
|
46
|
+
decoded = decodeURIComponent(raw);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// A lone `%` is not valid percent-encoding. Fall back to the raw text and
|
|
50
|
+
// let the shape check below reject it, rather than throwing out of a
|
|
51
|
+
// render on a malformed URL somebody pasted.
|
|
52
|
+
}
|
|
53
|
+
const id = decoded.trim();
|
|
54
|
+
return ID_SHAPE.test(id) ? id : null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The URL that opens one item's thread on the page it was filed on. Used by
|
|
58
|
+
* the inbox sheet for an item on another page of the same site.
|
|
59
|
+
*
|
|
60
|
+
* Any existing fragment is dropped, because a URL has one fragment and this
|
|
61
|
+
* one is ours. That is safe precisely because page identity drops the fragment
|
|
62
|
+
* too - see the note at the top of this file - so the URL this returns and the
|
|
63
|
+
* `pageUrl` the item was stored under reduce to the same identity.
|
|
64
|
+
*/
|
|
65
|
+
export function buildDeepLink(pageUrl, feedbackItemId) {
|
|
66
|
+
const hashAt = pageUrl.indexOf("#");
|
|
67
|
+
const base = hashAt === -1 ? pageUrl : pageUrl.slice(0, hashAt);
|
|
68
|
+
return `${base}${DEEP_LINK_PREFIX}${encodeURIComponent(feedbackItemId)}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The captured value, held at module scope rather than in component state.
|
|
72
|
+
*
|
|
73
|
+
* Two reasons it cannot live in a component. The hash has to be read before
|
|
74
|
+
* anything else in the tree can observe `location`, and `captureDeepLink` is
|
|
75
|
+
* consumed from a `useState` initialiser precisely so it lands in the shell's
|
|
76
|
+
* first render pass, ahead of the pin layer's own render. And the read is
|
|
77
|
+
* destructive - it strips the fragment - so a second reader must be given the
|
|
78
|
+
* same answer rather than the empty hash left behind.
|
|
79
|
+
*
|
|
80
|
+
* `null` means "not captured yet"; `{ id: null }` means "captured, and there
|
|
81
|
+
* was no deep link", which are different states.
|
|
82
|
+
*/
|
|
83
|
+
let captured = null;
|
|
84
|
+
/**
|
|
85
|
+
* Read the deep link, once. A pure read: nothing about the document changes.
|
|
86
|
+
*
|
|
87
|
+
* It used to strip the fragment here as well, which was a `replaceState` from
|
|
88
|
+
* inside a render pass - a side effect on the HOST's document, in a phase
|
|
89
|
+
* React may run twice and may throw away. The clear is now the shell's job
|
|
90
|
+
* (`clearDeepLinkHash` from an effect), and this function only answers.
|
|
91
|
+
*
|
|
92
|
+
* Nothing downstream may assume the clear stuck in any case: a render-phase
|
|
93
|
+
* clear was overwritten by Next's App Router a moment later, which is why the
|
|
94
|
+
* pin layer's idea of "this page" comes from `currentPageUrl` rather than from
|
|
95
|
+
* whatever the address bar ended up saying.
|
|
96
|
+
*/
|
|
97
|
+
export function captureDeepLink() {
|
|
98
|
+
if (captured !== null)
|
|
99
|
+
return captured.id;
|
|
100
|
+
// Server render: no `location` to read, and nothing to memoise - the first
|
|
101
|
+
// browser render is the one that captures.
|
|
102
|
+
if (typeof window === "undefined")
|
|
103
|
+
return null;
|
|
104
|
+
captured = { id: parseDeepLink(window.location.hash) };
|
|
105
|
+
return captured.id;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The identity of the page the reviewer is on: its URL with no fragment.
|
|
109
|
+
*
|
|
110
|
+
* This is what "which page is this" means everywhere the toolbar asks - the
|
|
111
|
+
* pin layer's page-scoped query, the inbox sheet's scope, and the metadata
|
|
112
|
+
* attached to a new report - and it is deliberately not `location.href`.
|
|
113
|
+
*
|
|
114
|
+
* Two reasons, and both are about the address bar not being ours to trust. A
|
|
115
|
+
* host framework can put our own fragment back after we have removed it
|
|
116
|
+
* (Next's App Router does, see `clearDeepLinkHash`), and a `pageUrl` carrying
|
|
117
|
+
* `#bitvea-feedback=...` matches no stored row. And a deep link has to replace
|
|
118
|
+
* the host's fragment to carry the id at all, so a landing URL and the URL a
|
|
119
|
+
* report was filed from differ by the fragment as a matter of course - see the
|
|
120
|
+
* note at the top of this file for why identity gives the fragment up rather
|
|
121
|
+
* than the other way round.
|
|
122
|
+
*
|
|
123
|
+
* Deriving it instead of trusting the URL makes that class of bug impossible
|
|
124
|
+
* rather than merely unlikely: it is right on the first render, whatever the
|
|
125
|
+
* host does to the address bar afterwards.
|
|
126
|
+
*/
|
|
127
|
+
export function currentPageUrl() {
|
|
128
|
+
if (typeof window === "undefined")
|
|
129
|
+
return "";
|
|
130
|
+
return pageIdentity(window.location.href);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* `href` reduced to a page identity: origin, path and query, no fragment.
|
|
134
|
+
*
|
|
135
|
+
* Every fragment, not only ours. See the note at the top of this file: a URL
|
|
136
|
+
* has one fragment and a deep link needs it, so an identity that keeps the
|
|
137
|
+
* host's cannot be produced by a link we build. Query strings are kept,
|
|
138
|
+
* because they do distinguish pages on a server-rendered host.
|
|
139
|
+
*/
|
|
140
|
+
export function pageIdentity(href) {
|
|
141
|
+
const hashAt = href.indexOf("#");
|
|
142
|
+
return hashAt === -1 ? href : href.slice(0, hashAt);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Strip our fragment, leaving the rest of the URL byte-identical. Re-checks
|
|
146
|
+
* the prefix so this can never be the thing that eats a host's own hash, and
|
|
147
|
+
* uses `replaceState` so it adds no history entry the back button has to walk
|
|
148
|
+
* back through.
|
|
149
|
+
*
|
|
150
|
+
* Idempotent, and called more than once on purpose. A render-phase clear is
|
|
151
|
+
* not the last word on the address bar: Next's App Router restores the URL it
|
|
152
|
+
* considered canonical at load - fragment included - from a commit-phase
|
|
153
|
+
* effect, and because a parent's effects run after its children's there is no
|
|
154
|
+
* effect the toolbar can register that is guaranteed to run later. So the
|
|
155
|
+
* shell schedules one more clear onto the next macrotask, which is after the
|
|
156
|
+
* whole commit; see `EnabledToolbar`. Re-checking the prefix is what makes
|
|
157
|
+
* that safe: by then the host may have navigated, and a second clear must
|
|
158
|
+
* never eat a fragment that is not ours.
|
|
159
|
+
*/
|
|
160
|
+
export function clearDeepLinkHash() {
|
|
161
|
+
if (typeof window === "undefined")
|
|
162
|
+
return;
|
|
163
|
+
const { hash, href } = window.location;
|
|
164
|
+
if (parseDeepLink(hash) === null)
|
|
165
|
+
return;
|
|
166
|
+
const withoutHash = href.slice(0, href.length - hash.length);
|
|
167
|
+
try {
|
|
168
|
+
window.history.replaceState(window.history.state, "", withoutHash);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// A sandboxed iframe refuses `replaceState`. The id is already captured,
|
|
172
|
+
// so the thread still opens; only the tidy-up is lost, and taking the
|
|
173
|
+
// host app down over a cosmetic URL is not a trade worth making.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Forget the capture. Only tests need this - a module-scope one-shot is
|
|
178
|
+
* exactly right in a browser, where there is one page load per module, and
|
|
179
|
+
* exactly wrong in a runner where there are many renders per module. It is
|
|
180
|
+
* deliberately not re-exported from the package barrel.
|
|
181
|
+
*/
|
|
182
|
+
export function resetDeepLinkCapture() {
|
|
183
|
+
captured = null;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=deepLink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepLink.js","sourceRoot":"","sources":["../src/deepLink.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,8CAA8C;AAC9C,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,WAAW;AACX,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,yBAAyB;AACzB,6EAA6E;AAC7E,4EAA4E;AAC5E,8EAA8E;AAC9E,sEAAsE;AACtE,yEAAyE;AACzE,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,iEAAiE;AACjE,wEAAwE;AAExE,2DAA2D;AAC3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAAmB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,uBAAuB,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,OAAO,GAAG,GAAG,CAAC;IAClB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,qEAAqE;QACrE,6CAA6C;IAC/C,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC1B,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,cAAsB;IACnE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAChE,OAAO,GAAG,IAAI,GAAG,gBAAgB,GAAG,kBAAkB,CAAC,cAAc,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,IAAI,QAAQ,GAAiC,IAAI,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC,EAAE,CAAC;IAC1C,2EAA2E;IAC3E,2CAA2C;IAC3C,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAE/C,QAAQ,GAAG,EAAE,EAAE,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACvD,OAAO,QAAQ,CAAC,EAAE,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAC7C,OAAO,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,OAAO,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO;IAC1C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;IACvC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO;IACzC,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;QACzE,sEAAsE;QACtE,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The toolbar's own copy of DESIGN.md's two faces, loaded into whatever page
|
|
3
|
+
* the toolbar was injected into.
|
|
4
|
+
*
|
|
5
|
+
* Naming `Geist` in a font stack is not enough, and it failed twice over:
|
|
6
|
+
*
|
|
7
|
+
* 1. A REVIEWED page is somebody else's site. It has no reason to have loaded
|
|
8
|
+
* Geist, so the stack fell through to `Inter, system-ui` and the toolbar
|
|
9
|
+
* rendered in whatever the host's system font is - which is exactly the
|
|
10
|
+
* "our font or the website's font" the redesign was asked to stop doing.
|
|
11
|
+
* 2. Even on this product's OWN preview route, which does load Geist, the
|
|
12
|
+
* stack still missed. `next/font/google` rewrites the family to a hashed
|
|
13
|
+
* name (`__Geist_1a2b3c`) and exposes it only through `--font-geist`, so
|
|
14
|
+
* the literal string `Geist` matches nothing there either.
|
|
15
|
+
*
|
|
16
|
+
* So the package ships the face itself. Not as base64 in the bundle: the two
|
|
17
|
+
* variable woff2 files are ~140KB together, woff2 is already compressed so
|
|
18
|
+
* base64 would add a third again and gzip would not take it back, and the
|
|
19
|
+
* bundle ceiling is 100KB gzipped (CLAUDE.md section 8). They are served as
|
|
20
|
+
* static files from the same origin the toolbar already talks to, and the
|
|
21
|
+
* `@font-face` below points at them absolutely - a relative URL would resolve
|
|
22
|
+
* against the CUSTOMER's origin, where the files do not exist.
|
|
23
|
+
*
|
|
24
|
+
* The family names are namespaced rather than plain `Geist`. A host page that
|
|
25
|
+
* has its own `Geist` - a Vercel customer, most likely of all - would
|
|
26
|
+
* otherwise have its definition and ours collide in one document, and which
|
|
27
|
+
* one won would depend on injection order.
|
|
28
|
+
*
|
|
29
|
+
* Everything here degrades to the rest of the stack: a host whose CSP sets
|
|
30
|
+
* `font-src 'self'` blocks the fetch, the family never resolves, and the
|
|
31
|
+
* toolbar renders in Inter or system-ui exactly as it does today. That is a
|
|
32
|
+
* worse-looking toolbar, never a broken one.
|
|
33
|
+
*/
|
|
34
|
+
/** The namespaced family names the type scale asks for. See the note above. */
|
|
35
|
+
export declare const TOOLBAR_FONT_SANS = "BitVea Geist";
|
|
36
|
+
export declare const TOOLBAR_FONT_MONO = "BitVea Geist Mono";
|
|
37
|
+
/** Where the two files sit, relative to the backend's origin. */
|
|
38
|
+
export declare const FONT_PATHS: {
|
|
39
|
+
readonly sans: "/fonts/geist-sans-variable.woff2";
|
|
40
|
+
readonly mono: "/fonts/geist-mono-variable.woff2";
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The id the injected `<style>` carries, so a second mount finds the first
|
|
44
|
+
* one instead of adding a duplicate. The toolbar can legitimately mount more
|
|
45
|
+
* than once in a document - React strict mode does it on every dev render.
|
|
46
|
+
*/
|
|
47
|
+
export declare const FONT_STYLE_ELEMENT_ID = "bitvea-feedback-toolbar-fonts";
|
|
48
|
+
/**
|
|
49
|
+
* `swap`, not `block`: the toolbar is an overlay on somebody else's page and
|
|
50
|
+
* has no business rendering invisible text while a font downloads. The
|
|
51
|
+
* fallback paints immediately and the swap is the only flash.
|
|
52
|
+
*
|
|
53
|
+
* Both files are VARIABLE, hence the `100 900` range on one src rather than a
|
|
54
|
+
* face per weight. DESIGN.md's working set is 400/500/600, all inside it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildFontFaceCss(apiUrl: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Put the two faces in the host document, once.
|
|
59
|
+
*
|
|
60
|
+
* This is the one thing the package writes into a page it does not own, which
|
|
61
|
+
* is why it is a single `<style>` with a known id defining two namespaced
|
|
62
|
+
* families and no selectors. It cannot restyle anything the host renders:
|
|
63
|
+
* an `@font-face` only offers a name, and nothing on the host's side asks for
|
|
64
|
+
* this one.
|
|
65
|
+
*
|
|
66
|
+
* Returns the element so a caller can remove it; a no-op with no API URL,
|
|
67
|
+
* which is the same condition that keeps the whole toolbar dark.
|
|
68
|
+
*/
|
|
69
|
+
export declare function injectFontFaces(doc: Document, apiUrl: string): HTMLStyleElement | null;
|
|
70
|
+
//# sourceMappingURL=fontFace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontFace.d.ts","sourceRoot":"","sources":["../src/fontFace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,iBAAiB,CAAC;AAChD,eAAO,MAAM,iBAAiB,sBAAsB,CAAC;AAErD,iEAAiE;AACjE,eAAO,MAAM,UAAU;;;CAGb,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAErE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,MAAM,GACb,gBAAgB,GAAG,IAAI,CAWzB"}
|
package/dist/fontFace.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The toolbar's own copy of DESIGN.md's two faces, loaded into whatever page
|
|
3
|
+
* the toolbar was injected into.
|
|
4
|
+
*
|
|
5
|
+
* Naming `Geist` in a font stack is not enough, and it failed twice over:
|
|
6
|
+
*
|
|
7
|
+
* 1. A REVIEWED page is somebody else's site. It has no reason to have loaded
|
|
8
|
+
* Geist, so the stack fell through to `Inter, system-ui` and the toolbar
|
|
9
|
+
* rendered in whatever the host's system font is - which is exactly the
|
|
10
|
+
* "our font or the website's font" the redesign was asked to stop doing.
|
|
11
|
+
* 2. Even on this product's OWN preview route, which does load Geist, the
|
|
12
|
+
* stack still missed. `next/font/google` rewrites the family to a hashed
|
|
13
|
+
* name (`__Geist_1a2b3c`) and exposes it only through `--font-geist`, so
|
|
14
|
+
* the literal string `Geist` matches nothing there either.
|
|
15
|
+
*
|
|
16
|
+
* So the package ships the face itself. Not as base64 in the bundle: the two
|
|
17
|
+
* variable woff2 files are ~140KB together, woff2 is already compressed so
|
|
18
|
+
* base64 would add a third again and gzip would not take it back, and the
|
|
19
|
+
* bundle ceiling is 100KB gzipped (CLAUDE.md section 8). They are served as
|
|
20
|
+
* static files from the same origin the toolbar already talks to, and the
|
|
21
|
+
* `@font-face` below points at them absolutely - a relative URL would resolve
|
|
22
|
+
* against the CUSTOMER's origin, where the files do not exist.
|
|
23
|
+
*
|
|
24
|
+
* The family names are namespaced rather than plain `Geist`. A host page that
|
|
25
|
+
* has its own `Geist` - a Vercel customer, most likely of all - would
|
|
26
|
+
* otherwise have its definition and ours collide in one document, and which
|
|
27
|
+
* one won would depend on injection order.
|
|
28
|
+
*
|
|
29
|
+
* Everything here degrades to the rest of the stack: a host whose CSP sets
|
|
30
|
+
* `font-src 'self'` blocks the fetch, the family never resolves, and the
|
|
31
|
+
* toolbar renders in Inter or system-ui exactly as it does today. That is a
|
|
32
|
+
* worse-looking toolbar, never a broken one.
|
|
33
|
+
*/
|
|
34
|
+
/** The namespaced family names the type scale asks for. See the note above. */
|
|
35
|
+
export const TOOLBAR_FONT_SANS = "BitVea Geist";
|
|
36
|
+
export const TOOLBAR_FONT_MONO = "BitVea Geist Mono";
|
|
37
|
+
/** Where the two files sit, relative to the backend's origin. */
|
|
38
|
+
export const FONT_PATHS = {
|
|
39
|
+
sans: "/fonts/geist-sans-variable.woff2",
|
|
40
|
+
mono: "/fonts/geist-mono-variable.woff2",
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The id the injected `<style>` carries, so a second mount finds the first
|
|
44
|
+
* one instead of adding a duplicate. The toolbar can legitimately mount more
|
|
45
|
+
* than once in a document - React strict mode does it on every dev render.
|
|
46
|
+
*/
|
|
47
|
+
export const FONT_STYLE_ELEMENT_ID = "bitvea-feedback-toolbar-fonts";
|
|
48
|
+
/**
|
|
49
|
+
* `swap`, not `block`: the toolbar is an overlay on somebody else's page and
|
|
50
|
+
* has no business rendering invisible text while a font downloads. The
|
|
51
|
+
* fallback paints immediately and the swap is the only flash.
|
|
52
|
+
*
|
|
53
|
+
* Both files are VARIABLE, hence the `100 900` range on one src rather than a
|
|
54
|
+
* face per weight. DESIGN.md's working set is 400/500/600, all inside it.
|
|
55
|
+
*/
|
|
56
|
+
export function buildFontFaceCss(apiUrl) {
|
|
57
|
+
const sans = `${apiUrl}${FONT_PATHS.sans}`;
|
|
58
|
+
const mono = `${apiUrl}${FONT_PATHS.mono}`;
|
|
59
|
+
return [
|
|
60
|
+
`@font-face{font-family:"${TOOLBAR_FONT_SANS}";src:url("${sans}") format("woff2");font-weight:100 900;font-style:normal;font-display:swap;}`,
|
|
61
|
+
`@font-face{font-family:"${TOOLBAR_FONT_MONO}";src:url("${mono}") format("woff2");font-weight:100 900;font-style:normal;font-display:swap;}`,
|
|
62
|
+
].join("");
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Put the two faces in the host document, once.
|
|
66
|
+
*
|
|
67
|
+
* This is the one thing the package writes into a page it does not own, which
|
|
68
|
+
* is why it is a single `<style>` with a known id defining two namespaced
|
|
69
|
+
* families and no selectors. It cannot restyle anything the host renders:
|
|
70
|
+
* an `@font-face` only offers a name, and nothing on the host's side asks for
|
|
71
|
+
* this one.
|
|
72
|
+
*
|
|
73
|
+
* Returns the element so a caller can remove it; a no-op with no API URL,
|
|
74
|
+
* which is the same condition that keeps the whole toolbar dark.
|
|
75
|
+
*/
|
|
76
|
+
export function injectFontFaces(doc, apiUrl) {
|
|
77
|
+
if (apiUrl === "")
|
|
78
|
+
return null;
|
|
79
|
+
const existing = doc.getElementById(FONT_STYLE_ELEMENT_ID);
|
|
80
|
+
if (existing !== null)
|
|
81
|
+
return existing;
|
|
82
|
+
const style = doc.createElement("style");
|
|
83
|
+
style.id = FONT_STYLE_ELEMENT_ID;
|
|
84
|
+
style.textContent = buildFontFaceCss(apiUrl);
|
|
85
|
+
doc.head.appendChild(style);
|
|
86
|
+
return style;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=fontFace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fontFace.js","sourceRoot":"","sources":["../src/fontFace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAErD,iEAAiE;AACjE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,kCAAkC;IACxC,IAAI,EAAE,kCAAkC;CAChC,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,+BAA+B,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,IAAI,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,GAAG,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAC3C,OAAO;QACL,2BAA2B,iBAAiB,cAAc,IAAI,8EAA8E;QAC5I,2BAA2B,iBAAiB,cAAc,IAAI,8EAA8E;KAC7I,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAa,EACb,MAAc;IAEd,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAc,CAAC,qBAAqB,CAAC,CAAC;IAC3D,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,QAA4B,CAAC;IAE3D,MAAM,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,GAAG,qBAAqB,CAAC;IACjC,KAAK,CAAC,WAAW,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC7C,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/gating.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The env var that forces the toolbar on where it would otherwise be off.
|
|
3
|
+
* Documented in the package README and written by the installer only when the
|
|
4
|
+
* developer asks for it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FORCE_ENV_VAR = "NEXT_PUBLIC_FEEDBACK_TOOLBAR_FORCE";
|
|
7
|
+
/** The env var the gate reads. `NEXT_PUBLIC_` so it survives into the bundle. */
|
|
8
|
+
export declare const ENV_VAR = "NEXT_PUBLIC_VERCEL_ENV";
|
|
9
|
+
export interface GateInput {
|
|
10
|
+
/** `NEXT_PUBLIC_VERCEL_ENV`, or undefined outside Vercel. */
|
|
11
|
+
vercelEnv?: string | undefined;
|
|
12
|
+
/** `NEXT_PUBLIC_FEEDBACK_TOOLBAR_FORCE`, or undefined. */
|
|
13
|
+
force?: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* On for `preview` and `development`, off for `production`, off when the
|
|
17
|
+
* environment is not stated at all.
|
|
18
|
+
*
|
|
19
|
+
* The last case is the one worth arguing about, and it is deliberately the
|
|
20
|
+
* cautious reading: an absent `VERCEL_ENV` means the toolbar cannot tell where
|
|
21
|
+
* it is running, and the cost of guessing wrong is a feedback widget on a live
|
|
22
|
+
* customer site. A developer running `next dev` outside Vercel who wants it
|
|
23
|
+
* sets the override, which is a one-line env var, and gets a build that says
|
|
24
|
+
* out loud that it is forced on.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isToolbarEnabled({ vercelEnv, force }: GateInput): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Read the gate from a process env bag. Kept separate from `isToolbarEnabled`
|
|
29
|
+
* so the decision stays testable without touching `process.env`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isToolbarEnabledFromEnv(env: Record<string, string | undefined>): boolean;
|
|
32
|
+
//# sourceMappingURL=gating.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gating.d.ts","sourceRoot":"","sources":["../src/gating.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,eAAO,MAAM,aAAa,uCAAuC,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,OAAO,2BAA2B,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AASD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,SAAS,GAAG,OAAO,CAGzE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,OAAO,CAKT"}
|
package/dist/gating.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Whether the toolbar runs at all.
|
|
2
|
+
//
|
|
3
|
+
// This is the one piece of logic in the package that has to be right by
|
|
4
|
+
// inspection, because getting it wrong ships a feedback widget to somebody's
|
|
5
|
+
// customers. It is therefore a pure function of two strings with no React, no
|
|
6
|
+
// DOM and no network in it, so it can be tested by feeding it the five inputs
|
|
7
|
+
// that actually occur.
|
|
8
|
+
/**
|
|
9
|
+
* The env var that forces the toolbar on where it would otherwise be off.
|
|
10
|
+
* Documented in the package README and written by the installer only when the
|
|
11
|
+
* developer asks for it.
|
|
12
|
+
*/
|
|
13
|
+
export const FORCE_ENV_VAR = "NEXT_PUBLIC_FEEDBACK_TOOLBAR_FORCE";
|
|
14
|
+
/** The env var the gate reads. `NEXT_PUBLIC_` so it survives into the bundle. */
|
|
15
|
+
export const ENV_VAR = "NEXT_PUBLIC_VERCEL_ENV";
|
|
16
|
+
/** `"1"`, `"true"` and `"yes"` all mean on; anything else, including absent. */
|
|
17
|
+
function isTruthy(value) {
|
|
18
|
+
if (!value)
|
|
19
|
+
return false;
|
|
20
|
+
const v = value.trim().toLowerCase();
|
|
21
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* On for `preview` and `development`, off for `production`, off when the
|
|
25
|
+
* environment is not stated at all.
|
|
26
|
+
*
|
|
27
|
+
* The last case is the one worth arguing about, and it is deliberately the
|
|
28
|
+
* cautious reading: an absent `VERCEL_ENV` means the toolbar cannot tell where
|
|
29
|
+
* it is running, and the cost of guessing wrong is a feedback widget on a live
|
|
30
|
+
* customer site. A developer running `next dev` outside Vercel who wants it
|
|
31
|
+
* sets the override, which is a one-line env var, and gets a build that says
|
|
32
|
+
* out loud that it is forced on.
|
|
33
|
+
*/
|
|
34
|
+
export function isToolbarEnabled({ vercelEnv, force }) {
|
|
35
|
+
if (isTruthy(force))
|
|
36
|
+
return true;
|
|
37
|
+
return vercelEnv === "preview" || vercelEnv === "development";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the gate from a process env bag. Kept separate from `isToolbarEnabled`
|
|
41
|
+
* so the decision stays testable without touching `process.env`.
|
|
42
|
+
*/
|
|
43
|
+
export function isToolbarEnabledFromEnv(env) {
|
|
44
|
+
return isToolbarEnabled({
|
|
45
|
+
vercelEnv: env[ENV_VAR],
|
|
46
|
+
force: env[FORCE_ENV_VAR],
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=gating.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gating.js","sourceRoot":"","sources":["../src/gating.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,EAAE;AACF,wEAAwE;AACxE,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,uBAAuB;AAEvB;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,oCAAoC,CAAC;AAElE,iFAAiF;AACjF,MAAM,CAAC,MAAM,OAAO,GAAG,wBAAwB,CAAC;AAShD,gFAAgF;AAChF,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC;AAChE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAa;IAC9D,IAAI,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,aAAa,CAAC;AAChE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAuC;IAEvC,OAAO,gBAAgB,CAAC;QACtB,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;QACvB,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC;KAC1B,CAAC,CAAC;AACL,CAAC"}
|
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The one drawing contract: a 24-unit box, a 1.8 stroke, round joins, and the
|
|
4
|
+
* host-proof sizing above. `aria-hidden` because every icon in this package
|
|
5
|
+
* sits inside a control that carries the accessible name itself.
|
|
6
|
+
*/
|
|
7
|
+
export declare function svgProps(): {
|
|
8
|
+
viewBox: string;
|
|
9
|
+
fill: "none";
|
|
10
|
+
stroke: "currentColor";
|
|
11
|
+
strokeWidth: number;
|
|
12
|
+
strokeLinecap: "round";
|
|
13
|
+
strokeLinejoin: "round";
|
|
14
|
+
style: CSSProperties;
|
|
15
|
+
"aria-hidden": true;
|
|
16
|
+
};
|
|
17
|
+
export declare function PinIcon(): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function NoteIcon(): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function InboxIcon(): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare function AccountIcon(): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
/**
|
|
22
|
+
* Collapse: a double chevron, folding the bar away toward the edge it is
|
|
23
|
+
* docked to.
|
|
24
|
+
*
|
|
25
|
+
* Two chevrons rather than one because a single one is the same glyph this
|
|
26
|
+
* product uses for "next" and for a disclosure caret, and a doubled one reads
|
|
27
|
+
* as "all of this, out of the way" - which is what the control does. It points
|
|
28
|
+
* along the dock's own axis, and since issue #21 that axis really can be any of
|
|
29
|
+
* four: the glyph is drawn pointing right and `dockLayout` answers how far to
|
|
30
|
+
* turn it. A static rotation on the leaf `<svg>`, which holds nothing that
|
|
31
|
+
* stacks - the same reasoning that lets `BarButton` squash itself.
|
|
32
|
+
*/
|
|
33
|
+
export declare function CollapseIcon({ turn }: {
|
|
34
|
+
turn: number;
|
|
35
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
/** Copy this message's text. */
|
|
37
|
+
export declare function CopyIcon(): import("react/jsx-runtime").JSX.Element;
|
|
38
|
+
/** Copy a link back to this thread - the deep link `buildDeepLink` composes. */
|
|
39
|
+
export declare function LinkIcon(): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
/** Resolve, and - flipped by its label rather than its shape - reopen. */
|
|
41
|
+
export declare function CheckIcon(): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
/**
|
|
43
|
+
* Send a reply. The one ink-filled control in the card.
|
|
44
|
+
*
|
|
45
|
+
* The translate is an OPTICAL centring, and it is measured rather than nudged
|
|
46
|
+
* by eye. A paper plane is a triangle with a long thin tail, so its area
|
|
47
|
+
* centroid is nowhere near the middle of its bounding box: for this outline -
|
|
48
|
+
* (21,3) (14.4,21) (10.5,12.9) (2.4,9) - the centroid is (13.32, 10.32) while
|
|
49
|
+
* the viewBox centre is (12, 12). Flex centring aligns the BOX, so the glyph
|
|
50
|
+
* sat 1.32 right and 1.68 high inside a perfectly centred button and read as
|
|
51
|
+
* misaligned.
|
|
52
|
+
*
|
|
53
|
+
* Shifting by exactly that difference puts the mass on the button's centre.
|
|
54
|
+
* The shifted bounds are x 1.08-19.68 and y 4.68-22.68, so with the 1.5 stroke
|
|
55
|
+
* nothing crosses the 24 viewBox and the glyph is not clipped.
|
|
56
|
+
*/
|
|
57
|
+
export declare function SendIcon(): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
/**
|
|
59
|
+
* Attach a screenshot to a reply.
|
|
60
|
+
*
|
|
61
|
+
* Disabled until a reply can carry one: `Screenshot` hangs off `FeedbackItem`
|
|
62
|
+
* and `replySchema` has no field for it, so the control is drawn and refused
|
|
63
|
+
* rather than drawn and broken.
|
|
64
|
+
*/
|
|
65
|
+
export declare function CameraIcon(): import("react/jsx-runtime").JSX.Element;
|
|
66
|
+
/** Dismiss a thread that has no marker left to re-click. */
|
|
67
|
+
export declare function CloseIcon(): import("react/jsx-runtime").JSX.Element;
|
|
68
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAO3C;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,EAAE,IAAI,CAAC;CACrB,CAWA;AAID,wBAAgB,OAAO,4CAOtB;AAED,wBAAgB,QAAQ,4CAMvB;AAED,wBAAgB,SAAS,4CAOxB;AAED,wBAAgB,WAAW,4CAO1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,2CAWtD;AAID,gCAAgC;AAChC,wBAAgB,QAAQ,4CAOvB;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,4CAOvB;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,4CAMxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,4CASvB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,4CAOzB;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,4CAOxB"}
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Size lives here rather than in `width`/`height` attributes: author CSS beats
|
|
3
|
+
// a presentation attribute, so a host `svg { width: 100% }` would resize every
|
|
4
|
+
// icon in the toolbar. Invisible in the preview harness, visible in the wild.
|
|
5
|
+
const ICON = { display: "block", width: "16px", height: "16px" };
|
|
6
|
+
/**
|
|
7
|
+
* The one drawing contract: a 24-unit box, a 1.8 stroke, round joins, and the
|
|
8
|
+
* host-proof sizing above. `aria-hidden` because every icon in this package
|
|
9
|
+
* sits inside a control that carries the accessible name itself.
|
|
10
|
+
*/
|
|
11
|
+
export function svgProps() {
|
|
12
|
+
return {
|
|
13
|
+
viewBox: "0 0 24 24",
|
|
14
|
+
fill: "none",
|
|
15
|
+
stroke: "currentColor",
|
|
16
|
+
strokeWidth: 1.8,
|
|
17
|
+
strokeLinecap: "round",
|
|
18
|
+
strokeLinejoin: "round",
|
|
19
|
+
style: ICON,
|
|
20
|
+
"aria-hidden": true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/* ── The bar ──────────────────────────────────────────────────────────── */
|
|
24
|
+
export function PinIcon() {
|
|
25
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("path", { d: "M12 21s7-6.2 7-11a7 7 0 1 0-14 0c0 4.8 7 11 7 11Z" }), _jsx("circle", { cx: "12", cy: "10", r: "2.5" })] }));
|
|
26
|
+
}
|
|
27
|
+
export function NoteIcon() {
|
|
28
|
+
return (_jsx("svg", { ...svgProps(), children: _jsx("path", { d: "M21 12a8 8 0 0 1-8 8H8l-4 3v-6.5A8 8 0 0 1 13 4a8 8 0 0 1 8 8Z" }) }));
|
|
29
|
+
}
|
|
30
|
+
export function InboxIcon() {
|
|
31
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("path", { d: "M3 13h4l1.5 3h7L17 13h4" }), _jsx("path", { d: "M5 5h14l2 8v6H3v-6l2-8Z" })] }));
|
|
32
|
+
}
|
|
33
|
+
export function AccountIcon() {
|
|
34
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("circle", { cx: "12", cy: "8.5", r: "3.5" }), _jsx("path", { d: "M4.5 20a7.5 7.5 0 0 1 15 0" })] }));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Collapse: a double chevron, folding the bar away toward the edge it is
|
|
38
|
+
* docked to.
|
|
39
|
+
*
|
|
40
|
+
* Two chevrons rather than one because a single one is the same glyph this
|
|
41
|
+
* product uses for "next" and for a disclosure caret, and a doubled one reads
|
|
42
|
+
* as "all of this, out of the way" - which is what the control does. It points
|
|
43
|
+
* along the dock's own axis, and since issue #21 that axis really can be any of
|
|
44
|
+
* four: the glyph is drawn pointing right and `dockLayout` answers how far to
|
|
45
|
+
* turn it. A static rotation on the leaf `<svg>`, which holds nothing that
|
|
46
|
+
* stacks - the same reasoning that lets `BarButton` squash itself.
|
|
47
|
+
*/
|
|
48
|
+
export function CollapseIcon({ turn }) {
|
|
49
|
+
const props = svgProps();
|
|
50
|
+
return (_jsxs("svg", { ...props, style: { ...props.style, transform: `rotate(${turn}deg)` }, children: [_jsx("path", { d: "M6 7l5 5-5 5" }), _jsx("path", { d: "M13 7l5 5-5 5" })] }));
|
|
51
|
+
}
|
|
52
|
+
/* ── A thread's message rows and composer (issue #20) ─────────────────── */
|
|
53
|
+
/** Copy this message's text. */
|
|
54
|
+
export function CopyIcon() {
|
|
55
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }), _jsx("path", { d: "M15 5.5A2.5 2.5 0 0 0 12.5 4h-6A2.5 2.5 0 0 0 4 6.5v6A2.5 2.5 0 0 0 5.5 15" })] }));
|
|
56
|
+
}
|
|
57
|
+
/** Copy a link back to this thread - the deep link `buildDeepLink` composes. */
|
|
58
|
+
export function LinkIcon() {
|
|
59
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("path", { d: "M10.5 13.5a3.5 3.5 0 0 0 5 0l3-3a3.5 3.5 0 0 0-5-5l-1.2 1.2" }), _jsx("path", { d: "M13.5 10.5a3.5 3.5 0 0 0-5 0l-3 3a3.5 3.5 0 0 0 5 5l1.2-1.2" })] }));
|
|
60
|
+
}
|
|
61
|
+
/** Resolve, and - flipped by its label rather than its shape - reopen. */
|
|
62
|
+
export function CheckIcon() {
|
|
63
|
+
return (_jsx("svg", { ...svgProps(), children: _jsx("path", { d: "M5 12.5l4.5 4.5L19 7" }) }));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Send a reply. The one ink-filled control in the card.
|
|
67
|
+
*
|
|
68
|
+
* The translate is an OPTICAL centring, and it is measured rather than nudged
|
|
69
|
+
* by eye. A paper plane is a triangle with a long thin tail, so its area
|
|
70
|
+
* centroid is nowhere near the middle of its bounding box: for this outline -
|
|
71
|
+
* (21,3) (14.4,21) (10.5,12.9) (2.4,9) - the centroid is (13.32, 10.32) while
|
|
72
|
+
* the viewBox centre is (12, 12). Flex centring aligns the BOX, so the glyph
|
|
73
|
+
* sat 1.32 right and 1.68 high inside a perfectly centred button and read as
|
|
74
|
+
* misaligned.
|
|
75
|
+
*
|
|
76
|
+
* Shifting by exactly that difference puts the mass on the button's centre.
|
|
77
|
+
* The shifted bounds are x 1.08-19.68 and y 4.68-22.68, so with the 1.5 stroke
|
|
78
|
+
* nothing crosses the 24 viewBox and the glyph is not clipped.
|
|
79
|
+
*/
|
|
80
|
+
export function SendIcon() {
|
|
81
|
+
return (_jsx("svg", { ...svgProps(), children: _jsxs("g", { transform: "translate(-1.32 1.68)", children: [_jsx("path", { d: "M21 3 10.5 13.5" }), _jsx("path", { d: "M21 3l-6.6 18-3.9-8.1L2.4 9 21 3Z" })] }) }));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Attach a screenshot to a reply.
|
|
85
|
+
*
|
|
86
|
+
* Disabled until a reply can carry one: `Screenshot` hangs off `FeedbackItem`
|
|
87
|
+
* and `replySchema` has no field for it, so the control is drawn and refused
|
|
88
|
+
* rather than drawn and broken.
|
|
89
|
+
*/
|
|
90
|
+
export function CameraIcon() {
|
|
91
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("path", { d: "M3 8.5a2 2 0 0 1 2-2h2l1.2-2h7.6L17 6.5h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" }), _jsx("circle", { cx: "12", cy: "13", r: "3.5" })] }));
|
|
92
|
+
}
|
|
93
|
+
/** Dismiss a thread that has no marker left to re-click. */
|
|
94
|
+
export function CloseIcon() {
|
|
95
|
+
return (_jsxs("svg", { ...svgProps(), children: [_jsx("path", { d: "M6.5 6.5l11 11" }), _jsx("path", { d: "M17.5 6.5l-11 11" })] }));
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { FeedbackToolbar, type FeedbackToolbarProps, type ToolbarInboxRenderProps, type ToolbarTool, } from "./FeedbackToolbar.js";
|
|
2
|
+
export { FeedbackPanel, type FeedbackPanelProps, AccountSurface, type AccountSurfaceProps, } from "./FeedbackPanel.js";
|
|
3
|
+
export { InboxSheet, type InboxSheetProps, type InboxScope, INBOX_OPEN_STORAGE_KEY, } from "./InboxSheet.js";
|
|
4
|
+
export { buildDeepLink, parseDeepLink, DEEP_LINK_PREFIX } from "./deepLink.js";
|
|
5
|
+
export { createToolbarClient, ToolbarApiError, type ToolbarClient, type ToolbarSession, } from "./client.js";
|
|
6
|
+
export { captureMetadata, captureScreenshot } from "./capture.js";
|
|
7
|
+
export { toggleMark, MARKDOWN_MARKS, MARKDOWN_ALLOWED_ELEMENTS, safeMarkdownUrl, type MarkdownMark, } from "./markdown.js";
|
|
8
|
+
export { PinLayer, type PinLayerProps } from "./PinLayer.js";
|
|
9
|
+
export { accent, darkTheme, DEFAULT_DOCK, exitFor, fonts, lightTheme, motion, motionTransform, radii, REDUCED_MOTION_QUERY, sizes, spacing, themeFor, TOOLBAR_DOCKS, transition, typeScale, type MotionDuration, type ResolvedTheme, type ToolbarDock, type ToolbarPalette, type ToolbarStatusPalette, type ToolbarStatusStyle, type ToolbarTheme, type ToolbarThemeMode, } from "./tokens.js";
|
|
10
|
+
export { buildSelector, resolveSelector, offsetWithin, locatePin, } from "./selector.js";
|
|
11
|
+
export { resolveConfig, API_URL_ENV_VAR, VERCEL_ENV_VARS, type ToolbarConfig, type ToolbarConfigOverrides, } from "./config.js";
|
|
12
|
+
export { DEFAULT_PIN_SHORTCUT, formatChord, isApplePlatform, type PinShortcutSetting, type ShortcutChord, } from "./shortcut.js";
|
|
13
|
+
export { isToolbarEnabled, isToolbarEnabledFromEnv, ENV_VAR, FORCE_ENV_VAR, type GateInput, } from "./gating.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|