@devaccel-labs/app-comments-toolbar 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/README.md +152 -18
  2. package/dist/react.js +516 -61
  3. package/dist/server.js +342 -14
  4. package/dist/types/capture/api.d.ts +30 -0
  5. package/dist/types/capture/editor.d.ts +10 -0
  6. package/dist/types/capture/ignore.d.ts +19 -0
  7. package/dist/types/capture/index.d.ts +1 -0
  8. package/dist/types/capture/picker.d.ts +9 -0
  9. package/dist/types/capture/picture.d.ts +29 -0
  10. package/dist/types/capture/surface.d.ts +39 -0
  11. package/dist/types/core/index.d.ts +2 -2
  12. package/dist/types/core/resolve.d.ts +2 -1
  13. package/dist/types/core/types.d.ts +11 -0
  14. package/dist/types/debug-context.d.ts +7 -0
  15. package/dist/types/dictation.d.ts +9 -0
  16. package/dist/types/embed/controller.d.ts +61 -0
  17. package/dist/types/embed/index.d.ts +1 -0
  18. package/dist/types/embed/options.d.ts +35 -0
  19. package/dist/types/help/index.d.ts +1 -0
  20. package/dist/types/help/panel-index.d.ts +1 -0
  21. package/dist/types/help/panel-styles.d.ts +1 -0
  22. package/dist/types/help/panel.d.ts +4 -0
  23. package/dist/types/help/pill-styles.d.ts +11 -0
  24. package/dist/types/help/pill.d.ts +44 -0
  25. package/dist/types/help/react.d.ts +12 -0
  26. package/dist/types/help/requests.d.ts +2 -0
  27. package/dist/types/help/types.d.ts +119 -0
  28. package/dist/types/help/wire.d.ts +61 -0
  29. package/dist/types/native-wire.d.ts +63 -0
  30. package/dist/types/native.d.ts +43 -0
  31. package/dist/types/react.d.ts +8 -1
  32. package/dist/types/replay.d.ts +6 -0
  33. package/dist/types/runtime/api.d.ts +250 -0
  34. package/dist/types/runtime/attachment-model.d.ts +16 -0
  35. package/dist/types/runtime/attachments.d.ts +43 -7
  36. package/dist/types/runtime/boot.d.ts +39 -0
  37. package/dist/types/runtime/browser-label.d.ts +5 -0
  38. package/dist/types/runtime/build-id.d.ts +7 -0
  39. package/dist/types/runtime/capture-ui.d.ts +45 -21
  40. package/dist/types/runtime/clarity-model.d.ts +260 -0
  41. package/dist/types/runtime/clarity-ui.d.ts +62 -0
  42. package/dist/types/runtime/client.d.ts +6 -197
  43. package/dist/types/runtime/composer-size.d.ts +30 -0
  44. package/dist/types/runtime/dashboard.d.ts +17 -2
  45. package/dist/types/runtime/debug-context-core.d.ts +88 -0
  46. package/dist/types/runtime/debug-context-host.d.ts +39 -0
  47. package/dist/types/runtime/debug-context-model.d.ts +352 -0
  48. package/dist/types/runtime/debug-context-redact.d.ts +48 -0
  49. package/dist/types/runtime/debug-context.d.ts +36 -0
  50. package/dist/types/runtime/details-model.d.ts +7 -0
  51. package/dist/types/runtime/details.d.ts +7 -5
  52. package/dist/types/runtime/dictation-engine.d.ts +98 -0
  53. package/dist/types/runtime/dictation-model.d.ts +28 -0
  54. package/dist/types/runtime/dictation-ui.d.ts +56 -0
  55. package/dist/types/runtime/dictation.d.ts +66 -0
  56. package/dist/types/runtime/draft-dock.d.ts +32 -0
  57. package/dist/types/runtime/draft-model.d.ts +104 -0
  58. package/dist/types/runtime/draft-store.d.ts +15 -0
  59. package/dist/types/runtime/edits.d.ts +1 -1
  60. package/dist/types/runtime/font.d.ts +16 -0
  61. package/dist/types/runtime/format.d.ts +30 -1
  62. package/dist/types/runtime/host-watch.d.ts +20 -0
  63. package/dist/types/runtime/icons.d.ts +16 -2
  64. package/dist/types/runtime/index.d.ts +3 -1
  65. package/dist/types/runtime/ink.d.ts +8 -0
  66. package/dist/types/runtime/kind-model.d.ts +14 -0
  67. package/dist/types/runtime/live-model.d.ts +25 -0
  68. package/dist/types/runtime/loader.d.ts +5 -11
  69. package/dist/types/runtime/markup-editor.d.ts +54 -0
  70. package/dist/types/runtime/markup-model.d.ts +126 -0
  71. package/dist/types/runtime/markup.d.ts +69 -0
  72. package/dist/types/runtime/mention-parse.d.ts +18 -0
  73. package/dist/types/runtime/mentions.d.ts +4 -4
  74. package/dist/types/runtime/page-background.d.ts +9 -0
  75. package/dist/types/runtime/page-image.d.ts +123 -0
  76. package/dist/types/runtime/placement.d.ts +6 -3
  77. package/dist/types/runtime/record.d.ts +18 -2
  78. package/dist/types/runtime/related.d.ts +1 -1
  79. package/dist/types/runtime/replay-model.d.ts +80 -0
  80. package/dist/types/runtime/replay.d.ts +5 -38
  81. package/dist/types/runtime/runtime.d.ts +6 -0
  82. package/dist/types/runtime/screen.d.ts +47 -0
  83. package/dist/types/runtime/screenshot.d.ts +60 -6
  84. package/dist/types/runtime/session-source.d.ts +45 -0
  85. package/dist/types/runtime/session.d.ts +20 -18
  86. package/dist/types/runtime/shield.d.ts +9 -0
  87. package/dist/types/runtime/shot.d.ts +45 -0
  88. package/dist/types/runtime/sketch.d.ts +26 -0
  89. package/dist/types/runtime/styles.d.ts +1 -1
  90. package/dist/types/runtime/sync.d.ts +1 -0
  91. package/dist/types/runtime/thread-model.d.ts +18 -0
  92. package/dist/types/runtime/ticket-form-model.d.ts +57 -0
  93. package/dist/types/runtime/ticket-form.d.ts +9 -0
  94. package/dist/types/runtime/toolbar.d.ts +37 -1
  95. package/dist/types/runtime/ui-scale.d.ts +17 -0
  96. package/dist/types/runtime/update.d.ts +37 -0
  97. package/dist/types/runtime/use-debug-context.d.ts +27 -0
  98. package/dist/types/server/index.d.ts +22 -0
  99. package/dist/types/server/support.d.ts +20 -0
  100. package/dist/types/server.d.ts +2 -0
  101. package/dist/types/vite/plugin.d.ts +15 -0
  102. package/dist/vite.js +121 -41
  103. package/package.json +26 -13
  104. package/src/capture/api.ts +128 -0
  105. package/src/capture/editor.tsx +178 -0
  106. package/src/capture/ignore.ts +38 -0
  107. package/src/capture/index.ts +27 -0
  108. package/src/capture/picker.tsx +171 -0
  109. package/src/capture/picture.ts +90 -0
  110. package/src/capture/surface.tsx +185 -0
  111. package/src/compiler/annotate.ts +202 -0
  112. package/src/compiler/babel.d.ts +11 -0
  113. package/src/compiler/enabled.ts +55 -0
  114. package/src/compiler/index.ts +2 -0
  115. package/src/core/index.ts +8 -0
  116. package/src/core/resolve.ts +307 -0
  117. package/src/core/types.ts +58 -0
  118. package/src/debug-context.ts +47 -0
  119. package/src/dictation.ts +17 -0
  120. package/src/embed/controller.ts +215 -0
  121. package/src/embed/index.ts +68 -0
  122. package/src/embed/options.ts +100 -0
  123. package/src/help/index.ts +38 -0
  124. package/src/help/panel-index.ts +17 -0
  125. package/src/help/panel-styles.ts +123 -0
  126. package/src/help/panel.ts +840 -0
  127. package/src/help/pill-styles.ts +75 -0
  128. package/src/help/pill.ts +338 -0
  129. package/src/help/react.tsx +143 -0
  130. package/src/help/requests.ts +111 -0
  131. package/src/help/types.ts +124 -0
  132. package/src/help/wire.ts +55 -0
  133. package/src/native-wire.ts +124 -0
  134. package/src/native.ts +282 -0
  135. package/src/react.ts +27 -0
  136. package/src/replay.ts +13 -0
  137. package/src/runtime/api.ts +531 -0
  138. package/src/runtime/attachment-model.ts +42 -0
  139. package/src/runtime/attachments.tsx +1260 -0
  140. package/src/runtime/boot.ts +95 -0
  141. package/src/runtime/browser-label.ts +31 -0
  142. package/src/runtime/build-id.ts +10 -0
  143. package/src/runtime/capture-ui.tsx +306 -0
  144. package/src/runtime/capture.ts +508 -0
  145. package/src/runtime/clarity-model.ts +634 -0
  146. package/src/runtime/clarity-ui.tsx +454 -0
  147. package/src/runtime/client.ts +151 -0
  148. package/src/runtime/composer-size.tsx +146 -0
  149. package/src/runtime/dashboard.tsx +1130 -0
  150. package/src/runtime/debug-context-core.ts +681 -0
  151. package/src/runtime/debug-context-host.ts +56 -0
  152. package/src/runtime/debug-context-model.ts +1319 -0
  153. package/src/runtime/debug-context-redact.ts +259 -0
  154. package/src/runtime/debug-context.ts +613 -0
  155. package/src/runtime/details-model.ts +30 -0
  156. package/src/runtime/details.tsx +395 -0
  157. package/src/runtime/dictation-engine.ts +616 -0
  158. package/src/runtime/dictation-model.ts +79 -0
  159. package/src/runtime/dictation-ui.tsx +363 -0
  160. package/src/runtime/dictation.ts +271 -0
  161. package/src/runtime/dock.ts +108 -0
  162. package/src/runtime/draft-dock.tsx +124 -0
  163. package/src/runtime/draft-model.ts +251 -0
  164. package/src/runtime/draft-store.ts +149 -0
  165. package/src/runtime/edits.tsx +165 -0
  166. package/src/runtime/font.ts +42 -0
  167. package/src/runtime/format.ts +129 -0
  168. package/src/runtime/freshness.ts +23 -0
  169. package/src/runtime/handoff.ts +76 -0
  170. package/src/runtime/host-watch.ts +64 -0
  171. package/src/runtime/icons.tsx +295 -0
  172. package/src/runtime/idle.ts +20 -0
  173. package/src/runtime/index.ts +11 -0
  174. package/src/runtime/ink.ts +6 -0
  175. package/src/runtime/kind-model.ts +20 -0
  176. package/src/runtime/live-model.ts +32 -0
  177. package/src/runtime/loader.tsx +87 -0
  178. package/src/runtime/markup-editor.tsx +1333 -0
  179. package/src/runtime/markup-model.ts +485 -0
  180. package/src/runtime/markup.ts +382 -0
  181. package/src/runtime/members.ts +58 -0
  182. package/src/runtime/mention-parse.ts +56 -0
  183. package/src/runtime/mentions.tsx +205 -0
  184. package/src/runtime/page-background.ts +51 -0
  185. package/src/runtime/page-image.ts +1592 -0
  186. package/src/runtime/placement.ts +336 -0
  187. package/src/runtime/record.ts +402 -0
  188. package/src/runtime/redact.ts +30 -0
  189. package/src/runtime/related.ts +34 -0
  190. package/src/runtime/render-page.ts +2 -0
  191. package/src/runtime/replay-model.ts +232 -0
  192. package/src/runtime/replay.ts +173 -0
  193. package/src/runtime/runtime-version.ts +7 -0
  194. package/src/runtime/runtime.tsx +51 -0
  195. package/src/runtime/screen.ts +182 -0
  196. package/src/runtime/screenshot.ts +260 -0
  197. package/src/runtime/session-source.ts +112 -0
  198. package/src/runtime/session.ts +108 -0
  199. package/src/runtime/shield.ts +34 -0
  200. package/src/runtime/shot.tsx +422 -0
  201. package/src/runtime/sketch.ts +139 -0
  202. package/src/runtime/styles.ts +1541 -0
  203. package/src/runtime/sync.ts +300 -0
  204. package/src/runtime/thread-model.ts +71 -0
  205. package/src/runtime/ticket-form-model.ts +128 -0
  206. package/src/runtime/ticket-form.tsx +342 -0
  207. package/src/runtime/toolbar.tsx +4427 -0
  208. package/src/runtime/ui-scale.ts +75 -0
  209. package/src/runtime/update.ts +98 -0
  210. package/src/runtime/use-debug-context.ts +81 -0
  211. package/src/server/index.ts +133 -0
  212. package/src/server/support.ts +499 -0
  213. package/src/server.ts +4 -0
  214. package/src/vite/plugin.ts +102 -0
  215. package/src/vite.ts +1 -0
  216. package/dist/chunks/chunk-CSKXLF33.js +0 -153
  217. package/dist/chunks/chunk-HUM5FQII.js +0 -160
  218. package/dist/chunks/render-page-76MLIT6M.js +0 -1421
  219. package/dist/chunks/runtime-JX75TKER.js +0 -9267
  220. package/dist/chunks/screenshot-66ARTBGR.js +0 -12
package/dist/react.js CHANGED
@@ -1,91 +1,546 @@
1
- import {
2
- captureHandoff,
3
- runtimeVersion,
4
- sessionFor,
5
- whenIdle
6
- } from "./chunks/chunk-CSKXLF33.js";
7
-
8
1
  // src/runtime/loader.tsx
9
2
  import { useEffect, useRef } from "react";
3
+
4
+ // src/runtime/idle.ts
5
+ var FALLBACK_SLICE_MS = 8;
6
+ function whenIdle(task, timeout) {
7
+ if (typeof requestIdleCallback === "function") {
8
+ const id2 = requestIdleCallback(task, { timeout });
9
+ return () => cancelIdleCallback(id2);
10
+ }
11
+ const id = setTimeout(
12
+ () => {
13
+ const end = performance.now() + FALLBACK_SLICE_MS;
14
+ task({ didTimeout: false, timeRemaining: () => Math.max(0, end - performance.now()) });
15
+ },
16
+ Math.min(timeout, 200)
17
+ );
18
+ return () => clearTimeout(id);
19
+ }
20
+
21
+ // src/runtime/runtime-version.ts
22
+ var runtimeVersion = 1;
23
+
24
+ // src/runtime/boot.ts
10
25
  var IDLE_TIMEOUT_MS = 3e3;
11
- captureHandoff();
12
- var bundled = () => import("./chunks/runtime-JX75TKER.js");
13
- async function loadRuntime(apiUrl, source) {
14
- if (source === "bundled") return bundled();
26
+ var untilIdle = (task) => whenIdle(task, IDLE_TIMEOUT_MS);
27
+ var importModule = (url) => import(
28
+ /* @vite-ignore */
29
+ /* webpackIgnore: true */
30
+ url
31
+ );
32
+ async function loadRuntime(apiUrl, load = importModule) {
15
33
  const url = `${apiUrl.replace(/\/+$/, "")}/v1/toolbar/${runtimeVersion}.js`;
34
+ const remote = await load(url);
35
+ if (typeof remote.mount === "function") return remote;
36
+ throw new Error(`${url} has no mount export`);
37
+ }
38
+ function bootToolbar(boot) {
39
+ let cancelled = false;
40
+ let mounted = null;
41
+ let container = null;
42
+ const start = async () => {
43
+ const session = await boot.getSession().catch(() => null);
44
+ if (!session || cancelled) return;
45
+ const runtime = await (boot.load ?? loadRuntime)(session.apiUrl);
46
+ if (cancelled) return;
47
+ container = document.createElement("div");
48
+ container.setAttribute("data-cs-runtime", "latest");
49
+ container.style.display = "contents";
50
+ document.body.appendChild(container);
51
+ mounted = runtime.mount(container, boot.props());
52
+ boot.onMount?.(mounted, container);
53
+ };
54
+ const cancelWait = (boot.wait ?? untilIdle)(() => {
55
+ start().catch((error) => console.warn("[comment-service] The toolbar could not start.", error)).finally(() => boot.onSettled?.());
56
+ });
57
+ return () => {
58
+ cancelled = true;
59
+ cancelWait();
60
+ queueMicrotask(() => {
61
+ mounted?.unmount();
62
+ container?.remove();
63
+ });
64
+ };
65
+ }
66
+
67
+ // src/runtime/handoff.ts
68
+ var key = "devaccel.comment.handoff";
69
+ var ttl = 30 * 60 * 1e3;
70
+ function readHandoff() {
71
+ try {
72
+ const value = JSON.parse(sessionStorage.getItem(key) ?? "null");
73
+ return value && Date.now() - value.at < ttl ? value : null;
74
+ } catch {
75
+ return null;
76
+ }
77
+ }
78
+ function write(value) {
16
79
  try {
17
- const remote = await import(
18
- /* @vite-ignore */
19
- /* webpackIgnore: true */
20
- url
21
- );
22
- if (typeof remote.mount === "function") return remote;
23
- throw new Error(`${url} has no mount export`);
24
- } catch (error) {
25
- console.warn(
26
- "[comment-service] Could not load the latest toolbar, using the copy bundled with the package.",
27
- error
28
- );
29
- return bundled();
80
+ if (value.invite || value.thread) sessionStorage.setItem(key, JSON.stringify(value));
81
+ else sessionStorage.removeItem(key);
82
+ } catch {
83
+ }
84
+ }
85
+ function linkParams() {
86
+ const url = new URL(window.location.href);
87
+ const hash = new URLSearchParams(url.hash.slice(1));
88
+ return {
89
+ url,
90
+ hash,
91
+ invite: url.searchParams.get("cs-invite") ?? hash.get("cs-invite"),
92
+ thread: url.searchParams.get("cs-thread") ?? hash.get("cs-thread")
93
+ };
94
+ }
95
+ function captureHandoff() {
96
+ if (typeof window === "undefined") return;
97
+ const { invite, thread } = linkParams();
98
+ if (!invite && !thread) return;
99
+ write({
100
+ ...readHandoff(),
101
+ ...invite ? { invite } : {},
102
+ ...thread ? { thread } : {},
103
+ at: Date.now()
104
+ });
105
+ }
106
+
107
+ // src/runtime/host-watch.ts
108
+ var HOST_CHECK_GAP_MS = 1e4;
109
+ var NAVIGATED = "cs-nav";
110
+ var HISTORY_FLAG = "__devaccelCommentHistory";
111
+ function announceNavigation() {
112
+ if (Reflect.get(window, HISTORY_FLAG)) return;
113
+ Reflect.set(window, HISTORY_FLAG, true);
114
+ for (const name of ["pushState", "replaceState"]) {
115
+ const original = history[name].bind(history);
116
+ history[name] = (...args) => {
117
+ original(...args);
118
+ window.dispatchEvent(new Event(NAVIGATED));
119
+ };
120
+ }
121
+ }
122
+ function watchHost(check, gap = HOST_CHECK_GAP_MS) {
123
+ announceNavigation();
124
+ const listening = new AbortController();
125
+ let last = Date.now();
126
+ let timer = 0;
127
+ const run = () => {
128
+ window.clearTimeout(timer);
129
+ timer = 0;
130
+ last = Date.now();
131
+ check();
132
+ };
133
+ const soon = (event) => {
134
+ if (timer || document.visibilityState === "hidden") return;
135
+ if (event.type === "pageshow" && !event.persisted) return;
136
+ const wait = last + gap - Date.now();
137
+ if (wait <= 0) run();
138
+ else timer = window.setTimeout(run, wait);
139
+ };
140
+ for (const type of ["focus", "popstate", "hashchange", "pageshow", NAVIGATED])
141
+ window.addEventListener(type, soon, { signal: listening.signal });
142
+ document.addEventListener("visibilitychange", soon, { signal: listening.signal });
143
+ return {
144
+ now: run,
145
+ stop() {
146
+ window.clearTimeout(timer);
147
+ listening.abort();
148
+ }
149
+ };
150
+ }
151
+
152
+ // src/runtime/session-source.ts
153
+ var EXPIRY_MARGIN_MS = 6e4;
154
+ var UNKNOWN_EXPIRY_MS = 5 * 6e4;
155
+ function readJwt(token) {
156
+ try {
157
+ const part = token.split(".")[1];
158
+ if (!part) return null;
159
+ const pad = part.length % 4 === 0 ? "" : "=".repeat(4 - part.length % 4);
160
+ return JSON.parse(atob(part.replaceAll("-", "+").replaceAll("_", "/") + pad));
161
+ } catch {
162
+ return null;
163
+ }
164
+ }
165
+ function sessionFrom(payload) {
166
+ const value = payload;
167
+ if (!value?.enabled || !value.apiUrl || !value.project || !value.environment || !value.hostToken)
168
+ return null;
169
+ return {
170
+ apiUrl: value.apiUrl,
171
+ project: value.project,
172
+ environment: value.environment,
173
+ hostToken: value.hostToken,
174
+ ...typeof value.loaderOrigin === "string" ? { loaderOrigin: value.loaderOrigin } : {}
175
+ };
176
+ }
177
+ var SessionOff = class extends Error {
178
+ };
179
+ function sessionSource(path, load, now2 = Date.now) {
180
+ let kept = null;
181
+ let pending = null;
182
+ return ({ refresh = false } = {}) => {
183
+ if (!refresh && kept && now2() < kept.until) return Promise.resolve(kept.session);
184
+ if (pending) return pending;
185
+ pending = load(path).then((payload) => {
186
+ const failed = payload;
187
+ if (failed && failed.enabled === false && failed.error) {
188
+ throw new SessionOff(
189
+ typeof failed.error === "string" ? failed.error : "Comment service is unreachable."
190
+ );
191
+ }
192
+ const session = sessionFrom(payload);
193
+ const expires = session ? readJwt(session.hostToken)?.exp : void 0;
194
+ const until = !session ? Infinity : expires ? expires * 1e3 - EXPIRY_MARGIN_MS : now2() + UNKNOWN_EXPIRY_MS;
195
+ kept = { session, until };
196
+ return session;
197
+ }).finally(() => {
198
+ pending = null;
199
+ });
200
+ return pending;
201
+ };
202
+ }
203
+
204
+ // src/runtime/session.ts
205
+ var SIGN_IN_PREFIX = "devaccel.comment.http";
206
+ function forgetSignIns(storage = localStorage) {
207
+ try {
208
+ for (let index = storage.length - 1; index >= 0; index--) {
209
+ const key2 = storage.key(index);
210
+ if (key2?.startsWith(SIGN_IN_PREFIX)) storage.removeItem(key2);
211
+ }
212
+ } catch {
213
+ }
214
+ }
215
+ var reported = /* @__PURE__ */ new Set();
216
+ function report(path, status, error, failed) {
217
+ const said = typeof error === "string" ? `: ${error}` : failed ? "." : " with comments off.";
218
+ const line = `[comment-service] ${path} answered ${status}${said}`;
219
+ if (reported.has(line)) return;
220
+ reported.add(line);
221
+ if (failed) console.warn(line);
222
+ else console.info(line);
223
+ }
224
+ async function fetchSession(path) {
225
+ const response = await fetch(path, {
226
+ headers: { accept: "application/json", "x-comment-service-origin": window.location.origin },
227
+ cache: "no-store"
228
+ });
229
+ const payload = await response.json().catch(() => null);
230
+ const off = response.status === 401 || response.status === 403;
231
+ if (!response.ok && !off) {
232
+ report(path, response.status, payload?.error, true);
233
+ throw new Error(`The session route answered ${response.status}.`);
234
+ }
235
+ if (off || payload?.enabled !== true) {
236
+ report(path, response.status, payload?.error, false);
237
+ forgetSignIns();
238
+ return { enabled: false };
30
239
  }
240
+ return payload;
31
241
  }
242
+ function sessionSource2(path, load = fetchSession, now2 = Date.now) {
243
+ return sessionSource(path, load, now2);
244
+ }
245
+ var sources = /* @__PURE__ */ new Map();
246
+ function sessionFor(path) {
247
+ let source = sources.get(path);
248
+ if (!source) {
249
+ source = sessionSource2(path);
250
+ sources.set(path, source);
251
+ }
252
+ return source;
253
+ }
254
+
255
+ // src/runtime/loader.tsx
256
+ var stampedBuild = typeof __COMMENT_TOOL_HOST_BUILD__ === "object" ? __COMMENT_TOOL_HOST_BUILD__ : void 0;
257
+ function withStampedBuild(config) {
258
+ if (!config || !stampedBuild) return config;
259
+ const options = config === true ? {} : config;
260
+ return options.build ? config : { ...options, build: stampedBuild };
261
+ }
262
+ captureHandoff();
263
+ var now = (task) => {
264
+ task();
265
+ return () => {
266
+ };
267
+ };
32
268
  function CommentToolbar({
33
- source = "latest",
34
269
  sessionPath = "/api/comment-service/session",
35
270
  pollMs,
36
271
  position,
37
272
  theme,
38
- navigate
273
+ navigate,
274
+ screen,
275
+ debugContext
39
276
  }) {
40
277
  const handle = useRef(null);
41
278
  const navigateRef = useRef(navigate);
42
- navigateRef.current = navigate;
43
- const hasNavigate = Boolean(navigate);
44
- const props = useRef({});
45
- props.current = {
46
- sessionPath,
47
- pollMs,
48
- position,
49
- theme,
50
- navigate: hasNavigate ? (href) => navigateRef.current?.(href) : void 0,
51
- getSession: sessionFor(sessionPath)
52
- };
279
+ const propsRef = useRef({});
53
280
  useEffect(() => {
281
+ navigateRef.current = navigate;
282
+ propsRef.current = {
283
+ sessionPath,
284
+ pollMs,
285
+ position,
286
+ theme,
287
+ navigate: navigate ? (href) => navigateRef.current?.(href) : void 0,
288
+ screen,
289
+ debugContext: withStampedBuild(debugContext),
290
+ getSession: sessionFor(sessionPath)
291
+ };
292
+ handle.current?.update(propsRef.current);
293
+ }, [debugContext, navigate, pollMs, position, screen, sessionPath, theme]);
294
+ useEffect(() => {
295
+ const boot = (refresh, wait) => bootToolbar({
296
+ getSession: () => sessionFor(sessionPath)({ refresh }),
297
+ props: () => propsRef.current,
298
+ onMount: (mounted) => {
299
+ handle.current = mounted;
300
+ },
301
+ ...wait ? { wait } : {}
302
+ });
303
+ let stop = boot(false);
304
+ const watch = watchHost(() => {
305
+ if (handle.current) return;
306
+ stop();
307
+ stop = boot(true, now);
308
+ });
309
+ return () => {
310
+ watch.stop();
311
+ handle.current = null;
312
+ stop();
313
+ };
314
+ }, [sessionPath]);
315
+ return null;
316
+ }
317
+
318
+ // src/runtime/debug-context-host.ts
319
+ var DEBUG_ERROR_EVENT = "comment-tool:error";
320
+ function reportToolbarError(error, componentStack) {
321
+ if (typeof window === "undefined" || typeof CustomEvent !== "function") return;
322
+ window.dispatchEvent(
323
+ new CustomEvent(DEBUG_ERROR_EVENT, {
324
+ detail: { error, componentStack: componentStack ?? null }
325
+ })
326
+ );
327
+ }
328
+ function queryCacheSummary(client, max = 50) {
329
+ return client.getQueryCache().getAll().slice(-max).map((query) => ({
330
+ key: query.queryKey,
331
+ status: query.state.status,
332
+ fetchStatus: query.state.fetchStatus,
333
+ error: query.state.error ?? void 0,
334
+ updatedAt: query.state.dataUpdatedAt,
335
+ observers: query.getObserversCount?.()
336
+ }));
337
+ }
338
+
339
+ // src/help/react.tsx
340
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
341
+ var loading = /* @__PURE__ */ new Map();
342
+ function loadHelp(serviceUrl) {
343
+ const page = window;
344
+ const there = () => typeof page.CommentToolHelp?.mount === "function" ? page.CommentToolHelp : null;
345
+ const ready = there();
346
+ if (ready) return Promise.resolve(ready);
347
+ const url = `${serviceUrl.replace(/\/+$/, "")}/v1/toolbar/help.js`;
348
+ let kept = loading.get(url);
349
+ if (!kept) {
350
+ kept = new Promise((resolve, reject) => {
351
+ const tag = document.createElement("script");
352
+ tag.src = url;
353
+ tag.async = true;
354
+ tag.onload = () => {
355
+ const api = there();
356
+ if (api) resolve(api);
357
+ else reject(new Error(`${url} defined no CommentToolHelp.`));
358
+ };
359
+ tag.onerror = () => {
360
+ tag.remove();
361
+ reject(new Error(`${url} did not load.`));
362
+ };
363
+ document.head.appendChild(tag);
364
+ });
365
+ kept.catch(() => loading.delete(url));
366
+ loading.set(url, kept);
367
+ }
368
+ return kept;
369
+ }
370
+ function HelpPill(props) {
371
+ const {
372
+ serviceUrl,
373
+ sessionPath = "/api/comment-service/session",
374
+ label,
375
+ position,
376
+ theme
377
+ } = props;
378
+ const hasList = typeof props.list === "function";
379
+ const hasMore = typeof props.moreOptions === "function";
380
+ const latest = useRef2(props);
381
+ const mounted = useRef2(null);
382
+ const options = useRef2(() => {
383
+ const now2 = latest.current;
384
+ return {
385
+ submit: (request) => latest.current.submit(request),
386
+ ...typeof now2.list === "function" ? {
387
+ list: () => {
388
+ const list = latest.current.list;
389
+ return list ? list() : Promise.resolve([]);
390
+ }
391
+ } : {},
392
+ label: now2.label,
393
+ position: now2.position,
394
+ theme: now2.theme,
395
+ captures: () => {
396
+ const captures = latest.current.captures;
397
+ return typeof captures === "function" ? captures() : captures !== false;
398
+ },
399
+ ...typeof now2.moreOptions === "function" ? { moreOptions: (draft) => latest.current.moreOptions?.(draft) } : {}
400
+ };
401
+ });
402
+ useEffect2(() => {
403
+ latest.current = props;
404
+ });
405
+ useEffect2(() => {
54
406
  let cancelled = false;
55
- let mounted = null;
56
- let container = null;
57
407
  const start = async () => {
58
- const session = await sessionFor(sessionPath)().catch(() => null);
59
- if (!session || cancelled) return;
60
- const runtime = await loadRuntime(session.apiUrl, source);
408
+ const address = serviceUrl ?? (await sessionFor(sessionPath)().catch(() => null))?.apiUrl ?? null;
409
+ if (cancelled) return;
410
+ if (!address) {
411
+ console.info(
412
+ `[comment-service] <HelpPill> is not shown: ${sessionPath} names no service for this person. Pass serviceUrl to show it to everyone.`
413
+ );
414
+ return;
415
+ }
416
+ const api = await loadHelp(address);
61
417
  if (cancelled) return;
62
- container = document.createElement("div");
63
- container.setAttribute("data-cs-runtime", source);
64
- container.style.display = "contents";
65
- document.body.append(container);
66
- mounted = runtime.mount(container, props.current);
67
- handle.current = mounted;
418
+ mounted.current = { api, handle: api.mount(options.current()) };
68
419
  };
69
- const cancelIdle = whenIdle(() => {
420
+ const cancelWait = whenIdle(() => {
70
421
  start().catch(
71
- (error) => console.warn("[comment-service] The toolbar could not start.", error)
422
+ (error) => console.warn("[comment-service] The help pill could not start.", error)
72
423
  );
73
424
  }, IDLE_TIMEOUT_MS);
74
425
  return () => {
75
426
  cancelled = true;
76
- cancelIdle();
77
- handle.current = null;
78
- queueMicrotask(() => {
79
- mounted?.unmount();
80
- container?.remove();
81
- });
427
+ cancelWait();
428
+ mounted.current?.handle.unmount();
429
+ mounted.current = null;
82
430
  };
83
- }, [sessionPath, source]);
84
- useEffect(() => {
85
- handle.current?.update(props.current);
86
- }, [pollMs, position, theme, hasNavigate]);
431
+ }, [serviceUrl, sessionPath]);
432
+ useEffect2(() => {
433
+ const now2 = mounted.current;
434
+ if (now2) now2.handle = now2.api.mount(options.current());
435
+ }, [label, position, theme, hasList, hasMore]);
87
436
  return null;
88
437
  }
438
+
439
+ // src/help/wire.ts
440
+ var HELP_LIMITS = {
441
+ /** A request's own id: letters, digits, `.`, `_`, `:` and `-`. */
442
+ id: 100,
443
+ message: 4e3,
444
+ images: 3,
445
+ /** One picture, before base64. */
446
+ imageBytes: 5 * 1024 * 1024,
447
+ name: 200,
448
+ label: 200,
449
+ url: 1e3,
450
+ /** "Your requests" shows at most this many. */
451
+ listed: 50
452
+ };
453
+ var HELP_BODY_BYTES = Math.ceil(HELP_LIMITS.images * HELP_LIMITS.imageBytes * 4 / 3) + 256 * 1024;
454
+
455
+ // src/help/types.ts
456
+ var HelpError = class extends Error {
457
+ name = "HelpError";
458
+ };
459
+
460
+ // src/help/requests.ts
461
+ var NOT_SENT = "That did not send. Check your connection and try again.";
462
+ var NOT_READ = "Your requests could not be loaded.";
463
+ async function base64Of(blob) {
464
+ const bytes = new Uint8Array(await blob.arrayBuffer());
465
+ let binary = "";
466
+ const chunk = 32768;
467
+ for (let index = 0; index < bytes.length; index += chunk) {
468
+ binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
469
+ }
470
+ return btoa(binary);
471
+ }
472
+ async function imageWire(image) {
473
+ if (image.blob.size > HELP_LIMITS.imageBytes) {
474
+ throw new HelpError("A picture is too large to send. Remove it and try again.");
475
+ }
476
+ return {
477
+ name: image.name,
478
+ contentType: image.blob.type,
479
+ width: image.width,
480
+ height: image.height,
481
+ markedUp: image.markedUp,
482
+ data: await base64Of(image.blob)
483
+ };
484
+ }
485
+ async function sentenceOf(response) {
486
+ const payload = await response.json().catch(() => null);
487
+ return typeof payload?.error === "string" && payload.error.trim() ? payload.error : null;
488
+ }
489
+ function supportRequestsAt(url) {
490
+ return {
491
+ async submit(request) {
492
+ const body = {
493
+ id: request.id,
494
+ message: request.message,
495
+ pageUrl: request.pageUrl,
496
+ route: request.route,
497
+ pointed: request.pointed,
498
+ images: await Promise.all(request.images.map(imageWire)),
499
+ client: request.client
500
+ };
501
+ let response;
502
+ try {
503
+ response = await fetch(url, {
504
+ method: "POST",
505
+ headers: { accept: "application/json", "content-type": "application/json" },
506
+ credentials: "same-origin",
507
+ cache: "no-store",
508
+ body: JSON.stringify(body)
509
+ });
510
+ } catch {
511
+ throw new HelpError(NOT_SENT);
512
+ }
513
+ if (!response.ok) throw new HelpError(await sentenceOf(response) ?? NOT_SENT);
514
+ const receipt = await response.json().catch(() => null);
515
+ return {
516
+ ...typeof receipt?.reference === "string" ? { reference: receipt.reference } : {},
517
+ ...typeof receipt?.url === "string" ? { url: receipt.url } : {},
518
+ ...typeof receipt?.note === "string" ? { note: receipt.note } : {}
519
+ };
520
+ },
521
+ async list() {
522
+ let response;
523
+ try {
524
+ response = await fetch(url, {
525
+ headers: { accept: "application/json" },
526
+ credentials: "same-origin",
527
+ cache: "no-store"
528
+ });
529
+ } catch {
530
+ throw new HelpError(NOT_READ);
531
+ }
532
+ if (!response.ok) throw new HelpError(await sentenceOf(response) ?? NOT_READ);
533
+ const payload = await response.json().catch(() => null);
534
+ if (!Array.isArray(payload?.requests)) throw new HelpError(NOT_READ);
535
+ return payload.requests;
536
+ }
537
+ };
538
+ }
89
539
  export {
90
- CommentToolbar
540
+ CommentToolbar,
541
+ HelpError,
542
+ HelpPill,
543
+ queryCacheSummary,
544
+ reportToolbarError,
545
+ supportRequestsAt
91
546
  };