@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.
Files changed (176) hide show
  1. package/README.md +262 -0
  2. package/dist/FeedbackPanel.d.ts +117 -0
  3. package/dist/FeedbackPanel.d.ts.map +1 -0
  4. package/dist/FeedbackPanel.js +622 -0
  5. package/dist/FeedbackPanel.js.map +1 -0
  6. package/dist/FeedbackToolbar.d.ts +54 -0
  7. package/dist/FeedbackToolbar.d.ts.map +1 -0
  8. package/dist/FeedbackToolbar.js +1149 -0
  9. package/dist/FeedbackToolbar.js.map +1 -0
  10. package/dist/InboxSheet.d.ts +80 -0
  11. package/dist/InboxSheet.d.ts.map +1 -0
  12. package/dist/InboxSheet.js +580 -0
  13. package/dist/InboxSheet.js.map +1 -0
  14. package/dist/PinLayer.d.ts +59 -0
  15. package/dist/PinLayer.d.ts.map +1 -0
  16. package/dist/PinLayer.js +1702 -0
  17. package/dist/PinLayer.js.map +1 -0
  18. package/dist/RichText.d.ts +6 -0
  19. package/dist/RichText.d.ts.map +1 -0
  20. package/dist/RichText.js +135 -0
  21. package/dist/RichText.js.map +1 -0
  22. package/dist/browser/entry.d.ts +3 -0
  23. package/dist/browser/entry.d.ts.map +1 -0
  24. package/dist/browser/entry.js +13 -0
  25. package/dist/browser/entry.js.map +1 -0
  26. package/dist/browser/mount.d.ts +37 -0
  27. package/dist/browser/mount.d.ts.map +1 -0
  28. package/dist/browser/mount.js +150 -0
  29. package/dist/browser/mount.js.map +1 -0
  30. package/dist/browser/toolbar.js +38 -0
  31. package/dist/browser/toolbar.js.map +7 -0
  32. package/dist/capture.d.ts +58 -0
  33. package/dist/capture.d.ts.map +1 -0
  34. package/dist/capture.js +98 -0
  35. package/dist/capture.js.map +1 -0
  36. package/dist/cli/bin.d.ts +3 -0
  37. package/dist/cli/bin.d.ts.map +1 -0
  38. package/dist/cli/bin.js +196 -0
  39. package/dist/cli/bin.js.map +1 -0
  40. package/dist/cli/fs.d.ts +16 -0
  41. package/dist/cli/fs.d.ts.map +1 -0
  42. package/dist/cli/fs.js +33 -0
  43. package/dist/cli/fs.js.map +1 -0
  44. package/dist/cli/init.d.ts +75 -0
  45. package/dist/cli/init.d.ts.map +1 -0
  46. package/dist/cli/init.js +266 -0
  47. package/dist/cli/init.js.map +1 -0
  48. package/dist/cli/register.d.ts +55 -0
  49. package/dist/cli/register.d.ts.map +1 -0
  50. package/dist/cli/register.js +84 -0
  51. package/dist/cli/register.js.map +1 -0
  52. package/dist/cli/registration.d.ts +37 -0
  53. package/dist/cli/registration.d.ts.map +1 -0
  54. package/dist/cli/registration.js +69 -0
  55. package/dist/cli/registration.js.map +1 -0
  56. package/dist/cli/strategies.d.ts +98 -0
  57. package/dist/cli/strategies.d.ts.map +1 -0
  58. package/dist/cli/strategies.js +198 -0
  59. package/dist/cli/strategies.js.map +1 -0
  60. package/dist/client.d.ts +59 -0
  61. package/dist/client.d.ts.map +1 -0
  62. package/dist/client.js +113 -0
  63. package/dist/client.js.map +1 -0
  64. package/dist/config.d.ts +92 -0
  65. package/dist/config.d.ts.map +1 -0
  66. package/dist/config.js +65 -0
  67. package/dist/config.js.map +1 -0
  68. package/dist/deepLink.d.ts +86 -0
  69. package/dist/deepLink.d.ts.map +1 -0
  70. package/dist/deepLink.js +185 -0
  71. package/dist/deepLink.js.map +1 -0
  72. package/dist/fontFace.d.ts +70 -0
  73. package/dist/fontFace.d.ts.map +1 -0
  74. package/dist/fontFace.js +88 -0
  75. package/dist/fontFace.js.map +1 -0
  76. package/dist/gating.d.ts +32 -0
  77. package/dist/gating.d.ts.map +1 -0
  78. package/dist/gating.js +49 -0
  79. package/dist/gating.js.map +1 -0
  80. package/dist/icons.d.ts +68 -0
  81. package/dist/icons.d.ts.map +1 -0
  82. package/dist/icons.js +97 -0
  83. package/dist/icons.js.map +1 -0
  84. package/dist/index.d.ts +14 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +68 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/markdown.d.ts +59 -0
  89. package/dist/markdown.d.ts.map +1 -0
  90. package/dist/markdown.js +130 -0
  91. package/dist/markdown.js.map +1 -0
  92. package/dist/motion.d.ts +178 -0
  93. package/dist/motion.d.ts.map +1 -0
  94. package/dist/motion.js +302 -0
  95. package/dist/motion.js.map +1 -0
  96. package/dist/preferences.d.ts +71 -0
  97. package/dist/preferences.d.ts.map +1 -0
  98. package/dist/preferences.js +195 -0
  99. package/dist/preferences.js.map +1 -0
  100. package/dist/rasterise.d.ts +45 -0
  101. package/dist/rasterise.d.ts.map +1 -0
  102. package/dist/rasterise.js +490 -0
  103. package/dist/rasterise.js.map +1 -0
  104. package/dist/selector.d.ts +19 -0
  105. package/dist/selector.d.ts.map +1 -0
  106. package/dist/selector.js +142 -0
  107. package/dist/selector.js.map +1 -0
  108. package/dist/session.d.ts +19 -0
  109. package/dist/session.d.ts.map +1 -0
  110. package/dist/session.js +36 -0
  111. package/dist/session.js.map +1 -0
  112. package/dist/shortcut.d.ts +88 -0
  113. package/dist/shortcut.d.ts.map +1 -0
  114. package/dist/shortcut.js +149 -0
  115. package/dist/shortcut.js.map +1 -0
  116. package/dist/tokens.d.ts +709 -0
  117. package/dist/tokens.d.ts.map +1 -0
  118. package/dist/tokens.js +749 -0
  119. package/dist/tokens.js.map +1 -0
  120. package/dist/vendor/types/account.d.ts +26 -0
  121. package/dist/vendor/types/account.d.ts.map +1 -0
  122. package/dist/vendor/types/account.js +23 -0
  123. package/dist/vendor/types/account.js.map +1 -0
  124. package/dist/vendor/types/apiKey.d.ts +38 -0
  125. package/dist/vendor/types/apiKey.d.ts.map +1 -0
  126. package/dist/vendor/types/apiKey.js +36 -0
  127. package/dist/vendor/types/apiKey.js.map +1 -0
  128. package/dist/vendor/types/auth.d.ts +10 -0
  129. package/dist/vendor/types/auth.d.ts.map +1 -0
  130. package/dist/vendor/types/auth.js +15 -0
  131. package/dist/vendor/types/auth.js.map +1 -0
  132. package/dist/vendor/types/entity.d.ts +74 -0
  133. package/dist/vendor/types/entity.d.ts.map +1 -0
  134. package/dist/vendor/types/entity.js +111 -0
  135. package/dist/vendor/types/entity.js.map +1 -0
  136. package/dist/vendor/types/feedback.d.ts +285 -0
  137. package/dist/vendor/types/feedback.d.ts.map +1 -0
  138. package/dist/vendor/types/feedback.js +286 -0
  139. package/dist/vendor/types/feedback.js.map +1 -0
  140. package/dist/vendor/types/index.d.ts +14 -0
  141. package/dist/vendor/types/index.d.ts.map +1 -0
  142. package/dist/vendor/types/index.js +16 -0
  143. package/dist/vendor/types/index.js.map +1 -0
  144. package/dist/vendor/types/invite.d.ts +42 -0
  145. package/dist/vendor/types/invite.d.ts.map +1 -0
  146. package/dist/vendor/types/invite.js +30 -0
  147. package/dist/vendor/types/invite.js.map +1 -0
  148. package/dist/vendor/types/mcpToken.d.ts +38 -0
  149. package/dist/vendor/types/mcpToken.d.ts.map +1 -0
  150. package/dist/vendor/types/mcpToken.js +39 -0
  151. package/dist/vendor/types/mcpToken.js.map +1 -0
  152. package/dist/vendor/types/organization.d.ts +13 -0
  153. package/dist/vendor/types/organization.d.ts.map +1 -0
  154. package/dist/vendor/types/organization.js +12 -0
  155. package/dist/vendor/types/organization.js.map +1 -0
  156. package/dist/vendor/types/project.d.ts +73 -0
  157. package/dist/vendor/types/project.d.ts.map +1 -0
  158. package/dist/vendor/types/project.js +117 -0
  159. package/dist/vendor/types/project.js.map +1 -0
  160. package/dist/vendor/types/role.d.ts +12 -0
  161. package/dist/vendor/types/role.d.ts.map +1 -0
  162. package/dist/vendor/types/role.js +20 -0
  163. package/dist/vendor/types/role.js.map +1 -0
  164. package/dist/vendor/types/roleDefinition.d.ts +61 -0
  165. package/dist/vendor/types/roleDefinition.d.ts.map +1 -0
  166. package/dist/vendor/types/roleDefinition.js +45 -0
  167. package/dist/vendor/types/roleDefinition.js.map +1 -0
  168. package/dist/vendor/types/team.d.ts +25 -0
  169. package/dist/vendor/types/team.d.ts.map +1 -0
  170. package/dist/vendor/types/team.js +17 -0
  171. package/dist/vendor/types/team.js.map +1 -0
  172. package/dist/vendor/types/util.d.ts +39 -0
  173. package/dist/vendor/types/util.d.ts.map +1 -0
  174. package/dist/vendor/types/util.js +31 -0
  175. package/dist/vendor/types/util.js.map +1 -0
  176. package/package.json +59 -0
@@ -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":"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"}
@@ -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"}
@@ -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"}
@@ -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"}
@@ -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":";AAuBA,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,MAAM,IAAI,GAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAEhF;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IAUtB,OAAO;QACL,OAAO,EAAE,WAAW;QACpB,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,cAAc;QACtB,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,OAAO;QACtB,cAAc,EAAE,OAAO;QACvB,KAAK,EAAE,IAAI;QACX,aAAa,EAAE,IAAI;KACpB,CAAC;AACJ,CAAC;AAED,6EAA6E;AAE7E,MAAM,UAAU,OAAO;IACrB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,mDAAmD,GAAG,EAC9D,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,KAAK,GAAG,IAC9B,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,CACL,iBAAS,QAAQ,EAAE,YACjB,eAAM,CAAC,EAAC,gEAAgE,GAAG,GACvE,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,yBAAyB,GAAG,EACpC,eAAM,CAAC,EAAC,yBAAyB,GAAG,IAChC,CACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,KAAK,EAAC,CAAC,EAAC,KAAK,GAAG,EACnC,eAAM,CAAC,EAAC,4BAA4B,GAAG,IACnC,CACP,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,IAAI,EAAoB;IACrD,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,OAAO,CACL,kBACM,KAAK,EACT,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,IAAI,MAAM,EAAE,aAE1D,eAAM,CAAC,EAAC,cAAc,GAAG,EACzB,eAAM,CAAC,EAAC,eAAe,GAAG,IACtB,CACP,CAAC;AACJ,CAAC;AAED,6EAA6E;AAE7E,gCAAgC;AAChC,MAAM,UAAU,QAAQ;IACtB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,GAAG,EAAC,CAAC,EAAC,GAAG,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,GAAG,EAClD,eAAM,CAAC,EAAC,4EAA4E,GAAG,IACnF,CACP,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,QAAQ;IACtB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,6DAA6D,GAAG,EACxE,eAAM,CAAC,EAAC,6DAA6D,GAAG,IACpE,CACP,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,iBAAS,QAAQ,EAAE,YACjB,eAAM,CAAC,EAAC,sBAAsB,GAAG,GAC7B,CACP,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,QAAQ;IACtB,OAAO,CACL,iBAAS,QAAQ,EAAE,YACjB,aAAG,SAAS,EAAC,uBAAuB,aAClC,eAAM,CAAC,EAAC,iBAAiB,GAAG,EAC5B,eAAM,CAAC,EAAC,mCAAmC,GAAG,IAC5C,GACA,CACP,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU;IACxB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,0FAA0F,GAAG,EACrG,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,KAAK,GAAG,IAC9B,CACP,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,SAAS;IACvB,OAAO,CACL,kBAAS,QAAQ,EAAE,aACjB,eAAM,CAAC,EAAC,gBAAgB,GAAG,EAC3B,eAAM,CAAC,EAAC,kBAAkB,GAAG,IACzB,CACP,CAAC;AACJ,CAAC"}
@@ -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