@devaccel-labs/app-comments-toolbar 1.1.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 (214) hide show
  1. package/README.md +148 -30
  2. package/dist/react.js +417 -64
  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 +7 -0
  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 +1 -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 +2 -0
  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 -3
  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 +64 -11
  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/screen.d.ts +47 -0
  82. package/dist/types/runtime/screenshot.d.ts +60 -8
  83. package/dist/types/runtime/session-source.d.ts +45 -0
  84. package/dist/types/runtime/session.d.ts +20 -18
  85. package/dist/types/runtime/shield.d.ts +9 -0
  86. package/dist/types/runtime/shot.d.ts +45 -0
  87. package/dist/types/runtime/sketch.d.ts +26 -0
  88. package/dist/types/runtime/styles.d.ts +1 -1
  89. package/dist/types/runtime/sync.d.ts +1 -0
  90. package/dist/types/runtime/thread-model.d.ts +18 -0
  91. package/dist/types/runtime/ticket-form-model.d.ts +57 -0
  92. package/dist/types/runtime/ticket-form.d.ts +9 -0
  93. package/dist/types/runtime/toolbar.d.ts +37 -1
  94. package/dist/types/runtime/ui-scale.d.ts +17 -0
  95. package/dist/types/runtime/update.d.ts +32 -8
  96. package/dist/types/runtime/use-debug-context.d.ts +27 -0
  97. package/dist/types/server/index.d.ts +22 -0
  98. package/dist/types/server/support.d.ts +20 -0
  99. package/dist/types/server.d.ts +2 -0
  100. package/dist/types/vite/plugin.d.ts +15 -0
  101. package/dist/vite.js +121 -41
  102. package/package.json +24 -11
  103. package/src/capture/api.ts +128 -0
  104. package/src/capture/editor.tsx +178 -0
  105. package/src/capture/ignore.ts +38 -0
  106. package/src/capture/index.ts +27 -0
  107. package/src/capture/picker.tsx +171 -0
  108. package/src/capture/picture.ts +90 -0
  109. package/src/capture/surface.tsx +185 -0
  110. package/src/compiler/annotate.ts +202 -0
  111. package/src/compiler/babel.d.ts +11 -0
  112. package/src/compiler/enabled.ts +55 -0
  113. package/src/compiler/index.ts +2 -0
  114. package/src/core/index.ts +8 -0
  115. package/src/core/resolve.ts +307 -0
  116. package/src/core/types.ts +58 -0
  117. package/src/debug-context.ts +47 -0
  118. package/src/dictation.ts +17 -0
  119. package/src/embed/controller.ts +215 -0
  120. package/src/embed/index.ts +68 -0
  121. package/src/embed/options.ts +100 -0
  122. package/src/help/index.ts +38 -0
  123. package/src/help/panel-index.ts +17 -0
  124. package/src/help/panel-styles.ts +123 -0
  125. package/src/help/panel.ts +840 -0
  126. package/src/help/pill-styles.ts +75 -0
  127. package/src/help/pill.ts +338 -0
  128. package/src/help/react.tsx +143 -0
  129. package/src/help/requests.ts +111 -0
  130. package/src/help/types.ts +124 -0
  131. package/src/help/wire.ts +55 -0
  132. package/src/native-wire.ts +124 -0
  133. package/src/native.ts +282 -0
  134. package/src/react.ts +27 -0
  135. package/src/replay.ts +13 -0
  136. package/src/runtime/api.ts +531 -0
  137. package/src/runtime/attachment-model.ts +42 -0
  138. package/src/runtime/attachments.tsx +1260 -0
  139. package/src/runtime/boot.ts +95 -0
  140. package/src/runtime/browser-label.ts +31 -0
  141. package/src/runtime/build-id.ts +10 -0
  142. package/src/runtime/capture-ui.tsx +306 -0
  143. package/src/runtime/capture.ts +508 -0
  144. package/src/runtime/clarity-model.ts +634 -0
  145. package/src/runtime/clarity-ui.tsx +454 -0
  146. package/src/runtime/client.ts +151 -0
  147. package/src/runtime/composer-size.tsx +146 -0
  148. package/src/runtime/dashboard.tsx +1130 -0
  149. package/src/runtime/debug-context-core.ts +681 -0
  150. package/src/runtime/debug-context-host.ts +56 -0
  151. package/src/runtime/debug-context-model.ts +1319 -0
  152. package/src/runtime/debug-context-redact.ts +259 -0
  153. package/src/runtime/debug-context.ts +613 -0
  154. package/src/runtime/details-model.ts +30 -0
  155. package/src/runtime/details.tsx +395 -0
  156. package/src/runtime/dictation-engine.ts +616 -0
  157. package/src/runtime/dictation-model.ts +79 -0
  158. package/src/runtime/dictation-ui.tsx +363 -0
  159. package/src/runtime/dictation.ts +271 -0
  160. package/src/runtime/dock.ts +108 -0
  161. package/src/runtime/draft-dock.tsx +124 -0
  162. package/src/runtime/draft-model.ts +251 -0
  163. package/src/runtime/draft-store.ts +149 -0
  164. package/src/runtime/edits.tsx +165 -0
  165. package/src/runtime/font.ts +42 -0
  166. package/src/runtime/format.ts +129 -0
  167. package/src/runtime/freshness.ts +23 -0
  168. package/src/runtime/handoff.ts +76 -0
  169. package/src/runtime/host-watch.ts +64 -0
  170. package/src/runtime/icons.tsx +295 -0
  171. package/src/runtime/idle.ts +20 -0
  172. package/src/runtime/index.ts +11 -0
  173. package/src/runtime/ink.ts +6 -0
  174. package/src/runtime/kind-model.ts +20 -0
  175. package/src/runtime/live-model.ts +32 -0
  176. package/src/runtime/loader.tsx +87 -0
  177. package/src/runtime/markup-editor.tsx +1333 -0
  178. package/src/runtime/markup-model.ts +485 -0
  179. package/src/runtime/markup.ts +382 -0
  180. package/src/runtime/members.ts +58 -0
  181. package/src/runtime/mention-parse.ts +56 -0
  182. package/src/runtime/mentions.tsx +205 -0
  183. package/src/runtime/page-background.ts +51 -0
  184. package/src/runtime/page-image.ts +1592 -0
  185. package/src/runtime/placement.ts +336 -0
  186. package/src/runtime/record.ts +402 -0
  187. package/src/runtime/redact.ts +30 -0
  188. package/src/runtime/related.ts +34 -0
  189. package/src/runtime/render-page.ts +2 -0
  190. package/src/runtime/replay-model.ts +232 -0
  191. package/src/runtime/replay.ts +173 -0
  192. package/src/runtime/runtime-version.ts +7 -0
  193. package/src/runtime/runtime.tsx +51 -0
  194. package/src/runtime/screen.ts +182 -0
  195. package/src/runtime/screenshot.ts +260 -0
  196. package/src/runtime/session-source.ts +112 -0
  197. package/src/runtime/session.ts +108 -0
  198. package/src/runtime/shield.ts +34 -0
  199. package/src/runtime/shot.tsx +422 -0
  200. package/src/runtime/sketch.ts +139 -0
  201. package/src/runtime/styles.ts +1541 -0
  202. package/src/runtime/sync.ts +300 -0
  203. package/src/runtime/thread-model.ts +71 -0
  204. package/src/runtime/ticket-form-model.ts +128 -0
  205. package/src/runtime/ticket-form.tsx +342 -0
  206. package/src/runtime/toolbar.tsx +4427 -0
  207. package/src/runtime/ui-scale.ts +75 -0
  208. package/src/runtime/update.ts +98 -0
  209. package/src/runtime/use-debug-context.ts +81 -0
  210. package/src/server/index.ts +133 -0
  211. package/src/server/support.ts +499 -0
  212. package/src/server.ts +4 -0
  213. package/src/vite/plugin.ts +102 -0
  214. package/src/vite.ts +1 -0
package/dist/react.js CHANGED
@@ -1,6 +1,69 @@
1
1
  // src/runtime/loader.tsx
2
2
  import { useEffect, useRef } from "react";
3
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
25
+ var IDLE_TIMEOUT_MS = 3e3;
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) {
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
+
4
67
  // src/runtime/handoff.ts
5
68
  var key = "devaccel.comment.handoff";
6
69
  var ttl = 30 * 60 * 1e3;
@@ -41,27 +104,52 @@ function captureHandoff() {
41
104
  });
42
105
  }
43
106
 
44
- // src/runtime/idle.ts
45
- var FALLBACK_SLICE_MS = 8;
46
- function whenIdle(task, timeout) {
47
- if (typeof requestIdleCallback === "function") {
48
- const id2 = requestIdleCallback(task, { timeout });
49
- return () => cancelIdleCallback(id2);
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
+ };
50
120
  }
51
- const id = setTimeout(
52
- () => {
53
- const end = performance.now() + FALLBACK_SLICE_MS;
54
- task({ didTimeout: false, timeRemaining: () => Math.max(0, end - performance.now()) });
55
- },
56
- Math.min(timeout, 200)
57
- );
58
- return () => clearTimeout(id);
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
+ };
59
150
  }
60
151
 
61
- // src/runtime/runtime-version.ts
62
- var runtimeVersion = 1;
63
-
64
- // src/runtime/session.ts
152
+ // src/runtime/session-source.ts
65
153
  var EXPIRY_MARGIN_MS = 6e4;
66
154
  var UNKNOWN_EXPIRY_MS = 5 * 6e4;
67
155
  function readJwt(token) {
@@ -82,26 +170,28 @@ function sessionFrom(payload) {
82
170
  apiUrl: value.apiUrl,
83
171
  project: value.project,
84
172
  environment: value.environment,
85
- hostToken: value.hostToken
173
+ hostToken: value.hostToken,
174
+ ...typeof value.loaderOrigin === "string" ? { loaderOrigin: value.loaderOrigin } : {}
86
175
  };
87
176
  }
88
- async function fetchSession(path) {
89
- const response = await fetch(path, {
90
- headers: { accept: "application/json", "x-comment-service-origin": window.location.origin },
91
- cache: "no-store"
92
- });
93
- return response.json();
94
- }
95
- function sessionSource(path, load = fetchSession, now = Date.now) {
177
+ var SessionOff = class extends Error {
178
+ };
179
+ function sessionSource(path, load, now2 = Date.now) {
96
180
  let kept = null;
97
181
  let pending = null;
98
182
  return ({ refresh = false } = {}) => {
99
- if (!refresh && kept && now() < kept.until) return Promise.resolve(kept.session);
183
+ if (!refresh && kept && now2() < kept.until) return Promise.resolve(kept.session);
100
184
  if (pending) return pending;
101
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
+ }
102
192
  const session = sessionFrom(payload);
103
193
  const expires = session ? readJwt(session.hostToken)?.exp : void 0;
104
- const until = !session ? Infinity : expires ? expires * 1e3 - EXPIRY_MARGIN_MS : now() + UNKNOWN_EXPIRY_MS;
194
+ const until = !session ? Infinity : expires ? expires * 1e3 - EXPIRY_MARGIN_MS : now2() + UNKNOWN_EXPIRY_MS;
105
195
  kept = { session, until };
106
196
  return session;
107
197
  }).finally(() => {
@@ -110,35 +200,79 @@ function sessionSource(path, load = fetchSession, now = Date.now) {
110
200
  return pending;
111
201
  };
112
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 };
239
+ }
240
+ return payload;
241
+ }
242
+ function sessionSource2(path, load = fetchSession, now2 = Date.now) {
243
+ return sessionSource(path, load, now2);
244
+ }
113
245
  var sources = /* @__PURE__ */ new Map();
114
246
  function sessionFor(path) {
115
247
  let source = sources.get(path);
116
248
  if (!source) {
117
- source = sessionSource(path);
249
+ source = sessionSource2(path);
118
250
  sources.set(path, source);
119
251
  }
120
252
  return source;
121
253
  }
122
254
 
123
255
  // src/runtime/loader.tsx
124
- var IDLE_TIMEOUT_MS = 3e3;
125
- captureHandoff();
126
- async function loadRuntime(apiUrl) {
127
- const url = `${apiUrl.replace(/\/+$/, "")}/v1/toolbar/${runtimeVersion}.js`;
128
- const remote = await import(
129
- /* @vite-ignore */
130
- /* webpackIgnore: true */
131
- url
132
- );
133
- if (typeof remote.mount === "function") return remote;
134
- throw new Error(`${url} has no mount export`);
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 };
135
261
  }
262
+ captureHandoff();
263
+ var now = (task) => {
264
+ task();
265
+ return () => {
266
+ };
267
+ };
136
268
  function CommentToolbar({
137
269
  sessionPath = "/api/comment-service/session",
138
270
  pollMs,
139
271
  position,
140
272
  theme,
141
- navigate
273
+ navigate,
274
+ screen,
275
+ debugContext
142
276
  }) {
143
277
  const handle = useRef(null);
144
278
  const navigateRef = useRef(navigate);
@@ -151,43 +285,262 @@ function CommentToolbar({
151
285
  position,
152
286
  theme,
153
287
  navigate: navigate ? (href) => navigateRef.current?.(href) : void 0,
288
+ screen,
289
+ debugContext: withStampedBuild(debugContext),
154
290
  getSession: sessionFor(sessionPath)
155
291
  };
156
292
  handle.current?.update(propsRef.current);
157
- }, [navigate, pollMs, position, sessionPath, theme]);
293
+ }, [debugContext, navigate, pollMs, position, screen, sessionPath, theme]);
158
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(() => {
159
406
  let cancelled = false;
160
- let mounted = null;
161
- let container = null;
162
407
  const start = async () => {
163
- const session = await sessionFor(sessionPath)().catch(() => null);
164
- if (!session || cancelled) return;
165
- const runtime = await loadRuntime(session.apiUrl);
408
+ const address = serviceUrl ?? (await sessionFor(sessionPath)().catch(() => null))?.apiUrl ?? null;
166
409
  if (cancelled) return;
167
- container = document.createElement("div");
168
- container.setAttribute("data-cs-runtime", "latest");
169
- container.style.display = "contents";
170
- document.body.append(container);
171
- mounted = runtime.mount(container, propsRef.current);
172
- handle.current = mounted;
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);
417
+ if (cancelled) return;
418
+ mounted.current = { api, handle: api.mount(options.current()) };
173
419
  };
174
- const cancelIdle = whenIdle(() => {
420
+ const cancelWait = whenIdle(() => {
175
421
  start().catch(
176
- (error) => console.warn("[comment-service] The toolbar could not start.", error)
422
+ (error) => console.warn("[comment-service] The help pill could not start.", error)
177
423
  );
178
424
  }, IDLE_TIMEOUT_MS);
179
425
  return () => {
180
426
  cancelled = true;
181
- cancelIdle();
182
- handle.current = null;
183
- queueMicrotask(() => {
184
- mounted?.unmount();
185
- container?.remove();
186
- });
427
+ cancelWait();
428
+ mounted.current?.handle.unmount();
429
+ mounted.current = null;
187
430
  };
188
- }, [sessionPath]);
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]);
189
436
  return null;
190
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
+ }
191
539
  export {
192
- CommentToolbar
540
+ CommentToolbar,
541
+ HelpError,
542
+ HelpPill,
543
+ queryCacheSummary,
544
+ reportToolbarError,
545
+ supportRequestsAt
193
546
  };