@wasao/kagemusha 0.1.1 → 0.3.4

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 (155) hide show
  1. package/README.md +168 -79
  2. package/dist/commands/add.d.ts +6 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +26 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/capture.d.ts +3 -2
  7. package/dist/commands/capture.d.ts.map +1 -1
  8. package/dist/commands/capture.js +256 -20
  9. package/dist/commands/capture.js.map +1 -1
  10. package/dist/commands/discover.d.ts +2 -0
  11. package/dist/commands/discover.d.ts.map +1 -0
  12. package/dist/commands/discover.js +62 -0
  13. package/dist/commands/discover.js.map +1 -0
  14. package/dist/commands/edit.d.ts +1 -1
  15. package/dist/commands/edit.d.ts.map +1 -1
  16. package/dist/commands/edit.js +82 -26
  17. package/dist/commands/edit.js.map +1 -1
  18. package/dist/commands/init.d.ts +1 -1
  19. package/dist/commands/init.d.ts.map +1 -1
  20. package/dist/commands/init.js +240 -105
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/list.d.ts +2 -0
  23. package/dist/commands/list.d.ts.map +1 -0
  24. package/dist/commands/list.js +33 -0
  25. package/dist/commands/list.js.map +1 -0
  26. package/dist/commands/login.d.ts +6 -0
  27. package/dist/commands/login.d.ts.map +1 -0
  28. package/dist/commands/login.js +131 -0
  29. package/dist/commands/login.js.map +1 -0
  30. package/dist/commands/validate.js +1 -1
  31. package/dist/commands/validate.js.map +1 -1
  32. package/dist/editor/inject-script/annotations.d.ts +11 -0
  33. package/dist/editor/inject-script/annotations.d.ts.map +1 -0
  34. package/dist/editor/inject-script/annotations.js +409 -0
  35. package/dist/editor/inject-script/annotations.js.map +1 -0
  36. package/dist/editor/inject-script/bridge.d.ts +13 -0
  37. package/dist/editor/inject-script/bridge.d.ts.map +1 -0
  38. package/dist/editor/inject-script/bridge.js +33 -0
  39. package/dist/editor/inject-script/bridge.js.map +1 -0
  40. package/dist/editor/inject-script/crop.d.ts +9 -0
  41. package/dist/editor/inject-script/crop.d.ts.map +1 -0
  42. package/dist/editor/inject-script/crop.js +236 -0
  43. package/dist/editor/inject-script/crop.js.map +1 -0
  44. package/dist/editor/inject-script/dom.d.ts +7 -0
  45. package/dist/editor/inject-script/dom.d.ts.map +1 -0
  46. package/dist/editor/inject-script/dom.js +32 -0
  47. package/dist/editor/inject-script/dom.js.map +1 -0
  48. package/dist/editor/inject-script/index.d.ts +2 -0
  49. package/dist/editor/inject-script/index.d.ts.map +1 -0
  50. package/dist/editor/inject-script/index.js +56 -0
  51. package/dist/editor/inject-script/index.js.map +1 -0
  52. package/dist/editor/inject-script/record.d.ts +5 -0
  53. package/dist/editor/inject-script/record.d.ts.map +1 -0
  54. package/dist/editor/inject-script/record.js +398 -0
  55. package/dist/editor/inject-script/record.js.map +1 -0
  56. package/dist/editor/inject-script/selector.d.ts +6 -0
  57. package/dist/editor/inject-script/selector.d.ts.map +1 -0
  58. package/dist/editor/inject-script/selector.js +112 -0
  59. package/dist/editor/inject-script/selector.js.map +1 -0
  60. package/dist/editor/inject-script/state.d.ts +27 -0
  61. package/dist/editor/inject-script/state.d.ts.map +1 -0
  62. package/dist/editor/inject-script/state.js +26 -0
  63. package/dist/editor/inject-script/state.js.map +1 -0
  64. package/dist/editor/inject-script/svg.d.ts +7 -0
  65. package/dist/editor/inject-script/svg.d.ts.map +1 -0
  66. package/dist/editor/inject-script/svg.js +39 -0
  67. package/dist/editor/inject-script/svg.js.map +1 -0
  68. package/dist/editor/inject-script/toolbar.d.ts +14 -0
  69. package/dist/editor/inject-script/toolbar.d.ts.map +1 -0
  70. package/dist/editor/inject-script/toolbar.js +240 -0
  71. package/dist/editor/inject-script/toolbar.js.map +1 -0
  72. package/dist/editor/inject-script/types.d.ts +102 -0
  73. package/dist/editor/inject-script/types.d.ts.map +1 -0
  74. package/dist/editor/inject-script/types.js +5 -0
  75. package/dist/editor/inject-script/types.js.map +1 -0
  76. package/dist/editor/inject-script.js +1276 -353
  77. package/dist/index.js +34 -16
  78. package/dist/index.js.map +1 -1
  79. package/dist/lib/annotate.d.ts +2 -2
  80. package/dist/lib/annotate.d.ts.map +1 -1
  81. package/dist/lib/annotate.js +35 -43
  82. package/dist/lib/annotate.js.map +1 -1
  83. package/dist/lib/auth.d.ts +18 -0
  84. package/dist/lib/auth.d.ts.map +1 -0
  85. package/dist/lib/auth.js +45 -0
  86. package/dist/lib/auth.js.map +1 -0
  87. package/dist/lib/aws-error.d.ts +7 -0
  88. package/dist/lib/aws-error.d.ts.map +1 -0
  89. package/dist/lib/aws-error.js +74 -0
  90. package/dist/lib/aws-error.js.map +1 -0
  91. package/dist/lib/canonical.d.ts +54 -0
  92. package/dist/lib/canonical.d.ts.map +1 -0
  93. package/dist/lib/canonical.js +152 -0
  94. package/dist/lib/canonical.js.map +1 -0
  95. package/dist/lib/config.d.ts +2 -0
  96. package/dist/lib/config.d.ts.map +1 -1
  97. package/dist/lib/config.js +23 -13
  98. package/dist/lib/config.js.map +1 -1
  99. package/dist/lib/crawl.d.ts +1 -1
  100. package/dist/lib/crawl.d.ts.map +1 -1
  101. package/dist/lib/crawl.js +213 -20
  102. package/dist/lib/crawl.js.map +1 -1
  103. package/dist/lib/definition.d.ts +2 -0
  104. package/dist/lib/definition.d.ts.map +1 -0
  105. package/dist/lib/definition.js +6 -0
  106. package/dist/lib/definition.js.map +1 -0
  107. package/dist/lib/diff.d.ts +71 -0
  108. package/dist/lib/diff.d.ts.map +1 -0
  109. package/dist/lib/diff.js +40 -0
  110. package/dist/lib/diff.js.map +1 -0
  111. package/dist/lib/login-error.d.ts +10 -0
  112. package/dist/lib/login-error.d.ts.map +1 -0
  113. package/dist/lib/login-error.js +13 -0
  114. package/dist/lib/login-error.js.map +1 -0
  115. package/dist/lib/page-ready.d.ts +18 -0
  116. package/dist/lib/page-ready.d.ts.map +1 -0
  117. package/dist/lib/page-ready.js +19 -0
  118. package/dist/lib/page-ready.js.map +1 -0
  119. package/dist/lib/screenshot.d.ts +11 -2
  120. package/dist/lib/screenshot.d.ts.map +1 -1
  121. package/dist/lib/screenshot.js +73 -64
  122. package/dist/lib/screenshot.js.map +1 -1
  123. package/dist/lib/staging.d.ts +7 -0
  124. package/dist/lib/staging.d.ts.map +1 -0
  125. package/dist/lib/staging.js +21 -0
  126. package/dist/lib/staging.js.map +1 -0
  127. package/dist/types.d.ts +10 -23
  128. package/dist/types.d.ts.map +1 -1
  129. package/package.json +20 -3
  130. package/dist/commands/preview.d.ts +0 -6
  131. package/dist/commands/preview.d.ts.map +0 -1
  132. package/dist/commands/preview.js +0 -33
  133. package/dist/commands/preview.js.map +0 -1
  134. package/dist/commands/run.d.ts +0 -6
  135. package/dist/commands/run.d.ts.map +0 -1
  136. package/dist/commands/run.js +0 -39
  137. package/dist/commands/run.js.map +0 -1
  138. package/dist/editor/editor/editor.html +0 -313
  139. package/dist/editor/editor/inject.ts +0 -385
  140. package/dist/editor/editor.html +0 -338
  141. package/dist/editor/inject-script.cjs +0 -398
  142. package/dist/editor/inject-script.cjs.map +0 -1
  143. package/dist/editor/inject-script.d.cts +0 -2
  144. package/dist/editor/inject-script.d.cts.map +0 -1
  145. package/dist/editor/inject-script.d.ts +0 -2
  146. package/dist/editor/inject-script.d.ts.map +0 -1
  147. package/dist/editor/inject-script.js.map +0 -1
  148. package/dist/editor/inject.d.ts +0 -2
  149. package/dist/editor/inject.d.ts.map +0 -1
  150. package/dist/editor/inject.js +0 -385
  151. package/dist/editor/inject.js.map +0 -1
  152. package/dist/lib/upload.d.ts +0 -9
  153. package/dist/lib/upload.d.ts.map +0 -1
  154. package/dist/lib/upload.js +0 -43
  155. package/dist/lib/upload.js.map +0 -1
@@ -1,385 +0,0 @@
1
- // This script is injected into the target page to provide annotation editing.
2
- // It adds a toolbar and SVG overlay layer on top of the actual page.
3
-
4
- export const EDITOR_SCRIPT = `
5
- (function() {
6
- let tool = 'rect';
7
- let annotations = [];
8
- let selectedId = null;
9
- let dragState = null;
10
- let nextId = 1;
11
-
12
- // --- TOOLBAR ---
13
- const toolbar = document.createElement('div');
14
- toolbar.id = 'kagemusha-toolbar';
15
- toolbar.innerHTML = \`
16
- <style>
17
- #kagemusha-toolbar {
18
- position: fixed; top: 0; left: 0; right: 0; z-index: 999999;
19
- background: #16213e; padding: 8px 16px; display: flex; align-items: center; gap: 12px;
20
- box-shadow: 0 2px 8px rgba(0,0,0,0.3); font-family: -apple-system, sans-serif;
21
- }
22
- #kagemusha-toolbar button {
23
- padding: 6px 14px; border: 1px solid #444; border-radius: 6px;
24
- background: #1a1a2e; color: #fff; font-size: 13px; cursor: pointer;
25
- }
26
- #kagemusha-toolbar button:hover { background: #2a2a4e; }
27
- #kagemusha-toolbar button.active { background: #6366f1; border-color: #6366f1; }
28
- #kagemusha-toolbar .sep { width: 1px; height: 24px; background: #444; }
29
- #kagemusha-toolbar .title { color: #888; font-size: 13px; }
30
- #kagemusha-toolbar .save-btn { background: #22c55e; border-color: #22c55e; font-weight: 600; margin-left: auto; }
31
- #kagemusha-toolbar .save-btn:hover { background: #16a34a; }
32
- #kagemusha-svg-layer {
33
- position: absolute; top: 0; left: 0; width: 100%;
34
- z-index: 999998; pointer-events: none;
35
- }
36
- #kagemusha-svg-layer.drawing { pointer-events: auto; cursor: crosshair; }
37
- #kagemusha-svg-layer .annotation { pointer-events: auto; cursor: move; }
38
- #kagemusha-svg-layer .annotation.selected { filter: drop-shadow(0 0 3px #6366f1); }
39
- .kagemusha-hint {
40
- position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
41
- color: #fff; background: rgba(0,0,0,0.7); padding: 6px 16px; border-radius: 8px;
42
- font-size: 12px; z-index: 999999; font-family: -apple-system, sans-serif;
43
- }
44
- </style>
45
- <span class="title">🥷 Annotation Editor</span>
46
- <button id="kg-tool-rect" class="active">▭ Rectangle</button>
47
- <button id="kg-tool-arrow">→ Arrow</button>
48
- <button id="kg-tool-label">T Label</button>
49
- <div class="sep"></div>
50
- <button id="kg-delete">🗑 Delete</button>
51
- <button class="save-btn" id="kg-save">💾 Save</button>
52
- \`;
53
- document.body.appendChild(toolbar);
54
-
55
- // Shift page content down so toolbar doesn't overlap
56
- document.body.style.paddingTop = '48px';
57
-
58
- // Hint
59
- const hint = document.createElement('div');
60
- hint.className = 'kagemusha-hint';
61
- hint.textContent = 'Click and drag to add annotations. Click to select. Press Delete to remove.';
62
- document.body.appendChild(hint);
63
-
64
- // --- SVG LAYER ---
65
- const svgNS = 'http://www.w3.org/2000/svg';
66
- const svg = document.createElementNS(svgNS, 'svg');
67
- svg.id = 'kagemusha-svg-layer';
68
- svg.classList.add('drawing');
69
- document.body.appendChild(svg);
70
-
71
- function updateSvgSize() {
72
- svg.setAttribute('width', String(window.innerWidth));
73
- svg.setAttribute('height', String(document.documentElement.scrollHeight));
74
- }
75
- updateSvgSize();
76
- window.addEventListener('resize', updateSvgSize);
77
-
78
- // Arrowhead marker
79
- const defs = document.createElementNS(svgNS, 'defs');
80
- defs.innerHTML = '<marker id="kg-arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto" fill="#FF0000"><polygon points="0 0, 10 3.5, 0 7"/></marker>';
81
- svg.appendChild(defs);
82
-
83
- // --- TOOLS ---
84
- function setTool(t) {
85
- tool = t;
86
- document.querySelectorAll('#kagemusha-toolbar button').forEach(b => b.classList.remove('active'));
87
- const btn = document.getElementById('kg-tool-' + t);
88
- if (btn) btn.classList.add('active');
89
- svg.classList.toggle('drawing', true);
90
- deselectAll();
91
- }
92
-
93
- document.getElementById('kg-tool-rect').addEventListener('click', () => setTool('rect'));
94
- document.getElementById('kg-tool-arrow').addEventListener('click', () => setTool('arrow'));
95
- document.getElementById('kg-tool-label').addEventListener('click', () => setTool('label'));
96
- document.getElementById('kg-delete').addEventListener('click', deleteSelected);
97
- document.getElementById('kg-save').addEventListener('click', save);
98
-
99
- function deselectAll() {
100
- selectedId = null;
101
- svg.querySelectorAll('.annotation').forEach(el => el.classList.remove('selected'));
102
- }
103
-
104
- function selectEl(el) {
105
- deselectAll();
106
- selectedId = el.dataset.id;
107
- el.classList.add('selected');
108
- }
109
-
110
- function deleteSelected() {
111
- if (!selectedId) return;
112
- const el = svg.querySelector('[data-id="' + selectedId + '"]');
113
- if (el) el.remove();
114
- annotations = annotations.filter(a => a.id !== selectedId);
115
- selectedId = null;
116
- }
117
-
118
- document.addEventListener('keydown', e => {
119
- if (e.key === 'Delete' || e.key === 'Backspace') {
120
- if (document.activeElement.tagName === 'INPUT') return;
121
- deleteSelected();
122
- }
123
- });
124
-
125
- // --- MOUSE HANDLING ---
126
- function getPos(e) {
127
- return { x: e.pageX, y: e.pageY };
128
- }
129
-
130
- svg.addEventListener('mousedown', e => {
131
- if (e.target.closest('.annotation')) return;
132
- const p = getPos(e);
133
- deselectAll();
134
-
135
- if (tool === 'rect') {
136
- const id = 'a' + nextId++;
137
- const rect = document.createElementNS(svgNS, 'rect');
138
- rect.setAttribute('x', p.x);
139
- rect.setAttribute('y', p.y);
140
- rect.setAttribute('width', '0');
141
- rect.setAttribute('height', '0');
142
- rect.setAttribute('fill', 'none');
143
- rect.setAttribute('stroke', '#FF0000');
144
- rect.setAttribute('stroke-width', '3');
145
- rect.setAttribute('rx', '4');
146
- rect.classList.add('annotation');
147
- rect.dataset.id = id;
148
- svg.appendChild(rect);
149
- dragState = { type: 'create-rect', id, el: rect, sx: p.x, sy: p.y };
150
- } else if (tool === 'arrow') {
151
- const id = 'a' + nextId++;
152
- const line = document.createElementNS(svgNS, 'line');
153
- line.setAttribute('x1', p.x);
154
- line.setAttribute('y1', p.y);
155
- line.setAttribute('x2', p.x);
156
- line.setAttribute('y2', p.y);
157
- line.setAttribute('stroke', '#FF0000');
158
- line.setAttribute('stroke-width', '3');
159
- line.setAttribute('marker-end', 'url(#kg-arrowhead)');
160
- line.classList.add('annotation');
161
- line.dataset.id = id;
162
- svg.appendChild(line);
163
- dragState = { type: 'create-arrow', id, el: line, sx: p.x, sy: p.y };
164
- } else if (tool === 'label') {
165
- const id = 'a' + nextId++;
166
- // Create inline input on the page
167
- const input = document.createElement('input');
168
- input.type = 'text';
169
- input.value = '';
170
- input.placeholder = 'Type label...';
171
- input.style.cssText = 'position:fixed;z-index:9999999;padding:4px 8px;background:#fff;border:none;border-radius:4px;color:#FF0000;font-size:14px;font-family:-apple-system,sans-serif;outline:2px solid #6366f1;min-width:80px;box-shadow:0 2px 8px rgba(0,0,0,0.2);';
172
- input.style.left = (e.clientX) + 'px';
173
- input.style.top = (e.clientY) + 'px';
174
- document.body.appendChild(input);
175
- svg.classList.remove('drawing');
176
- setTimeout(() => input.focus(), 50);
177
-
178
- function finishLabel() {
179
- const text = input.value.trim();
180
- input.remove();
181
- svg.classList.add('drawing');
182
- if (!text) return;
183
-
184
- const g = document.createElementNS(svgNS, 'g');
185
- g.classList.add('annotation');
186
- g.dataset.id = id;
187
- const bg = document.createElementNS(svgNS, 'rect');
188
- const txt = document.createElementNS(svgNS, 'text');
189
- txt.textContent = text;
190
- txt.setAttribute('x', String(p.x + 6));
191
- txt.setAttribute('y', String(p.y + 16));
192
- txt.setAttribute('fill', '#FF0000');
193
- txt.setAttribute('font-size', '14');
194
- txt.setAttribute('font-family', '-apple-system, sans-serif');
195
- const tw = text.length * 9 + 12;
196
- bg.setAttribute('x', String(p.x));
197
- bg.setAttribute('y', String(p.y));
198
- bg.setAttribute('width', String(tw));
199
- bg.setAttribute('height', '24');
200
- bg.setAttribute('fill', '#FFFFFF');
201
- bg.setAttribute('rx', '4');
202
- g.appendChild(bg);
203
- g.appendChild(txt);
204
- svg.appendChild(g);
205
- annotations.push({ id, type: 'label', x: p.x, y: p.y, text });
206
- selectEl(g);
207
- g.addEventListener('mousedown', e => startMove(e, id));
208
- }
209
-
210
- input.addEventListener('keydown', e => {
211
- if (e.key === 'Enter') finishLabel();
212
- if (e.key === 'Escape') { input.remove(); svg.classList.add('drawing'); }
213
- });
214
- input.addEventListener('blur', finishLabel);
215
- }
216
- });
217
-
218
- document.addEventListener('mousemove', e => {
219
- if (!dragState) return;
220
- const p = getPos(e);
221
-
222
- if (dragState.type === 'create-rect') {
223
- const el = dragState.el;
224
- const x = Math.min(dragState.sx, p.x);
225
- const y = Math.min(dragState.sy, p.y);
226
- el.setAttribute('x', x);
227
- el.setAttribute('y', y);
228
- el.setAttribute('width', Math.abs(p.x - dragState.sx));
229
- el.setAttribute('height', Math.abs(p.y - dragState.sy));
230
- } else if (dragState.type === 'create-arrow') {
231
- dragState.el.setAttribute('x2', p.x);
232
- dragState.el.setAttribute('y2', p.y);
233
- } else if (dragState.type === 'move') {
234
- const a = annotations.find(a => a.id === dragState.id);
235
- if (!a) return;
236
- const dx = p.x - dragState.lastX;
237
- const dy = p.y - dragState.lastY;
238
- dragState.lastX = p.x;
239
- dragState.lastY = p.y;
240
-
241
- if (a.type === 'rect') {
242
- a.x += dx; a.y += dy;
243
- dragState.el.setAttribute('x', a.x);
244
- dragState.el.setAttribute('y', a.y);
245
- } else if (a.type === 'arrow') {
246
- a.fromX += dx; a.fromY += dy; a.toX += dx; a.toY += dy;
247
- dragState.el.setAttribute('x1', a.fromX);
248
- dragState.el.setAttribute('y1', a.fromY);
249
- dragState.el.setAttribute('x2', a.toX);
250
- dragState.el.setAttribute('y2', a.toY);
251
- } else if (a.type === 'label') {
252
- a.x += dx; a.y += dy;
253
- const bg = dragState.el.querySelector('rect');
254
- const txt = dragState.el.querySelector('text');
255
- bg.setAttribute('x', a.x);
256
- bg.setAttribute('y', a.y);
257
- txt.setAttribute('x', a.x + 6);
258
- txt.setAttribute('y', a.y + 16);
259
- }
260
- }
261
- });
262
-
263
- document.addEventListener('mouseup', e => {
264
- if (!dragState) return;
265
- const p = getPos(e);
266
-
267
- if (dragState.type === 'create-rect') {
268
- const w = Math.abs(p.x - dragState.sx);
269
- const h = Math.abs(p.y - dragState.sy);
270
- if (w < 5 && h < 5) { dragState.el.remove(); }
271
- else {
272
- const a = { id: dragState.id, type: 'rect', x: Math.min(dragState.sx, p.x), y: Math.min(dragState.sy, p.y), width: w, height: h };
273
- annotations.push(a);
274
- selectEl(dragState.el);
275
- dragState.el.addEventListener('mousedown', e => startMove(e, dragState.id));
276
- }
277
- } else if (dragState.type === 'create-arrow') {
278
- const dist = Math.hypot(p.x - dragState.sx, p.y - dragState.sy);
279
- if (dist < 5) { dragState.el.remove(); }
280
- else {
281
- const a = { id: dragState.id, type: 'arrow', fromX: dragState.sx, fromY: dragState.sy, toX: p.x, toY: p.y };
282
- annotations.push(a);
283
- selectEl(dragState.el);
284
- dragState.el.addEventListener('mousedown', e => startMove(e, dragState.id));
285
- }
286
- }
287
- dragState = null;
288
- });
289
-
290
- function startMove(e, id) {
291
- e.stopPropagation();
292
- const el = svg.querySelector('[data-id="' + id + '"]');
293
- selectEl(el);
294
- const p = getPos(e);
295
- dragState = { type: 'move', id, el, lastX: p.x, lastY: p.y };
296
- }
297
-
298
- // --- LOAD EXISTING ---
299
- window.__kagemusha_loadAnnotations = function(decorations) {
300
- const pad = 48; // toolbar height to add to Y
301
- const dpr = window.__kagemusha_dpr || 1;
302
- decorations.forEach(d => {
303
- const id = 'a' + nextId++;
304
- if (d.type === 'rect' && d.target && 'x' in d.target) {
305
- const rx = d.target.x / dpr, ry = d.target.y / dpr + pad;
306
- const rect = document.createElementNS(svgNS, 'rect');
307
- const rw = d.target.width / dpr, rh = d.target.height / dpr;
308
- rect.setAttribute('x', rx);
309
- rect.setAttribute('y', ry);
310
- rect.setAttribute('width', rw);
311
- rect.setAttribute('height', rh);
312
- rect.setAttribute('fill', 'none');
313
- rect.setAttribute('stroke', d.style?.color || '#FF0000');
314
- rect.setAttribute('stroke-width', '3');
315
- rect.setAttribute('rx', '4');
316
- rect.classList.add('annotation');
317
- rect.dataset.id = id;
318
- svg.appendChild(rect);
319
- annotations.push({ id, type: 'rect', x: rx, y: ry, width: rw, height: rh });
320
- rect.addEventListener('mousedown', e => startMove(e, id));
321
- } else if (d.type === 'arrow' && d.from && 'x' in d.from) {
322
- const line = document.createElementNS(svgNS, 'line');
323
- const ax1 = d.from.x / dpr, ay1 = d.from.y / dpr + pad;
324
- const ax2 = d.to.x / dpr, ay2 = d.to.y / dpr + pad;
325
- line.setAttribute('x1', ax1);
326
- line.setAttribute('y1', ay1);
327
- line.setAttribute('x2', ax2);
328
- line.setAttribute('y2', ay2);
329
- line.setAttribute('stroke', d.style?.color || '#FF0000');
330
- line.setAttribute('stroke-width', '3');
331
- line.setAttribute('marker-end', 'url(#kg-arrowhead)');
332
- line.classList.add('annotation');
333
- line.dataset.id = id;
334
- svg.appendChild(line);
335
- annotations.push({ id, type: 'arrow', fromX: ax1, fromY: ay1, toX: ax2, toY: ay2 });
336
- line.addEventListener('mousedown', e => startMove(e, id));
337
- } else if (d.type === 'label' && d.position && 'x' in d.position) {
338
- const lx = d.position.x / dpr, ly = d.position.y / dpr + pad;
339
- const g = document.createElementNS(svgNS, 'g');
340
- g.classList.add('annotation');
341
- g.dataset.id = id;
342
- const bg = document.createElementNS(svgNS, 'rect');
343
- const txt = document.createElementNS(svgNS, 'text');
344
- txt.textContent = d.text;
345
- txt.setAttribute('x', lx + 6);
346
- txt.setAttribute('y', ly + 16);
347
- txt.setAttribute('fill', d.style?.color || '#FF0000');
348
- txt.setAttribute('font-size', d.style?.fontSize || '14');
349
- txt.setAttribute('font-family', '-apple-system, sans-serif');
350
- const tw = (d.text || '').length * 9 + 12;
351
- bg.setAttribute('x', lx);
352
- bg.setAttribute('y', ly);
353
- bg.setAttribute('width', tw);
354
- bg.setAttribute('height', '24');
355
- bg.setAttribute('fill', d.style?.background || '#FFFFFF');
356
- bg.setAttribute('rx', '4');
357
- g.appendChild(bg);
358
- g.appendChild(txt);
359
- svg.appendChild(g);
360
- annotations.push({ id, type: 'label', x: lx, y: ly, text: d.text });
361
- g.addEventListener('mousedown', e => startMove(e, id));
362
- }
363
- });
364
- };
365
-
366
- // --- SAVE ---
367
- function save() {
368
- const pad = 48; // toolbar height to subtract from Y
369
- const dpr = window.__kagemusha_dpr || 1;
370
- const s = Math.round; // shorthand
371
- const decorations = annotations.map(a => {
372
- if (a.type === 'rect') {
373
- return { type: 'rect', target: { x: s(a.x * dpr), y: s((a.y - pad) * dpr), width: s(a.width * dpr), height: s(a.height * dpr) }, style: { color: '#FF0000', strokeWidth: s(3 * dpr) } };
374
- } else if (a.type === 'arrow') {
375
- return { type: 'arrow', from: { x: s(a.fromX * dpr), y: s((a.fromY - pad) * dpr) }, to: { x: s(a.toX * dpr), y: s((a.toY - pad) * dpr) }, style: { color: '#FF0000', strokeWidth: s(3 * dpr) } };
376
- } else if (a.type === 'label') {
377
- return { type: 'label', text: a.text, position: { x: s(a.x * dpr), y: s((a.y - pad) * dpr) }, style: { fontSize: s(14 * dpr), color: '#FF0000', background: '#FFFFFF' } };
378
- }
379
- }).filter(Boolean);
380
-
381
- window.__kagemusha_saved = decorations;
382
- document.title = 'KAGEMUSHA_SAVED';
383
- }
384
- })();
385
- `;