promptotype 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1858 @@
1
+ (function() {
2
+ //#region src/extract-styles.ts
3
+ function rgbToHex(rgb) {
4
+ const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
5
+ if (!match) return rgb;
6
+ return "#" + [
7
+ parseInt(match[1]),
8
+ parseInt(match[2]),
9
+ parseInt(match[3])
10
+ ].map((v) => v.toString(16).padStart(2, "0")).join("").toUpperCase();
11
+ }
12
+ function extractStyles(el) {
13
+ const computed = window.getComputedStyle(el);
14
+ return {
15
+ font: {
16
+ family: computed.fontFamily.split(",")[0].replace(/['"]/g, "").trim(),
17
+ size: computed.fontSize,
18
+ weight: computed.fontWeight,
19
+ lineHeight: computed.lineHeight
20
+ },
21
+ color: {
22
+ text: rgbToHex(computed.color),
23
+ background: rgbToHex(computed.backgroundColor)
24
+ },
25
+ spacing: {
26
+ margin: `${computed.marginTop} ${computed.marginRight} ${computed.marginBottom} ${computed.marginLeft}`,
27
+ padding: `${computed.paddingTop} ${computed.paddingRight} ${computed.paddingBottom} ${computed.paddingLeft}`
28
+ },
29
+ alignment: {
30
+ textAlign: computed.textAlign,
31
+ display: computed.display,
32
+ alignItems: computed.alignItems,
33
+ justifyContent: computed.justifyContent
34
+ }
35
+ };
36
+ }
37
+ function generateSelector(el) {
38
+ const tag = el.tagName.toLowerCase();
39
+ const id = el.id ? `#${el.id}` : "";
40
+ const classes = el.className && typeof el.className === "string" ? "." + el.className.trim().split(/\s+/).filter((c) => !c.startsWith("pt-")).slice(0, 2).join(".") : "";
41
+ if (id) return `${tag}${id}`;
42
+ if (classes && classes !== ".") return `${tag}${classes}`;
43
+ return tag;
44
+ }
45
+ //#endregion
46
+ //#region src/styles.ts
47
+ var tokens = {
48
+ color: {
49
+ primary: {
50
+ 50: "#FAF5FF",
51
+ 100: "#F3E8FF",
52
+ 200: "#E9D5FF",
53
+ 300: "#D8B4FE",
54
+ 400: "#C084FC",
55
+ 500: "#A855F7",
56
+ 600: "#7C3AED",
57
+ 700: "#6D28D9",
58
+ 800: "#5B21B6",
59
+ 900: "#4C1D95"
60
+ },
61
+ surface: {
62
+ base: "#161618",
63
+ raised: "#1C1C1F",
64
+ overlay: "#222225",
65
+ elevated: "#2A2A2E",
66
+ border: "#333338",
67
+ borderSubtle: "#2A2A2E"
68
+ },
69
+ text: {
70
+ primary: "#F0F0F2",
71
+ secondary: "#A0A0A8",
72
+ tertiary: "#6B6B74",
73
+ inverse: "#161618"
74
+ },
75
+ success: "#34D399",
76
+ warning: "#FBBF24",
77
+ error: "#F87171",
78
+ info: "#60A5FA",
79
+ highlight: {
80
+ border: "#A855F7",
81
+ fill: "rgba(168, 85, 247, 0.08)",
82
+ fillAnnotated: "rgba(168, 85, 247, 0.15)",
83
+ margin: "rgba(249, 168, 37, 0.25)",
84
+ padding: "rgba(52, 211, 153, 0.25)"
85
+ }
86
+ },
87
+ space: {
88
+ 1: "4px",
89
+ 2: "8px",
90
+ 3: "12px",
91
+ 4: "16px",
92
+ 5: "20px",
93
+ 6: "24px",
94
+ 8: "32px",
95
+ 10: "40px",
96
+ 12: "48px"
97
+ },
98
+ font: {
99
+ family: "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif",
100
+ mono: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace",
101
+ size: {
102
+ xs: "11px",
103
+ sm: "12px",
104
+ base: "13px",
105
+ md: "14px",
106
+ lg: "16px"
107
+ },
108
+ weight: {
109
+ regular: "400",
110
+ medium: "500",
111
+ semibold: "600",
112
+ bold: "700"
113
+ },
114
+ lineHeight: {
115
+ tight: "1.2",
116
+ normal: "1.5",
117
+ relaxed: "1.6"
118
+ }
119
+ },
120
+ radius: {
121
+ sm: "4px",
122
+ md: "6px",
123
+ lg: "8px",
124
+ xl: "12px",
125
+ full: "9999px"
126
+ },
127
+ shadow: {
128
+ sm: "0 1px 3px rgba(0,0,0,0.3), 0 1px 2px rgba(0,0,0,0.2)",
129
+ md: "0 4px 12px rgba(0,0,0,0.35), 0 2px 4px rgba(0,0,0,0.2)",
130
+ lg: "0 8px 32px rgba(0,0,0,0.4), 0 4px 8px rgba(0,0,0,0.2)",
131
+ xl: "0 16px 48px rgba(0,0,0,0.5)",
132
+ glow: "0 0 20px rgba(168, 85, 247, 0.3)"
133
+ },
134
+ transition: {
135
+ fast: "100ms ease-out",
136
+ normal: "150ms ease-in-out",
137
+ slow: "200ms ease-out",
138
+ spring: "300ms cubic-bezier(0.34, 1.56, 0.64, 1)"
139
+ },
140
+ z: {
141
+ highlight: 2147483638,
142
+ highlightLabel: 2147483639,
143
+ pins: 2147483640,
144
+ breadcrumb: 2147483641,
145
+ statusBar: 2147483642,
146
+ popover: 2147483643,
147
+ reviewPanel: 2147483644,
148
+ toast: 2147483646
149
+ }
150
+ };
151
+ function injectGlobalStyles() {
152
+ if (document.getElementById("pt-global-styles")) return;
153
+ const style = document.createElement("style");
154
+ style.id = "pt-global-styles";
155
+ style.textContent = `
156
+ @keyframes pt-fade-in {
157
+ from { opacity: 0; }
158
+ to { opacity: 1; }
159
+ }
160
+ @keyframes pt-fade-out {
161
+ from { opacity: 1; }
162
+ to { opacity: 0; }
163
+ }
164
+ @keyframes pt-slide-up {
165
+ from { opacity: 0; transform: translateY(8px); }
166
+ to { opacity: 1; transform: translateY(0); }
167
+ }
168
+ @keyframes pt-slide-in-right {
169
+ from { transform: translateX(100%); }
170
+ to { transform: translateX(0); }
171
+ }
172
+ @keyframes pt-slide-out-right {
173
+ from { transform: translateX(0); }
174
+ to { transform: translateX(100%); }
175
+ }
176
+ @keyframes pt-scale-in {
177
+ from { opacity: 0; transform: scale(0.8); }
178
+ to { opacity: 1; transform: scale(1); }
179
+ }
180
+ @keyframes pt-pulse-ring {
181
+ 0% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0.4); }
182
+ 70% { box-shadow: 0 0 0 8px rgba(168, 85, 247, 0); }
183
+ 100% { box-shadow: 0 0 0 0 rgba(168, 85, 247, 0); }
184
+ }
185
+ @keyframes pt-pulse-highlight {
186
+ 0% { opacity: 1; }
187
+ 100% { opacity: 0; transform: scale(1.02); }
188
+ }
189
+ @keyframes pt-shimmer {
190
+ from { background-position: -200% 0; }
191
+ to { background-position: 200% 0; }
192
+ }
193
+ @keyframes pt-toast-progress {
194
+ from { width: 100%; }
195
+ to { width: 0%; }
196
+ }
197
+
198
+ .pt-inspect-cursor, .pt-inspect-cursor * {
199
+ cursor: crosshair !important;
200
+ }
201
+
202
+ /* Thin, transparent scrollbar — visible only on hover/scroll */
203
+ [id^="pt-"] {
204
+ scrollbar-width: thin;
205
+ scrollbar-color: transparent transparent;
206
+ }
207
+ [id^="pt-"]:hover {
208
+ scrollbar-color: rgba(255,255,255,0.15) transparent;
209
+ }
210
+ [id^="pt-"]::-webkit-scrollbar {
211
+ width: 5px;
212
+ }
213
+ [id^="pt-"]::-webkit-scrollbar-track {
214
+ background: transparent;
215
+ }
216
+ [id^="pt-"]::-webkit-scrollbar-thumb {
217
+ background-color: transparent;
218
+ border-radius: 3px;
219
+ }
220
+ [id^="pt-"]:hover::-webkit-scrollbar-thumb {
221
+ background-color: rgba(255,255,255,0.15);
222
+ }
223
+ [id^="pt-"]::-webkit-scrollbar-thumb:hover {
224
+ background-color: rgba(255,255,255,0.3);
225
+ }
226
+
227
+ @media (prefers-reduced-motion: reduce) {
228
+ [id^="pt-"], [class^="pt-"] {
229
+ animation-duration: 0.01ms !important;
230
+ transition-duration: 0.01ms !important;
231
+ }
232
+ }
233
+ `;
234
+ document.head.appendChild(style);
235
+ }
236
+ //#endregion
237
+ //#region src/highlight-overlay.ts
238
+ var OVERLAY_ID = "pt-highlight-overlay";
239
+ var LABEL_ID = "pt-highlight-label";
240
+ var MARGIN_ID = "pt-highlight-margin";
241
+ var PADDING_ID = "pt-highlight-padding";
242
+ var overlay = null;
243
+ var label = null;
244
+ var marginBox = null;
245
+ var paddingBox = null;
246
+ function ensureElements() {
247
+ if (overlay) return;
248
+ marginBox = document.createElement("div");
249
+ marginBox.id = MARGIN_ID;
250
+ marginBox.style.cssText = `
251
+ position: fixed;
252
+ pointer-events: none;
253
+ z-index: ${tokens.z.highlight};
254
+ background: ${tokens.color.highlight.margin};
255
+ display: none;
256
+ `;
257
+ document.body.appendChild(marginBox);
258
+ overlay = document.createElement("div");
259
+ overlay.id = OVERLAY_ID;
260
+ overlay.style.cssText = `
261
+ position: fixed;
262
+ pointer-events: none;
263
+ z-index: ${tokens.z.highlight};
264
+ border: 2px solid ${tokens.color.highlight.border};
265
+ background: ${tokens.color.highlight.fill};
266
+ transition: left ${tokens.transition.fast}, top ${tokens.transition.fast},
267
+ width ${tokens.transition.fast}, height ${tokens.transition.fast};
268
+ display: none;
269
+ `;
270
+ document.body.appendChild(overlay);
271
+ paddingBox = document.createElement("div");
272
+ paddingBox.id = PADDING_ID;
273
+ paddingBox.style.cssText = `
274
+ position: fixed;
275
+ pointer-events: none;
276
+ z-index: ${tokens.z.highlight};
277
+ background: ${tokens.color.highlight.padding};
278
+ display: none;
279
+ `;
280
+ document.body.appendChild(paddingBox);
281
+ label = document.createElement("div");
282
+ label.id = LABEL_ID;
283
+ label.style.cssText = `
284
+ position: fixed;
285
+ pointer-events: none;
286
+ z-index: ${tokens.z.highlightLabel};
287
+ background: ${tokens.color.primary[600]};
288
+ color: white;
289
+ font: ${tokens.font.weight.medium} ${tokens.font.size.xs}/${tokens.font.lineHeight.tight} ${tokens.font.family};
290
+ padding: 3px 8px;
291
+ border-radius: ${tokens.radius.sm};
292
+ white-space: nowrap;
293
+ display: none;
294
+ box-shadow: ${tokens.shadow.sm};
295
+ letter-spacing: 0.2px;
296
+ `;
297
+ document.body.appendChild(label);
298
+ }
299
+ function showHighlight(el, selector, isAnnotated) {
300
+ ensureElements();
301
+ const rect = el.getBoundingClientRect();
302
+ const computed = window.getComputedStyle(el);
303
+ const borderColor = isAnnotated ? tokens.color.primary[500] : tokens.color.highlight.border;
304
+ const bgColor = isAnnotated ? tokens.color.highlight.fillAnnotated : tokens.color.highlight.fill;
305
+ overlay.style.left = rect.left + "px";
306
+ overlay.style.top = rect.top + "px";
307
+ overlay.style.width = rect.width + "px";
308
+ overlay.style.height = rect.height + "px";
309
+ overlay.style.borderColor = borderColor;
310
+ overlay.style.background = bgColor;
311
+ overlay.style.display = "block";
312
+ const mt = parseFloat(computed.marginTop) || 0;
313
+ const mr = parseFloat(computed.marginRight) || 0;
314
+ const mb = parseFloat(computed.marginBottom) || 0;
315
+ const ml = parseFloat(computed.marginLeft) || 0;
316
+ if ((mt || mr || mb || ml) && marginBox) {
317
+ marginBox.style.left = rect.left - ml + "px";
318
+ marginBox.style.top = rect.top - mt + "px";
319
+ marginBox.style.width = rect.width + ml + mr + "px";
320
+ marginBox.style.height = rect.height + mt + mb + "px";
321
+ marginBox.style.display = "block";
322
+ const innerL = ml;
323
+ const innerT = mt;
324
+ const innerR = ml + rect.width;
325
+ const innerB = mt + rect.height;
326
+ rect.width + ml + mr;
327
+ rect.height + mt + mb;
328
+ marginBox.style.clipPath = `polygon(
329
+ 0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 0%,
330
+ ${innerL}px ${innerT}px, ${innerL}px ${innerB}px, ${innerR}px ${innerB}px, ${innerR}px ${innerT}px, ${innerL}px ${innerT}px
331
+ )`;
332
+ } else if (marginBox) marginBox.style.display = "none";
333
+ const pt = parseFloat(computed.paddingTop) || 0;
334
+ const pr = parseFloat(computed.paddingRight) || 0;
335
+ const pb = parseFloat(computed.paddingBottom) || 0;
336
+ const pl = parseFloat(computed.paddingLeft) || 0;
337
+ if ((pt || pr || pb || pl) && paddingBox) {
338
+ paddingBox.style.left = rect.left + "px";
339
+ paddingBox.style.top = rect.top + "px";
340
+ paddingBox.style.width = rect.width + "px";
341
+ paddingBox.style.height = rect.height + "px";
342
+ paddingBox.style.display = "block";
343
+ const contentL = pl;
344
+ const contentT = pt;
345
+ const contentR = rect.width - pr;
346
+ const contentB = rect.height - pb;
347
+ paddingBox.style.clipPath = `polygon(
348
+ 0% 0%, 100% 0%, 100% 100%, 0% 100%, 0% 0%,
349
+ ${contentL}px ${contentT}px, ${contentL}px ${contentB}px, ${contentR}px ${contentB}px, ${contentR}px ${contentT}px, ${contentL}px ${contentT}px
350
+ )`;
351
+ } else if (paddingBox) paddingBox.style.display = "none";
352
+ if (label) {
353
+ const dims = `${Math.round(rect.width)} × ${Math.round(rect.height)}`;
354
+ label.textContent = `${selector} ${dims}`;
355
+ label.style.left = rect.left + "px";
356
+ label.style.top = Math.max(0, rect.top - 26) + "px";
357
+ label.style.background = borderColor;
358
+ label.style.display = "block";
359
+ }
360
+ }
361
+ function hideHighlight() {
362
+ if (overlay) overlay.style.display = "none";
363
+ if (label) label.style.display = "none";
364
+ if (marginBox) marginBox.style.display = "none";
365
+ if (paddingBox) paddingBox.style.display = "none";
366
+ }
367
+ function pulseHighlight(el) {
368
+ const rect = el.getBoundingClientRect();
369
+ const pulse = document.createElement("div");
370
+ pulse.style.cssText = `
371
+ position: fixed;
372
+ left: ${rect.left}px;
373
+ top: ${rect.top}px;
374
+ width: ${rect.width}px;
375
+ height: ${rect.height}px;
376
+ border: 2px solid ${tokens.color.primary[500]};
377
+ background: rgba(168, 85, 247, 0.12);
378
+ z-index: ${tokens.z.highlight};
379
+ pointer-events: none;
380
+ border-radius: ${tokens.radius.sm};
381
+ animation: pt-pulse-highlight 0.6s ease-out forwards;
382
+ `;
383
+ document.body.appendChild(pulse);
384
+ setTimeout(() => pulse.remove(), 600);
385
+ }
386
+ function destroyHighlight() {
387
+ overlay?.remove();
388
+ label?.remove();
389
+ marginBox?.remove();
390
+ paddingBox?.remove();
391
+ overlay = null;
392
+ label = null;
393
+ marginBox = null;
394
+ paddingBox = null;
395
+ }
396
+ //#endregion
397
+ //#region src/breadcrumb-bar.ts
398
+ var BAR_ID$1 = "pt-breadcrumb-bar";
399
+ var bar$1 = null;
400
+ function ensureBar$1() {
401
+ if (bar$1) return bar$1;
402
+ bar$1 = document.createElement("div");
403
+ bar$1.id = BAR_ID$1;
404
+ bar$1.style.cssText = `
405
+ position: fixed;
406
+ top: 0;
407
+ left: 0;
408
+ right: 0;
409
+ z-index: ${tokens.z.breadcrumb};
410
+ background: ${tokens.color.surface.base}F0;
411
+ color: ${tokens.color.text.secondary};
412
+ font: ${tokens.font.weight.regular} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.mono};
413
+ padding: ${tokens.space[2]} ${tokens.space[4]};
414
+ display: none;
415
+ overflow-x: auto;
416
+ white-space: nowrap;
417
+ backdrop-filter: blur(12px);
418
+ -webkit-backdrop-filter: blur(12px);
419
+ border-bottom: 1px solid ${tokens.color.surface.borderSubtle};
420
+ `;
421
+ document.body.appendChild(bar$1);
422
+ return bar$1;
423
+ }
424
+ function buildPath(el) {
425
+ const path = [];
426
+ let current = el;
427
+ while (current && current !== document.body && current !== document.documentElement) {
428
+ path.unshift(current);
429
+ current = current.parentElement;
430
+ }
431
+ return path.slice(-6);
432
+ }
433
+ function tagLabel(el) {
434
+ const tag = el.tagName.toLowerCase();
435
+ const cls = el.className && typeof el.className === "string" ? "." + el.className.trim().split(/\s+/).filter((c) => !c.startsWith("pt-")).slice(0, 1).join(".") : "";
436
+ return tag + (el.id ? `#${el.id}` : "") + (cls !== "." ? cls : "");
437
+ }
438
+ function updateBreadcrumb(el, onSelect) {
439
+ const b = ensureBar$1();
440
+ const path = buildPath(el);
441
+ b.innerHTML = "";
442
+ path.forEach((node, i) => {
443
+ if (i > 0) {
444
+ const sep = document.createElement("span");
445
+ sep.textContent = "›";
446
+ sep.style.cssText = `color: ${tokens.color.text.tertiary}; margin: 0 6px; font-size: 14px;`;
447
+ b.appendChild(sep);
448
+ }
449
+ const span = document.createElement("span");
450
+ span.textContent = tagLabel(node);
451
+ const isLast = i === path.length - 1;
452
+ span.style.cssText = `
453
+ cursor: pointer;
454
+ color: ${isLast ? tokens.color.primary[400] : tokens.color.text.secondary};
455
+ font-weight: ${isLast ? tokens.font.weight.semibold : tokens.font.weight.regular};
456
+ padding: 2px 4px;
457
+ border-radius: ${tokens.radius.sm};
458
+ transition: background ${tokens.transition.fast}, color ${tokens.transition.fast};
459
+ `;
460
+ span.addEventListener("mouseenter", () => {
461
+ span.style.background = tokens.color.surface.elevated;
462
+ if (!isLast) span.style.color = tokens.color.text.primary;
463
+ });
464
+ span.addEventListener("mouseleave", () => {
465
+ span.style.background = "transparent";
466
+ if (!isLast) span.style.color = tokens.color.text.secondary;
467
+ });
468
+ span.addEventListener("click", (e) => {
469
+ e.stopPropagation();
470
+ onSelect(node);
471
+ });
472
+ b.appendChild(span);
473
+ });
474
+ b.style.display = "block";
475
+ }
476
+ function hideBreadcrumb() {
477
+ if (bar$1) bar$1.style.display = "none";
478
+ }
479
+ function destroyBreadcrumb() {
480
+ bar$1?.remove();
481
+ bar$1 = null;
482
+ }
483
+ //#endregion
484
+ //#region src/annotation-popover.ts
485
+ var POPOVER_ID = "pt-annotation-popover";
486
+ var popover = null;
487
+ function colorChip(hex, label) {
488
+ return `
489
+ <div class="pt-color-chip" data-hex="${hex}" style="
490
+ display:inline-flex;
491
+ align-items:center;
492
+ gap:${tokens.space[1]};
493
+ background:${tokens.color.surface.elevated};
494
+ border:1px solid ${tokens.color.surface.border};
495
+ border-radius:${tokens.radius.full};
496
+ padding:2px 8px 2px 4px;
497
+ cursor:pointer;
498
+ transition:border-color ${tokens.transition.fast};
499
+ font-size:${tokens.font.size.xs};
500
+ ">
501
+ <span style="
502
+ width:14px;
503
+ height:14px;
504
+ background:${hex};
505
+ border-radius:${tokens.radius.full};
506
+ border:1px solid rgba(255,255,255,0.1);
507
+ flex-shrink:0;
508
+ "></span>
509
+ <span style="color:${tokens.color.text.secondary};font-family:${tokens.font.mono};font-size:${tokens.font.size.xs};">${label}: ${hex}</span>
510
+ </div>
511
+ `;
512
+ }
513
+ function propertySection(title, content) {
514
+ return `
515
+ <div style="margin-bottom:${tokens.space[3]};">
516
+ <div style="
517
+ color:${tokens.color.text.tertiary};
518
+ font-size:${tokens.font.size.xs};
519
+ font-weight:${tokens.font.weight.medium};
520
+ text-transform:uppercase;
521
+ letter-spacing:0.8px;
522
+ margin-bottom:${tokens.space[1]};
523
+ ">${title}</div>
524
+ <div style="color:${tokens.color.text.primary};font-size:${tokens.font.size.sm};line-height:${tokens.font.lineHeight.relaxed};">
525
+ ${content}
526
+ </div>
527
+ </div>
528
+ `;
529
+ }
530
+ function formatSpacing(val) {
531
+ return val.replace(/px/g, "").split(" ").map((v) => {
532
+ const num = parseFloat(v);
533
+ return `<span style="
534
+ display:inline-flex;
535
+ align-items:center;
536
+ justify-content:center;
537
+ min-width:28px;
538
+ height:20px;
539
+ background:${num === 0 ? tokens.color.surface.elevated : tokens.color.surface.overlay};
540
+ border-radius:${tokens.radius.sm};
541
+ font-family:${tokens.font.mono};
542
+ font-size:${tokens.font.size.xs};
543
+ color:${num === 0 ? tokens.color.text.tertiary : tokens.color.text.primary};
544
+ padding:0 4px;
545
+ ">${v}</span>`;
546
+ }).join(" ");
547
+ }
548
+ function showPopover(el, styles, existing, onSave, onCancel) {
549
+ hidePopover();
550
+ const rect = el.getBoundingClientRect();
551
+ popover = document.createElement("div");
552
+ popover.id = POPOVER_ID;
553
+ let left = rect.right + 16;
554
+ let top = rect.top;
555
+ if (left + 360 > window.innerWidth) left = rect.left - 360 - 16;
556
+ if (left < 12) {
557
+ left = Math.max(12, rect.left);
558
+ top = rect.bottom + 16;
559
+ }
560
+ if (top + 460 > window.innerHeight) top = Math.max(12, window.innerHeight - 480);
561
+ popover.style.cssText = `
562
+ position: fixed;
563
+ left: ${left}px;
564
+ top: ${top}px;
565
+ width: 340px;
566
+ z-index: ${tokens.z.popover};
567
+ background: ${tokens.color.surface.raised};
568
+ color: ${tokens.color.text.primary};
569
+ border: 1px solid ${tokens.color.surface.border};
570
+ border-radius: ${tokens.radius.xl};
571
+ font: ${tokens.font.weight.regular} ${tokens.font.size.base}/${tokens.font.lineHeight.normal} ${tokens.font.family};
572
+ box-shadow: ${tokens.shadow.xl};
573
+ overflow: hidden;
574
+ animation: pt-scale-in 0.15s ease-out;
575
+ `;
576
+ const selector = el.tagName.toLowerCase() + (el.id ? `#${el.id}` : "") + (el.className && typeof el.className === "string" ? "." + el.className.trim().split(/\s+/).filter((c) => !c.startsWith("pt-")).slice(0, 2).join(".") : "");
577
+ const fontInfo = `
578
+ <span style="font-weight:${tokens.font.weight.medium};color:${tokens.color.text.primary};">${styles.font.family}</span>
579
+ <span style="color:${tokens.color.text.tertiary};">·</span>
580
+ ${styles.font.size}
581
+ <span style="color:${tokens.color.text.tertiary};">·</span>
582
+ <span style="font-weight:${styles.font.weight};">${styles.font.weight}</span>
583
+ <span style="color:${tokens.color.text.tertiary};">·</span>
584
+ <span style="color:${tokens.color.text.secondary};">/${styles.font.lineHeight}</span>
585
+ `;
586
+ const colorInfo = `
587
+ <div style="display:flex;flex-wrap:wrap;gap:${tokens.space[2]};">
588
+ ${colorChip(styles.color.text, "Text")}
589
+ ${colorChip(styles.color.background, "Bg")}
590
+ </div>
591
+ `;
592
+ const spacingInfo = `
593
+ <div style="display:grid;grid-template-columns:auto 1fr;gap:4px 8px;align-items:center;">
594
+ <span style="color:${tokens.color.text.tertiary};font-size:${tokens.font.size.xs};">Margin</span>
595
+ <div style="display:flex;gap:2px;">${formatSpacing(styles.spacing.margin)}</div>
596
+ <span style="color:${tokens.color.text.tertiary};font-size:${tokens.font.size.xs};">Padding</span>
597
+ <div style="display:flex;gap:2px;">${formatSpacing(styles.spacing.padding)}</div>
598
+ </div>
599
+ `;
600
+ const alignInfo = `
601
+ <div style="display:flex;flex-wrap:wrap;gap:${tokens.space[1]};">
602
+ ${[
603
+ styles.alignment.textAlign,
604
+ styles.alignment.display,
605
+ `align: ${styles.alignment.alignItems}`
606
+ ].filter((v) => v && !v.includes("normal")).map((v) => `<span style="
607
+ background:${tokens.color.surface.elevated};
608
+ border-radius:${tokens.radius.sm};
609
+ padding:2px 6px;
610
+ font-size:${tokens.font.size.xs};
611
+ font-family:${tokens.font.mono};
612
+ color:${tokens.color.text.secondary};
613
+ ">${v}</span>`).join("")}
614
+ </div>
615
+ `;
616
+ popover.innerHTML = `
617
+ <div style="
618
+ padding:${tokens.space[3]} ${tokens.space[4]};
619
+ border-bottom:1px solid ${tokens.color.surface.border};
620
+ display:flex;
621
+ justify-content:space-between;
622
+ align-items:center;
623
+ ">
624
+ <span style="
625
+ font-weight:${tokens.font.weight.semibold};
626
+ color:${tokens.color.primary[400]};
627
+ font-size:${tokens.font.size.sm};
628
+ font-family:${tokens.font.mono};
629
+ ">${selector}</span>
630
+ <button id="pt-popover-close" style="
631
+ background:${tokens.color.surface.elevated};
632
+ border:none;
633
+ color:${tokens.color.text.tertiary};
634
+ cursor:pointer;
635
+ width:24px;
636
+ height:24px;
637
+ border-radius:${tokens.radius.sm};
638
+ display:flex;
639
+ align-items:center;
640
+ justify-content:center;
641
+ font-size:14px;
642
+ transition:background ${tokens.transition.fast}, color ${tokens.transition.fast};
643
+ ">×</button>
644
+ </div>
645
+
646
+ <div style="
647
+ padding:${tokens.space[4]};
648
+ border-bottom:1px solid ${tokens.color.surface.border};
649
+ max-height:220px;
650
+ overflow-y:auto;
651
+ ">
652
+ ${propertySection("Typography", fontInfo)}
653
+ ${propertySection("Color", colorInfo)}
654
+ ${propertySection("Spacing", spacingInfo)}
655
+ ${propertySection("Layout", alignInfo)}
656
+ </div>
657
+
658
+ <div style="padding:${tokens.space[4]};">
659
+ <label for="pt-prompt" style="
660
+ display:block;
661
+ color:${tokens.color.text.secondary};
662
+ font-size:${tokens.font.size.xs};
663
+ font-weight:${tokens.font.weight.medium};
664
+ text-transform:uppercase;
665
+ letter-spacing:0.8px;
666
+ margin-bottom:${tokens.space[2]};
667
+ ">Your prompt</label>
668
+ <textarea
669
+ id="pt-prompt"
670
+ rows="3"
671
+ placeholder="What should change?"
672
+ style="
673
+ width:100%;
674
+ box-sizing:border-box;
675
+ background:${tokens.color.surface.base};
676
+ border:1px solid ${tokens.color.surface.border};
677
+ border-radius:${tokens.radius.md};
678
+ color:${tokens.color.text.primary};
679
+ font:${tokens.font.weight.regular} ${tokens.font.size.base}/${tokens.font.lineHeight.normal} ${tokens.font.family};
680
+ padding:${tokens.space[3]};
681
+ resize:vertical;
682
+ outline:none;
683
+ transition:border-color ${tokens.transition.fast}, box-shadow ${tokens.transition.fast};
684
+ "
685
+ >${existing?.prompt ?? ""}</textarea>
686
+
687
+ <div style="margin-top:${tokens.space[3]};display:flex;align-items:center;gap:${tokens.space[2]};">
688
+ <label for="pt-color" style="color:${tokens.color.text.tertiary};font-size:${tokens.font.size.xs};white-space:nowrap;">Suggest color:</label>
689
+ <div style="
690
+ position:relative;
691
+ flex:1;
692
+ display:flex;
693
+ align-items:center;
694
+ ">
695
+ <span id="pt-color-preview" style="
696
+ position:absolute;
697
+ left:8px;
698
+ width:16px;
699
+ height:16px;
700
+ border-radius:${tokens.radius.sm};
701
+ border:1px solid ${tokens.color.surface.border};
702
+ background:transparent;
703
+ "></span>
704
+ <input
705
+ id="pt-color"
706
+ type="text"
707
+ placeholder="#000000"
708
+ value="${existing?.colorSuggestion ?? ""}"
709
+ maxlength="7"
710
+ style="
711
+ width:100%;
712
+ background:${tokens.color.surface.base};
713
+ border:1px solid ${tokens.color.surface.border};
714
+ border-radius:${tokens.radius.md};
715
+ color:${tokens.color.text.primary};
716
+ font:${tokens.font.weight.regular} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.mono};
717
+ padding:${tokens.space[2]} ${tokens.space[2]} ${tokens.space[2]} 32px;
718
+ outline:none;
719
+ transition:border-color ${tokens.transition.fast};
720
+ "
721
+ >
722
+ </div>
723
+ </div>
724
+
725
+ <div style="
726
+ margin-top:${tokens.space[4]};
727
+ display:flex;
728
+ justify-content:space-between;
729
+ align-items:center;
730
+ ">
731
+ <span style="color:${tokens.color.text.tertiary};font-size:${tokens.font.size.xs};">
732
+ <kbd style="
733
+ background:${tokens.color.surface.elevated};
734
+ border:1px solid ${tokens.color.surface.border};
735
+ border-radius:${tokens.radius.sm};
736
+ padding:1px 4px;
737
+ font-size:10px;
738
+ ">⌘↵</kbd> to save
739
+ </span>
740
+ <div style="display:flex;gap:${tokens.space[2]};">
741
+ <button id="pt-popover-cancel" style="
742
+ background:transparent;
743
+ border:1px solid ${tokens.color.surface.border};
744
+ border-radius:${tokens.radius.md};
745
+ color:${tokens.color.text.secondary};
746
+ padding:${tokens.space[2]} ${tokens.space[4]};
747
+ font:${tokens.font.weight.regular} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
748
+ cursor:pointer;
749
+ transition:background ${tokens.transition.fast}, border-color ${tokens.transition.fast};
750
+ ">Cancel</button>
751
+ <button id="pt-popover-save" style="
752
+ background:${tokens.color.primary[600]};
753
+ border:none;
754
+ border-radius:${tokens.radius.md};
755
+ color:white;
756
+ padding:${tokens.space[2]} ${tokens.space[4]};
757
+ font:${tokens.font.weight.medium} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
758
+ cursor:pointer;
759
+ transition:background ${tokens.transition.fast}, transform ${tokens.transition.fast};
760
+ ">${existing ? "Update" : "Save Annotation"}</button>
761
+ </div>
762
+ </div>
763
+ </div>
764
+ `;
765
+ document.body.appendChild(popover);
766
+ const textarea = popover.querySelector("#pt-prompt");
767
+ const colorInput = popover.querySelector("#pt-color");
768
+ const colorPreview = popover.querySelector("#pt-color-preview");
769
+ const closeBtn = popover.querySelector("#pt-popover-close");
770
+ const cancelBtn = popover.querySelector("#pt-popover-cancel");
771
+ const saveBtn = popover.querySelector("#pt-popover-save");
772
+ setTimeout(() => textarea.focus(), 50);
773
+ textarea.addEventListener("focus", () => {
774
+ textarea.style.borderColor = tokens.color.primary[600];
775
+ textarea.style.boxShadow = `0 0 0 2px ${tokens.color.primary[600]}33`;
776
+ });
777
+ textarea.addEventListener("blur", () => {
778
+ textarea.style.borderColor = tokens.color.surface.border;
779
+ textarea.style.boxShadow = "none";
780
+ });
781
+ const updateColorPreview = () => {
782
+ const val = colorInput.value.trim();
783
+ if (/^#[A-Fa-f0-9]{6}$/.test(val)) colorPreview.style.background = val;
784
+ else colorPreview.style.background = "transparent";
785
+ };
786
+ colorInput.addEventListener("input", updateColorPreview);
787
+ updateColorPreview();
788
+ closeBtn.addEventListener("mouseenter", () => {
789
+ closeBtn.style.background = tokens.color.surface.overlay;
790
+ closeBtn.style.color = tokens.color.text.primary;
791
+ });
792
+ closeBtn.addEventListener("mouseleave", () => {
793
+ closeBtn.style.background = tokens.color.surface.elevated;
794
+ closeBtn.style.color = tokens.color.text.tertiary;
795
+ });
796
+ closeBtn.addEventListener("click", onCancel);
797
+ cancelBtn.addEventListener("mouseenter", () => {
798
+ cancelBtn.style.background = tokens.color.surface.elevated;
799
+ cancelBtn.style.borderColor = tokens.color.text.tertiary;
800
+ });
801
+ cancelBtn.addEventListener("mouseleave", () => {
802
+ cancelBtn.style.background = "transparent";
803
+ cancelBtn.style.borderColor = tokens.color.surface.border;
804
+ });
805
+ cancelBtn.addEventListener("click", onCancel);
806
+ saveBtn.addEventListener("mouseenter", () => {
807
+ saveBtn.style.background = tokens.color.primary[700];
808
+ saveBtn.style.transform = "translateY(-1px)";
809
+ });
810
+ saveBtn.addEventListener("mouseleave", () => {
811
+ saveBtn.style.background = tokens.color.primary[600];
812
+ saveBtn.style.transform = "translateY(0)";
813
+ });
814
+ saveBtn.addEventListener("mousedown", () => {
815
+ saveBtn.style.transform = "scale(0.98)";
816
+ });
817
+ saveBtn.addEventListener("mouseup", () => {
818
+ saveBtn.style.transform = "translateY(-1px)";
819
+ });
820
+ saveBtn.addEventListener("click", () => {
821
+ onSave(textarea.value.trim(), colorInput.value.trim());
822
+ });
823
+ textarea.addEventListener("keydown", (e) => {
824
+ if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
825
+ e.preventDefault();
826
+ saveBtn.click();
827
+ }
828
+ if (e.key === "Escape") {
829
+ e.preventDefault();
830
+ onCancel();
831
+ }
832
+ });
833
+ popover.querySelectorAll(".pt-color-chip").forEach((chip) => {
834
+ chip.addEventListener("mouseenter", () => {
835
+ chip.style.borderColor = tokens.color.primary[500];
836
+ });
837
+ chip.addEventListener("mouseleave", () => {
838
+ chip.style.borderColor = tokens.color.surface.border;
839
+ });
840
+ chip.addEventListener("click", () => {
841
+ const hex = chip.dataset.hex;
842
+ if (hex) {
843
+ colorInput.value = hex;
844
+ updateColorPreview();
845
+ }
846
+ });
847
+ });
848
+ }
849
+ function hidePopover() {
850
+ popover?.remove();
851
+ popover = null;
852
+ }
853
+ function isPopoverOpen() {
854
+ return popover !== null;
855
+ }
856
+ //#endregion
857
+ //#region src/pin-markers.ts
858
+ var PIN_CLASS = "pt-pin-marker";
859
+ var pins = /* @__PURE__ */ new Map();
860
+ function addPin(annotation, index) {
861
+ removePin(annotation.id);
862
+ const rect = annotation.element.getBoundingClientRect();
863
+ const pin = document.createElement("div");
864
+ pin.className = PIN_CLASS;
865
+ pin.dataset.annotationId = annotation.id;
866
+ pin.style.cssText = `
867
+ position: fixed;
868
+ left: ${rect.right - 12}px;
869
+ top: ${rect.top - 12}px;
870
+ width: 24px;
871
+ height: 24px;
872
+ background: ${tokens.color.primary[600]};
873
+ color: white;
874
+ border-radius: ${tokens.radius.full};
875
+ font: ${tokens.font.weight.bold} ${tokens.font.size.xs}/${tokens.font.lineHeight.tight} ${tokens.font.family};
876
+ display: flex;
877
+ align-items: center;
878
+ justify-content: center;
879
+ z-index: ${tokens.z.pins};
880
+ pointer-events: auto;
881
+ cursor: pointer;
882
+ box-shadow: ${tokens.shadow.md}, 0 0 0 2px ${tokens.color.surface.base};
883
+ transition: transform ${tokens.transition.spring}, box-shadow ${tokens.transition.normal};
884
+ animation: pt-scale-in 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
885
+ `;
886
+ pin.textContent = String(index + 1);
887
+ pin.title = annotation.prompt ? annotation.prompt.slice(0, 60) + (annotation.prompt.length > 60 ? "..." : "") : annotation.colorSuggestion ? `Color: ${annotation.colorSuggestion}` : "Properties captured";
888
+ pin.addEventListener("mouseenter", () => {
889
+ pin.style.transform = "scale(1.15)";
890
+ pin.style.boxShadow = `${tokens.shadow.lg}, ${tokens.shadow.glow}, 0 0 0 2px ${tokens.color.surface.base}`;
891
+ });
892
+ pin.addEventListener("mouseleave", () => {
893
+ pin.style.transform = "scale(1)";
894
+ pin.style.boxShadow = `${tokens.shadow.md}, 0 0 0 2px ${tokens.color.surface.base}`;
895
+ });
896
+ document.body.appendChild(pin);
897
+ pins.set(annotation.id, pin);
898
+ }
899
+ function removePin(id) {
900
+ const pin = pins.get(id);
901
+ if (pin) {
902
+ pin.remove();
903
+ pins.delete(id);
904
+ }
905
+ }
906
+ function updateAllPins(annotations) {
907
+ clearAllPins();
908
+ annotations.forEach((a, i) => addPin(a, i));
909
+ }
910
+ function clearAllPins() {
911
+ pins.forEach((pin) => pin.remove());
912
+ pins.clear();
913
+ }
914
+ function onPinClick(handler) {
915
+ document.addEventListener("click", (e) => {
916
+ const pin = e.target.closest(`.${PIN_CLASS}`);
917
+ if (pin?.dataset.annotationId) {
918
+ e.stopPropagation();
919
+ handler(pin.dataset.annotationId);
920
+ }
921
+ }, true);
922
+ }
923
+ //#endregion
924
+ //#region src/status-bar.ts
925
+ var BAR_ID = "pt-status-bar";
926
+ var bar = null;
927
+ function ensureBar() {
928
+ if (bar) return bar;
929
+ bar = document.createElement("div");
930
+ bar.id = BAR_ID;
931
+ bar.style.cssText = `
932
+ position: fixed;
933
+ bottom: 0;
934
+ left: 0;
935
+ right: 0;
936
+ z-index: ${tokens.z.statusBar};
937
+ background: ${tokens.color.surface.base}F2;
938
+ color: ${tokens.color.text.secondary};
939
+ font: ${tokens.font.weight.regular} ${tokens.font.size.base}/${tokens.font.lineHeight.tight} ${tokens.font.family};
940
+ padding: ${tokens.space[3]} ${tokens.space[5]};
941
+ display: flex;
942
+ align-items: center;
943
+ gap: ${tokens.space[3]};
944
+ backdrop-filter: blur(16px);
945
+ -webkit-backdrop-filter: blur(16px);
946
+ border-top: 1px solid ${tokens.color.surface.borderSubtle};
947
+ animation: pt-slide-up 0.2s ease-out;
948
+ `;
949
+ document.body.appendChild(bar);
950
+ return bar;
951
+ }
952
+ var logoSVG = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
953
+ <rect x="3" y="3" width="18" height="18" rx="3" stroke="currentColor" stroke-width="2"/>
954
+ <circle cx="8" cy="8" r="2" fill="currentColor"/>
955
+ <line x1="13" y1="8" x2="19" y2="8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
956
+ <line x1="7" y1="13" x2="17" y2="13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
957
+ <line x1="7" y1="17" x2="14" y2="17" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
958
+ </svg>`;
959
+ function updateStatusBar(count, onReview, onExit) {
960
+ const b = ensureBar();
961
+ b.innerHTML = `
962
+ <div style="display:flex;align-items:center;gap:${tokens.space[2]};">
963
+ <div style="
964
+ color: ${tokens.color.primary[400]};
965
+ display: flex;
966
+ align-items: center;
967
+ ">${logoSVG}</div>
968
+ <span style="
969
+ color: ${tokens.color.text.primary};
970
+ font-weight: ${tokens.font.weight.semibold};
971
+ font-size: ${tokens.font.size.sm};
972
+ letter-spacing: 0.3px;
973
+ ">Promptotype</span>
974
+ </div>
975
+
976
+ <div style="width:1px;height:16px;background:${tokens.color.surface.border};"></div>
977
+
978
+ <span style="color:${tokens.color.text.secondary};font-size:${tokens.font.size.sm};">
979
+ ${count === 0 ? "Click elements to annotate" : `<span style="
980
+ display:inline-flex;
981
+ align-items:center;
982
+ gap:${tokens.space[1]};
983
+ ">
984
+ <span style="
985
+ background:${tokens.color.primary[600]};
986
+ color:white;
987
+ min-width:20px;
988
+ height:20px;
989
+ border-radius:${tokens.radius.full};
990
+ display:inline-flex;
991
+ align-items:center;
992
+ justify-content:center;
993
+ font-size:${tokens.font.size.xs};
994
+ font-weight:${tokens.font.weight.bold};
995
+ ">${count}</span>
996
+ annotation${count !== 1 ? "s" : ""}
997
+ </span>`}
998
+ </span>
999
+
1000
+ <div style="flex:1;"></div>
1001
+
1002
+ <span style="color:${tokens.color.text.tertiary};font-size:${tokens.font.size.xs};">
1003
+ <kbd style="
1004
+ background:${tokens.color.surface.elevated};
1005
+ border:1px solid ${tokens.color.surface.border};
1006
+ border-radius:${tokens.radius.sm};
1007
+ padding:1px 5px;
1008
+ font-size:${tokens.font.size.xs};
1009
+ font-family:${tokens.font.family};
1010
+ ">Esc</kbd> to exit
1011
+ </span>
1012
+
1013
+ ${count > 0 ? `
1014
+ <button id="pt-review-btn" style="
1015
+ background: ${tokens.color.primary[600]};
1016
+ color: white;
1017
+ border: none;
1018
+ border-radius: ${tokens.radius.md};
1019
+ padding: ${tokens.space[2]} ${tokens.space[4]};
1020
+ font: ${tokens.font.weight.medium} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
1021
+ cursor: pointer;
1022
+ transition: background ${tokens.transition.fast}, transform ${tokens.transition.fast};
1023
+ display: flex;
1024
+ align-items: center;
1025
+ gap: ${tokens.space[2]};
1026
+ ">
1027
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1028
+ <polyline points="9 11 12 14 22 4"/>
1029
+ <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
1030
+ </svg>
1031
+ Review & Submit
1032
+ </button>
1033
+ ` : ""}
1034
+ `;
1035
+ if (count > 0) {
1036
+ const btn = b.querySelector("#pt-review-btn");
1037
+ btn.addEventListener("click", onReview);
1038
+ btn.addEventListener("mouseenter", () => {
1039
+ btn.style.background = tokens.color.primary[700];
1040
+ btn.style.transform = "translateY(-1px)";
1041
+ });
1042
+ btn.addEventListener("mouseleave", () => {
1043
+ btn.style.background = tokens.color.primary[600];
1044
+ btn.style.transform = "translateY(0)";
1045
+ });
1046
+ btn.addEventListener("mousedown", () => {
1047
+ btn.style.transform = "translateY(0) scale(0.98)";
1048
+ });
1049
+ btn.addEventListener("mouseup", () => {
1050
+ btn.style.transform = "translateY(-1px)";
1051
+ });
1052
+ }
1053
+ }
1054
+ function destroyStatusBar() {
1055
+ bar?.remove();
1056
+ bar = null;
1057
+ }
1058
+ //#endregion
1059
+ //#region src/output.ts
1060
+ function generateMarkdown(annotations) {
1061
+ let md = `## Design Annotations (${annotations.length} element${annotations.length !== 1 ? "s" : ""})\n\n`;
1062
+ annotations.forEach((a, i) => {
1063
+ const s = a.styles;
1064
+ md += `### ${i + 1}. \`${a.selector}\`\n`;
1065
+ md += `**Current styles:**\n`;
1066
+ md += `- Font: ${s.font.family}, ${s.font.size}, weight ${s.font.weight}, line-height ${s.font.lineHeight}\n`;
1067
+ md += `- Color: ${s.color.text} (on background ${s.color.background})\n`;
1068
+ md += `- Margin: ${s.spacing.margin}\n`;
1069
+ md += `- Padding: ${s.spacing.padding}\n`;
1070
+ md += `- Alignment: ${s.alignment.textAlign}, ${s.alignment.display}, align-items: ${s.alignment.alignItems}\n`;
1071
+ md += `\n`;
1072
+ if (a.prompt) md += `**Prompt:** ${a.prompt}\n`;
1073
+ if (a.colorSuggestion) md += `\n**Suggested color:** ${a.colorSuggestion}\n`;
1074
+ if (!a.prompt && !a.colorSuggestion) md += `**Prompt:** Review this element\n`;
1075
+ md += `\n---\n\n`;
1076
+ });
1077
+ return md.trim();
1078
+ }
1079
+ async function copyToClipboard(text) {
1080
+ try {
1081
+ await navigator.clipboard.writeText(text);
1082
+ return true;
1083
+ } catch {
1084
+ const textarea = document.createElement("textarea");
1085
+ textarea.value = text;
1086
+ textarea.style.cssText = "position:fixed;opacity:0;";
1087
+ document.body.appendChild(textarea);
1088
+ textarea.select();
1089
+ const success = document.execCommand("copy");
1090
+ textarea.remove();
1091
+ return success;
1092
+ }
1093
+ }
1094
+ /**
1095
+ * Check if we're running through the Promptotype proxy.
1096
+ */
1097
+ function isProxyMode() {
1098
+ return !!window.__PT_PROXY__;
1099
+ }
1100
+ /**
1101
+ * Submit annotations to the proxy server's API endpoint.
1102
+ * Returns true if successful, false otherwise.
1103
+ */
1104
+ async function submitToProxy(markdown) {
1105
+ try {
1106
+ const origin = window.__PT_PROXY_ORIGIN__ || window.location.origin;
1107
+ const token = window.__PT_SESSION_TOKEN__ || "";
1108
+ return (await fetch(`${origin}/__pt__/api/annotations`, {
1109
+ method: "POST",
1110
+ headers: { "Content-Type": "application/json" },
1111
+ body: JSON.stringify({
1112
+ markdown,
1113
+ token
1114
+ })
1115
+ })).ok;
1116
+ } catch {
1117
+ return false;
1118
+ }
1119
+ }
1120
+ //#endregion
1121
+ //#region src/review-panel.ts
1122
+ var PANEL_ID = "pt-review-panel";
1123
+ var panel = null;
1124
+ function compactProps(a) {
1125
+ const s = a.styles;
1126
+ const parts = [];
1127
+ parts.push(`<span style="font-family:${tokens.font.mono};font-size:${tokens.font.size.xs};">${s.font.family} ${s.font.size} · ${s.font.weight}</span>`);
1128
+ const colorDot = (hex) => `<span style="
1129
+ display:inline-block;
1130
+ width:10px;height:10px;
1131
+ background:${hex};
1132
+ border-radius:${tokens.radius.full};
1133
+ border:1px solid rgba(255,255,255,0.15);
1134
+ vertical-align:middle;
1135
+ "></span>`;
1136
+ parts.push(`${colorDot(s.color.text)} ${s.color.text} ${colorDot(s.color.background)} ${s.color.background}`);
1137
+ return parts.join("<span style=\"color:" + tokens.color.text.tertiary + ";margin:0 6px;\">·</span>");
1138
+ }
1139
+ function showReviewPanel(annotations, onEdit, onDelete, onCopy, onBack) {
1140
+ hideReviewPanel();
1141
+ panel = document.createElement("div");
1142
+ panel.id = PANEL_ID;
1143
+ panel.style.cssText = `
1144
+ position: fixed;
1145
+ top: 0;
1146
+ right: 0;
1147
+ bottom: 0;
1148
+ width: 400px;
1149
+ z-index: ${tokens.z.reviewPanel};
1150
+ background: ${tokens.color.surface.base};
1151
+ color: ${tokens.color.text.primary};
1152
+ font: ${tokens.font.weight.regular} ${tokens.font.size.base}/${tokens.font.lineHeight.normal} ${tokens.font.family};
1153
+ border-left: 1px solid ${tokens.color.surface.border};
1154
+ display: flex;
1155
+ flex-direction: column;
1156
+ box-shadow: ${tokens.shadow.xl};
1157
+ animation: pt-slide-in-right 0.25s ease-out;
1158
+ `;
1159
+ const header = `
1160
+ <div style="
1161
+ padding:${tokens.space[4]} ${tokens.space[5]};
1162
+ border-bottom:1px solid ${tokens.color.surface.border};
1163
+ display:flex;
1164
+ justify-content:space-between;
1165
+ align-items:center;
1166
+ flex-shrink:0;
1167
+ ">
1168
+ <div style="display:flex;align-items:center;gap:${tokens.space[3]};">
1169
+ <span style="font-weight:${tokens.font.weight.semibold};font-size:${tokens.font.size.md};">Review</span>
1170
+ <span style="
1171
+ background:${tokens.color.surface.elevated};
1172
+ color:${tokens.color.text.secondary};
1173
+ border-radius:${tokens.radius.full};
1174
+ padding:2px 8px;
1175
+ font-size:${tokens.font.size.xs};
1176
+ font-weight:${tokens.font.weight.medium};
1177
+ ">${annotations.length} annotation${annotations.length !== 1 ? "s" : ""}</span>
1178
+ </div>
1179
+ <button id="pt-review-close" style="
1180
+ background:${tokens.color.surface.elevated};
1181
+ border:none;
1182
+ color:${tokens.color.text.tertiary};
1183
+ cursor:pointer;
1184
+ width:28px;height:28px;
1185
+ border-radius:${tokens.radius.md};
1186
+ display:flex;align-items:center;justify-content:center;
1187
+ font-size:16px;
1188
+ transition:background ${tokens.transition.fast}, color ${tokens.transition.fast};
1189
+ ">×</button>
1190
+ </div>
1191
+ `;
1192
+ let cardsHtml = "";
1193
+ if (annotations.length === 0) cardsHtml = `
1194
+ <div style="
1195
+ flex:1;display:flex;align-items:center;justify-content:center;
1196
+ color:${tokens.color.text.tertiary};text-align:center;padding:${tokens.space[8]};
1197
+ ">
1198
+ <div>
1199
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="${tokens.color.text.tertiary}" stroke-width="1.5" style="margin:0 auto ${tokens.space[4]};display:block;opacity:0.5;">
1200
+ <rect x="3" y="3" width="18" height="18" rx="3"/>
1201
+ <circle cx="8" cy="8" r="2"/>
1202
+ <line x1="13" y1="8" x2="19" y2="8" stroke-linecap="round"/>
1203
+ <line x1="7" y1="13" x2="17" y2="13" stroke-linecap="round"/>
1204
+ <line x1="7" y1="17" x2="14" y2="17" stroke-linecap="round"/>
1205
+ </svg>
1206
+ <div style="font-size:${tokens.font.size.md};font-weight:${tokens.font.weight.medium};color:${tokens.color.text.secondary};margin-bottom:${tokens.space[1]};">No annotations yet</div>
1207
+ <div style="font-size:${tokens.font.size.sm};color:${tokens.color.text.tertiary};">Go back and click elements to annotate them</div>
1208
+ </div>
1209
+ </div>
1210
+ `;
1211
+ else {
1212
+ cardsHtml = `<div style="flex:1;overflow-y:auto;padding:${tokens.space[3]};">`;
1213
+ annotations.forEach((a, i) => {
1214
+ const promptText = a.prompt.replace(/</g, "&lt;").replace(/>/g, "&gt;");
1215
+ cardsHtml += `
1216
+ <div class="pt-review-card" data-id="${a.id}" style="
1217
+ background:${tokens.color.surface.raised};
1218
+ border:1px solid ${tokens.color.surface.border};
1219
+ border-radius:${tokens.radius.lg};
1220
+ padding:${tokens.space[4]};
1221
+ margin-bottom:${tokens.space[2]};
1222
+ cursor:pointer;
1223
+ transition:border-color ${tokens.transition.fast}, box-shadow ${tokens.transition.fast};
1224
+ ">
1225
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:${tokens.space[3]};">
1226
+ <div style="display:flex;align-items:center;gap:${tokens.space[2]};">
1227
+ <span style="
1228
+ background:${tokens.color.primary[600]};
1229
+ color:white;
1230
+ width:22px;height:22px;
1231
+ border-radius:${tokens.radius.full};
1232
+ font-size:${tokens.font.size.xs};
1233
+ font-weight:${tokens.font.weight.bold};
1234
+ display:flex;align-items:center;justify-content:center;
1235
+ ">${i + 1}</span>
1236
+ <span style="
1237
+ color:${tokens.color.primary[400]};
1238
+ font-weight:${tokens.font.weight.medium};
1239
+ font-size:${tokens.font.size.sm};
1240
+ font-family:${tokens.font.mono};
1241
+ ">${a.selector}</span>
1242
+ </div>
1243
+ <button class="pt-delete-btn" data-id="${a.id}" style="
1244
+ background:transparent;
1245
+ border:none;
1246
+ color:${tokens.color.text.tertiary};
1247
+ cursor:pointer;
1248
+ width:24px;height:24px;
1249
+ border-radius:${tokens.radius.sm};
1250
+ display:flex;align-items:center;justify-content:center;
1251
+ font-size:14px;
1252
+ transition:background ${tokens.transition.fast}, color ${tokens.transition.fast};
1253
+ ">×</button>
1254
+ </div>
1255
+
1256
+ <div style="
1257
+ color:${tokens.color.text.secondary};
1258
+ font-size:${tokens.font.size.xs};
1259
+ line-height:${tokens.font.lineHeight.relaxed};
1260
+ margin-bottom:${tokens.space[3]};
1261
+ ">
1262
+ ${compactProps(a)}
1263
+ </div>
1264
+
1265
+ ${promptText ? `
1266
+ <div style="
1267
+ color:${tokens.color.text.primary};
1268
+ font-size:${tokens.font.size.sm};
1269
+ line-height:${tokens.font.lineHeight.relaxed};
1270
+ border-left:2px solid ${tokens.color.primary[600]};
1271
+ padding-left:${tokens.space[3]};
1272
+ margin-bottom:${tokens.space[2]};
1273
+ ">
1274
+ ${promptText}
1275
+ </div>
1276
+ ` : `
1277
+ <div style="
1278
+ color:${tokens.color.text.tertiary};
1279
+ font-size:${tokens.font.size.xs};
1280
+ font-style:italic;
1281
+ margin-bottom:${tokens.space[2]};
1282
+ ">
1283
+ No prompt — properties only
1284
+ </div>
1285
+ `}
1286
+
1287
+ ${a.colorSuggestion ? `
1288
+ <div style="
1289
+ display:inline-flex;
1290
+ align-items:center;
1291
+ gap:${tokens.space[1]};
1292
+ background:${tokens.color.surface.elevated};
1293
+ border-radius:${tokens.radius.full};
1294
+ padding:2px 8px 2px 4px;
1295
+ font-size:${tokens.font.size.xs};
1296
+ color:${tokens.color.text.secondary};
1297
+ margin-bottom:${tokens.space[2]};
1298
+ ">
1299
+ <span style="
1300
+ width:12px;height:12px;
1301
+ background:${a.colorSuggestion};
1302
+ border-radius:${tokens.radius.full};
1303
+ border:1px solid rgba(255,255,255,0.15);
1304
+ "></span>
1305
+ <span style="font-family:${tokens.font.mono};">${a.colorSuggestion}</span>
1306
+ </div>
1307
+ ` : ""}
1308
+
1309
+ <div style="text-align:right;">
1310
+ <button class="pt-edit-btn" data-id="${a.id}" style="
1311
+ background:${tokens.color.surface.elevated};
1312
+ border:1px solid ${tokens.color.surface.border};
1313
+ border-radius:${tokens.radius.md};
1314
+ color:${tokens.color.text.secondary};
1315
+ padding:${tokens.space[1]} ${tokens.space[3]};
1316
+ font:${tokens.font.weight.regular} ${tokens.font.size.xs}/${tokens.font.lineHeight.tight} ${tokens.font.family};
1317
+ cursor:pointer;
1318
+ transition:background ${tokens.transition.fast}, border-color ${tokens.transition.fast};
1319
+ ">Edit</button>
1320
+ </div>
1321
+ </div>
1322
+ `;
1323
+ });
1324
+ cardsHtml += "</div>";
1325
+ }
1326
+ const footer = `
1327
+ <div style="
1328
+ padding:${tokens.space[4]} ${tokens.space[5]};
1329
+ border-top:1px solid ${tokens.color.surface.border};
1330
+ display:flex;
1331
+ gap:${tokens.space[3]};
1332
+ flex-shrink:0;
1333
+ ">
1334
+ <button id="pt-back-btn" style="
1335
+ flex:1;
1336
+ background:transparent;
1337
+ border:1px solid ${tokens.color.surface.border};
1338
+ border-radius:${tokens.radius.md};
1339
+ color:${tokens.color.text.secondary};
1340
+ padding:${tokens.space[3]};
1341
+ font:${tokens.font.weight.regular} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
1342
+ cursor:pointer;
1343
+ transition:background ${tokens.transition.fast}, border-color ${tokens.transition.fast};
1344
+ ">Back to Inspect</button>
1345
+ ${annotations.length > 0 ? `
1346
+ <button id="pt-copy-btn" style="
1347
+ flex:1;
1348
+ background:${tokens.color.primary[600]};
1349
+ border:none;
1350
+ border-radius:${tokens.radius.md};
1351
+ color:white;
1352
+ padding:${tokens.space[3]};
1353
+ font:${tokens.font.weight.medium} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
1354
+ cursor:pointer;
1355
+ transition:background ${tokens.transition.fast}, transform ${tokens.transition.fast};
1356
+ display:flex;
1357
+ align-items:center;
1358
+ justify-content:center;
1359
+ gap:${tokens.space[2]};
1360
+ ">
1361
+ ${isProxyMode() ? `
1362
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1363
+ <line x1="22" y1="2" x2="11" y2="13"/>
1364
+ <polygon points="22 2 15 22 11 13 2 9 22 2"/>
1365
+ </svg>
1366
+ Submit to Agent
1367
+ ` : `
1368
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1369
+ <rect x="9" y="9" width="13" height="13" rx="2"/>
1370
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
1371
+ </svg>
1372
+ Copy to Clipboard
1373
+ `}
1374
+ </button>
1375
+ ` : ""}
1376
+ </div>
1377
+ `;
1378
+ panel.innerHTML = header + cardsHtml + footer;
1379
+ document.body.appendChild(panel);
1380
+ const closeBtn = panel.querySelector("#pt-review-close");
1381
+ closeBtn.addEventListener("mouseenter", () => {
1382
+ closeBtn.style.background = tokens.color.surface.overlay;
1383
+ closeBtn.style.color = tokens.color.text.primary;
1384
+ });
1385
+ closeBtn.addEventListener("mouseleave", () => {
1386
+ closeBtn.style.background = tokens.color.surface.elevated;
1387
+ closeBtn.style.color = tokens.color.text.tertiary;
1388
+ });
1389
+ closeBtn.addEventListener("click", onBack);
1390
+ const backBtn = panel.querySelector("#pt-back-btn");
1391
+ backBtn.addEventListener("mouseenter", () => {
1392
+ backBtn.style.background = tokens.color.surface.elevated;
1393
+ backBtn.style.borderColor = tokens.color.text.tertiary;
1394
+ });
1395
+ backBtn.addEventListener("mouseleave", () => {
1396
+ backBtn.style.background = "transparent";
1397
+ backBtn.style.borderColor = tokens.color.surface.border;
1398
+ });
1399
+ backBtn.addEventListener("click", onBack);
1400
+ if (annotations.length > 0) {
1401
+ const copyBtn = panel.querySelector("#pt-copy-btn");
1402
+ copyBtn.addEventListener("mouseenter", () => {
1403
+ copyBtn.style.background = tokens.color.primary[700];
1404
+ copyBtn.style.transform = "translateY(-1px)";
1405
+ });
1406
+ copyBtn.addEventListener("mouseleave", () => {
1407
+ copyBtn.style.background = tokens.color.primary[600];
1408
+ copyBtn.style.transform = "translateY(0)";
1409
+ });
1410
+ copyBtn.addEventListener("mousedown", () => {
1411
+ copyBtn.style.transform = "scale(0.98)";
1412
+ });
1413
+ copyBtn.addEventListener("mouseup", () => {
1414
+ copyBtn.style.transform = "translateY(-1px)";
1415
+ });
1416
+ copyBtn.addEventListener("click", async () => {
1417
+ const proxy = isProxyMode();
1418
+ copyBtn.disabled = true;
1419
+ copyBtn.style.opacity = "0.7";
1420
+ const originalHtml = copyBtn.innerHTML;
1421
+ const success = await onCopy();
1422
+ copyBtn.disabled = false;
1423
+ copyBtn.style.opacity = "1";
1424
+ if (success) {
1425
+ copyBtn.innerHTML = `
1426
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
1427
+ <polyline points="20 6 9 17 4 12"/>
1428
+ </svg>
1429
+ ${proxy ? "Sent!" : "Copied!"}
1430
+ `;
1431
+ copyBtn.style.background = tokens.color.success;
1432
+ } else {
1433
+ copyBtn.innerHTML = `
1434
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1435
+ <circle cx="12" cy="12" r="10"/>
1436
+ <line x1="15" y1="9" x2="9" y2="15"/>
1437
+ <line x1="9" y1="9" x2="15" y2="15"/>
1438
+ </svg>
1439
+ Failed
1440
+ `;
1441
+ copyBtn.style.background = tokens.color.error;
1442
+ }
1443
+ setTimeout(() => {
1444
+ if (copyBtn.isConnected) {
1445
+ copyBtn.innerHTML = originalHtml;
1446
+ copyBtn.style.background = tokens.color.primary[600];
1447
+ }
1448
+ }, 2e3);
1449
+ });
1450
+ }
1451
+ panel.querySelectorAll(".pt-review-card").forEach((card) => {
1452
+ const cardEl = card;
1453
+ cardEl.addEventListener("mouseenter", () => {
1454
+ cardEl.style.borderColor = tokens.color.primary[600] + "66";
1455
+ cardEl.style.boxShadow = `0 0 0 1px ${tokens.color.primary[600]}33`;
1456
+ });
1457
+ cardEl.addEventListener("mouseleave", () => {
1458
+ cardEl.style.borderColor = tokens.color.surface.border;
1459
+ cardEl.style.boxShadow = "none";
1460
+ });
1461
+ cardEl.addEventListener("click", (e) => {
1462
+ const target = e.target;
1463
+ if (target.closest(".pt-edit-btn") || target.closest(".pt-delete-btn")) return;
1464
+ const id = cardEl.dataset.id;
1465
+ const annotation = annotations.find((a) => a.id === id);
1466
+ if (annotation?.element?.isConnected) {
1467
+ annotation.element.scrollIntoView({
1468
+ behavior: "smooth",
1469
+ block: "center"
1470
+ });
1471
+ pulseHighlight(annotation.element);
1472
+ }
1473
+ });
1474
+ });
1475
+ panel.querySelectorAll(".pt-edit-btn").forEach((btn) => {
1476
+ const el = btn;
1477
+ el.addEventListener("mouseenter", () => {
1478
+ el.style.background = tokens.color.surface.overlay;
1479
+ el.style.borderColor = tokens.color.text.tertiary;
1480
+ });
1481
+ el.addEventListener("mouseleave", () => {
1482
+ el.style.background = tokens.color.surface.elevated;
1483
+ el.style.borderColor = tokens.color.surface.border;
1484
+ });
1485
+ el.addEventListener("click", (e) => {
1486
+ e.stopPropagation();
1487
+ onEdit(el.dataset.id);
1488
+ });
1489
+ });
1490
+ panel.querySelectorAll(".pt-delete-btn").forEach((btn) => {
1491
+ const el = btn;
1492
+ el.addEventListener("mouseenter", () => {
1493
+ el.style.background = tokens.color.surface.elevated;
1494
+ el.style.color = tokens.color.error;
1495
+ });
1496
+ el.addEventListener("mouseleave", () => {
1497
+ el.style.background = "transparent";
1498
+ el.style.color = tokens.color.text.tertiary;
1499
+ });
1500
+ el.addEventListener("click", (e) => {
1501
+ e.stopPropagation();
1502
+ onDelete(el.dataset.id);
1503
+ });
1504
+ });
1505
+ }
1506
+ function hideReviewPanel() {
1507
+ panel?.remove();
1508
+ panel = null;
1509
+ }
1510
+ function isReviewOpen() {
1511
+ return panel !== null;
1512
+ }
1513
+ //#endregion
1514
+ //#region src/index.ts
1515
+ var mode = "inactive";
1516
+ var annotations = [];
1517
+ var hoveredElement = null;
1518
+ var depthStack = [];
1519
+ var depthIndex = 0;
1520
+ function buildDepthStack(el) {
1521
+ const stack = [];
1522
+ let current = el;
1523
+ while (current && current !== document.body && current !== document.documentElement) {
1524
+ stack.unshift(current);
1525
+ current = current.parentElement;
1526
+ }
1527
+ return stack;
1528
+ }
1529
+ function getElementAtDepth(x, y, goDeeper) {
1530
+ if (!hoveredElement) return null;
1531
+ if (goDeeper) {
1532
+ const children = Array.from(hoveredElement.children);
1533
+ for (const child of children) {
1534
+ const rect = child.getBoundingClientRect();
1535
+ if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) return child;
1536
+ }
1537
+ return hoveredElement;
1538
+ } else return hoveredElement.parentElement && hoveredElement.parentElement !== document.body ? hoveredElement.parentElement : hoveredElement;
1539
+ }
1540
+ function isOwnElement(el) {
1541
+ return !!(el.id?.startsWith("pt-") || el.className?.toString().includes("pt-") || el.closest("[id^=\"pt-\"]"));
1542
+ }
1543
+ function findAnnotation(el) {
1544
+ return annotations.find((a) => a.element === el) ?? null;
1545
+ }
1546
+ function isElementAnnotated(el) {
1547
+ return annotations.some((a) => a.element === el);
1548
+ }
1549
+ function activate() {
1550
+ if (mode !== "inactive") return;
1551
+ mode = "inspect";
1552
+ injectGlobalStyles();
1553
+ document.documentElement.classList.add("pt-inspect-cursor");
1554
+ updateStatusBar(annotations.length, openReview, deactivate);
1555
+ updateAllPins(annotations);
1556
+ document.addEventListener("mousemove", handleMouseMove, true);
1557
+ document.addEventListener("click", handleClick, true);
1558
+ document.addEventListener("wheel", handleWheel, {
1559
+ capture: true,
1560
+ passive: false
1561
+ });
1562
+ document.addEventListener("keydown", handleKeyDown, true);
1563
+ }
1564
+ function deactivate() {
1565
+ if (annotations.length > 0 && mode !== "review") {
1566
+ if (!confirm(`You have ${annotations.length} annotation${annotations.length !== 1 ? "s" : ""}. Exit and discard?`)) return;
1567
+ }
1568
+ mode = "inactive";
1569
+ annotations = [];
1570
+ hoveredElement = null;
1571
+ document.documentElement.classList.remove("pt-inspect-cursor");
1572
+ document.removeEventListener("mousemove", handleMouseMove, true);
1573
+ document.removeEventListener("click", handleClick, true);
1574
+ document.removeEventListener("wheel", handleWheel, true);
1575
+ document.removeEventListener("keydown", handleKeyDown, true);
1576
+ hideHighlight();
1577
+ hideBreadcrumb();
1578
+ hidePopover();
1579
+ hideReviewPanel();
1580
+ clearAllPins();
1581
+ destroyHighlight();
1582
+ destroyBreadcrumb();
1583
+ destroyStatusBar();
1584
+ }
1585
+ function enterAnnotateMode(el) {
1586
+ mode = "annotate";
1587
+ hideHighlight();
1588
+ document.documentElement.classList.remove("pt-inspect-cursor");
1589
+ const styles = extractStyles(el);
1590
+ const existing = findAnnotation(el);
1591
+ showPopover(el, styles, existing, (prompt, colorSuggestion) => {
1592
+ if (existing) {
1593
+ existing.prompt = prompt;
1594
+ existing.colorSuggestion = colorSuggestion;
1595
+ } else annotations.push({
1596
+ id: "ann-" + Date.now() + "-" + Math.random().toString(36).slice(2, 6),
1597
+ element: el,
1598
+ selector: generateSelector(el),
1599
+ styles,
1600
+ prompt,
1601
+ colorSuggestion,
1602
+ timestamp: Date.now()
1603
+ });
1604
+ hidePopover();
1605
+ returnToInspect();
1606
+ }, () => {
1607
+ hidePopover();
1608
+ returnToInspect();
1609
+ });
1610
+ }
1611
+ function returnToInspect() {
1612
+ mode = "inspect";
1613
+ document.documentElement.classList.add("pt-inspect-cursor");
1614
+ updateAllPins(annotations);
1615
+ updateStatusBar(annotations.length, openReview, deactivate);
1616
+ }
1617
+ function openReview() {
1618
+ mode = "review";
1619
+ hideHighlight();
1620
+ hideBreadcrumb();
1621
+ hidePopover();
1622
+ document.documentElement.classList.remove("pt-inspect-cursor");
1623
+ showReviewPanel(annotations, (id) => {
1624
+ hideReviewPanel();
1625
+ const ann = annotations.find((a) => a.id === id);
1626
+ if (ann?.element?.isConnected) {
1627
+ ann.element.scrollIntoView({
1628
+ behavior: "smooth",
1629
+ block: "center"
1630
+ });
1631
+ setTimeout(() => enterAnnotateMode(ann.element), 300);
1632
+ } else returnToInspect();
1633
+ }, (id) => {
1634
+ annotations = annotations.filter((a) => a.id !== id);
1635
+ updateAllPins(annotations);
1636
+ openReview();
1637
+ }, async () => {
1638
+ const md = generateMarkdown(annotations);
1639
+ if (isProxyMode()) if (await submitToProxy(md)) {
1640
+ showToast(`Sent ${annotations.length} annotation${annotations.length !== 1 ? "s" : ""} to AI agent — you can close this tab`);
1641
+ return true;
1642
+ } else {
1643
+ const copied = await copyToClipboard(md);
1644
+ showToast(copied ? "Proxy unavailable — copied to clipboard instead" : "Submit failed — check console");
1645
+ console.log("--- Promptotype Output ---\n\n" + md);
1646
+ return copied;
1647
+ }
1648
+ else {
1649
+ const success = await copyToClipboard(md);
1650
+ if (success) showToast(`Copied ${annotations.length} annotation${annotations.length !== 1 ? "s" : ""} — paste into your AI agent`);
1651
+ else {
1652
+ showToast("Copy failed — check console for output");
1653
+ console.log("--- Promptotype Output ---\n\n" + md);
1654
+ }
1655
+ return success;
1656
+ }
1657
+ }, () => {
1658
+ hideReviewPanel();
1659
+ returnToInspect();
1660
+ });
1661
+ }
1662
+ function showToast(message) {
1663
+ const toast = document.createElement("div");
1664
+ toast.style.cssText = `
1665
+ position: fixed;
1666
+ bottom: 60px;
1667
+ left: 50%;
1668
+ transform: translateX(-50%);
1669
+ background: ${tokens.color.surface.raised};
1670
+ color: ${tokens.color.text.primary};
1671
+ border: 1px solid ${tokens.color.surface.border};
1672
+ padding: ${tokens.space[3]} ${tokens.space[5]};
1673
+ border-radius: ${tokens.radius.lg};
1674
+ font: ${tokens.font.weight.medium} ${tokens.font.size.sm}/${tokens.font.lineHeight.tight} ${tokens.font.family};
1675
+ z-index: ${tokens.z.toast};
1676
+ box-shadow: ${tokens.shadow.lg};
1677
+ animation: pt-slide-up 0.2s ease-out;
1678
+ display: flex;
1679
+ align-items: center;
1680
+ gap: ${tokens.space[2]};
1681
+ overflow: hidden;
1682
+ `;
1683
+ toast.innerHTML = `
1684
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="${tokens.color.success}" stroke-width="2.5" stroke-linecap="round">
1685
+ <polyline points="20 6 9 17 4 12"/>
1686
+ </svg>
1687
+ <span>${message}</span>
1688
+ `;
1689
+ const progress = document.createElement("div");
1690
+ progress.style.cssText = `
1691
+ position: absolute;
1692
+ bottom: 0;
1693
+ left: 0;
1694
+ height: 2px;
1695
+ background: ${tokens.color.primary[600]};
1696
+ animation: pt-toast-progress 3s linear forwards;
1697
+ `;
1698
+ toast.style.position = "fixed";
1699
+ toast.appendChild(progress);
1700
+ document.body.appendChild(toast);
1701
+ setTimeout(() => {
1702
+ toast.style.opacity = "0";
1703
+ toast.style.transition = `opacity ${tokens.transition.slow}`;
1704
+ setTimeout(() => toast.remove(), 200);
1705
+ }, 3e3);
1706
+ }
1707
+ function handleMouseMove(e) {
1708
+ if (mode !== "inspect") return;
1709
+ const el = document.elementFromPoint(e.clientX, e.clientY);
1710
+ if (!el || isOwnElement(el)) {
1711
+ hideHighlight();
1712
+ hideBreadcrumb();
1713
+ hoveredElement = null;
1714
+ return;
1715
+ }
1716
+ if (el !== hoveredElement) {
1717
+ hoveredElement = el;
1718
+ depthStack = buildDepthStack(el);
1719
+ depthIndex = depthStack.length - 1;
1720
+ }
1721
+ const current = depthStack[depthIndex] || el;
1722
+ showHighlight(current, generateSelector(current), isElementAnnotated(current));
1723
+ updateBreadcrumb(current, (selected) => {
1724
+ hoveredElement = selected;
1725
+ depthStack = buildDepthStack(selected);
1726
+ depthIndex = depthStack.length - 1;
1727
+ showHighlight(selected, generateSelector(selected), isElementAnnotated(selected));
1728
+ });
1729
+ }
1730
+ function handleClick(e) {
1731
+ if (mode !== "inspect") return;
1732
+ const el = e.target;
1733
+ if (isOwnElement(el)) return;
1734
+ e.preventDefault();
1735
+ e.stopPropagation();
1736
+ const current = depthStack[depthIndex] || hoveredElement;
1737
+ if (current) enterAnnotateMode(current);
1738
+ }
1739
+ function handleWheel(e) {
1740
+ if (mode !== "inspect") return;
1741
+ if (!e.altKey) return;
1742
+ e.preventDefault();
1743
+ e.stopPropagation();
1744
+ if (e.deltaY > 0) {
1745
+ const child = getElementAtDepth(e.clientX, e.clientY, true);
1746
+ if (child && child !== hoveredElement) {
1747
+ hoveredElement = child;
1748
+ depthStack = buildDepthStack(child);
1749
+ depthIndex = depthStack.length - 1;
1750
+ }
1751
+ } else if (depthIndex > 0) {
1752
+ depthIndex--;
1753
+ hoveredElement = depthStack[depthIndex];
1754
+ }
1755
+ if (hoveredElement) {
1756
+ const selector = generateSelector(hoveredElement);
1757
+ showHighlight(hoveredElement, selector, isElementAnnotated(hoveredElement));
1758
+ updateBreadcrumb(hoveredElement, (selected) => {
1759
+ hoveredElement = selected;
1760
+ depthStack = buildDepthStack(selected);
1761
+ depthIndex = depthStack.length - 1;
1762
+ });
1763
+ }
1764
+ }
1765
+ function handleKeyDown(e) {
1766
+ if (e.key === "Escape") {
1767
+ e.preventDefault();
1768
+ e.stopPropagation();
1769
+ if (isPopoverOpen()) {
1770
+ hidePopover();
1771
+ returnToInspect();
1772
+ } else if (isReviewOpen()) {
1773
+ hideReviewPanel();
1774
+ returnToInspect();
1775
+ } else deactivate();
1776
+ }
1777
+ }
1778
+ onPinClick((id) => {
1779
+ if (mode === "inspect") {
1780
+ const ann = annotations.find((a) => a.id === id);
1781
+ if (ann?.element?.isConnected) enterAnnotateMode(ann.element);
1782
+ }
1783
+ });
1784
+ function toggle() {
1785
+ if (mode === "inactive") activate();
1786
+ else deactivate();
1787
+ }
1788
+ document.addEventListener("keydown", (e) => {
1789
+ if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "d") {
1790
+ e.preventDefault();
1791
+ toggle();
1792
+ }
1793
+ });
1794
+ function createToggleButton() {
1795
+ const btn = document.createElement("div");
1796
+ btn.id = "pt-toggle-button";
1797
+ btn.style.cssText = `
1798
+ position: fixed;
1799
+ bottom: ${tokens.space[4]};
1800
+ left: ${tokens.space[4]};
1801
+ width: 44px;
1802
+ height: 44px;
1803
+ background: ${tokens.color.primary[600]};
1804
+ color: white;
1805
+ border-radius: ${tokens.radius.lg};
1806
+ display: flex;
1807
+ align-items: center;
1808
+ justify-content: center;
1809
+ cursor: pointer;
1810
+ z-index: 2147483630;
1811
+ box-shadow: ${tokens.shadow.md};
1812
+ transition: background ${tokens.transition.fast}, transform ${tokens.transition.spring}, box-shadow ${tokens.transition.normal};
1813
+ user-select: none;
1814
+ `;
1815
+ btn.innerHTML = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1816
+ <rect x="3" y="3" width="18" height="18" rx="3"/>
1817
+ <circle cx="8" cy="8" r="2" fill="currentColor"/>
1818
+ <line x1="13" y1="8" x2="19" y2="8" stroke-linecap="round"/>
1819
+ <line x1="7" y1="13" x2="17" y2="13" stroke-linecap="round"/>
1820
+ <line x1="7" y1="17" x2="14" y2="17" stroke-linecap="round"/>
1821
+ </svg>`;
1822
+ btn.title = "Toggle Promptotype (Cmd+Shift+D)";
1823
+ btn.addEventListener("mouseenter", () => {
1824
+ btn.style.background = tokens.color.primary[700];
1825
+ btn.style.transform = "scale(1.08)";
1826
+ btn.style.boxShadow = `${tokens.shadow.lg}, ${tokens.shadow.glow}`;
1827
+ });
1828
+ btn.addEventListener("mouseleave", () => {
1829
+ btn.style.background = tokens.color.primary[600];
1830
+ btn.style.transform = "scale(1)";
1831
+ btn.style.boxShadow = tokens.shadow.md;
1832
+ });
1833
+ btn.addEventListener("mousedown", () => {
1834
+ btn.style.transform = "scale(0.95)";
1835
+ });
1836
+ btn.addEventListener("mouseup", () => {
1837
+ btn.style.transform = "scale(1.08)";
1838
+ });
1839
+ btn.addEventListener("click", (e) => {
1840
+ e.stopPropagation();
1841
+ toggle();
1842
+ });
1843
+ document.body.appendChild(btn);
1844
+ }
1845
+ function init() {
1846
+ if (document.getElementById("pt-toggle-button")) return;
1847
+ createToggleButton();
1848
+ console.log("%c Promptotype %c Ready — Cmd+Shift+D to activate", `background:${tokens.color.primary[600]};color:white;padding:2px 8px;border-radius:4px;font-weight:600`, `color:${tokens.color.primary[500]}`);
1849
+ }
1850
+ if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init);
1851
+ else init();
1852
+ window.Promptotype = {
1853
+ activate,
1854
+ deactivate,
1855
+ toggle
1856
+ };
1857
+ //#endregion
1858
+ })();