dev-annotate 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,532 @@
1
+ // src/core/types.ts
2
+ var DEFAULTS = {
3
+ endpoint: "/api/dev/save-annotation",
4
+ colors: ["#ff3b30", "#ff9500", "#ffd60a", "#ffffff"],
5
+ sizes: [
6
+ { label: "S", pen: 3, font: 14 },
7
+ { label: "M", pen: 6, font: 18 },
8
+ { label: "L", pen: 12, font: 24 }
9
+ ],
10
+ shortcutKey: "a",
11
+ zIndexBase: 1e5
12
+ };
13
+
14
+ // src/core/client.ts
15
+ function initDevAnnotation(options = {}) {
16
+ const endpoint = options.endpoint ?? DEFAULTS.endpoint;
17
+ const colors = options.colors ?? DEFAULTS.colors;
18
+ const sizePresets = options.sizes ?? DEFAULTS.sizes;
19
+ const shortcutKey = (options.shortcutKey ?? DEFAULTS.shortcutKey).toLowerCase();
20
+ const z = options.zIndexBase ?? DEFAULTS.zIndexBase;
21
+ const nodes = [];
22
+ const cleanups = [];
23
+ const track = (n) => {
24
+ nodes.push(n);
25
+ return n;
26
+ };
27
+ const on = (target, type, handler, opts) => {
28
+ target.addEventListener(type, handler, opts);
29
+ cleanups.push(() => target.removeEventListener(type, handler, opts));
30
+ };
31
+ let panelOpen = false;
32
+ let tool = "free";
33
+ let color = colors[0] ?? "#ff3b30";
34
+ let penSize = 3;
35
+ let fontSize = 16;
36
+ let pinCount = 0;
37
+ let isDrawing = false;
38
+ let lastX = 0;
39
+ let lastY = 0;
40
+ const history = [];
41
+ const style = document.createElement("style");
42
+ style.textContent = `
43
+ #__da-toggle {
44
+ position: fixed; bottom: 24px; right: 24px;
45
+ width: 48px; height: 48px; border-radius: 50%;
46
+ background: rgba(15,15,30,0.9); border: 2px solid #ff3b30;
47
+ color: #ff3b30; font-size: 18px; cursor: pointer;
48
+ z-index: ${z}; display: flex; align-items: center; justify-content: center;
49
+ box-shadow: 0 4px 16px rgba(0,0,0,0.5); transition: transform 0.15s;
50
+ line-height: 1;
51
+ }
52
+ #__da-toggle:hover { transform: scale(1.1); }
53
+ #__da-toggle.on { background: #ff3b30; color: #fff; }
54
+
55
+ #__da-canvas {
56
+ position: fixed; inset: 0; z-index: ${z - 2};
57
+ pointer-events: none; cursor: crosshair;
58
+ }
59
+ #__da-canvas.on { }
60
+
61
+ #__da-bar {
62
+ position: fixed; bottom: 80px; right: 16px;
63
+ background: rgba(10,10,20,0.97); border: 1px solid rgba(255,255,255,0.1);
64
+ border-radius: 12px; padding: 12px 14px; z-index: ${z + 1};
65
+ display: none; flex-direction: column; gap: 10px;
66
+ box-shadow: 0 8px 32px rgba(0,0,0,0.6); min-width: 172px;
67
+ font-family: monospace;
68
+ }
69
+ #__da-bar.on { display: flex; }
70
+
71
+ .__da-sec-label {
72
+ font-size: 9px; letter-spacing: 0.12em;
73
+ color: rgba(255,255,255,0.3); margin-bottom: -4px;
74
+ }
75
+ .__da-row { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
76
+
77
+ .__da-btn {
78
+ padding: 5px 10px; border-radius: 6px;
79
+ border: 1px solid rgba(255,255,255,0.13);
80
+ background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.8);
81
+ font: 12px/1 monospace; cursor: pointer; transition: background 0.1s;
82
+ white-space: nowrap;
83
+ }
84
+ .__da-btn:hover { background: rgba(255,255,255,0.13); }
85
+ .__da-btn.on { background: #ff3b30; border-color: #ff3b30; color: #fff; }
86
+
87
+ .__da-swatch {
88
+ width: 22px; height: 22px; border-radius: 50%;
89
+ border: 2px solid transparent; cursor: pointer; flex-shrink: 0;
90
+ transition: transform 0.1s;
91
+ }
92
+ .__da-swatch:hover { transform: scale(1.15); }
93
+ .__da-swatch.on { border-color: #fff; }
94
+
95
+ .__da-hr { height: 1px; background: rgba(255,255,255,0.07); }
96
+
97
+ #__da-save {
98
+ background: #ff3b30 !important; border-color: #ff3b30 !important;
99
+ color: #fff !important; font-weight: 700; text-align: center;
100
+ width: 100%; padding: 7px 0;
101
+ }
102
+ #__da-save:hover { background: #ff6259 !important; }
103
+ #__da-save:disabled { opacity: 0.6; cursor: default; }
104
+
105
+ #__da-status {
106
+ font-size: 10px; color: #4caf50; text-align: center;
107
+ display: none; word-break: break-all; line-height: 1.4;
108
+ }
109
+
110
+ #__da-text-input {
111
+ position: fixed; z-index: ${z + 2};
112
+ background: rgba(10,10,20,0.92);
113
+ border: 1.5px solid #ff3b30; border-radius: 4px;
114
+ padding: 4px 8px; color: #fff;
115
+ font-size: 16px;
116
+ font-family: 'Noto Sans JP', sans-serif;
117
+ outline: none; min-width: 160px; max-width: 320px;
118
+ box-shadow: 0 2px 12px rgba(0,0,0,0.5);
119
+ display: none;
120
+ }
121
+ #__da-text-hint {
122
+ position: fixed; z-index: ${z + 2};
123
+ font: 10px/1 monospace;
124
+ color: rgba(255,255,255,0.4);
125
+ background: rgba(0,0,0,0.4); padding: 2px 6px; border-radius: 3px;
126
+ pointer-events: none; display: none;
127
+ }
128
+ `;
129
+ document.head.appendChild(track(style));
130
+ const canvas = document.createElement("canvas");
131
+ canvas.id = "__da-canvas";
132
+ document.body.appendChild(track(canvas));
133
+ const ctx = canvas.getContext("2d");
134
+ const textInput = document.createElement("input");
135
+ textInput.id = "__da-text-input";
136
+ textInput.placeholder = "\u30C6\u30AD\u30B9\u30C8\u3092\u5165\u529B\u2026 Enter \u3067\u78BA\u5B9A";
137
+ document.body.appendChild(track(textInput));
138
+ const textHint = document.createElement("div");
139
+ textHint.id = "__da-text-hint";
140
+ textHint.textContent = "Enter: \u78BA\u5B9A Esc: \u30AD\u30E3\u30F3\u30BB\u30EB";
141
+ document.body.appendChild(track(textHint));
142
+ const bar = document.createElement("div");
143
+ bar.id = "__da-bar";
144
+ const toggle = document.createElement("button");
145
+ toggle.id = "__da-toggle";
146
+ toggle.title = "\u30A2\u30CE\u30C6\u30FC\u30B7\u30E7\u30F3 (A)";
147
+ toggle.textContent = "\u270F";
148
+ document.body.appendChild(track(toggle));
149
+ document.body.appendChild(track(bar));
150
+ function mkSection(label) {
151
+ const el = document.createElement("div");
152
+ el.className = "__da-sec-label";
153
+ el.textContent = label;
154
+ bar.appendChild(el);
155
+ }
156
+ function mkRow() {
157
+ const el = document.createElement("div");
158
+ el.className = "__da-row";
159
+ bar.appendChild(el);
160
+ return el;
161
+ }
162
+ function mkHr() {
163
+ const el = document.createElement("div");
164
+ el.className = "__da-hr";
165
+ bar.appendChild(el);
166
+ }
167
+ function mkBtn(label, parent, active = false) {
168
+ const el = document.createElement("button");
169
+ el.className = "__da-btn" + (active ? " on" : "");
170
+ el.textContent = label;
171
+ parent.appendChild(el);
172
+ return el;
173
+ }
174
+ mkSection("TOOL");
175
+ const toolRow = mkRow();
176
+ const freeBtn = mkBtn("\u{1F590} \u30D5\u30EA\u30FC", toolRow, true);
177
+ const penBtn = mkBtn("\u270F \u30DA\u30F3", toolRow);
178
+ const textBtn = mkBtn("T \u30C6\u30AD\u30B9\u30C8", toolRow);
179
+ const pinBtn = mkBtn("\u{1F4CD} \u30D4\u30F3", toolRow);
180
+ const allToolBtns = [freeBtn, penBtn, textBtn, pinBtn];
181
+ mkSection("COLOR");
182
+ const colorRow = mkRow();
183
+ const swatches = colors.map((hex, i) => {
184
+ const el = document.createElement("div");
185
+ el.className = "__da-swatch" + (i === 0 ? " on" : "");
186
+ el.style.background = hex;
187
+ el.title = hex;
188
+ on(el, "click", () => {
189
+ color = hex;
190
+ swatches.forEach((s) => s.classList.remove("on"));
191
+ el.classList.add("on");
192
+ textInput.style.color = hex;
193
+ textInput.style.borderColor = hex;
194
+ });
195
+ colorRow.appendChild(el);
196
+ return el;
197
+ });
198
+ mkSection("SIZE");
199
+ const sizeRow = mkRow();
200
+ const sizeBtns = sizePresets.map(({ label, pen, font }, i) => {
201
+ const el = mkBtn(label, sizeRow, i === 0);
202
+ on(el, "click", () => {
203
+ penSize = pen;
204
+ fontSize = font;
205
+ sizeBtns.forEach((b) => b.classList.remove("on"));
206
+ el.classList.add("on");
207
+ textInput.style.fontSize = font + "px";
208
+ });
209
+ return el;
210
+ });
211
+ mkHr();
212
+ mkSection("ACTION");
213
+ const actRow = mkRow();
214
+ const undoBtn = mkBtn("\u21A9 Undo", actRow);
215
+ const clearBtn = mkBtn("\u{1F5D1} Clear", actRow);
216
+ mkHr();
217
+ const cameraBtn = document.createElement("button");
218
+ cameraBtn.id = "__da-save";
219
+ cameraBtn.className = "__da-btn";
220
+ cameraBtn.textContent = "\u{1F4F7} \u64AE\u5F71\u30E2\u30FC\u30C9\uFF08UI\u3092\u96A0\u3059\uFF09";
221
+ bar.appendChild(cameraBtn);
222
+ const uploadBtn = document.createElement("button");
223
+ uploadBtn.className = "__da-btn";
224
+ uploadBtn.style.cssText = "width:100%; padding:7px 0; margin-top:8px; text-align:center; font-weight:700;";
225
+ uploadBtn.textContent = "\u2B06 \u30B9\u30AF\u30B7\u30E7\u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9";
226
+ bar.appendChild(uploadBtn);
227
+ const fileInput = document.createElement("input");
228
+ fileInput.type = "file";
229
+ fileInput.accept = "image/*";
230
+ fileInput.style.display = "none";
231
+ document.body.appendChild(track(fileInput));
232
+ const statusEl = document.createElement("div");
233
+ statusEl.id = "__da-status";
234
+ bar.appendChild(statusEl);
235
+ function resizeCanvas() {
236
+ const data = ctx && canvas.width > 0 ? ctx.getImageData(0, 0, canvas.width, canvas.height) : null;
237
+ canvas.width = window.innerWidth;
238
+ canvas.height = window.innerHeight;
239
+ if (ctx && data) ctx.putImageData(data, 0, 0);
240
+ }
241
+ resizeCanvas();
242
+ on(window, "resize", resizeCanvas);
243
+ function updateCanvasInteraction() {
244
+ const drawing = tool !== "free";
245
+ canvas.style.pointerEvents = drawing ? "all" : "none";
246
+ canvas.style.touchAction = drawing ? "none" : "auto";
247
+ canvas.classList.toggle("on", panelOpen || drawing);
248
+ }
249
+ function updateToggleAppearance() {
250
+ const drawing = tool !== "free";
251
+ toggle.textContent = panelOpen ? "\u2715" : "\u270F";
252
+ toggle.classList.toggle("on", panelOpen || drawing);
253
+ }
254
+ function setTool(t) {
255
+ tool = t;
256
+ allToolBtns.forEach((b) => b.classList.remove("on"));
257
+ if (t === "free") {
258
+ freeBtn.classList.add("on");
259
+ canvas.style.cursor = "default";
260
+ }
261
+ if (t === "pen") {
262
+ penBtn.classList.add("on");
263
+ canvas.style.cursor = "crosshair";
264
+ }
265
+ if (t === "text") {
266
+ textBtn.classList.add("on");
267
+ canvas.style.cursor = "text";
268
+ }
269
+ if (t === "pin") {
270
+ pinBtn.classList.add("on");
271
+ canvas.style.cursor = "cell";
272
+ }
273
+ updateCanvasInteraction();
274
+ updateToggleAppearance();
275
+ }
276
+ on(freeBtn, "click", () => setTool("free"));
277
+ on(penBtn, "click", () => setTool("pen"));
278
+ on(textBtn, "click", () => setTool("text"));
279
+ on(pinBtn, "click", () => setTool("pin"));
280
+ function pushHistory() {
281
+ if (!ctx) return;
282
+ if (history.length >= 30) history.shift();
283
+ history.push(ctx.getImageData(0, 0, canvas.width, canvas.height));
284
+ }
285
+ function drawPin(x, y, n) {
286
+ if (!ctx) return;
287
+ ctx.save();
288
+ ctx.shadowColor = "rgba(0,0,0,0.5)";
289
+ ctx.shadowBlur = 4;
290
+ ctx.fillStyle = color;
291
+ ctx.strokeStyle = "#fff";
292
+ ctx.lineWidth = 1.5;
293
+ ctx.beginPath();
294
+ ctx.arc(x, y, 13, 0, Math.PI * 2);
295
+ ctx.fill();
296
+ ctx.stroke();
297
+ ctx.shadowBlur = 0;
298
+ ctx.fillStyle = "#fff";
299
+ ctx.font = "bold 12px monospace";
300
+ ctx.textAlign = "center";
301
+ ctx.textBaseline = "middle";
302
+ ctx.fillText(String(n), x, y);
303
+ ctx.restore();
304
+ }
305
+ let textX = 0;
306
+ let textY = 0;
307
+ function showTextInput(x, y) {
308
+ textX = x;
309
+ textY = y;
310
+ textInput.value = "";
311
+ textInput.style.display = "block";
312
+ textInput.style.color = color;
313
+ textInput.style.borderColor = color;
314
+ textInput.style.fontSize = Math.max(fontSize, 16) + "px";
315
+ const maxLeft = window.innerWidth - 340;
316
+ const maxTop = window.innerHeight - 80;
317
+ textInput.style.left = Math.min(x, maxLeft) + "px";
318
+ textInput.style.top = Math.min(y, maxTop) + "px";
319
+ textHint.style.display = "block";
320
+ textHint.style.left = Math.min(x, maxLeft) + "px";
321
+ textHint.style.top = Math.min(y, maxTop) - 22 + "px";
322
+ canvas.style.pointerEvents = "none";
323
+ requestAnimationFrame(() => textInput.focus());
324
+ }
325
+ function commitText() {
326
+ const text = textInput.value.trim();
327
+ if (text && ctx) {
328
+ pushHistory();
329
+ ctx.save();
330
+ const fSize = Math.max(fontSize, 14);
331
+ ctx.font = `bold ${fSize}px 'Noto Sans JP', sans-serif`;
332
+ ctx.textBaseline = "top";
333
+ const metrics = ctx.measureText(text);
334
+ const pad = 5;
335
+ const bgH = fSize + pad * 2;
336
+ ctx.fillStyle = "rgba(0,0,0,0.6)";
337
+ ctx.fillRect(textX - pad, textY - pad, metrics.width + pad * 2, bgH);
338
+ ctx.fillStyle = color;
339
+ ctx.fillText(text, textX, textY);
340
+ ctx.restore();
341
+ }
342
+ hideTextInput();
343
+ }
344
+ function hideTextInput() {
345
+ textInput.style.display = "none";
346
+ textHint.style.display = "none";
347
+ textInput.value = "";
348
+ if (tool !== "free") canvas.style.pointerEvents = "all";
349
+ }
350
+ on(textInput, "keydown", (e) => {
351
+ const ke = e;
352
+ if (ke.key === "Enter") {
353
+ ke.preventDefault();
354
+ commitText();
355
+ }
356
+ if (ke.key === "Escape") {
357
+ ke.preventDefault();
358
+ hideTextInput();
359
+ }
360
+ ke.stopPropagation();
361
+ });
362
+ on(textInput, "blur", () => {
363
+ setTimeout(() => {
364
+ if (document.activeElement !== textInput) commitText();
365
+ }, 150);
366
+ });
367
+ function onStart(x, y) {
368
+ if (tool === "pin") {
369
+ pushHistory();
370
+ pinCount++;
371
+ drawPin(x, y, pinCount);
372
+ return;
373
+ }
374
+ if (tool === "text") {
375
+ showTextInput(x, y);
376
+ return;
377
+ }
378
+ isDrawing = true;
379
+ lastX = x;
380
+ lastY = y;
381
+ pushHistory();
382
+ if (ctx) {
383
+ ctx.beginPath();
384
+ ctx.moveTo(x, y);
385
+ }
386
+ }
387
+ function onMove(x, y) {
388
+ if (!isDrawing || tool !== "pen" || !ctx) return;
389
+ ctx.strokeStyle = color;
390
+ ctx.lineWidth = penSize;
391
+ ctx.lineCap = "round";
392
+ ctx.lineJoin = "round";
393
+ ctx.lineTo(x, y);
394
+ ctx.stroke();
395
+ lastX = x;
396
+ lastY = y;
397
+ }
398
+ function onEnd() {
399
+ isDrawing = false;
400
+ }
401
+ on(canvas, "mousedown", (e) => {
402
+ const me = e;
403
+ onStart(me.offsetX, me.offsetY);
404
+ });
405
+ on(canvas, "mousemove", (e) => {
406
+ const me = e;
407
+ onMove(me.offsetX, me.offsetY);
408
+ });
409
+ on(canvas, "mouseup", onEnd);
410
+ on(canvas, "mouseleave", onEnd);
411
+ on(canvas, "touchstart", (e) => {
412
+ const te = e;
413
+ if (tool === "free") return;
414
+ te.preventDefault();
415
+ const t = te.touches[0];
416
+ if (!t) return;
417
+ const r = canvas.getBoundingClientRect();
418
+ onStart(t.clientX - r.left, t.clientY - r.top);
419
+ }, { passive: false });
420
+ on(canvas, "touchmove", (e) => {
421
+ const te = e;
422
+ if (tool === "free") return;
423
+ te.preventDefault();
424
+ const t = te.touches[0];
425
+ if (!t) return;
426
+ const r = canvas.getBoundingClientRect();
427
+ onMove(t.clientX - r.left, t.clientY - r.top);
428
+ }, { passive: false });
429
+ on(canvas, "touchend", (e) => {
430
+ const te = e;
431
+ if (tool === "free") return;
432
+ te.preventDefault();
433
+ onEnd();
434
+ }, { passive: false });
435
+ on(undoBtn, "click", () => {
436
+ if (!history.length || !ctx) return;
437
+ ctx.putImageData(history.pop(), 0, 0);
438
+ if (tool === "pin") pinCount = Math.max(0, pinCount - 1);
439
+ });
440
+ on(clearBtn, "click", () => {
441
+ pushHistory();
442
+ if (ctx) ctx.clearRect(0, 0, canvas.width, canvas.height);
443
+ pinCount = 0;
444
+ });
445
+ const camTap = document.createElement("div");
446
+ camTap.style.cssText = `position:fixed; inset:0; z-index:${z + 3}; display:none; background:transparent;`;
447
+ document.body.appendChild(track(camTap));
448
+ const camToast = document.createElement("div");
449
+ camToast.style.cssText = `position:fixed; left:50%; top:50%; transform:translate(-50%,-50%);z-index:${z + 4}; max-width:80vw; padding:14px 18px; border-radius:12px;background:rgba(10,10,20,0.92); border:1px solid #ff3b30; color:#fff;font:600 13px/1.6 'Noto Sans JP',sans-serif; text-align:center;box-shadow:0 8px 32px rgba(0,0,0,0.6); pointer-events:none;opacity:0; transition:opacity 0.4s; display:none;`;
450
+ document.body.appendChild(track(camToast));
451
+ function enterCameraMode() {
452
+ panelOpen = false;
453
+ bar.classList.remove("on");
454
+ toggle.style.display = "none";
455
+ hideTextInput();
456
+ camTap.style.display = "block";
457
+ camToast.style.display = "block";
458
+ camToast.textContent = "\u{1F4F8} \u30E1\u30CB\u30E5\u30FC\u3092\u96A0\u3057\u307E\u3057\u305F\u3002\u3053\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u304C\u6D88\u3048\u305F\u3089 OS \u30B9\u30AF\u30B7\u30E7\u3092\u64AE\u5F71 \u2192 \u753B\u9762\u30BF\u30C3\u30D7\u3067\u30E1\u30CB\u30E5\u30FC\u5FA9\u5E30";
459
+ requestAnimationFrame(() => {
460
+ camToast.style.opacity = "1";
461
+ });
462
+ setTimeout(() => {
463
+ camToast.style.opacity = "0";
464
+ }, 2200);
465
+ setTimeout(() => {
466
+ camToast.style.display = "none";
467
+ }, 2700);
468
+ }
469
+ function exitCameraMode() {
470
+ camTap.style.display = "none";
471
+ camToast.style.display = "none";
472
+ camToast.style.opacity = "0";
473
+ toggle.style.display = "";
474
+ panelOpen = true;
475
+ bar.classList.add("on");
476
+ updateToggleAppearance();
477
+ }
478
+ on(camTap, "click", exitCameraMode);
479
+ on(cameraBtn, "click", enterCameraMode);
480
+ function showStatus(msg, col) {
481
+ statusEl.style.display = "block";
482
+ statusEl.style.color = col;
483
+ statusEl.textContent = msg;
484
+ setTimeout(() => {
485
+ statusEl.style.display = "none";
486
+ }, 5e3);
487
+ }
488
+ on(uploadBtn, "click", () => fileInput.click());
489
+ on(fileInput, "change", async () => {
490
+ const file = fileInput.files?.[0];
491
+ if (!file) return;
492
+ uploadBtn.disabled = true;
493
+ uploadBtn.textContent = "\u23F3 \u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D...";
494
+ try {
495
+ const fd = new FormData();
496
+ fd.append("file", file, file.name);
497
+ const res = await fetch(endpoint, { method: "POST", body: fd });
498
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
499
+ const data = await res.json();
500
+ showStatus(`\u2713 \u4FDD\u5B58: ${data.path} (${Math.round(data.bytes / 1024)}KB)`, "#4caf50");
501
+ } catch (err) {
502
+ showStatus(`\u2717 \u5931\u6557: ${err}`, "#ff3b30");
503
+ } finally {
504
+ uploadBtn.disabled = false;
505
+ uploadBtn.textContent = "\u2B06 \u30B9\u30AF\u30B7\u30E7\u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9";
506
+ fileInput.value = "";
507
+ }
508
+ });
509
+ function togglePanel() {
510
+ panelOpen = !panelOpen;
511
+ bar.classList.toggle("on", panelOpen);
512
+ canvas.classList.toggle("on", panelOpen || tool !== "free");
513
+ if (!panelOpen) {
514
+ isDrawing = false;
515
+ hideTextInput();
516
+ }
517
+ updateToggleAppearance();
518
+ }
519
+ on(toggle, "click", togglePanel);
520
+ on(window, "keydown", (e) => {
521
+ const ke = e;
522
+ const tag = ke.target.tagName;
523
+ if (ke.key.toLowerCase() === shortcutKey && tag !== "INPUT" && tag !== "TEXTAREA") togglePanel();
524
+ });
525
+ return () => {
526
+ for (const c of cleanups) c();
527
+ for (const n of nodes) n.remove();
528
+ };
529
+ }
530
+ export {
531
+ initDevAnnotation
532
+ };
@@ -0,0 +1,9 @@
1
+ import { D as DevAnnotationOptions } from '../types-BzKl2hyk.js';
2
+
3
+ interface ModuleOptions extends DevAnnotationOptions {
4
+ /** Server-side save directory. Default '.playwright-mcp/design-review'. */
5
+ dir?: string;
6
+ }
7
+ declare const _default: NuxtModule<TOptions, TOptions, false>;
8
+
9
+ export { type ModuleOptions, _default as default };
@@ -0,0 +1,28 @@
1
+ // src/nuxt/module.ts
2
+ import { defineNuxtModule, addPlugin, addServerHandler, createResolver } from "@nuxt/kit";
3
+ var module_default = defineNuxtModule({
4
+ meta: { name: "dev-annotate", configKey: "devAnnotate" },
5
+ defaults: {},
6
+ setup(options, nuxt) {
7
+ if (!nuxt.options.dev) return;
8
+ const resolver = createResolver(import.meta.url);
9
+ nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public ?? {};
10
+ nuxt.options.runtimeConfig.public.devAnnotate = {
11
+ endpoint: options.endpoint,
12
+ colors: options.colors,
13
+ sizes: options.sizes,
14
+ shortcutKey: options.shortcutKey,
15
+ zIndexBase: options.zIndexBase
16
+ };
17
+ nuxt.options.runtimeConfig.devAnnotate = { dir: options.dir };
18
+ addPlugin({ src: resolver.resolve("./runtime/plugin.client.js"), mode: "client" });
19
+ addServerHandler({
20
+ route: options.endpoint ?? "/api/dev/save-annotation",
21
+ method: "post",
22
+ handler: resolver.resolve("./runtime/server-route.js")
23
+ });
24
+ }
25
+ });
26
+ export {
27
+ module_default as default
28
+ };
@@ -0,0 +1,3 @@
1
+ declare const _default: () => void;
2
+
3
+ export { _default as default };