@clue-ai/browser-sdk 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 (102) hide show
  1. package/README.md +100 -0
  2. package/dist/authoring/overlay.d.ts +12 -0
  3. package/dist/authoring/overlay.js +468 -0
  4. package/dist/authoring/recording.d.ts +125 -0
  5. package/dist/authoring/recording.js +481 -0
  6. package/dist/authoring/service-logo.d.ts +1 -0
  7. package/dist/authoring/service-logo.generated.d.ts +1 -0
  8. package/dist/authoring/service-logo.generated.js +3 -0
  9. package/dist/authoring/service-logo.js +1 -0
  10. package/dist/authoring/session.d.ts +23 -0
  11. package/dist/authoring/session.js +127 -0
  12. package/dist/authoring/surface.d.ts +11 -0
  13. package/dist/authoring/surface.js +63 -0
  14. package/dist/authoring/toolbar-constants.d.ts +23 -0
  15. package/dist/authoring/toolbar-constants.js +42 -0
  16. package/dist/authoring/toolbar-drag.d.ts +29 -0
  17. package/dist/authoring/toolbar-drag.js +270 -0
  18. package/dist/authoring/toolbar-view.d.ts +21 -0
  19. package/dist/authoring/toolbar-view.js +2584 -0
  20. package/dist/capture/action.d.ts +2 -0
  21. package/dist/capture/action.js +62 -0
  22. package/dist/capture/dom.d.ts +23 -0
  23. package/dist/capture/dom.js +329 -0
  24. package/dist/capture/drag.d.ts +2 -0
  25. package/dist/capture/drag.js +75 -0
  26. package/dist/capture/error.d.ts +2 -0
  27. package/dist/capture/error.js +193 -0
  28. package/dist/capture/form.d.ts +2 -0
  29. package/dist/capture/form.js +137 -0
  30. package/dist/capture/frustration.d.ts +2 -0
  31. package/dist/capture/frustration.js +171 -0
  32. package/dist/capture/input.d.ts +2 -0
  33. package/dist/capture/input.js +109 -0
  34. package/dist/capture/location.d.ts +10 -0
  35. package/dist/capture/location.js +42 -0
  36. package/dist/capture/navigation.d.ts +2 -0
  37. package/dist/capture/navigation.js +100 -0
  38. package/dist/capture/network.d.ts +13 -0
  39. package/dist/capture/network.js +903 -0
  40. package/dist/capture/page.d.ts +2 -0
  41. package/dist/capture/page.js +78 -0
  42. package/dist/capture/performance.d.ts +2 -0
  43. package/dist/capture/performance.js +268 -0
  44. package/dist/context/account.d.ts +12 -0
  45. package/dist/context/account.js +129 -0
  46. package/dist/context/environment.d.ts +42 -0
  47. package/dist/context/environment.js +208 -0
  48. package/dist/context/identity.d.ts +14 -0
  49. package/dist/context/identity.js +123 -0
  50. package/dist/context/session.d.ts +28 -0
  51. package/dist/context/session.js +155 -0
  52. package/dist/context/tab.d.ts +22 -0
  53. package/dist/context/tab.js +142 -0
  54. package/dist/context/trace.d.ts +32 -0
  55. package/dist/context/trace.js +65 -0
  56. package/dist/core/config.d.ts +4 -0
  57. package/dist/core/config.js +199 -0
  58. package/dist/core/constants.d.ts +43 -0
  59. package/dist/core/constants.js +109 -0
  60. package/dist/core/contracts.d.ts +58 -0
  61. package/dist/core/contracts.js +53 -0
  62. package/dist/core/sdk.d.ts +2 -0
  63. package/dist/core/sdk.js +831 -0
  64. package/dist/core/types.d.ts +413 -0
  65. package/dist/core/types.js +1 -0
  66. package/dist/core/usage-governor.d.ts +7 -0
  67. package/dist/core/usage-governor.js +127 -0
  68. package/dist/index.d.ts +17 -0
  69. package/dist/index.js +36 -0
  70. package/dist/integrations/next-router.d.ts +16 -0
  71. package/dist/integrations/next-router.js +18 -0
  72. package/dist/integrations/react-router.d.ts +7 -0
  73. package/dist/integrations/react-router.js +37 -0
  74. package/dist/internal/metrics.d.ts +9 -0
  75. package/dist/internal/metrics.js +38 -0
  76. package/dist/normalize/builders.d.ts +15 -0
  77. package/dist/normalize/builders.js +786 -0
  78. package/dist/normalize/canonical.d.ts +13 -0
  79. package/dist/normalize/canonical.js +77 -0
  80. package/dist/normalize/event-id.d.ts +8 -0
  81. package/dist/normalize/event-id.js +39 -0
  82. package/dist/normalize/path-template.d.ts +1 -0
  83. package/dist/normalize/path-template.js +33 -0
  84. package/dist/privacy/local-minimization.d.ts +29 -0
  85. package/dist/privacy/local-minimization.js +88 -0
  86. package/dist/privacy/mask.d.ts +7 -0
  87. package/dist/privacy/mask.js +60 -0
  88. package/dist/privacy/parameter-snapshot.d.ts +14 -0
  89. package/dist/privacy/parameter-snapshot.js +206 -0
  90. package/dist/privacy/sanitize.d.ts +11 -0
  91. package/dist/privacy/sanitize.js +145 -0
  92. package/dist/privacy/schema-evidence.d.ts +20 -0
  93. package/dist/privacy/schema-evidence.js +238 -0
  94. package/dist/transport/batch.d.ts +37 -0
  95. package/dist/transport/batch.js +182 -0
  96. package/dist/transport/client.d.ts +61 -0
  97. package/dist/transport/client.js +267 -0
  98. package/dist/transport/queue.d.ts +22 -0
  99. package/dist/transport/queue.js +56 -0
  100. package/dist/transport/retry.d.ts +14 -0
  101. package/dist/transport/retry.js +46 -0
  102. package/package.json +38 -0
@@ -0,0 +1,2584 @@
1
+ import { ChevronDown, ChevronUp, PauseCircle } from "lucide";
2
+ import { SERVICE_LOGO_URL } from "./service-logo";
3
+ import { AUTHORING_ROOT_ATTRIBUTE, AUTHORING_ROOT_ATTRIBUTE_VALUE, markAuthoringRoot, } from "./surface";
4
+ import { AUTHORING_ACTIONS, TOOLBAR_ACTION_BUTTON_SIZE, TOOLBAR_BASE_BUTTON_EDGE_OFFSET, TOOLBAR_BASE_BUTTON_SIZE, TOOLBAR_COLLAPSED_HEIGHT, TOOLBAR_COLLAPSED_WIDTH, TOOLBAR_DOCK_GAP, TOOLBAR_INITIAL_TOP, TOOLBAR_PANEL_HEIGHT, TOOLBAR_PANEL_WIDTH, TOOLBAR_ROOT_ID, TOOLBAR_VIEWPORT_GUTTER, } from "./toolbar-constants";
5
+ import { applyToolbarPosition, clampToolbarPosition, getViewportSize, installToolbarDrag, measureToolbar, } from "./toolbar-drag";
6
+ const toolbarDisposers = new WeakMap();
7
+ const AUTHORING_GLASS_BACKGROUND = "rgba(255,255,255,0.16)";
8
+ const AUTHORING_GLASS_BACKGROUND_STRONG = "rgba(255,253,249,0.2)";
9
+ const AUTHORING_GLASS_BACKGROUND_SOFT = "rgba(255,255,255,0.12)";
10
+ const AUTHORING_GLASS_BORDER = "1px solid rgba(255,255,255,0.24)";
11
+ const AUTHORING_GLASS_BLUR = "saturate(145%) blur(8px)";
12
+ function applyGlassSurfaceStyles(element, variant = "default") {
13
+ element.style.background =
14
+ variant === "strong"
15
+ ? AUTHORING_GLASS_BACKGROUND_STRONG
16
+ : variant === "soft"
17
+ ? AUTHORING_GLASS_BACKGROUND_SOFT
18
+ : AUTHORING_GLASS_BACKGROUND;
19
+ element.style.border = AUTHORING_GLASS_BORDER;
20
+ element.style.backdropFilter = AUTHORING_GLASS_BLUR;
21
+ element.style.setProperty("-webkit-backdrop-filter", AUTHORING_GLASS_BLUR);
22
+ }
23
+ function createSvgIcon(markup, size) {
24
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
25
+ svg.setAttribute("viewBox", "0 0 24 24");
26
+ svg.setAttribute("width", String(size));
27
+ svg.setAttribute("height", String(size));
28
+ svg.setAttribute("aria-hidden", "true");
29
+ svg.style.display = "block";
30
+ svg.innerHTML = markup;
31
+ return svg;
32
+ }
33
+ function createLucideIcon(icon, size) {
34
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
35
+ svg.setAttribute("viewBox", "0 0 24 24");
36
+ svg.setAttribute("width", String(size));
37
+ svg.setAttribute("height", String(size));
38
+ svg.setAttribute("fill", "none");
39
+ svg.setAttribute("stroke", "currentColor");
40
+ svg.setAttribute("stroke-width", "1.9");
41
+ svg.setAttribute("stroke-linecap", "round");
42
+ svg.setAttribute("stroke-linejoin", "round");
43
+ svg.setAttribute("aria-hidden", "true");
44
+ svg.style.display = "block";
45
+ for (const [tagName, attrs] of icon) {
46
+ const node = document.createElementNS("http://www.w3.org/2000/svg", tagName);
47
+ for (const [name, value] of Object.entries(attrs)) {
48
+ if (value === undefined) {
49
+ continue;
50
+ }
51
+ node.setAttribute(name, String(value));
52
+ }
53
+ svg.append(node);
54
+ }
55
+ return svg;
56
+ }
57
+ function createFallbackLogo(size) {
58
+ return createSvgIcon([
59
+ '<path d="M4.6 6.2c0-.66.54-1.2 1.2-1.2h11.7c.97 0 1.45 1.18.77 1.86l-4.58 4.58a1.2 1.2 0 0 1-1.7 0L7.06 6.9A1.2 1.2 0 0 0 4.6 6.2Z" fill="currentColor"/>',
60
+ '<path d="M19.4 17.8c0 .66-.54 1.2-1.2 1.2H6.5c-.97 0-1.45-1.18-.77-1.86l4.58-4.58a1.2 1.2 0 0 1 1.7 0l4.93 4.56a1.2 1.2 0 0 0 2.46.68Z" fill="currentColor"/>',
61
+ ].join(""), size);
62
+ }
63
+ function createServiceLogo(size) {
64
+ const image = document.createElement("img");
65
+ image.src = SERVICE_LOGO_URL;
66
+ image.alt = "";
67
+ image.width = size;
68
+ image.height = size;
69
+ image.decoding = "async";
70
+ image.setAttribute("aria-hidden", "true");
71
+ image.style.display = "block";
72
+ image.style.width = `${size}px`;
73
+ image.style.height = `${size}px`;
74
+ image.style.objectFit = "contain";
75
+ image.style.objectPosition = "center";
76
+ image.style.flexShrink = "0";
77
+ image.addEventListener("error", () => {
78
+ image.replaceWith(createFallbackLogo(size));
79
+ }, { once: true });
80
+ return image;
81
+ }
82
+ function escapeAttributeValue(value) {
83
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
84
+ }
85
+ function escapeCssIdentifier(value) {
86
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
87
+ return CSS.escape(value);
88
+ }
89
+ return value.replace(/[^a-zA-Z0-9_-]/g, "\\$&");
90
+ }
91
+ function buildStructuralPosition(element) {
92
+ const parent = element.parentElement;
93
+ if (!parent) {
94
+ return "root";
95
+ }
96
+ const siblings = Array.from(parent.children);
97
+ const sameTagSiblings = siblings.filter((child) => child.tagName === element.tagName);
98
+ return `p${siblings.indexOf(element) + 1}-t${sameTagSiblings.indexOf(element) + 1}`;
99
+ }
100
+ function matchesStructuralStableKey(element, stableKey) {
101
+ const match = /^struct:([^:]+):([^:]+):([^:]+):(.+)$/.exec(stableKey);
102
+ if (!match) {
103
+ return false;
104
+ }
105
+ const [, tag, role, inputType, position] = match;
106
+ if (element.tagName.toLowerCase() !== tag.toLowerCase()) {
107
+ return false;
108
+ }
109
+ const elementRole = (element.getAttribute("role") ?? "none").toLowerCase();
110
+ if (elementRole !== role.toLowerCase()) {
111
+ return false;
112
+ }
113
+ const elementInputType = element instanceof HTMLInputElement
114
+ ? element.type || "na"
115
+ : element instanceof HTMLTextAreaElement
116
+ ? "textarea"
117
+ : element instanceof HTMLSelectElement
118
+ ? "select"
119
+ : element instanceof HTMLElement && element.isContentEditable
120
+ ? "contenteditable"
121
+ : "na";
122
+ if (elementInputType.toLowerCase() !== inputType.toLowerCase()) {
123
+ return false;
124
+ }
125
+ return buildStructuralPosition(element) === position;
126
+ }
127
+ function resolveUiTargetIdTargets(uiTargetId) {
128
+ if (!uiTargetId) {
129
+ return [];
130
+ }
131
+ const escapedId = escapeCssIdentifier(uiTargetId);
132
+ const escapedValue = escapeAttributeValue(uiTargetId);
133
+ return Array.from(new Set([
134
+ ...Array.from(document.querySelectorAll(`#${escapedId}`)),
135
+ ...Array.from(document.querySelectorAll(`[data-testid="${escapedValue}"]`)),
136
+ ...Array.from(document.querySelectorAll(`[data-qa="${escapedValue}"]`)),
137
+ ].filter((candidate) => candidate instanceof HTMLElement)));
138
+ }
139
+ function resolveStableKeyTargets(stableKey) {
140
+ if (!stableKey) {
141
+ return [];
142
+ }
143
+ const [prefix, ...rest] = stableKey.split(":");
144
+ const value = rest.join(":");
145
+ if (!value) {
146
+ return [];
147
+ }
148
+ const escapedValue = escapeAttributeValue(value);
149
+ switch (prefix) {
150
+ case "dtid":
151
+ return Array.from(document.querySelectorAll(`[data-testid="${escapedValue}"]`));
152
+ case "dqa":
153
+ return Array.from(document.querySelectorAll(`[data-qa="${escapedValue}"]`));
154
+ case "name":
155
+ return Array.from(document.querySelectorAll(`[name="${escapedValue}"]`));
156
+ case "aria":
157
+ return Array.from(document.querySelectorAll(`[aria-label="${escapedValue}"]`));
158
+ case "struct":
159
+ return Array.from(document.querySelectorAll(value.split(":")[0] || "*")).filter((element) => matchesStructuralStableKey(element, stableKey));
160
+ default:
161
+ return [];
162
+ }
163
+ }
164
+ function getEntryAnchorAtom(entry) {
165
+ return (entry.actionUnit.atoms.find((atom) => atom.event_role === "anchor" && (atom.stable_key || atom.ui_target_id)) ??
166
+ entry.actionUnit.atoms.find((atom) => atom.stable_key || atom.ui_target_id) ??
167
+ null);
168
+ }
169
+ function readAtomRawProperties(record) {
170
+ return record && typeof record === "object" && !Array.isArray(record)
171
+ ? record
172
+ : null;
173
+ }
174
+ function readStringAttribute(record, keys) {
175
+ for (const key of keys) {
176
+ const value = record?.[key];
177
+ if (typeof value === "string" && value.trim().length > 0) {
178
+ return value.trim();
179
+ }
180
+ }
181
+ return null;
182
+ }
183
+ function normalizeComparableLabel(value) {
184
+ if (!value) {
185
+ return null;
186
+ }
187
+ const normalized = value
188
+ .trim()
189
+ .replace(/\s+/g, " ")
190
+ .replace(/[·•]/g, " ")
191
+ .toLowerCase();
192
+ return normalized.length > 0 ? normalized : null;
193
+ }
194
+ function readElementComparableLabel(element) {
195
+ return (normalizeComparableLabel(element.getAttribute("aria-label")) ??
196
+ normalizeComparableLabel(element instanceof HTMLInputElement ||
197
+ element instanceof HTMLTextAreaElement
198
+ ? element.placeholder
199
+ : null) ??
200
+ normalizeComparableLabel(element.textContent));
201
+ }
202
+ function buildEntryTargetSignature(entry) {
203
+ const anchorAtom = getEntryAnchorAtom(entry);
204
+ const attributes = readAtomRawProperties(anchorAtom?.raw_properties_json);
205
+ const targetRecord = attributes?.target && typeof attributes.target === "object" && !Array.isArray(attributes.target)
206
+ ? attributes.target
207
+ : null;
208
+ return {
209
+ uiTargetId: entry.actionUnit.primary_ui_target_id ?? anchorAtom?.ui_target_id ?? null,
210
+ stableKey: anchorAtom?.stable_key ?? null,
211
+ tag: readStringAttribute(attributes, ["element_tag", "tag"]) ??
212
+ readStringAttribute(targetRecord, ["element_tag", "tag"]),
213
+ role: readStringAttribute(attributes, ["element_role", "role"]) ??
214
+ readStringAttribute(targetRecord, ["element_role", "role"]),
215
+ label: normalizeComparableLabel(readStringAttribute(attributes, [
216
+ "target_label",
217
+ "ui_target_display_name",
218
+ "field_label",
219
+ "button_text",
220
+ "visible_text",
221
+ "aria_label",
222
+ "placeholder",
223
+ ]) ??
224
+ readStringAttribute(targetRecord, [
225
+ "target_label",
226
+ "ui_target_display_name",
227
+ "field_label",
228
+ "button_text",
229
+ "label",
230
+ "text",
231
+ "aria_label",
232
+ "placeholder",
233
+ ])),
234
+ };
235
+ }
236
+ function queryLabelTargets(label) {
237
+ if (!label) {
238
+ return [];
239
+ }
240
+ return Array.from(document.querySelectorAll('button, a, input, textarea, select, [role="button"], [aria-label], [placeholder]')).filter((element) => readElementComparableLabel(element) === label);
241
+ }
242
+ function scoreTargetCandidate(candidate, signature) {
243
+ let score = 0;
244
+ if (signature.uiTargetId) {
245
+ if (candidate.id === signature.uiTargetId) {
246
+ score += 8;
247
+ }
248
+ if (candidate.getAttribute("data-testid") === signature.uiTargetId ||
249
+ candidate.getAttribute("data-qa") === signature.uiTargetId) {
250
+ score += 8;
251
+ }
252
+ }
253
+ if (signature.stableKey?.startsWith("name:")) {
254
+ const nameValue = signature.stableKey.slice("name:".length);
255
+ if (candidate.getAttribute("name") === nameValue) {
256
+ score += 6;
257
+ }
258
+ }
259
+ if (signature.stableKey?.startsWith("aria:")) {
260
+ const ariaValue = signature.stableKey.slice("aria:".length);
261
+ if (candidate.getAttribute("aria-label") === ariaValue) {
262
+ score += 6;
263
+ }
264
+ }
265
+ if (signature.tag && candidate.tagName.toLowerCase() === signature.tag.toLowerCase()) {
266
+ score += 2;
267
+ }
268
+ if (signature.role && (candidate.getAttribute("role") ?? "").toLowerCase() === signature.role.toLowerCase()) {
269
+ score += 2;
270
+ }
271
+ if (signature.label && readElementComparableLabel(candidate) === signature.label) {
272
+ score += 4;
273
+ }
274
+ const rect = candidate.getBoundingClientRect();
275
+ if (rect.width > 0 && rect.height > 0) {
276
+ score += 1;
277
+ }
278
+ return score;
279
+ }
280
+ function normalizeComparablePath(value) {
281
+ if (!value) {
282
+ return null;
283
+ }
284
+ const trimmed = value.trim();
285
+ if (!trimmed) {
286
+ return null;
287
+ }
288
+ let pathname = trimmed;
289
+ try {
290
+ pathname = new URL(trimmed, window.location.origin).pathname;
291
+ }
292
+ catch {
293
+ pathname = trimmed.split("#")[0]?.split("?")[0] ?? trimmed;
294
+ }
295
+ if (!pathname.startsWith("/")) {
296
+ return null;
297
+ }
298
+ if (pathname.length > 1 && pathname.endsWith("/")) {
299
+ return pathname.slice(0, -1);
300
+ }
301
+ return pathname || "/";
302
+ }
303
+ function entryMatchesCurrentScreen(entry) {
304
+ const anchorAtom = getEntryAnchorAtom(entry);
305
+ const currentPath = normalizeComparablePath(window.location.pathname);
306
+ if (!currentPath) {
307
+ return false;
308
+ }
309
+ const screenCandidates = [
310
+ anchorAtom?.screen_key ?? null,
311
+ anchorAtom?.url_canonical ?? null,
312
+ entry.actionUnit.route_from ?? null,
313
+ ]
314
+ .map((candidate) => normalizeComparablePath(candidate))
315
+ .filter((candidate) => candidate !== null);
316
+ if (screenCandidates.length === 0) {
317
+ return false;
318
+ }
319
+ return screenCandidates.includes(currentPath);
320
+ }
321
+ function resolveEntryTarget(entry) {
322
+ if (!entryMatchesCurrentScreen(entry)) {
323
+ return null;
324
+ }
325
+ const anchorAtom = getEntryAnchorAtom(entry);
326
+ const signature = buildEntryTargetSignature(entry);
327
+ const primaryUiTargets = resolveUiTargetIdTargets(entry.actionUnit.primary_ui_target_id);
328
+ const anchorUiTargets = resolveUiTargetIdTargets(anchorAtom?.ui_target_id);
329
+ const stableKeyTargets = resolveStableKeyTargets(anchorAtom?.stable_key ?? null);
330
+ const labelTargets = queryLabelTargets(signature.label);
331
+ const uniqueUiTarget = primaryUiTargets.length === 1
332
+ ? primaryUiTargets[0]
333
+ : anchorUiTargets.length === 1
334
+ ? anchorUiTargets[0]
335
+ : null;
336
+ const uniqueStableTarget = stableKeyTargets.length === 1 &&
337
+ stableKeyTargets[0] instanceof HTMLElement
338
+ ? stableKeyTargets[0]
339
+ : null;
340
+ if (uniqueUiTarget && uniqueStableTarget && uniqueUiTarget !== uniqueStableTarget) {
341
+ return null;
342
+ }
343
+ if (uniqueUiTarget) {
344
+ return uniqueUiTarget;
345
+ }
346
+ if (uniqueStableTarget) {
347
+ return uniqueStableTarget;
348
+ }
349
+ const candidatePool = Array.from(new Set([
350
+ ...primaryUiTargets.filter((candidate) => candidate instanceof HTMLElement),
351
+ ...anchorUiTargets.filter((candidate) => candidate instanceof HTMLElement),
352
+ ...stableKeyTargets.filter((candidate) => candidate instanceof HTMLElement),
353
+ ...labelTargets,
354
+ ]));
355
+ if (candidatePool.length === 0) {
356
+ return null;
357
+ }
358
+ const scored = candidatePool
359
+ .map((candidate) => ({
360
+ candidate,
361
+ score: scoreTargetCandidate(candidate, signature),
362
+ }))
363
+ .filter((entryScore) => entryScore.score >= 6)
364
+ .sort((left, right) => right.score - left.score);
365
+ const best = scored[0] ?? null;
366
+ const second = scored[1] ?? null;
367
+ if (!best) {
368
+ return null;
369
+ }
370
+ if (second && second.score === best.score) {
371
+ return null;
372
+ }
373
+ return best.candidate;
374
+ }
375
+ function parseNumericZIndex(value) {
376
+ if (!value || value === "auto") {
377
+ return null;
378
+ }
379
+ const parsed = Number.parseInt(value, 10);
380
+ return Number.isFinite(parsed) ? parsed : null;
381
+ }
382
+ function readEffectiveZIndex(element) {
383
+ let maxValue = 0;
384
+ let current = element;
385
+ while (current) {
386
+ const computed = window.getComputedStyle(current);
387
+ const zIndex = parseNumericZIndex(computed.zIndex);
388
+ if (zIndex !== null) {
389
+ maxValue = Math.max(maxValue, zIndex);
390
+ }
391
+ current = current.parentElement;
392
+ }
393
+ return maxValue;
394
+ }
395
+ function readElementSurfaceTitle(root) {
396
+ const directTitle = root.getAttribute("aria-label")?.trim() ||
397
+ root.getAttribute("title")?.trim() ||
398
+ root.getAttribute("data-title")?.trim();
399
+ if (directTitle) {
400
+ return directTitle;
401
+ }
402
+ const labelledBy = root.getAttribute("aria-labelledby");
403
+ if (labelledBy) {
404
+ for (const id of labelledBy.split(/\s+/).filter(Boolean)) {
405
+ const labelledElement = document.getElementById(id);
406
+ if (labelledElement?.textContent?.trim()) {
407
+ return labelledElement.textContent.trim();
408
+ }
409
+ }
410
+ }
411
+ const titleCandidate = root.querySelector('h1, h2, h3, h4, h5, h6, [aria-current="page"], [aria-selected="true"], [data-title]');
412
+ return titleCandidate?.textContent?.trim() || null;
413
+ }
414
+ function inferSurfaceContextKind(root) {
415
+ const role = (root.getAttribute("role") ?? "").trim().toLowerCase();
416
+ const dataSide = root.getAttribute("data-side")?.trim().toLowerCase() ?? null;
417
+ const computed = window.getComputedStyle(root);
418
+ const isPortalSurface = root.parentElement === document.body || root.parentElement === document.documentElement;
419
+ const isFloating = (computed.position === "fixed" || computed.position === "absolute") &&
420
+ readEffectiveZIndex(root) > 0;
421
+ if (role === "dialog" || role === "alertdialog" || root.getAttribute("aria-modal") === "true") {
422
+ if (dataSide === "left" || dataSide === "right") {
423
+ return "drawer";
424
+ }
425
+ if (dataSide === "top" || dataSide === "bottom") {
426
+ return "sheet";
427
+ }
428
+ return "dialog";
429
+ }
430
+ if (role === "menu" || role === "listbox") {
431
+ return "dropdown";
432
+ }
433
+ if (root.getAttribute("popover") !== null || role === "tooltip") {
434
+ return "popover";
435
+ }
436
+ if (isFloating && isPortalSurface) {
437
+ const rect = root.getBoundingClientRect();
438
+ if (rect.width >= window.innerWidth * 0.9 && rect.height >= window.innerHeight * 0.9) {
439
+ return "overlay";
440
+ }
441
+ return "portal";
442
+ }
443
+ return "unknown";
444
+ }
445
+ const GENERATED_SURFACE_IDS = new WeakMap();
446
+ let generatedSurfaceId = 0;
447
+ function resolveGeneratedSurfaceId(root) {
448
+ const existing = GENERATED_SURFACE_IDS.get(root);
449
+ if (existing) {
450
+ return existing;
451
+ }
452
+ generatedSurfaceId += 1;
453
+ const next = `surface-${generatedSurfaceId}`;
454
+ GENERATED_SURFACE_IDS.set(root, next);
455
+ return next;
456
+ }
457
+ function isHighlightCandidateElement(element) {
458
+ if (element.closest(`[${AUTHORING_ROOT_ATTRIBUTE}="${AUTHORING_ROOT_ATTRIBUTE_VALUE}"]`)) {
459
+ return false;
460
+ }
461
+ const computed = window.getComputedStyle(element);
462
+ if (computed.display === "none" || computed.visibility === "hidden") {
463
+ return false;
464
+ }
465
+ const rect = element.getBoundingClientRect();
466
+ return rect.width > 0 && rect.height > 0;
467
+ }
468
+ function resolveSurfaceContext(element) {
469
+ const surfaces = [];
470
+ let current = element;
471
+ while (current) {
472
+ const kind = inferSurfaceContextKind(current);
473
+ if (kind !== "unknown") {
474
+ surfaces.push(current);
475
+ }
476
+ current = current.parentElement;
477
+ }
478
+ const nearest = surfaces[0] ?? null;
479
+ if (!nearest) {
480
+ return {
481
+ kind: "page",
482
+ root: null,
483
+ rootId: `page:${normalizeComparablePath(window.location.pathname) ?? "/"}`,
484
+ depth: 0,
485
+ title: document.title || null,
486
+ isPortalSurface: false,
487
+ effectiveZIndex: 0,
488
+ };
489
+ }
490
+ const kind = inferSurfaceContextKind(nearest);
491
+ const title = readElementSurfaceTitle(nearest);
492
+ const directId = nearest.id.trim() ||
493
+ nearest.getAttribute("data-surface-id")?.trim() ||
494
+ nearest.getAttribute("aria-label")?.trim() ||
495
+ title ||
496
+ resolveGeneratedSurfaceId(nearest);
497
+ return {
498
+ kind,
499
+ root: nearest,
500
+ rootId: `${kind}:${normalizeComparableLabel(directId) ?? directId}`,
501
+ depth: surfaces.length,
502
+ title,
503
+ isPortalSurface: nearest.parentElement === document.body ||
504
+ nearest.parentElement === document.documentElement,
505
+ effectiveZIndex: readEffectiveZIndex(nearest),
506
+ };
507
+ }
508
+ function readStableAttributesFromElement(element) {
509
+ const attributes = [];
510
+ const specs = [
511
+ ["id", element.id || null],
512
+ ["name", element.getAttribute("name")],
513
+ ["aria-label", element.getAttribute("aria-label")],
514
+ ["placeholder", element.getAttribute("placeholder")],
515
+ ["data-testid", element.getAttribute("data-testid")],
516
+ ["data-qa", element.getAttribute("data-qa")],
517
+ ];
518
+ for (const [kind, value] of specs) {
519
+ if (value && value.trim()) {
520
+ attributes.push({ kind, value: value.trim() });
521
+ }
522
+ }
523
+ return attributes;
524
+ }
525
+ function buildContainerSignature(element) {
526
+ const tokens = [];
527
+ let current = element.parentElement;
528
+ while (current && tokens.length < 3) {
529
+ if (current.closest(`[${AUTHORING_ROOT_ATTRIBUTE}="${AUTHORING_ROOT_ATTRIBUTE_VALUE}"]`)) {
530
+ break;
531
+ }
532
+ const tag = current.tagName.toLowerCase();
533
+ const role = (current.getAttribute("role") ?? "").trim().toLowerCase();
534
+ tokens.push(role ? `${tag}:${role}` : tag);
535
+ current = current.parentElement;
536
+ }
537
+ return tokens.length > 0 ? tokens.join(">") : null;
538
+ }
539
+ function buildHighlightCandidate(element) {
540
+ const surface = resolveSurfaceContext(element);
541
+ return {
542
+ element,
543
+ surfaceContextKind: surface.kind,
544
+ surfaceRootId: surface.rootId,
545
+ surfaceDepth: surface.depth,
546
+ surfaceTitle: surface.title,
547
+ isPortalSurface: surface.isPortalSurface,
548
+ tagName: element.tagName.toLowerCase(),
549
+ role: (element.getAttribute("role") ?? "").trim().toLowerCase() || null,
550
+ uiTargetId: element.id ||
551
+ element.getAttribute("data-testid") ||
552
+ element.getAttribute("data-qa"),
553
+ stableKey: null,
554
+ stableAttributes: readStableAttributesFromElement(element),
555
+ normalizedText: readElementComparableLabel(element),
556
+ ariaLabel: normalizeComparableLabel(element.getAttribute("aria-label")),
557
+ placeholder: normalizeComparableLabel(element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement
558
+ ? element.placeholder
559
+ : null),
560
+ containerSignature: buildContainerSignature(element),
561
+ effectiveZIndex: readEffectiveZIndex(element),
562
+ };
563
+ }
564
+ function collectHighlightCandidates() {
565
+ return Array.from(document.querySelectorAll('button, a, input, textarea, select, [role], [tabindex], [contenteditable="true"], summary'))
566
+ .filter((element) => isHighlightCandidateElement(element))
567
+ .map(buildHighlightCandidate);
568
+ }
569
+ function buildSurfaceAwareSignature(entry) {
570
+ const baseSignature = buildEntryTargetSignature(entry);
571
+ const anchorAtom = getEntryAnchorAtom(entry);
572
+ const attributes = readAtomRawProperties(anchorAtom?.raw_properties_json);
573
+ const stableAttributes = [];
574
+ const pushAttribute = (kind, value) => {
575
+ if (value && value.trim()) {
576
+ stableAttributes.push({ kind, value: value.trim() });
577
+ }
578
+ };
579
+ pushAttribute("name", anchorAtom?.stable_key?.startsWith("name:")
580
+ ? anchorAtom.stable_key.slice("name:".length)
581
+ : null);
582
+ pushAttribute("aria-label", anchorAtom?.stable_key?.startsWith("aria:")
583
+ ? anchorAtom.stable_key.slice("aria:".length)
584
+ : null);
585
+ pushAttribute("name", readStringAttribute(attributes, ["name"]));
586
+ pushAttribute("aria-label", readStringAttribute(attributes, ["aria_label", "accessibility_label"]));
587
+ pushAttribute("placeholder", readStringAttribute(attributes, ["placeholder", "placeholder_text"]));
588
+ pushAttribute("data-testid", readStringAttribute(attributes, ["data_testid", "test_id"]));
589
+ pushAttribute("data-qa", readStringAttribute(attributes, ["data_qa", "qa_key"]));
590
+ return {
591
+ ...baseSignature,
592
+ stableAttributes,
593
+ containerSignature: readStringAttribute(attributes, ["container_signature"]) ?? null,
594
+ ariaLabel: normalizeComparableLabel(readStringAttribute(attributes, ["aria_label", "accessibility_label"])),
595
+ placeholder: normalizeComparableLabel(readStringAttribute(attributes, ["placeholder", "placeholder_text"])),
596
+ };
597
+ }
598
+ function matchStableAttribute(candidate, attribute) {
599
+ return candidate.stableAttributes.some((candidateAttribute) => candidateAttribute.kind === attribute.kind &&
600
+ normalizeComparableLabel(candidateAttribute.value) ===
601
+ normalizeComparableLabel(attribute.value));
602
+ }
603
+ function deriveExpectedSurfaceContext(candidates) {
604
+ const first = candidates[0] ?? null;
605
+ if (!first) {
606
+ return null;
607
+ }
608
+ if (candidates.every((candidate) => candidate.surfaceRootId === first.surfaceRootId)) {
609
+ return {
610
+ rootId: first.surfaceRootId,
611
+ kind: first.surfaceContextKind,
612
+ title: first.surfaceTitle,
613
+ effectiveZIndex: first.effectiveZIndex,
614
+ };
615
+ }
616
+ if (candidates.every((candidate) => candidate.surfaceContextKind === first.surfaceContextKind &&
617
+ candidate.surfaceTitle === first.surfaceTitle)) {
618
+ return {
619
+ rootId: null,
620
+ kind: first.surfaceContextKind,
621
+ title: first.surfaceTitle,
622
+ effectiveZIndex: first.effectiveZIndex,
623
+ };
624
+ }
625
+ if (candidates.every((candidate) => candidate.surfaceContextKind === first.surfaceContextKind)) {
626
+ return {
627
+ rootId: null,
628
+ kind: first.surfaceContextKind,
629
+ title: null,
630
+ effectiveZIndex: first.effectiveZIndex,
631
+ };
632
+ }
633
+ return null;
634
+ }
635
+ function scoreSurfaceAwareCandidate(candidate, signature, expectedSurface) {
636
+ let score = scoreTargetCandidate(candidate.element, signature);
637
+ if (signature.uiTargetId && candidate.uiTargetId === signature.uiTargetId) {
638
+ score += 12;
639
+ }
640
+ for (const attribute of signature.stableAttributes) {
641
+ if (matchStableAttribute(candidate, attribute)) {
642
+ score += 10;
643
+ }
644
+ }
645
+ if (signature.tag && candidate.tagName === signature.tag.toLowerCase()) {
646
+ score += 3;
647
+ }
648
+ if (signature.role && candidate.role === signature.role.toLowerCase()) {
649
+ score += 3;
650
+ }
651
+ if (signature.label && candidate.normalizedText === signature.label) {
652
+ score += 6;
653
+ }
654
+ if (signature.ariaLabel && candidate.ariaLabel === signature.ariaLabel) {
655
+ score += 4;
656
+ }
657
+ if (signature.placeholder && candidate.placeholder === signature.placeholder) {
658
+ score += 4;
659
+ }
660
+ if (signature.containerSignature &&
661
+ candidate.containerSignature === signature.containerSignature) {
662
+ score += 4;
663
+ }
664
+ if (expectedSurface?.title &&
665
+ candidate.surfaceTitle &&
666
+ normalizeComparableLabel(expectedSurface.title) ===
667
+ normalizeComparableLabel(candidate.surfaceTitle)) {
668
+ score += 4;
669
+ }
670
+ if (expectedSurface && candidate.surfaceContextKind === expectedSurface.kind) {
671
+ score += 3;
672
+ }
673
+ if (expectedSurface && candidate.surfaceDepth === 1) {
674
+ score += 1;
675
+ }
676
+ return score;
677
+ }
678
+ function resolveEntryTargetWithSurfaceContext(entry) {
679
+ const baseTrace = {
680
+ highlightSurfaceContextKind: "unknown",
681
+ highlightSurfaceRootId: null,
682
+ highlightSurfaceTitle: null,
683
+ candidateCountTotal: 0,
684
+ candidateCountAfterContextFilter: 0,
685
+ highlightMatchStage: "init",
686
+ highlightRenderMode: "none",
687
+ highlightRejectReason: null,
688
+ top1Score: null,
689
+ top2Score: null,
690
+ usedSameSurfaceFilter: false,
691
+ overlayZIndex: null,
692
+ targetEffectiveZIndex: null,
693
+ surfaceEffectiveZIndex: null,
694
+ };
695
+ if (!entryMatchesCurrentScreen(entry)) {
696
+ return {
697
+ candidate: null,
698
+ trace: {
699
+ ...baseTrace,
700
+ highlightMatchStage: "context_filter_screen",
701
+ highlightRejectReason: "screen_mismatch",
702
+ },
703
+ };
704
+ }
705
+ const anchorAtom = getEntryAnchorAtom(entry);
706
+ const signature = buildSurfaceAwareSignature(entry);
707
+ const strongCandidates = [
708
+ ...resolveUiTargetIdTargets(entry.actionUnit.primary_ui_target_id),
709
+ ...resolveUiTargetIdTargets(anchorAtom?.ui_target_id),
710
+ ...resolveStableKeyTargets(anchorAtom?.stable_key ?? null),
711
+ ]
712
+ .filter((candidate, index, all) => all.indexOf(candidate) === index)
713
+ .filter((candidate) => candidate instanceof HTMLElement)
714
+ .map(buildHighlightCandidate);
715
+ const expectedSurface = deriveExpectedSurfaceContext(strongCandidates);
716
+ const totalCandidates = collectHighlightCandidates();
717
+ baseTrace.candidateCountTotal = totalCandidates.length;
718
+ let filteredCandidates = totalCandidates;
719
+ if (expectedSurface?.rootId) {
720
+ const next = totalCandidates.filter((candidate) => candidate.surfaceRootId === expectedSurface.rootId);
721
+ if (next.length > 0) {
722
+ filteredCandidates = next;
723
+ baseTrace.usedSameSurfaceFilter = true;
724
+ baseTrace.highlightMatchStage = "context_filter_surface_root";
725
+ }
726
+ }
727
+ else if (expectedSurface?.title) {
728
+ const next = totalCandidates.filter((candidate) => candidate.surfaceContextKind === expectedSurface.kind &&
729
+ normalizeComparableLabel(candidate.surfaceTitle) ===
730
+ normalizeComparableLabel(expectedSurface.title));
731
+ if (next.length > 0) {
732
+ filteredCandidates = next;
733
+ baseTrace.usedSameSurfaceFilter = true;
734
+ baseTrace.highlightMatchStage = "context_filter_kind_title";
735
+ }
736
+ }
737
+ else if (expectedSurface) {
738
+ const next = totalCandidates.filter((candidate) => candidate.surfaceContextKind === expectedSurface.kind);
739
+ if (next.length > 0) {
740
+ filteredCandidates = next;
741
+ baseTrace.usedSameSurfaceFilter = true;
742
+ baseTrace.highlightMatchStage = "context_filter_kind";
743
+ }
744
+ }
745
+ baseTrace.candidateCountAfterContextFilter = filteredCandidates.length;
746
+ if (filteredCandidates.length === 0) {
747
+ return {
748
+ candidate: null,
749
+ trace: {
750
+ ...baseTrace,
751
+ highlightRejectReason: "candidate_not_found",
752
+ },
753
+ };
754
+ }
755
+ const strictUiMatches = filteredCandidates.filter((candidate) => signature.uiTargetId && candidate.uiTargetId === signature.uiTargetId);
756
+ if (strictUiMatches.length === 1) {
757
+ const candidate = strictUiMatches[0];
758
+ return {
759
+ candidate,
760
+ trace: {
761
+ ...baseTrace,
762
+ highlightMatchStage: "strict_ui_target_id",
763
+ highlightSurfaceContextKind: candidate.surfaceContextKind,
764
+ highlightSurfaceRootId: candidate.surfaceRootId,
765
+ highlightSurfaceTitle: candidate.surfaceTitle,
766
+ },
767
+ };
768
+ }
769
+ if (strictUiMatches.length > 1) {
770
+ return {
771
+ candidate: null,
772
+ trace: {
773
+ ...baseTrace,
774
+ highlightMatchStage: "strict_ui_target_id",
775
+ highlightRejectReason: "ambiguous_ui_target_id",
776
+ },
777
+ };
778
+ }
779
+ const strictStableMatches = filteredCandidates.filter((candidate) => signature.stableAttributes.some((attribute) => matchStableAttribute(candidate, attribute)));
780
+ if (strictStableMatches.length === 1) {
781
+ const candidate = strictStableMatches[0];
782
+ return {
783
+ candidate,
784
+ trace: {
785
+ ...baseTrace,
786
+ highlightMatchStage: "strict_stable_attributes",
787
+ highlightSurfaceContextKind: candidate.surfaceContextKind,
788
+ highlightSurfaceRootId: candidate.surfaceRootId,
789
+ highlightSurfaceTitle: candidate.surfaceTitle,
790
+ },
791
+ };
792
+ }
793
+ if (strictStableMatches.length > 1) {
794
+ return {
795
+ candidate: null,
796
+ trace: {
797
+ ...baseTrace,
798
+ highlightMatchStage: "strict_stable_attributes",
799
+ highlightRejectReason: "ambiguous_stable_attributes",
800
+ },
801
+ };
802
+ }
803
+ const scored = filteredCandidates
804
+ .map((candidate) => ({
805
+ candidate,
806
+ score: scoreSurfaceAwareCandidate(candidate, signature, expectedSurface),
807
+ }))
808
+ .sort((left, right) => right.score - left.score);
809
+ const top1 = scored[0] ?? null;
810
+ const top2 = scored[1] ?? null;
811
+ baseTrace.top1Score = top1?.score ?? null;
812
+ baseTrace.top2Score = top2?.score ?? null;
813
+ if (!top1) {
814
+ return {
815
+ candidate: null,
816
+ trace: {
817
+ ...baseTrace,
818
+ highlightMatchStage: "scored_match",
819
+ highlightRejectReason: "candidate_not_found",
820
+ },
821
+ };
822
+ }
823
+ if (top1.score < 14) {
824
+ return {
825
+ candidate: null,
826
+ trace: {
827
+ ...baseTrace,
828
+ highlightMatchStage: "scored_match",
829
+ highlightRejectReason: "below_threshold",
830
+ },
831
+ };
832
+ }
833
+ if (top2 && top1.score - top2.score < 3) {
834
+ return {
835
+ candidate: null,
836
+ trace: {
837
+ ...baseTrace,
838
+ highlightMatchStage: "scored_match",
839
+ highlightRejectReason: top1.score === top2.score ? "score_tie" : "score_margin_too_small",
840
+ },
841
+ };
842
+ }
843
+ return {
844
+ candidate: top1.candidate,
845
+ trace: {
846
+ ...baseTrace,
847
+ highlightMatchStage: "scored_match",
848
+ highlightSurfaceContextKind: top1.candidate.surfaceContextKind,
849
+ highlightSurfaceRootId: top1.candidate.surfaceRootId,
850
+ highlightSurfaceTitle: top1.candidate.surfaceTitle,
851
+ },
852
+ };
853
+ }
854
+ function createTargetSpotlight() {
855
+ const ring = document.createElement("div");
856
+ markAuthoringRoot(ring);
857
+ ring.dataset.clueAuthoringTargetHighlight = "true";
858
+ ring.style.position = "fixed";
859
+ ring.style.left = "0px";
860
+ ring.style.top = "0px";
861
+ ring.style.width = "0px";
862
+ ring.style.height = "0px";
863
+ ring.style.borderRadius = "16px";
864
+ ring.style.border = "1.5px solid rgba(244,150,39,0.95)";
865
+ ring.style.background = "rgba(255,250,244,0.12)";
866
+ ring.style.boxShadow =
867
+ "rgba(255,255,255,0.96) 0px 1px 0px inset, rgba(244,150,39,0.18) 0px 0px 0px 1px, rgba(244,150,39,0.24) 0px 10px 30px";
868
+ ring.style.pointerEvents = "none";
869
+ ring.style.opacity = "0";
870
+ ring.style.transform = "scale(0.98)";
871
+ ring.style.transition =
872
+ "opacity 180ms ease, transform 220ms cubic-bezier(0.22, 1, 0.36, 1)";
873
+ ring.style.zIndex = "2147483300";
874
+ const badge = document.createElement("div");
875
+ markAuthoringRoot(badge);
876
+ badge.style.position = "fixed";
877
+ badge.style.left = "0px";
878
+ badge.style.top = "0px";
879
+ badge.style.display = "inline-flex";
880
+ badge.style.alignItems = "center";
881
+ badge.style.padding = "6px 10px";
882
+ badge.style.borderRadius = "9999px";
883
+ badge.style.background = "rgba(255,253,249,0.98)";
884
+ badge.style.border = "1px solid rgba(244,150,39,0.22)";
885
+ badge.style.boxShadow =
886
+ "rgba(255,255,255,0.96) 0px 1px 0px inset, rgba(17,24,39,0.06) 0px 10px 24px";
887
+ badge.style.fontSize = "11px";
888
+ badge.style.fontWeight = "600";
889
+ badge.style.letterSpacing = "0.08em";
890
+ badge.style.textTransform = "uppercase";
891
+ badge.style.color = "#8f5b2f";
892
+ badge.style.pointerEvents = "none";
893
+ badge.style.opacity = "0";
894
+ badge.style.transform = "translateY(4px)";
895
+ badge.style.transition =
896
+ "opacity 180ms ease, transform 220ms cubic-bezier(0.22, 1, 0.36, 1)";
897
+ badge.style.zIndex = "2147483301";
898
+ badge.textContent = "対象要素";
899
+ (document.body ?? document.documentElement).append(ring, badge);
900
+ let activeElement = null;
901
+ let activeTrace = null;
902
+ let activeInlineElement = null;
903
+ const inlineRestoreState = new WeakMap();
904
+ const hide = () => {
905
+ ring.style.opacity = "0";
906
+ ring.style.transform = "scale(0.98)";
907
+ badge.style.opacity = "0";
908
+ badge.style.transform = "translateY(4px)";
909
+ };
910
+ const clearInlineOutline = () => {
911
+ if (!activeInlineElement) {
912
+ return;
913
+ }
914
+ const previous = inlineRestoreState.get(activeInlineElement);
915
+ if (previous) {
916
+ activeInlineElement.style.outline = previous.outline;
917
+ activeInlineElement.style.outlineOffset = previous.outlineOffset;
918
+ activeInlineElement.style.boxShadow = previous.boxShadow;
919
+ }
920
+ else {
921
+ activeInlineElement.style.outline = "";
922
+ activeInlineElement.style.outlineOffset = "";
923
+ activeInlineElement.style.boxShadow = "";
924
+ }
925
+ inlineRestoreState.delete(activeInlineElement);
926
+ activeInlineElement = null;
927
+ };
928
+ const hasProblematicHighlightAncestors = (element) => {
929
+ const hasMeaningfulComputedValue = (value) => {
930
+ const normalized = (value ?? "").trim().toLowerCase();
931
+ return normalized.length > 0 && normalized !== "none" && normalized !== "auto";
932
+ };
933
+ let current = element.parentElement;
934
+ while (current) {
935
+ const computed = window.getComputedStyle(current);
936
+ if (computed.overflowX === "hidden" ||
937
+ computed.overflowY === "hidden" ||
938
+ computed.overflowX === "clip" ||
939
+ computed.overflowY === "clip" ||
940
+ hasMeaningfulComputedValue(computed.transform) ||
941
+ hasMeaningfulComputedValue(computed.filter) ||
942
+ hasMeaningfulComputedValue(computed.backdropFilter) ||
943
+ hasMeaningfulComputedValue(computed.perspective) ||
944
+ hasMeaningfulComputedValue(computed.clipPath) ||
945
+ computed.isolation === "isolate") {
946
+ return true;
947
+ }
948
+ current = current.parentElement;
949
+ }
950
+ return false;
951
+ };
952
+ const applyInlineOutline = (element) => {
953
+ clearInlineOutline();
954
+ inlineRestoreState.set(element, {
955
+ outline: element.style.outline,
956
+ outlineOffset: element.style.outlineOffset,
957
+ boxShadow: element.style.boxShadow,
958
+ });
959
+ element.style.outline = "2px solid rgba(244,150,39,0.96)";
960
+ element.style.outlineOffset = "3px";
961
+ element.style.boxShadow =
962
+ "rgba(244,150,39,0.22) 0px 0px 0px 4px, rgba(255,255,255,0.78) 0px 0px 0px 1px";
963
+ activeInlineElement = element;
964
+ };
965
+ const update = () => {
966
+ if (!activeElement || !activeElement.isConnected) {
967
+ hide();
968
+ return;
969
+ }
970
+ const rect = activeElement.getBoundingClientRect();
971
+ if (rect.width <= 0 || rect.height <= 0) {
972
+ hide();
973
+ return;
974
+ }
975
+ const ringInset = 8;
976
+ const left = Math.max(8, rect.left - ringInset);
977
+ const top = Math.max(8, rect.top - ringInset);
978
+ const width = rect.width + ringInset * 2;
979
+ const height = rect.height + ringInset * 2;
980
+ const badgeTop = top > 42 ? top - 36 : top + height + 8;
981
+ const badgeLeft = Math.max(8, Math.min(left, window.innerWidth - 120));
982
+ const surface = resolveSurfaceContext(activeElement);
983
+ const targetEffectiveZIndex = readEffectiveZIndex(activeElement);
984
+ const surfaceEffectiveZIndex = surface.effectiveZIndex;
985
+ const overlayZIndex = Math.max(2147483300, targetEffectiveZIndex, surfaceEffectiveZIndex) + 2;
986
+ ring.style.left = `${Math.round(left)}px`;
987
+ ring.style.top = `${Math.round(top)}px`;
988
+ ring.style.width = `${Math.round(width)}px`;
989
+ ring.style.height = `${Math.round(height)}px`;
990
+ ring.style.borderRadius = `${Math.max(12, Math.min(20, height / 4))}px`;
991
+ ring.style.opacity = "1";
992
+ ring.style.transform = "scale(1)";
993
+ ring.style.zIndex = String(overlayZIndex);
994
+ badge.style.left = `${Math.round(badgeLeft)}px`;
995
+ badge.style.top = `${Math.round(badgeTop)}px`;
996
+ badge.style.opacity = "1";
997
+ badge.style.transform = "translateY(0px)";
998
+ badge.style.zIndex = String(overlayZIndex + 1);
999
+ if (activeTrace) {
1000
+ activeTrace.highlightRenderMode = "overlay_box";
1001
+ activeTrace.overlayZIndex = overlayZIndex;
1002
+ activeTrace.targetEffectiveZIndex = targetEffectiveZIndex;
1003
+ activeTrace.surfaceEffectiveZIndex = surfaceEffectiveZIndex;
1004
+ }
1005
+ };
1006
+ const handleViewportChange = () => {
1007
+ window.requestAnimationFrame(update);
1008
+ };
1009
+ window.addEventListener("scroll", handleViewportChange, true);
1010
+ window.addEventListener("resize", handleViewportChange);
1011
+ return {
1012
+ clear() {
1013
+ activeElement = null;
1014
+ activeTrace = null;
1015
+ clearInlineOutline();
1016
+ hide();
1017
+ },
1018
+ focusEntry(entry) {
1019
+ clearInlineOutline();
1020
+ const match = resolveEntryTargetWithSurfaceContext(entry);
1021
+ activeTrace = match.trace;
1022
+ if (!(match.candidate?.element instanceof HTMLElement)) {
1023
+ activeElement = null;
1024
+ hide();
1025
+ return match.trace;
1026
+ }
1027
+ const target = match.candidate.element;
1028
+ activeElement = target;
1029
+ if (typeof target.scrollIntoView === "function") {
1030
+ target.scrollIntoView({
1031
+ block: "center",
1032
+ inline: "center",
1033
+ behavior: "smooth",
1034
+ });
1035
+ }
1036
+ if (hasProblematicHighlightAncestors(target)) {
1037
+ applyInlineOutline(target);
1038
+ hide();
1039
+ match.trace.highlightRenderMode = "inline_outline";
1040
+ match.trace.highlightSurfaceContextKind = match.candidate.surfaceContextKind;
1041
+ match.trace.highlightSurfaceRootId = match.candidate.surfaceRootId;
1042
+ match.trace.highlightSurfaceTitle = match.candidate.surfaceTitle;
1043
+ match.trace.targetEffectiveZIndex = match.candidate.effectiveZIndex;
1044
+ match.trace.surfaceEffectiveZIndex = resolveSurfaceContext(target).effectiveZIndex;
1045
+ match.trace.overlayZIndex = null;
1046
+ return match.trace;
1047
+ }
1048
+ const surface = resolveSurfaceContext(target);
1049
+ const targetEffectiveZIndex = readEffectiveZIndex(target);
1050
+ const surfaceEffectiveZIndex = surface.effectiveZIndex;
1051
+ const overlayZIndex = Math.max(2147483300, targetEffectiveZIndex, surfaceEffectiveZIndex) + 2;
1052
+ match.trace.highlightRenderMode = "overlay_box";
1053
+ match.trace.overlayZIndex = overlayZIndex;
1054
+ match.trace.targetEffectiveZIndex = targetEffectiveZIndex;
1055
+ match.trace.surfaceEffectiveZIndex = surfaceEffectiveZIndex;
1056
+ update();
1057
+ window.requestAnimationFrame(() => {
1058
+ update();
1059
+ window.requestAnimationFrame(update);
1060
+ });
1061
+ return match.trace;
1062
+ },
1063
+ dispose() {
1064
+ clearInlineOutline();
1065
+ window.removeEventListener("scroll", handleViewportChange, true);
1066
+ window.removeEventListener("resize", handleViewportChange);
1067
+ ring.remove();
1068
+ badge.remove();
1069
+ },
1070
+ };
1071
+ }
1072
+ function getTooltipPlacement(x, y) {
1073
+ if (Math.abs(x) < 4) {
1074
+ return "right";
1075
+ }
1076
+ if (Math.abs(y) > Math.abs(x)) {
1077
+ return y < 0 ? "top" : "bottom";
1078
+ }
1079
+ return x < 0 ? "left" : "right";
1080
+ }
1081
+ function createActionTooltip(label, placement) {
1082
+ const tooltip = document.createElement("div");
1083
+ tooltip.dataset.clueAuthoringActionTooltip = "true";
1084
+ tooltip.textContent = label;
1085
+ tooltip.setAttribute("aria-hidden", "true");
1086
+ tooltip.style.position = "absolute";
1087
+ tooltip.style.display = "inline-flex";
1088
+ tooltip.style.alignItems = "center";
1089
+ tooltip.style.justifyContent = "center";
1090
+ tooltip.style.width = "max-content";
1091
+ tooltip.style.maxWidth = "176px";
1092
+ tooltip.style.padding = "8px 10px";
1093
+ tooltip.style.borderRadius = "12px";
1094
+ tooltip.style.background = "rgba(255,255,255,0.98)";
1095
+ tooltip.style.border = "1px solid rgba(17,24,39,0.12)";
1096
+ tooltip.style.boxShadow =
1097
+ "rgba(255,255,255,0.96) 0px 1px 0px inset, rgba(17,24,39,0.08) 0px 0px 0px 1px, rgba(17,24,39,0.05) 0px 10px 22px";
1098
+ tooltip.style.fontSize = "12px";
1099
+ tooltip.style.fontWeight = "500";
1100
+ tooltip.style.lineHeight = "1.35";
1101
+ tooltip.style.letterSpacing = "0.12px";
1102
+ tooltip.style.color = "#111827";
1103
+ tooltip.style.whiteSpace = "nowrap";
1104
+ tooltip.style.pointerEvents = "none";
1105
+ tooltip.style.opacity = "0";
1106
+ tooltip.style.transition =
1107
+ "opacity 180ms ease, transform 220ms cubic-bezier(0.22, 1, 0.36, 1)";
1108
+ tooltip.style.zIndex = "40";
1109
+ switch (placement) {
1110
+ case "top":
1111
+ tooltip.style.left = "50%";
1112
+ tooltip.style.bottom = "calc(100% + 12px)";
1113
+ tooltip.style.transform = "translate(-50%, 8px) scale(0.96)";
1114
+ break;
1115
+ case "bottom":
1116
+ tooltip.style.left = "50%";
1117
+ tooltip.style.top = "calc(100% + 12px)";
1118
+ tooltip.style.transform = "translate(-50%, -8px) scale(0.96)";
1119
+ break;
1120
+ case "left":
1121
+ tooltip.style.right = "calc(100% + 12px)";
1122
+ tooltip.style.top = "50%";
1123
+ tooltip.style.transform = "translate(8px, -50%) scale(0.96)";
1124
+ break;
1125
+ case "right":
1126
+ tooltip.style.left = "calc(100% + 12px)";
1127
+ tooltip.style.top = "50%";
1128
+ tooltip.style.transform = "translate(-8px, -50%) scale(0.96)";
1129
+ break;
1130
+ }
1131
+ return tooltip;
1132
+ }
1133
+ function setTooltipVisibility(tooltip, placement, visible) {
1134
+ tooltip.style.opacity = visible ? "1" : "0";
1135
+ switch (placement) {
1136
+ case "top":
1137
+ tooltip.style.transform = visible
1138
+ ? "translate(-50%, 0px) scale(1)"
1139
+ : "translate(-50%, 8px) scale(0.96)";
1140
+ break;
1141
+ case "bottom":
1142
+ tooltip.style.transform = visible
1143
+ ? "translate(-50%, 0px) scale(1)"
1144
+ : "translate(-50%, -8px) scale(0.96)";
1145
+ break;
1146
+ case "left":
1147
+ tooltip.style.transform = visible
1148
+ ? "translate(0px, -50%) scale(1)"
1149
+ : "translate(8px, -50%) scale(0.96)";
1150
+ break;
1151
+ case "right":
1152
+ tooltip.style.transform = visible
1153
+ ? "translate(0px, -50%) scale(1)"
1154
+ : "translate(-8px, -50%) scale(0.96)";
1155
+ break;
1156
+ }
1157
+ }
1158
+ function createActionButton(label, x, y) {
1159
+ const button = document.createElement("button");
1160
+ const placement = getTooltipPlacement(x, y);
1161
+ const tooltip = createActionTooltip(label, placement);
1162
+ const actionIconSize = Math.round(TOOLBAR_ACTION_BUTTON_SIZE * 0.41);
1163
+ const actionAnchor = TOOLBAR_BASE_BUTTON_EDGE_OFFSET + TOOLBAR_BASE_BUTTON_SIZE / 2;
1164
+ button.type = "button";
1165
+ button.setAttribute("aria-label", label);
1166
+ button.style.display = "grid";
1167
+ button.style.placeItems = "center";
1168
+ button.style.width = `${TOOLBAR_ACTION_BUTTON_SIZE}px`;
1169
+ button.style.height = `${TOOLBAR_ACTION_BUTTON_SIZE}px`;
1170
+ button.style.border = "none";
1171
+ button.style.borderRadius = "9999px";
1172
+ button.style.padding = "0";
1173
+ button.style.color = "#111827";
1174
+ button.style.cursor = "pointer";
1175
+ button.style.position = "absolute";
1176
+ button.style.left = `${actionAnchor}px`;
1177
+ button.style.top = `${actionAnchor}px`;
1178
+ button.style.marginLeft = `${-TOOLBAR_ACTION_BUTTON_SIZE / 2}px`;
1179
+ button.style.marginTop = `${-TOOLBAR_ACTION_BUTTON_SIZE / 2}px`;
1180
+ button.style.opacity = "0";
1181
+ button.style.transform = "translate(0px, 0px) scale(0.72)";
1182
+ button.style.transformOrigin = "center";
1183
+ button.style.transition =
1184
+ "transform 320ms cubic-bezier(0.22, 1, 0.36, 1), opacity 220ms ease, box-shadow 220ms ease";
1185
+ button.style.boxShadow =
1186
+ "rgba(17,24,39,0.08) 0px 10px 24px, rgba(17,24,39,0.06) 0px 0px 0px 1px";
1187
+ button.style.pointerEvents = "none";
1188
+ button.style.willChange = "transform, opacity";
1189
+ button.style.overflow = "visible";
1190
+ button.style.zIndex = "30";
1191
+ applyGlassSurfaceStyles(button);
1192
+ const showTooltip = () => setTooltipVisibility(tooltip, placement, true);
1193
+ const hideTooltip = () => setTooltipVisibility(tooltip, placement, false);
1194
+ button.addEventListener("mouseenter", showTooltip);
1195
+ button.addEventListener("mouseleave", hideTooltip);
1196
+ button.addEventListener("focus", showTooltip);
1197
+ button.addEventListener("blur", hideTooltip);
1198
+ button.append(tooltip);
1199
+ return button;
1200
+ }
1201
+ function setActionButtonVisual(button, label, icon) {
1202
+ const actionIconSize = Math.round(TOOLBAR_ACTION_BUTTON_SIZE * 0.41);
1203
+ button.setAttribute("aria-label", label);
1204
+ const tooltip = button.querySelector("[data-clue-authoring-action-tooltip='true']");
1205
+ if (tooltip) {
1206
+ tooltip.textContent = label;
1207
+ }
1208
+ const existingIcon = button.querySelector("svg");
1209
+ if (existingIcon) {
1210
+ existingIcon.remove();
1211
+ }
1212
+ button.append(createLucideIcon(icon, actionIconSize));
1213
+ }
1214
+ function createRecordedEventCard(entry, options) {
1215
+ const card = document.createElement("div");
1216
+ card.dataset.clueAuthoringEventId = entry.id;
1217
+ if (entry.highlightTrace) {
1218
+ card.dataset.clueAuthoringHighlightTrace = JSON.stringify(entry.highlightTrace);
1219
+ }
1220
+ card.style.position = "relative";
1221
+ card.style.display = "grid";
1222
+ card.style.width = "100%";
1223
+ card.style.gap = "10px";
1224
+ card.style.padding = "14px";
1225
+ card.style.borderRadius = "14px";
1226
+ card.style.border = entry.selected
1227
+ ? "1.5px solid rgba(244,150,39,0.9)"
1228
+ : "1px solid rgba(17,24,39,0.08)";
1229
+ card.style.boxShadow = entry.selected
1230
+ ? "rgba(244,150,39,0.12) 0px 14px 30px, rgba(244,150,39,0.08) 0px 0px 0px 1px"
1231
+ : "rgba(17,24,39,0.08) 0px 10px 24px, rgba(17,24,39,0.05) 0px 0px 0px 1px";
1232
+ card.style.textAlign = "left";
1233
+ card.style.cursor = "pointer";
1234
+ card.style.transition =
1235
+ "border-color 180ms ease, background 180ms ease, box-shadow 220ms ease, transform 220ms ease";
1236
+ card.style.color = "#111827";
1237
+ applyGlassSurfaceStyles(card, entry.selected ? "strong" : "default");
1238
+ if (entry.selected) {
1239
+ card.style.border = "1.5px solid rgba(244,150,39,0.58)";
1240
+ }
1241
+ if (entry.pendingRemoval) {
1242
+ card.style.opacity = "0.56";
1243
+ card.style.transform = "translateY(0px)";
1244
+ }
1245
+ const cardButton = document.createElement("button");
1246
+ cardButton.type = "button";
1247
+ cardButton.style.display = "grid";
1248
+ cardButton.style.gap = "10px";
1249
+ cardButton.style.padding = "0";
1250
+ cardButton.style.margin = "0";
1251
+ cardButton.style.border = "none";
1252
+ cardButton.style.background = "transparent";
1253
+ cardButton.style.textAlign = "left";
1254
+ cardButton.style.cursor = "pointer";
1255
+ cardButton.style.color = "inherit";
1256
+ const topRow = document.createElement("div");
1257
+ topRow.style.display = "flex";
1258
+ topRow.style.alignItems = "center";
1259
+ topRow.style.justifyContent = "space-between";
1260
+ topRow.style.gap = "12px";
1261
+ const title = document.createElement("p");
1262
+ title.textContent = entry.title;
1263
+ title.style.margin = "0";
1264
+ title.style.fontSize = "14px";
1265
+ title.style.fontWeight = "600";
1266
+ title.style.lineHeight = "1.5";
1267
+ const screenLabel = document.createElement("p");
1268
+ screenLabel.textContent = entry.screenLabel;
1269
+ screenLabel.style.margin = "0";
1270
+ screenLabel.style.fontSize = "11px";
1271
+ screenLabel.style.fontWeight = "600";
1272
+ screenLabel.style.lineHeight = "1.4";
1273
+ screenLabel.style.letterSpacing = "0.02em";
1274
+ screenLabel.style.color = "#777169";
1275
+ const time = document.createElement("span");
1276
+ time.textContent = entry.occurredAtLabel;
1277
+ time.style.fontSize = "12px";
1278
+ time.style.fontWeight = "500";
1279
+ time.style.color = "#777169";
1280
+ time.style.flexShrink = "0";
1281
+ const actionMeta = document.createElement("div");
1282
+ actionMeta.style.display = "inline-flex";
1283
+ actionMeta.style.alignItems = "center";
1284
+ actionMeta.style.gap = "8px";
1285
+ const removeButton = document.createElement("button");
1286
+ removeButton.dataset.clueAuthoringRemoveAction = entry.id;
1287
+ removeButton.type = "button";
1288
+ removeButton.textContent = "削除";
1289
+ removeButton.style.display = entry.pendingRemoval ? "none" : "inline-flex";
1290
+ removeButton.style.alignItems = "center";
1291
+ removeButton.style.justifyContent = "center";
1292
+ removeButton.style.padding = "4px 8px";
1293
+ removeButton.style.borderRadius = "9999px";
1294
+ removeButton.style.border = "1px solid rgba(17,24,39,0.08)";
1295
+ removeButton.style.background = "rgba(255,255,255,0.34)";
1296
+ removeButton.style.fontSize = "11px";
1297
+ removeButton.style.fontWeight = "600";
1298
+ removeButton.style.color = "#8d3d2e";
1299
+ removeButton.style.cursor = "pointer";
1300
+ actionMeta.append(removeButton, time);
1301
+ const badge = document.createElement("span");
1302
+ badge.textContent = entry.badgeLabel;
1303
+ badge.style.display = "inline-flex";
1304
+ badge.style.alignItems = "center";
1305
+ badge.style.width = "fit-content";
1306
+ badge.style.padding = "3px 8px";
1307
+ badge.style.borderRadius = "9999px";
1308
+ badge.style.background = entry.selected
1309
+ ? "rgba(244,150,39,0.12)"
1310
+ : "rgba(244,244,244,0.96)";
1311
+ badge.style.border = "1px solid rgba(17,24,39,0.06)";
1312
+ badge.style.fontSize = "11px";
1313
+ badge.style.lineHeight = "1.2";
1314
+ badge.style.fontWeight = "500";
1315
+ badge.style.color = "#777169";
1316
+ const relatedDisclosure = document.createElement("div");
1317
+ relatedDisclosure.style.display = "grid";
1318
+ relatedDisclosure.style.gap = "0";
1319
+ relatedDisclosure.style.borderRadius = "10px";
1320
+ relatedDisclosure.style.overflow = "hidden";
1321
+ applyGlassSurfaceStyles(relatedDisclosure, "soft");
1322
+ const relatedButton = document.createElement("button");
1323
+ relatedButton.type = "button";
1324
+ relatedButton.style.display = "flex";
1325
+ relatedButton.style.alignItems = "center";
1326
+ relatedButton.style.justifyContent = "space-between";
1327
+ relatedButton.style.width = "100%";
1328
+ relatedButton.style.gap = "12px";
1329
+ relatedButton.style.padding = "10px 12px";
1330
+ relatedButton.style.margin = "0";
1331
+ relatedButton.style.border = "none";
1332
+ relatedButton.style.background = "transparent";
1333
+ relatedButton.style.cursor = "pointer";
1334
+ relatedButton.style.color = "inherit";
1335
+ relatedButton.style.textAlign = "left";
1336
+ const relatedLabel = document.createElement("span");
1337
+ relatedLabel.textContent = "関連イベント";
1338
+ relatedLabel.style.fontSize = "11px";
1339
+ relatedLabel.style.fontWeight = "600";
1340
+ relatedLabel.style.color = "#111827";
1341
+ const relatedMeta = document.createElement("div");
1342
+ relatedMeta.style.display = "inline-flex";
1343
+ relatedMeta.style.alignItems = "center";
1344
+ relatedMeta.style.gap = "8px";
1345
+ const relatedCount = document.createElement("span");
1346
+ relatedCount.textContent = `${entry.actionUnit.atom_count}件`;
1347
+ relatedCount.style.fontSize = "11px";
1348
+ relatedCount.style.fontWeight = "600";
1349
+ relatedCount.style.color = "#111827";
1350
+ const relatedChevron = createLucideIcon(options.expanded ? ChevronUp : ChevronDown, 16);
1351
+ relatedChevron.style.color = "rgba(119,113,105,0.7)";
1352
+ relatedMeta.append(relatedCount, relatedChevron);
1353
+ relatedButton.append(relatedLabel, relatedMeta);
1354
+ const relatedContentWrap = document.createElement("div");
1355
+ relatedContentWrap.style.display = "grid";
1356
+ relatedContentWrap.style.overflow = "hidden";
1357
+ relatedContentWrap.style.maxHeight = "0px";
1358
+ relatedContentWrap.style.opacity = "0";
1359
+ relatedContentWrap.style.transition =
1360
+ "max-height 280ms cubic-bezier(0.22, 1, 0.36, 1), opacity 180ms ease";
1361
+ const evidence = document.createElement("div");
1362
+ evidence.style.display = "grid";
1363
+ evidence.style.gap = "10px";
1364
+ evidence.style.padding = "10px 12px 12px";
1365
+ evidence.style.borderTop = "1px solid rgba(17,24,39,0.08)";
1366
+ applyGlassSurfaceStyles(evidence, "soft");
1367
+ topRow.append(badge, actionMeta);
1368
+ cardButton.append(topRow);
1369
+ if (entry.screenLabel) {
1370
+ cardButton.append(screenLabel);
1371
+ }
1372
+ cardButton.append(title);
1373
+ relatedDisclosure.append(relatedButton, relatedContentWrap);
1374
+ card.append(cardButton, relatedDisclosure);
1375
+ if (entry.atoms.length > 0) {
1376
+ const atomSection = document.createElement("div");
1377
+ atomSection.style.display = "grid";
1378
+ atomSection.style.gap = "8px";
1379
+ const atomHeading = document.createElement("p");
1380
+ atomHeading.textContent = "関連イベント詳細";
1381
+ atomHeading.style.margin = "0";
1382
+ atomHeading.style.fontSize = "11px";
1383
+ atomHeading.style.fontWeight = "700";
1384
+ atomHeading.style.letterSpacing = "0.08em";
1385
+ atomHeading.style.textTransform = "uppercase";
1386
+ atomHeading.style.color = "#777169";
1387
+ atomSection.append(atomHeading);
1388
+ for (const atom of entry.atoms) {
1389
+ const atomRow = document.createElement("div");
1390
+ atomRow.style.display = "grid";
1391
+ atomRow.style.gap = "4px";
1392
+ atomRow.style.padding = "10px 12px";
1393
+ atomRow.style.borderRadius = "12px";
1394
+ applyGlassSurfaceStyles(atomRow, "soft");
1395
+ const atomTop = document.createElement("div");
1396
+ atomTop.style.display = "flex";
1397
+ atomTop.style.alignItems = "center";
1398
+ atomTop.style.justifyContent = "space-between";
1399
+ atomTop.style.gap = "12px";
1400
+ const atomTitle = document.createElement("p");
1401
+ atomTitle.textContent = atom.title;
1402
+ atomTitle.style.margin = "0";
1403
+ atomTitle.style.fontSize = "12px";
1404
+ atomTitle.style.fontWeight = "600";
1405
+ atomTitle.style.color = "#111827";
1406
+ const atomTime = document.createElement("span");
1407
+ atomTime.textContent = atom.occurredAtLabel;
1408
+ atomTime.style.fontSize = "11px";
1409
+ atomTime.style.color = "#777169";
1410
+ const atomMeta = document.createElement("p");
1411
+ atomMeta.textContent = atom.detailLabel ?? atom.subtitle ?? atom.badgeLabel;
1412
+ atomMeta.style.margin = "0";
1413
+ atomMeta.style.fontSize = "11px";
1414
+ atomMeta.style.lineHeight = "1.5";
1415
+ atomMeta.style.color = "#4e4e4e";
1416
+ atomMeta.style.wordBreak = "break-word";
1417
+ atomTop.append(atomTitle, atomTime);
1418
+ atomRow.append(atomTop, atomMeta);
1419
+ atomSection.append(atomRow);
1420
+ }
1421
+ evidence.append(atomSection);
1422
+ }
1423
+ relatedContentWrap.append(evidence);
1424
+ let isExpanded = options.expanded;
1425
+ const setExpanded = (nextExpanded, immediate = false) => {
1426
+ isExpanded = nextExpanded;
1427
+ relatedChevron.replaceWith(createLucideIcon(nextExpanded ? ChevronUp : ChevronDown, 16));
1428
+ const nextChevron = relatedMeta.querySelector("svg");
1429
+ if (nextChevron instanceof SVGElement) {
1430
+ nextChevron.style.display = "block";
1431
+ nextChevron.style.color = "rgba(119,113,105,0.7)";
1432
+ }
1433
+ if (immediate) {
1434
+ relatedContentWrap.style.transition = "none";
1435
+ }
1436
+ else {
1437
+ relatedContentWrap.style.transition =
1438
+ "max-height 280ms cubic-bezier(0.22, 1, 0.36, 1), opacity 180ms ease";
1439
+ }
1440
+ if (nextExpanded) {
1441
+ relatedContentWrap.style.display = "grid";
1442
+ relatedContentWrap.style.maxHeight = "0px";
1443
+ relatedContentWrap.style.opacity = "0";
1444
+ window.requestAnimationFrame(() => {
1445
+ relatedContentWrap.style.maxHeight = `${evidence.scrollHeight}px`;
1446
+ relatedContentWrap.style.opacity = "1";
1447
+ });
1448
+ }
1449
+ else {
1450
+ const currentHeight = evidence.scrollHeight;
1451
+ relatedContentWrap.style.maxHeight = `${currentHeight}px`;
1452
+ relatedContentWrap.style.opacity = "1";
1453
+ window.requestAnimationFrame(() => {
1454
+ relatedContentWrap.style.maxHeight = "0px";
1455
+ relatedContentWrap.style.opacity = "0";
1456
+ });
1457
+ }
1458
+ if (immediate) {
1459
+ window.requestAnimationFrame(() => {
1460
+ relatedContentWrap.style.transition =
1461
+ "max-height 280ms cubic-bezier(0.22, 1, 0.36, 1), opacity 180ms ease";
1462
+ });
1463
+ }
1464
+ };
1465
+ relatedContentWrap.addEventListener("transitionend", (event) => {
1466
+ if (event.propertyName !== "max-height") {
1467
+ return;
1468
+ }
1469
+ if (isExpanded) {
1470
+ relatedContentWrap.style.maxHeight = "none";
1471
+ relatedContentWrap.style.opacity = "1";
1472
+ }
1473
+ });
1474
+ setExpanded(options.expanded, true);
1475
+ cardButton.addEventListener("click", () => {
1476
+ if (entry.pendingRemoval) {
1477
+ return;
1478
+ }
1479
+ options.onSelect();
1480
+ });
1481
+ relatedButton.addEventListener("click", () => {
1482
+ const nextExpanded = !isExpanded;
1483
+ setExpanded(nextExpanded);
1484
+ options.onToggleExpanded();
1485
+ });
1486
+ removeButton.addEventListener("click", (event) => {
1487
+ event.stopPropagation();
1488
+ options.onMarkPendingRemoval();
1489
+ });
1490
+ if (entry.pendingRemoval) {
1491
+ const pendingOverlay = document.createElement("div");
1492
+ pendingOverlay.dataset.clueAuthoringPendingRemoval = entry.id;
1493
+ pendingOverlay.style.position = "absolute";
1494
+ pendingOverlay.style.inset = "0";
1495
+ pendingOverlay.style.display = "grid";
1496
+ pendingOverlay.style.alignContent = "center";
1497
+ pendingOverlay.style.justifyItems = "center";
1498
+ pendingOverlay.style.gap = "10px";
1499
+ pendingOverlay.style.borderRadius = "14px";
1500
+ pendingOverlay.style.background = "rgba(255,253,249,0.46)";
1501
+ pendingOverlay.style.backdropFilter = "blur(6px)";
1502
+ pendingOverlay.style.setProperty("-webkit-backdrop-filter", "blur(6px)");
1503
+ pendingOverlay.style.pointerEvents = "auto";
1504
+ const pendingBadge = document.createElement("div");
1505
+ pendingBadge.textContent = "削除予定";
1506
+ pendingBadge.style.display = "inline-flex";
1507
+ pendingBadge.style.alignItems = "center";
1508
+ pendingBadge.style.justifyContent = "center";
1509
+ pendingBadge.style.padding = "7px 12px";
1510
+ pendingBadge.style.borderRadius = "9999px";
1511
+ pendingBadge.style.background = "rgba(255,255,255,0.78)";
1512
+ pendingBadge.style.border = "1px solid rgba(17,24,39,0.08)";
1513
+ pendingBadge.style.fontSize = "12px";
1514
+ pendingBadge.style.fontWeight = "700";
1515
+ pendingBadge.style.color = "#7a5540";
1516
+ const restoreButton = document.createElement("button");
1517
+ restoreButton.dataset.clueAuthoringRestoreAction = entry.id;
1518
+ restoreButton.type = "button";
1519
+ restoreButton.textContent = "元に戻す";
1520
+ restoreButton.style.display = "inline-flex";
1521
+ restoreButton.style.alignItems = "center";
1522
+ restoreButton.style.justifyContent = "center";
1523
+ restoreButton.style.padding = "8px 12px";
1524
+ restoreButton.style.borderRadius = "10px";
1525
+ restoreButton.style.border = "1px solid rgba(17,24,39,0.08)";
1526
+ restoreButton.style.background = "rgba(255,255,255,0.86)";
1527
+ restoreButton.style.fontSize = "12px";
1528
+ restoreButton.style.fontWeight = "600";
1529
+ restoreButton.style.color = "#111827";
1530
+ restoreButton.style.cursor = "pointer";
1531
+ restoreButton.addEventListener("click", (event) => {
1532
+ event.stopPropagation();
1533
+ options.onRestorePendingRemoval();
1534
+ });
1535
+ pendingOverlay.append(pendingBadge, restoreButton);
1536
+ card.append(pendingOverlay);
1537
+ }
1538
+ card.addEventListener("mouseenter", () => {
1539
+ if (entry.pendingRemoval) {
1540
+ return;
1541
+ }
1542
+ card.style.transform = "translateY(-1px)";
1543
+ });
1544
+ card.addEventListener("mouseleave", () => {
1545
+ card.style.transform = "translateY(0px)";
1546
+ });
1547
+ return card;
1548
+ }
1549
+ function createEntrySection(titleText, entries, options) {
1550
+ const section = document.createElement("section");
1551
+ section.style.display = "grid";
1552
+ section.style.gap = "10px";
1553
+ const heading = document.createElement("h3");
1554
+ heading.textContent = titleText;
1555
+ heading.style.margin = "0";
1556
+ heading.style.fontSize = "13px";
1557
+ heading.style.fontWeight = "700";
1558
+ heading.style.lineHeight = "1.4";
1559
+ heading.style.color = "#111827";
1560
+ const cards = document.createElement("div");
1561
+ cards.style.display = "grid";
1562
+ cards.style.gap = "10px";
1563
+ cards.append(...entries.map((entry) => createRecordedEventCard(entry, {
1564
+ expanded: options.isExpanded(entry.id),
1565
+ onSelect: () => options.onSelect(entry.id),
1566
+ onToggleExpanded: () => options.onToggleExpanded(entry.id),
1567
+ onMarkPendingRemoval: () => options.onMarkPendingRemoval(entry.id),
1568
+ onRestorePendingRemoval: () => options.onRestorePendingRemoval(entry.id),
1569
+ })));
1570
+ section.append(heading, cards);
1571
+ return section;
1572
+ }
1573
+ function createPanel(session, recording, options) {
1574
+ const targetSpotlight = createTargetSpotlight();
1575
+ const panelWrap = document.createElement("div");
1576
+ panelWrap.style.position = "absolute";
1577
+ panelWrap.style.left = "0px";
1578
+ panelWrap.style.top = "0px";
1579
+ panelWrap.style.boxSizing = "border-box";
1580
+ panelWrap.style.width = "0";
1581
+ panelWrap.style.height = `${TOOLBAR_PANEL_HEIGHT}px`;
1582
+ panelWrap.style.opacity = "0";
1583
+ panelWrap.style.transform = "translateX(-26px) scale(0.96)";
1584
+ panelWrap.style.transformOrigin = "left center";
1585
+ panelWrap.style.overflow = "hidden";
1586
+ panelWrap.style.borderRadius = "16px";
1587
+ panelWrap.style.boxShadow = "rgba(17,24,39,0.08) 0px 16px 40px";
1588
+ panelWrap.style.zIndex = "10";
1589
+ panelWrap.style.transition =
1590
+ "width 320ms cubic-bezier(0.22, 1, 0.36, 1), opacity 240ms ease, transform 320ms cubic-bezier(0.22, 1, 0.36, 1)";
1591
+ panelWrap.style.pointerEvents = "none";
1592
+ panelWrap.style.willChange = "width, transform, opacity";
1593
+ applyGlassSurfaceStyles(panelWrap, "strong");
1594
+ const panel = document.createElement("div");
1595
+ panel.dataset.clueAuthoringPanel = "true";
1596
+ panel.style.boxSizing = "border-box";
1597
+ panel.style.width = `${TOOLBAR_PANEL_WIDTH}px`;
1598
+ panel.style.minWidth = `${TOOLBAR_PANEL_WIDTH}px`;
1599
+ panel.style.height = "100%";
1600
+ panel.style.padding = "10px";
1601
+ panel.style.borderRadius = "16px";
1602
+ panel.style.display = "grid";
1603
+ panel.style.gridTemplateRows = "auto 1fr";
1604
+ panel.style.gap = "12px";
1605
+ panel.style.pointerEvents = "auto";
1606
+ panel.style.background = "transparent";
1607
+ panel.style.border = "none";
1608
+ panel.style.backdropFilter = "none";
1609
+ panel.style.setProperty("-webkit-backdrop-filter", "none");
1610
+ const panelHeader = document.createElement("div");
1611
+ panelHeader.style.display = "grid";
1612
+ panelHeader.style.gap = "0";
1613
+ const eventList = document.createElement("div");
1614
+ eventList.dataset.clueAuthoringEventList = "true";
1615
+ eventList.style.display = "grid";
1616
+ eventList.style.width = "100%";
1617
+ eventList.style.minWidth = "0";
1618
+ eventList.style.alignContent = "start";
1619
+ eventList.style.gap = "10px";
1620
+ eventList.style.padding = "0";
1621
+ eventList.style.minHeight = "0";
1622
+ eventList.style.background = "rgba(255,255,255,0.08)";
1623
+ eventList.style.border = "1px solid rgba(255,255,255,0.14)";
1624
+ eventList.style.borderRadius = "14px";
1625
+ eventList.style.overflowY = "auto";
1626
+ const statusRow = document.createElement("div");
1627
+ statusRow.style.display = "flex";
1628
+ statusRow.style.alignItems = "center";
1629
+ statusRow.style.justifyContent = "space-between";
1630
+ statusRow.style.gap = "12px";
1631
+ statusRow.style.padding = "4px 4px 12px";
1632
+ statusRow.style.borderBottom = "1px solid rgba(17,24,39,0.08)";
1633
+ const statusPill = document.createElement("span");
1634
+ statusPill.style.display = "inline-flex";
1635
+ statusPill.style.alignItems = "center";
1636
+ statusPill.style.padding = "6px 10px";
1637
+ statusPill.style.borderRadius = "9999px";
1638
+ statusPill.style.fontSize = "10px";
1639
+ statusPill.style.fontWeight = "600";
1640
+ statusPill.style.letterSpacing = "0.22em";
1641
+ statusPill.style.textTransform = "uppercase";
1642
+ applyGlassSurfaceStyles(statusPill, "soft");
1643
+ const statusSummary = document.createElement("p");
1644
+ statusSummary.style.margin = "0";
1645
+ statusSummary.style.fontSize = "11px";
1646
+ statusSummary.style.color = "#777169";
1647
+ statusSummary.style.flexShrink = "0";
1648
+ const headerActions = document.createElement("div");
1649
+ headerActions.style.display = "inline-flex";
1650
+ headerActions.style.alignItems = "center";
1651
+ headerActions.style.gap = "8px";
1652
+ const resetButton = document.createElement("button");
1653
+ resetButton.type = "button";
1654
+ resetButton.dataset.clueAuthoringResetButton = "true";
1655
+ resetButton.textContent = "リセット";
1656
+ resetButton.style.display = "inline-flex";
1657
+ resetButton.style.alignItems = "center";
1658
+ resetButton.style.justifyContent = "center";
1659
+ resetButton.style.padding = "6px 10px";
1660
+ resetButton.style.borderRadius = "9999px";
1661
+ resetButton.style.border = "1px solid rgba(255,255,255,0.2)";
1662
+ resetButton.style.background = "rgba(255,255,255,0.18)";
1663
+ resetButton.style.fontSize = "11px";
1664
+ resetButton.style.fontWeight = "600";
1665
+ resetButton.style.letterSpacing = "0.02em";
1666
+ resetButton.style.color = "#777169";
1667
+ resetButton.style.cursor = "pointer";
1668
+ resetButton.style.backdropFilter = AUTHORING_GLASS_BLUR;
1669
+ resetButton.style.setProperty("-webkit-backdrop-filter", AUTHORING_GLASS_BLUR);
1670
+ resetButton.addEventListener("click", () => {
1671
+ void options.onResetRequested();
1672
+ });
1673
+ const emptyState = document.createElement("div");
1674
+ emptyState.style.minHeight = "260px";
1675
+ emptyState.style.borderRadius = "0";
1676
+ emptyState.style.border = "none";
1677
+ emptyState.style.background = "transparent";
1678
+ emptyState.style.display = "grid";
1679
+ emptyState.style.alignContent = "center";
1680
+ emptyState.style.justifyItems = "center";
1681
+ emptyState.style.gap = "12px";
1682
+ emptyState.style.padding = "12px 12px 18px";
1683
+ emptyState.style.textAlign = "center";
1684
+ const emptyIconWrap = document.createElement("div");
1685
+ emptyIconWrap.style.display = "grid";
1686
+ emptyIconWrap.style.placeItems = "center";
1687
+ emptyIconWrap.style.width = "44px";
1688
+ emptyIconWrap.style.height = "44px";
1689
+ emptyIconWrap.style.borderRadius = "12px";
1690
+ emptyIconWrap.style.boxShadow = "rgba(17,24,39,0.05) 0px 6px 16px";
1691
+ emptyIconWrap.style.color = "#777169";
1692
+ applyGlassSurfaceStyles(emptyIconWrap, "soft");
1693
+ emptyIconWrap.append(createLucideIcon(AUTHORING_ACTIONS[0]?.icon ?? [], 22));
1694
+ const emptyCopy = document.createElement("p");
1695
+ emptyCopy.style.margin = "0";
1696
+ emptyCopy.style.fontSize = "13px";
1697
+ emptyCopy.style.lineHeight = "1.6";
1698
+ emptyCopy.style.letterSpacing = "0.12px";
1699
+ emptyCopy.style.color = "#777169";
1700
+ const entriesList = document.createElement("div");
1701
+ entriesList.style.display = "grid";
1702
+ entriesList.style.width = "100%";
1703
+ entriesList.style.minWidth = "0";
1704
+ entriesList.style.gap = "18px";
1705
+ emptyState.append(emptyIconWrap, emptyCopy);
1706
+ headerActions.append(statusSummary, resetButton);
1707
+ statusRow.append(statusPill, headerActions);
1708
+ panelHeader.append(statusRow);
1709
+ eventList.append(emptyState, entriesList);
1710
+ panel.append(panelHeader, eventList);
1711
+ panelWrap.append(panel);
1712
+ let previousEntryCount = 0;
1713
+ let lastSnapshot = null;
1714
+ let spotlightEventId = null;
1715
+ const expandedEventIds = new Set();
1716
+ const renderSnapshot = (snapshot) => {
1717
+ lastSnapshot = snapshot;
1718
+ statusPill.textContent = snapshot.hasRecordedWindow
1719
+ ? snapshot.isRecording
1720
+ ? "Recording"
1721
+ : "Paused"
1722
+ : "Ready";
1723
+ statusPill.style.background = snapshot.hasRecordedWindow
1724
+ ? snapshot.isRecording
1725
+ ? "rgba(181,129,79,0.18)"
1726
+ : "rgba(245,242,239,0.5)"
1727
+ : "rgba(245,242,239,0.5)";
1728
+ statusPill.style.color =
1729
+ snapshot.hasRecordedWindow && snapshot.isRecording
1730
+ ? "#8f5b2f"
1731
+ : "#777169";
1732
+ statusSummary.textContent = `${snapshot.entries.length}件記録`;
1733
+ emptyCopy.textContent = !snapshot.hasRecordedWindow
1734
+ ? "イベントを記録するを押すと、その時点以降の action unit をここに表示します"
1735
+ : snapshot.isRecording
1736
+ ? "このまま操作を続けると、記録開始以降の action unit が時系列でここに追加されます"
1737
+ : "記録は一時停止中です。再開すると、その時点以降の action unit がここに追加されます";
1738
+ const shouldAutoScroll = snapshot.entries.length > previousEntryCount &&
1739
+ eventList.scrollHeight - eventList.scrollTop - eventList.clientHeight < 120;
1740
+ const nextEntryIds = new Set(snapshot.entries.map((entry) => entry.id));
1741
+ for (const expandedId of expandedEventIds) {
1742
+ if (!nextEntryIds.has(expandedId)) {
1743
+ expandedEventIds.delete(expandedId);
1744
+ }
1745
+ }
1746
+ if (spotlightEventId && !nextEntryIds.has(spotlightEventId)) {
1747
+ spotlightEventId = null;
1748
+ targetSpotlight.clear();
1749
+ }
1750
+ if (spotlightEventId) {
1751
+ const spotlightEntry = snapshot.entries.find((entry) => entry.id === spotlightEventId) ?? null;
1752
+ if (!spotlightEntry?.selected) {
1753
+ spotlightEventId = null;
1754
+ targetSpotlight.clear();
1755
+ }
1756
+ }
1757
+ const toggleSelected = (eventId) => {
1758
+ const targetEntry = lastSnapshot?.entries.find((candidate) => candidate.id === eventId) ?? null;
1759
+ if (targetEntry?.pendingRemoval) {
1760
+ return;
1761
+ }
1762
+ const isCurrentlySelected = targetEntry?.selected ?? false;
1763
+ if (isCurrentlySelected) {
1764
+ recording.toggleSelected(eventId);
1765
+ if (spotlightEventId === eventId) {
1766
+ spotlightEventId = null;
1767
+ targetSpotlight.clear();
1768
+ }
1769
+ recording.setHighlightTrace(eventId, null);
1770
+ }
1771
+ else if (targetEntry) {
1772
+ recording.toggleSelected(eventId);
1773
+ if (spotlightEventId && spotlightEventId !== eventId) {
1774
+ recording.setHighlightTrace(spotlightEventId, null);
1775
+ }
1776
+ spotlightEventId = eventId;
1777
+ const trace = targetSpotlight.focusEntry(targetEntry);
1778
+ recording.setHighlightTrace(eventId, trace);
1779
+ }
1780
+ };
1781
+ const toggleExpanded = (eventId) => {
1782
+ if (expandedEventIds.has(eventId)) {
1783
+ expandedEventIds.delete(eventId);
1784
+ }
1785
+ else {
1786
+ expandedEventIds.add(eventId);
1787
+ }
1788
+ if (lastSnapshot) {
1789
+ renderSnapshot(lastSnapshot);
1790
+ }
1791
+ };
1792
+ const markPendingRemoval = (eventId) => {
1793
+ if (spotlightEventId === eventId) {
1794
+ recording.setHighlightTrace(eventId, null);
1795
+ spotlightEventId = null;
1796
+ targetSpotlight.clear();
1797
+ }
1798
+ recording.markPendingRemoval(eventId);
1799
+ };
1800
+ const restorePendingRemoval = (eventId) => {
1801
+ recording.restorePendingRemoval(eventId);
1802
+ };
1803
+ const groupedSections = [];
1804
+ for (const entry of snapshot.entries) {
1805
+ const sectionTitle = entry.screenLabel ?? "イベント";
1806
+ const previousSection = groupedSections[groupedSections.length - 1] ?? null;
1807
+ if (previousSection && previousSection.title === sectionTitle) {
1808
+ previousSection.entries.push(entry);
1809
+ continue;
1810
+ }
1811
+ groupedSections.push({
1812
+ title: sectionTitle,
1813
+ entries: [entry],
1814
+ });
1815
+ }
1816
+ const sectionNodes = groupedSections.map((section) => createEntrySection(section.title, section.entries, {
1817
+ isExpanded: (eventId) => expandedEventIds.has(eventId),
1818
+ onSelect: toggleSelected,
1819
+ onToggleExpanded: toggleExpanded,
1820
+ onMarkPendingRemoval: markPendingRemoval,
1821
+ onRestorePendingRemoval: restorePendingRemoval,
1822
+ }));
1823
+ entriesList.replaceChildren(...sectionNodes);
1824
+ emptyState.style.display = snapshot.entries.length === 0 ? "grid" : "none";
1825
+ previousEntryCount = snapshot.entries.length;
1826
+ if (shouldAutoScroll) {
1827
+ window.requestAnimationFrame(() => {
1828
+ eventList.scrollTop = eventList.scrollHeight;
1829
+ });
1830
+ }
1831
+ };
1832
+ const unsubscribe = recording.subscribe(renderSnapshot);
1833
+ return {
1834
+ panelWrap,
1835
+ dispose: () => {
1836
+ unsubscribe();
1837
+ targetSpotlight.dispose();
1838
+ },
1839
+ };
1840
+ }
1841
+ function createBaseButton() {
1842
+ const logoSlotSize = Math.round((TOOLBAR_BASE_BUTTON_SIZE * 2) / 3);
1843
+ const logoSize = Math.round(TOOLBAR_BASE_BUTTON_SIZE * 0.53);
1844
+ const baseButton = document.createElement("button");
1845
+ baseButton.type = "button";
1846
+ baseButton.dataset.clueAuthoringBaseButton = "true";
1847
+ baseButton.setAttribute("aria-label", "Authoring toolbar");
1848
+ baseButton.setAttribute("aria-expanded", "false");
1849
+ baseButton.style.position = "absolute";
1850
+ baseButton.style.left = `${TOOLBAR_BASE_BUTTON_EDGE_OFFSET}px`;
1851
+ baseButton.style.top = `${TOOLBAR_BASE_BUTTON_EDGE_OFFSET}px`;
1852
+ baseButton.style.width = `${TOOLBAR_BASE_BUTTON_SIZE}px`;
1853
+ baseButton.style.height = `${TOOLBAR_BASE_BUTTON_SIZE}px`;
1854
+ baseButton.style.marginLeft = "0px";
1855
+ baseButton.style.marginTop = "0px";
1856
+ baseButton.style.display = "grid";
1857
+ baseButton.style.placeItems = "center";
1858
+ baseButton.style.borderRadius = `${Math.round(TOOLBAR_BASE_BUTTON_SIZE * 0.31)}px`;
1859
+ baseButton.style.boxShadow =
1860
+ "rgba(17,24,39,0.1) 0px 12px 28px, rgba(17,24,39,0.08) 0px 0px 0px 1px";
1861
+ baseButton.style.color = "#050505";
1862
+ baseButton.style.cursor = "grab";
1863
+ baseButton.style.pointerEvents = "auto";
1864
+ baseButton.style.transition =
1865
+ "transform 260ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 240ms ease";
1866
+ baseButton.style.touchAction = "none";
1867
+ applyGlassSurfaceStyles(baseButton, "strong");
1868
+ const logoSlot = document.createElement("div");
1869
+ logoSlot.style.display = "grid";
1870
+ logoSlot.style.placeItems = "center";
1871
+ logoSlot.style.width = `${logoSlotSize}px`;
1872
+ logoSlot.style.height = `${logoSlotSize}px`;
1873
+ logoSlot.style.justifySelf = "center";
1874
+ logoSlot.style.alignSelf = "center";
1875
+ logoSlot.style.borderRadius = `${Math.round(logoSlotSize * 0.4)}px`;
1876
+ applyGlassSurfaceStyles(logoSlot);
1877
+ logoSlot.append(createServiceLogo(logoSize));
1878
+ baseButton.append(logoSlot);
1879
+ return baseButton;
1880
+ }
1881
+ function createSaveDialog(options) {
1882
+ const backdrop = document.createElement("div");
1883
+ backdrop.dataset.clueAuthoringSaveDialog = "true";
1884
+ markAuthoringRoot(backdrop);
1885
+ backdrop.style.position = "fixed";
1886
+ backdrop.style.inset = "0";
1887
+ backdrop.style.display = "none";
1888
+ backdrop.style.placeItems = "center";
1889
+ backdrop.style.padding = "24px";
1890
+ backdrop.style.background = "rgba(255,251,247,0.24)";
1891
+ backdrop.style.backdropFilter = "blur(8px)";
1892
+ backdrop.style.setProperty("-webkit-backdrop-filter", "blur(8px)");
1893
+ backdrop.style.zIndex = "2147483400";
1894
+ const dialog = document.createElement("div");
1895
+ markAuthoringRoot(dialog);
1896
+ dialog.style.width = "min(560px, 100%)";
1897
+ dialog.style.maxHeight = "min(680px, calc(100vh - 48px))";
1898
+ dialog.style.display = "grid";
1899
+ dialog.style.gridTemplateRows = "auto auto auto 1fr auto";
1900
+ dialog.style.gap = "14px";
1901
+ dialog.style.padding = "18px";
1902
+ dialog.style.borderRadius = "20px";
1903
+ dialog.style.overflow = "hidden";
1904
+ dialog.style.boxShadow = "rgba(17,24,39,0.12) 0px 22px 48px";
1905
+ dialog.style.color = "#111827";
1906
+ applyGlassSurfaceStyles(dialog, "strong");
1907
+ const heading = document.createElement("div");
1908
+ heading.style.display = "grid";
1909
+ heading.style.gap = "4px";
1910
+ const title = document.createElement("h3");
1911
+ title.textContent = "Business Event を保存";
1912
+ title.style.margin = "0";
1913
+ title.style.fontSize = "18px";
1914
+ title.style.fontWeight = "700";
1915
+ title.style.lineHeight = "1.4";
1916
+ const description = document.createElement("p");
1917
+ description.textContent = "保存対象の action_unit を確認して、下書きまたは公開を選択します。";
1918
+ description.style.margin = "0";
1919
+ description.style.fontSize = "12px";
1920
+ description.style.lineHeight = "1.6";
1921
+ description.style.color = "#777169";
1922
+ heading.append(title, description);
1923
+ const titleInput = document.createElement("input");
1924
+ titleInput.dataset.clueAuthoringSaveTitle = "true";
1925
+ titleInput.type = "text";
1926
+ titleInput.placeholder = "Business Event タイトル";
1927
+ titleInput.style.width = "100%";
1928
+ titleInput.style.borderRadius = "12px";
1929
+ titleInput.style.border = "1px solid rgba(17,24,39,0.08)";
1930
+ titleInput.style.padding = "10px 12px";
1931
+ titleInput.style.background = "rgba(255,255,255,0.48)";
1932
+ titleInput.style.fontSize = "14px";
1933
+ titleInput.style.color = "#111827";
1934
+ const descriptionInput = document.createElement("textarea");
1935
+ descriptionInput.dataset.clueAuthoringSaveDescription = "true";
1936
+ descriptionInput.placeholder = "Business Event 説明";
1937
+ descriptionInput.rows = 3;
1938
+ descriptionInput.style.width = "100%";
1939
+ descriptionInput.style.resize = "vertical";
1940
+ descriptionInput.style.borderRadius = "12px";
1941
+ descriptionInput.style.border = "1px solid rgba(17,24,39,0.08)";
1942
+ descriptionInput.style.padding = "10px 12px";
1943
+ descriptionInput.style.background = "rgba(255,255,255,0.42)";
1944
+ descriptionInput.style.fontSize = "13px";
1945
+ descriptionInput.style.color = "#111827";
1946
+ const modeRow = document.createElement("div");
1947
+ modeRow.style.display = "inline-flex";
1948
+ modeRow.style.alignItems = "center";
1949
+ modeRow.style.gap = "10px";
1950
+ const draftButton = document.createElement("button");
1951
+ draftButton.dataset.clueAuthoringSaveMode = "draft";
1952
+ draftButton.type = "button";
1953
+ draftButton.textContent = "下書き";
1954
+ draftButton.style.padding = "8px 12px";
1955
+ draftButton.style.borderRadius = "9999px";
1956
+ draftButton.style.border = "1px solid rgba(17,24,39,0.08)";
1957
+ draftButton.style.background = "rgba(255,255,255,0.56)";
1958
+ draftButton.style.fontSize = "12px";
1959
+ draftButton.style.fontWeight = "600";
1960
+ draftButton.style.cursor = "pointer";
1961
+ const publishButton = document.createElement("button");
1962
+ publishButton.dataset.clueAuthoringSaveMode = "publish";
1963
+ publishButton.type = "button";
1964
+ publishButton.textContent = "公開";
1965
+ publishButton.style.padding = "8px 12px";
1966
+ publishButton.style.borderRadius = "9999px";
1967
+ publishButton.style.border = "1px solid rgba(17,24,39,0.08)";
1968
+ publishButton.style.background = "rgba(255,255,255,0.32)";
1969
+ publishButton.style.fontSize = "12px";
1970
+ publishButton.style.fontWeight = "600";
1971
+ publishButton.style.cursor = "pointer";
1972
+ const listWrap = document.createElement("div");
1973
+ listWrap.dataset.clueAuthoringSaveSelectionList = "true";
1974
+ listWrap.style.display = "grid";
1975
+ listWrap.style.gap = "10px";
1976
+ listWrap.style.minHeight = "120px";
1977
+ listWrap.style.maxHeight = "240px";
1978
+ listWrap.style.overflow = "auto";
1979
+ listWrap.style.padding = "4px";
1980
+ const validation = document.createElement("p");
1981
+ validation.style.margin = "0";
1982
+ validation.style.fontSize = "12px";
1983
+ validation.style.color = "#8d3d2e";
1984
+ const footer = document.createElement("div");
1985
+ footer.style.display = "flex";
1986
+ footer.style.justifyContent = "flex-end";
1987
+ footer.style.gap = "10px";
1988
+ const cancelButton = document.createElement("button");
1989
+ cancelButton.dataset.clueAuthoringSaveCancel = "true";
1990
+ cancelButton.type = "button";
1991
+ cancelButton.textContent = "Cancel";
1992
+ cancelButton.style.padding = "9px 12px";
1993
+ cancelButton.style.borderRadius = "10px";
1994
+ cancelButton.style.border = "1px solid rgba(17,24,39,0.08)";
1995
+ cancelButton.style.background = "rgba(255,255,255,0.54)";
1996
+ cancelButton.style.fontSize = "12px";
1997
+ cancelButton.style.fontWeight = "600";
1998
+ cancelButton.style.cursor = "pointer";
1999
+ const confirmButton = document.createElement("button");
2000
+ confirmButton.dataset.clueAuthoringSaveConfirm = "true";
2001
+ confirmButton.type = "button";
2002
+ confirmButton.textContent = "保存";
2003
+ confirmButton.style.padding = "9px 14px";
2004
+ confirmButton.style.borderRadius = "10px";
2005
+ confirmButton.style.border = "1px solid rgba(181,129,79,0.14)";
2006
+ confirmButton.style.background = "rgba(181,129,79,0.16)";
2007
+ confirmButton.style.fontSize = "12px";
2008
+ confirmButton.style.fontWeight = "700";
2009
+ confirmButton.style.color = "#8f5b2f";
2010
+ confirmButton.style.cursor = "pointer";
2011
+ let saveMode = "draft";
2012
+ const resetForm = () => {
2013
+ titleInput.value = "";
2014
+ descriptionInput.value = "";
2015
+ saveMode = "draft";
2016
+ syncSaveMode();
2017
+ };
2018
+ const syncSaveMode = () => {
2019
+ draftButton.style.background =
2020
+ saveMode === "draft" ? "rgba(181,129,79,0.16)" : "rgba(255,255,255,0.56)";
2021
+ draftButton.style.color = saveMode === "draft" ? "#8f5b2f" : "#111827";
2022
+ publishButton.style.background =
2023
+ saveMode === "publish"
2024
+ ? "rgba(181,129,79,0.16)"
2025
+ : "rgba(255,255,255,0.32)";
2026
+ publishButton.style.color = saveMode === "publish" ? "#8f5b2f" : "#111827";
2027
+ confirmButton.textContent = saveMode === "publish" ? "公開する" : "下書き保存";
2028
+ };
2029
+ const renderSelectionList = () => {
2030
+ const snapshot = options.recording.getSnapshot();
2031
+ const activeEntries = snapshot.entries.filter((entry) => !entry.pendingRemoval);
2032
+ listWrap.replaceChildren(...activeEntries.map((entry) => {
2033
+ const row = document.createElement("div");
2034
+ row.style.display = "grid";
2035
+ row.style.gap = "2px";
2036
+ row.style.padding = "10px 12px";
2037
+ row.style.borderRadius = "12px";
2038
+ row.style.background = "rgba(255,255,255,0.36)";
2039
+ row.style.border = "1px solid rgba(17,24,39,0.06)";
2040
+ const rowTitle = document.createElement("p");
2041
+ rowTitle.textContent = entry.title;
2042
+ rowTitle.style.margin = "0";
2043
+ rowTitle.style.fontSize = "13px";
2044
+ rowTitle.style.fontWeight = "600";
2045
+ const rowMeta = document.createElement("p");
2046
+ rowMeta.textContent = entry.screenLabel ?? entry.badgeLabel;
2047
+ rowMeta.style.margin = "0";
2048
+ rowMeta.style.fontSize = "11px";
2049
+ rowMeta.style.color = "#777169";
2050
+ row.append(rowTitle, rowMeta);
2051
+ return row;
2052
+ }));
2053
+ validation.textContent =
2054
+ activeEntries.length === 0
2055
+ ? "保存対象の action_unit がありません。削除予定を戻してください。"
2056
+ : titleInput.value.trim().length === 0
2057
+ ? "タイトルを入力してください。"
2058
+ : "";
2059
+ confirmButton.disabled =
2060
+ activeEntries.length === 0 || titleInput.value.trim().length === 0;
2061
+ confirmButton.style.opacity = confirmButton.disabled ? "0.5" : "1";
2062
+ };
2063
+ titleInput.addEventListener("input", renderSelectionList);
2064
+ draftButton.addEventListener("click", () => {
2065
+ saveMode = "draft";
2066
+ syncSaveMode();
2067
+ });
2068
+ publishButton.addEventListener("click", () => {
2069
+ saveMode = "publish";
2070
+ syncSaveMode();
2071
+ });
2072
+ cancelButton.addEventListener("click", () => {
2073
+ backdrop.style.display = "none";
2074
+ });
2075
+ confirmButton.addEventListener("click", async () => {
2076
+ const snapshot = options.recording.getSnapshot();
2077
+ const activeEntries = snapshot.entries.filter((entry) => !entry.pendingRemoval);
2078
+ if (activeEntries.length === 0 || titleInput.value.trim().length === 0) {
2079
+ renderSelectionList();
2080
+ return;
2081
+ }
2082
+ confirmButton.disabled = true;
2083
+ try {
2084
+ await options.onSave({
2085
+ title: titleInput.value.trim(),
2086
+ description: descriptionInput.value.trim(),
2087
+ saveMode,
2088
+ selectedActionUnitIds: activeEntries.map((entry) => entry.id),
2089
+ });
2090
+ backdrop.style.display = "none";
2091
+ await options.onSaveCompleted({ saveMode });
2092
+ resetForm();
2093
+ }
2094
+ finally {
2095
+ confirmButton.disabled = false;
2096
+ renderSelectionList();
2097
+ }
2098
+ });
2099
+ backdrop.addEventListener("click", (event) => {
2100
+ if (event.target === backdrop) {
2101
+ backdrop.style.display = "none";
2102
+ }
2103
+ });
2104
+ modeRow.append(draftButton, publishButton);
2105
+ footer.append(cancelButton, confirmButton);
2106
+ dialog.append(heading, titleInput, descriptionInput, modeRow, listWrap, validation, footer);
2107
+ backdrop.append(dialog);
2108
+ syncSaveMode();
2109
+ return {
2110
+ root: backdrop,
2111
+ open() {
2112
+ backdrop.style.display = "grid";
2113
+ renderSelectionList();
2114
+ window.requestAnimationFrame(() => {
2115
+ titleInput.focus();
2116
+ });
2117
+ },
2118
+ close() {
2119
+ backdrop.style.display = "none";
2120
+ },
2121
+ dispose() {
2122
+ backdrop.remove();
2123
+ },
2124
+ };
2125
+ }
2126
+ function createSaveCompleteDialog(options) {
2127
+ const backdrop = document.createElement("div");
2128
+ backdrop.dataset.clueAuthoringSaveCompleteDialog = "true";
2129
+ markAuthoringRoot(backdrop);
2130
+ backdrop.style.position = "fixed";
2131
+ backdrop.style.inset = "0";
2132
+ backdrop.style.display = "none";
2133
+ backdrop.style.placeItems = "center";
2134
+ backdrop.style.padding = "24px";
2135
+ backdrop.style.background = "rgba(255,251,247,0.22)";
2136
+ backdrop.style.backdropFilter = "blur(8px)";
2137
+ backdrop.style.setProperty("-webkit-backdrop-filter", "blur(8px)");
2138
+ backdrop.style.zIndex = "2147483410";
2139
+ const dialog = document.createElement("div");
2140
+ markAuthoringRoot(dialog);
2141
+ dialog.style.width = "min(420px, 100%)";
2142
+ dialog.style.display = "grid";
2143
+ dialog.style.gap = "14px";
2144
+ dialog.style.padding = "18px";
2145
+ dialog.style.borderRadius = "18px";
2146
+ dialog.style.boxShadow = "rgba(17,24,39,0.12) 0px 22px 48px";
2147
+ dialog.style.color = "#111827";
2148
+ applyGlassSurfaceStyles(dialog, "strong");
2149
+ const title = document.createElement("h3");
2150
+ title.style.margin = "0";
2151
+ title.style.fontSize = "18px";
2152
+ title.style.fontWeight = "700";
2153
+ title.style.lineHeight = "1.4";
2154
+ const description = document.createElement("p");
2155
+ description.style.margin = "0";
2156
+ description.style.fontSize = "13px";
2157
+ description.style.lineHeight = "1.7";
2158
+ description.style.color = "#5b554e";
2159
+ description.textContent = "このまま終了しますか?";
2160
+ const footer = document.createElement("div");
2161
+ footer.style.display = "flex";
2162
+ footer.style.justifyContent = "flex-end";
2163
+ footer.style.gap = "10px";
2164
+ const continueButton = document.createElement("button");
2165
+ continueButton.dataset.clueAuthoringSaveCompleteContinue = "true";
2166
+ continueButton.type = "button";
2167
+ continueButton.textContent = "続けて記録する";
2168
+ continueButton.style.padding = "9px 12px";
2169
+ continueButton.style.borderRadius = "10px";
2170
+ continueButton.style.border = "1px solid rgba(17,24,39,0.08)";
2171
+ continueButton.style.background = "rgba(255,255,255,0.54)";
2172
+ continueButton.style.fontSize = "12px";
2173
+ continueButton.style.fontWeight = "600";
2174
+ continueButton.style.cursor = "pointer";
2175
+ const finishButton = document.createElement("button");
2176
+ finishButton.dataset.clueAuthoringSaveCompleteFinish = "true";
2177
+ finishButton.type = "button";
2178
+ finishButton.textContent = "終了する";
2179
+ finishButton.style.padding = "9px 14px";
2180
+ finishButton.style.borderRadius = "10px";
2181
+ finishButton.style.border = "1px solid rgba(181,129,79,0.14)";
2182
+ finishButton.style.background = "rgba(181,129,79,0.16)";
2183
+ finishButton.style.fontSize = "12px";
2184
+ finishButton.style.fontWeight = "700";
2185
+ finishButton.style.color = "#8f5b2f";
2186
+ finishButton.style.cursor = "pointer";
2187
+ const setPending = (pending) => {
2188
+ continueButton.disabled = pending;
2189
+ finishButton.disabled = pending;
2190
+ continueButton.style.opacity = pending ? "0.5" : "1";
2191
+ finishButton.style.opacity = pending ? "0.5" : "1";
2192
+ };
2193
+ continueButton.addEventListener("click", () => {
2194
+ setPending(true);
2195
+ void options
2196
+ .onRestartAuthoring()
2197
+ .then(() => {
2198
+ backdrop.style.display = "none";
2199
+ })
2200
+ .finally(() => {
2201
+ setPending(false);
2202
+ });
2203
+ });
2204
+ finishButton.addEventListener("click", () => {
2205
+ options.onCloseAuthoring();
2206
+ });
2207
+ backdrop.addEventListener("click", (event) => {
2208
+ if (event.target === backdrop) {
2209
+ backdrop.style.display = "none";
2210
+ }
2211
+ });
2212
+ footer.append(continueButton, finishButton);
2213
+ dialog.append(title, description, footer);
2214
+ backdrop.append(dialog);
2215
+ return {
2216
+ root: backdrop,
2217
+ open(params) {
2218
+ title.textContent =
2219
+ params.saveMode === "publish" ? "公開が完了しました" : "下書き保存が完了しました";
2220
+ description.textContent = "このまま終了しますか?";
2221
+ backdrop.style.display = "grid";
2222
+ setPending(false);
2223
+ },
2224
+ close() {
2225
+ backdrop.style.display = "none";
2226
+ },
2227
+ dispose() {
2228
+ backdrop.remove();
2229
+ },
2230
+ };
2231
+ }
2232
+ function createResetConfirmDialog(options) {
2233
+ const backdrop = document.createElement("div");
2234
+ backdrop.dataset.clueAuthoringResetDialog = "true";
2235
+ markAuthoringRoot(backdrop);
2236
+ backdrop.style.position = "fixed";
2237
+ backdrop.style.inset = "0";
2238
+ backdrop.style.display = "none";
2239
+ backdrop.style.placeItems = "center";
2240
+ backdrop.style.padding = "24px";
2241
+ backdrop.style.background = "rgba(255,251,247,0.22)";
2242
+ backdrop.style.backdropFilter = "blur(8px)";
2243
+ backdrop.style.setProperty("-webkit-backdrop-filter", "blur(8px)");
2244
+ backdrop.style.zIndex = "2147483405";
2245
+ const dialog = document.createElement("div");
2246
+ markAuthoringRoot(dialog);
2247
+ dialog.style.width = "min(420px, 100%)";
2248
+ dialog.style.display = "grid";
2249
+ dialog.style.gap = "14px";
2250
+ dialog.style.padding = "18px";
2251
+ dialog.style.borderRadius = "18px";
2252
+ dialog.style.boxShadow = "rgba(17,24,39,0.12) 0px 22px 48px";
2253
+ dialog.style.color = "#111827";
2254
+ applyGlassSurfaceStyles(dialog, "strong");
2255
+ const title = document.createElement("h3");
2256
+ title.style.margin = "0";
2257
+ title.style.fontSize = "18px";
2258
+ title.style.fontWeight = "700";
2259
+ title.style.lineHeight = "1.4";
2260
+ title.textContent = "イベントをリセット";
2261
+ const description = document.createElement("p");
2262
+ description.style.margin = "0";
2263
+ description.style.fontSize = "13px";
2264
+ description.style.lineHeight = "1.7";
2265
+ description.style.color = "#5b554e";
2266
+ description.textContent = "現在のイベントをすべてリセットします。よろしいですか?";
2267
+ const footer = document.createElement("div");
2268
+ footer.style.display = "flex";
2269
+ footer.style.justifyContent = "flex-end";
2270
+ footer.style.gap = "10px";
2271
+ const cancelButton = document.createElement("button");
2272
+ cancelButton.dataset.clueAuthoringResetCancel = "true";
2273
+ cancelButton.type = "button";
2274
+ cancelButton.textContent = "キャンセル";
2275
+ cancelButton.style.padding = "9px 12px";
2276
+ cancelButton.style.borderRadius = "10px";
2277
+ cancelButton.style.border = "1px solid rgba(17,24,39,0.08)";
2278
+ cancelButton.style.background = "rgba(255,255,255,0.54)";
2279
+ cancelButton.style.fontSize = "12px";
2280
+ cancelButton.style.fontWeight = "600";
2281
+ cancelButton.style.cursor = "pointer";
2282
+ const confirmButton = document.createElement("button");
2283
+ confirmButton.dataset.clueAuthoringResetConfirm = "true";
2284
+ confirmButton.type = "button";
2285
+ confirmButton.textContent = "はい";
2286
+ confirmButton.style.padding = "9px 14px";
2287
+ confirmButton.style.borderRadius = "10px";
2288
+ confirmButton.style.border = "1px solid rgba(181,129,79,0.14)";
2289
+ confirmButton.style.background = "rgba(181,129,79,0.16)";
2290
+ confirmButton.style.fontSize = "12px";
2291
+ confirmButton.style.fontWeight = "700";
2292
+ confirmButton.style.color = "#8f5b2f";
2293
+ confirmButton.style.cursor = "pointer";
2294
+ const setPending = (pending) => {
2295
+ cancelButton.disabled = pending;
2296
+ confirmButton.disabled = pending;
2297
+ cancelButton.style.opacity = pending ? "0.5" : "1";
2298
+ confirmButton.style.opacity = pending ? "0.5" : "1";
2299
+ };
2300
+ cancelButton.addEventListener("click", () => {
2301
+ backdrop.style.display = "none";
2302
+ });
2303
+ confirmButton.addEventListener("click", () => {
2304
+ setPending(true);
2305
+ void options
2306
+ .onConfirm()
2307
+ .then(() => {
2308
+ backdrop.style.display = "none";
2309
+ })
2310
+ .finally(() => {
2311
+ setPending(false);
2312
+ });
2313
+ });
2314
+ backdrop.addEventListener("click", (event) => {
2315
+ if (event.target === backdrop) {
2316
+ backdrop.style.display = "none";
2317
+ }
2318
+ });
2319
+ footer.append(cancelButton, confirmButton);
2320
+ dialog.append(title, description, footer);
2321
+ backdrop.append(dialog);
2322
+ return {
2323
+ root: backdrop,
2324
+ open() {
2325
+ backdrop.style.display = "grid";
2326
+ setPending(false);
2327
+ },
2328
+ close() {
2329
+ backdrop.style.display = "none";
2330
+ },
2331
+ dispose() {
2332
+ backdrop.remove();
2333
+ },
2334
+ };
2335
+ }
2336
+ export function mountToolbar(session, options) {
2337
+ const existingRoot = document.getElementById(TOOLBAR_ROOT_ID);
2338
+ if (existingRoot) {
2339
+ options.onReady();
2340
+ return Promise.resolve({
2341
+ root: existingRoot,
2342
+ dispose: toolbarDisposers.get(existingRoot) ?? (() => undefined),
2343
+ });
2344
+ }
2345
+ const shell = document.createElement("div");
2346
+ shell.id = TOOLBAR_ROOT_ID;
2347
+ markAuthoringRoot(shell);
2348
+ shell.style.position = "fixed";
2349
+ shell.style.top = `${TOOLBAR_INITIAL_TOP}px`;
2350
+ shell.style.left = `${TOOLBAR_VIEWPORT_GUTTER}px`;
2351
+ shell.style.zIndex = "2147483000";
2352
+ shell.style.pointerEvents = "none";
2353
+ shell.style.width = `${TOOLBAR_COLLAPSED_WIDTH}px`;
2354
+ shell.style.height = `${TOOLBAR_COLLAPSED_HEIGHT}px`;
2355
+ const toolbar = document.createElement("div");
2356
+ toolbar.style.position = "relative";
2357
+ toolbar.style.pointerEvents = "none";
2358
+ toolbar.style.width = "100%";
2359
+ toolbar.style.height = "100%";
2360
+ toolbar.style.overflow = "visible";
2361
+ toolbar.style.fontFamily =
2362
+ 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
2363
+ toolbar.style.userSelect = "none";
2364
+ const cluster = document.createElement("div");
2365
+ cluster.style.position = "relative";
2366
+ cluster.style.width = `${TOOLBAR_COLLAPSED_WIDTH}px`;
2367
+ cluster.style.height = `${TOOLBAR_COLLAPSED_HEIGHT}px`;
2368
+ cluster.style.pointerEvents = "none";
2369
+ let closeToolbarAndEnd = () => undefined;
2370
+ const saveCompleteDialog = createSaveCompleteDialog({
2371
+ onCloseAuthoring: () => {
2372
+ closeToolbarAndEnd();
2373
+ },
2374
+ onRestartAuthoring: async () => {
2375
+ const snapshot = options.recording.getSnapshot();
2376
+ if (!snapshot.isRecording) {
2377
+ await options.onSetRecording(true);
2378
+ }
2379
+ options.recording.replaceActionUnits([]);
2380
+ },
2381
+ });
2382
+ const resetConfirmDialog = createResetConfirmDialog({
2383
+ onConfirm: async () => {
2384
+ await options.onResetRecording();
2385
+ },
2386
+ });
2387
+ const saveDialog = createSaveDialog({
2388
+ recording: options.recording,
2389
+ onSave: options.onSave,
2390
+ onSaveCompleted: async ({ saveMode }) => {
2391
+ saveCompleteDialog.open({ saveMode });
2392
+ },
2393
+ });
2394
+ const panelHandle = createPanel(session, options.recording, {
2395
+ onResetRequested: async () => {
2396
+ saveDialog.close();
2397
+ saveCompleteDialog.close();
2398
+ resetConfirmDialog.open();
2399
+ },
2400
+ });
2401
+ const panelWrap = panelHandle.panelWrap;
2402
+ const baseButton = createBaseButton();
2403
+ const actionElements = AUTHORING_ACTIONS.map((action) => {
2404
+ const button = createActionButton(action.label, action.x, action.y);
2405
+ button.dataset.clueAuthoringAction = action.key;
2406
+ button.append(createLucideIcon(action.icon, 20));
2407
+ cluster.append(button);
2408
+ return {
2409
+ ...action,
2410
+ button,
2411
+ };
2412
+ });
2413
+ let isDisposed = false;
2414
+ let isExpanded = false;
2415
+ const baseAnchorX = TOOLBAR_BASE_BUTTON_EDGE_OFFSET + TOOLBAR_BASE_BUTTON_SIZE / 2;
2416
+ const baseAnchorY = TOOLBAR_BASE_BUTTON_EDGE_OFFSET + TOOLBAR_BASE_BUTTON_SIZE / 2;
2417
+ const maxActionOffset = Math.max(...AUTHORING_ACTIONS.map((action) => action.y));
2418
+ const syncFloatingLayout = () => {
2419
+ const shellLeft = Number.parseFloat(shell.style.left) || shell.offsetLeft;
2420
+ const shellTop = Number.parseFloat(shell.style.top) || shell.offsetTop;
2421
+ const viewport = getViewportSize();
2422
+ const anchorViewportX = shellLeft + baseAnchorX;
2423
+ const anchorViewportY = shellTop + baseAnchorY;
2424
+ const bottomMostActionBottom = anchorViewportY + maxActionOffset + TOOLBAR_ACTION_BUTTON_SIZE / 2;
2425
+ const shouldStackUp = bottomMostActionBottom > viewport.height - TOOLBAR_VIEWPORT_GUTTER;
2426
+ const direction = shouldStackUp ? -1 : 1;
2427
+ actionElements.forEach((action) => {
2428
+ action.button.dataset.clueAuthoringStackDirection = shouldStackUp ? "up" : "down";
2429
+ action.button.style.transform = isExpanded
2430
+ ? `translate(${action.x}px, ${action.y * direction}px) scale(1)`
2431
+ : "translate(0px, 0px) scale(0.72)";
2432
+ });
2433
+ if (!isExpanded) {
2434
+ return;
2435
+ }
2436
+ const preferredRightPanelLeft = anchorViewportX + TOOLBAR_BASE_BUTTON_SIZE / 2 + TOOLBAR_DOCK_GAP;
2437
+ const preferredLeftPanelLeft = anchorViewportX -
2438
+ TOOLBAR_BASE_BUTTON_SIZE / 2 -
2439
+ TOOLBAR_DOCK_GAP -
2440
+ TOOLBAR_PANEL_WIDTH;
2441
+ const maxPanelLeft = Math.max(TOOLBAR_VIEWPORT_GUTTER, viewport.width - TOOLBAR_PANEL_WIDTH - TOOLBAR_VIEWPORT_GUTTER);
2442
+ const rightPanelLeft = Math.min(Math.max(preferredRightPanelLeft, TOOLBAR_VIEWPORT_GUTTER), maxPanelLeft);
2443
+ const leftPanelLeft = Math.min(Math.max(preferredLeftPanelLeft, TOOLBAR_VIEWPORT_GUTTER), maxPanelLeft);
2444
+ const canPlaceRight = preferredRightPanelLeft + TOOLBAR_PANEL_WIDTH <=
2445
+ viewport.width - TOOLBAR_VIEWPORT_GUTTER;
2446
+ const canPlaceLeft = preferredLeftPanelLeft >= TOOLBAR_VIEWPORT_GUTTER;
2447
+ const nextPanelLeft = canPlaceRight || !canPlaceLeft ? rightPanelLeft : leftPanelLeft;
2448
+ const preferredPanelTop = anchorViewportY - TOOLBAR_BASE_BUTTON_SIZE / 2;
2449
+ const maxPanelTop = Math.max(TOOLBAR_INITIAL_TOP, viewport.height - TOOLBAR_PANEL_HEIGHT - TOOLBAR_VIEWPORT_GUTTER);
2450
+ const nextPanelTop = Math.min(Math.max(preferredPanelTop, TOOLBAR_INITIAL_TOP), maxPanelTop);
2451
+ panelWrap.style.left = `${Math.round(nextPanelLeft - shellLeft)}px`;
2452
+ panelWrap.style.top = `${Math.round(nextPanelTop - shellTop)}px`;
2453
+ panelWrap.style.transformOrigin =
2454
+ nextPanelLeft >= anchorViewportX ? "left top" : "right top";
2455
+ };
2456
+ const syncExpandedState = (toolbarDrag) => {
2457
+ baseButton.setAttribute("aria-expanded", isExpanded ? "true" : "false");
2458
+ baseButton.style.transform = isExpanded ? "scale(1.04)" : "scale(1)";
2459
+ baseButton.style.boxShadow = isExpanded
2460
+ ? "rgba(17,24,39,0.14) 0px 16px 34px, rgba(17,24,39,0.1) 0px 0px 0px 1px"
2461
+ : "rgba(17,24,39,0.1) 0px 12px 28px, rgba(17,24,39,0.08) 0px 0px 0px 1px";
2462
+ panelWrap.style.width = isExpanded ? `${TOOLBAR_PANEL_WIDTH}px` : "0";
2463
+ panelWrap.style.opacity = isExpanded ? "1" : "0";
2464
+ panelWrap.style.transform = isExpanded
2465
+ ? "translateX(0px) scale(1)"
2466
+ : "translateX(-26px) scale(0.96)";
2467
+ panelWrap.style.pointerEvents = isExpanded ? "auto" : "none";
2468
+ actionElements.forEach((action, index) => {
2469
+ action.button.style.transitionDelay = isExpanded
2470
+ ? `${index * 28}ms`
2471
+ : `${(actionElements.length - index - 1) * 18}ms`;
2472
+ action.button.style.opacity = isExpanded ? "1" : "0";
2473
+ action.button.style.pointerEvents = isExpanded ? "auto" : "none";
2474
+ });
2475
+ syncFloatingLayout();
2476
+ window.requestAnimationFrame(() => {
2477
+ toolbarDrag.updateBounds();
2478
+ });
2479
+ };
2480
+ cluster.append(baseButton);
2481
+ toolbar.append(cluster, panelWrap);
2482
+ shell.append(toolbar);
2483
+ (document.body ?? document.documentElement).append(shell);
2484
+ (document.body ?? document.documentElement).append(saveDialog.root);
2485
+ (document.body ?? document.documentElement).append(resetConfirmDialog.root);
2486
+ (document.body ?? document.documentElement).append(saveCompleteDialog.root);
2487
+ const initialPosition = clampToolbarPosition({
2488
+ x: Math.round((getViewportSize().width - measureToolbar(shell).width) / 2),
2489
+ y: TOOLBAR_INITIAL_TOP,
2490
+ ...measureToolbar(shell),
2491
+ });
2492
+ applyToolbarPosition(shell, initialPosition);
2493
+ return installToolbarDrag(shell, baseButton, {
2494
+ onPositionChange: () => {
2495
+ syncFloatingLayout();
2496
+ },
2497
+ }).then((toolbarDrag) => {
2498
+ const handleResize = () => {
2499
+ syncFloatingLayout();
2500
+ toolbarDrag.updateBounds();
2501
+ };
2502
+ const dispose = () => {
2503
+ if (isDisposed) {
2504
+ return;
2505
+ }
2506
+ isDisposed = true;
2507
+ toolbarDisposers.delete(shell);
2508
+ window.removeEventListener("resize", handleResize);
2509
+ panelHandle.dispose();
2510
+ saveDialog.dispose();
2511
+ resetConfirmDialog.dispose();
2512
+ saveCompleteDialog.dispose();
2513
+ toolbarDrag.dispose();
2514
+ };
2515
+ const recordAction = actionElements.find((action) => action.key === "record");
2516
+ recordAction?.button.addEventListener("click", () => {
2517
+ if (!recordButton || recordButton.dataset.pending === "true") {
2518
+ return;
2519
+ }
2520
+ recordButton.dataset.pending = "true";
2521
+ recordButton.disabled = true;
2522
+ void options
2523
+ .onSetRecording(!options.recording.getSnapshot().isRecording)
2524
+ .finally(() => {
2525
+ recordButton.dataset.pending = "false";
2526
+ recordButton.disabled = false;
2527
+ });
2528
+ });
2529
+ const recordButton = recordAction?.button ?? null;
2530
+ const unsubscribeRecordingState = options.recording.subscribe((snapshot) => {
2531
+ if (recordButton) {
2532
+ setActionButtonVisual(recordButton, snapshot.isRecording
2533
+ ? "イベント記録を一時停止する"
2534
+ : "イベントを記録する", snapshot.isRecording ? PauseCircle : recordAction?.icon ?? []);
2535
+ recordButton.setAttribute("aria-pressed", snapshot.isRecording ? "true" : "false");
2536
+ recordButton.style.background = snapshot.isRecording
2537
+ ? "rgba(181,129,79,0.14)"
2538
+ : "#ffffff";
2539
+ recordButton.style.color = snapshot.isRecording
2540
+ ? "#8f5b2f"
2541
+ : "#111827";
2542
+ }
2543
+ });
2544
+ const disposeToolbar = () => {
2545
+ unsubscribeRecordingState();
2546
+ dispose();
2547
+ };
2548
+ closeToolbarAndEnd = () => {
2549
+ saveDialog.close();
2550
+ resetConfirmDialog.close();
2551
+ saveCompleteDialog.close();
2552
+ disposeToolbar();
2553
+ shell.remove();
2554
+ delete document.documentElement.dataset.clueBusinessEventAuthoring;
2555
+ options.onClose();
2556
+ };
2557
+ const closeAction = actionElements.find((action) => action.key === "close");
2558
+ const saveAction = actionElements.find((action) => action.key === "save");
2559
+ saveAction?.button.addEventListener("click", () => {
2560
+ saveCompleteDialog.close();
2561
+ saveDialog.open();
2562
+ });
2563
+ closeAction?.button.addEventListener("click", () => {
2564
+ closeToolbarAndEnd();
2565
+ });
2566
+ baseButton.addEventListener("click", () => {
2567
+ if (toolbarDrag.consumeSuppressedClick()) {
2568
+ return;
2569
+ }
2570
+ isExpanded = !isExpanded;
2571
+ syncExpandedState(toolbarDrag);
2572
+ });
2573
+ syncExpandedState(toolbarDrag);
2574
+ window.addEventListener("resize", handleResize);
2575
+ toolbarDisposers.set(shell, disposeToolbar);
2576
+ document.documentElement.dataset.clueBusinessEventAuthoring =
2577
+ session.session_id;
2578
+ options.onReady();
2579
+ return {
2580
+ root: shell,
2581
+ dispose: disposeToolbar,
2582
+ };
2583
+ });
2584
+ }