feedback-collector 0.1.0 → 0.2.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.
- package/README.md +99 -106
- package/dist/api.d.ts +49 -0
- package/dist/api.js +24 -0
- package/dist/api.js.map +1 -0
- package/dist/feedback-collector.js +24 -0
- package/dist/feedback-collector.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/docs/decisoes.md +87 -0
- package/docs/integracao-compiler-futura.md +56 -0
- package/package.json +36 -8
- package/skills/feedback-collector-setup/SKILL.md +130 -87
- package/src/feedback-collector.js +23 -461
- package/src/index.d.ts +0 -6
- package/src/index.js +0 -14
|
@@ -1,461 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
* ESCOPO (POC): sem screenshot, sem extensão, sem backend. Teste primeiro se
|
|
26
|
-
* source mapping + DOM + instrução bastam — provavelmente sim pra layout/estilo.
|
|
27
|
-
*/
|
|
28
|
-
(function () {
|
|
29
|
-
'use strict';
|
|
30
|
-
if (window.__FEEDBACK_COLLECTOR__) return;
|
|
31
|
-
window.__FEEDBACK_COLLECTOR__ = true;
|
|
32
|
-
|
|
33
|
-
var STORAGE_KEY = '__fbc_items_v1';
|
|
34
|
-
|
|
35
|
-
/* ------------------------------------------------------------------ */
|
|
36
|
-
/* Estado */
|
|
37
|
-
/* ------------------------------------------------------------------ */
|
|
38
|
-
var items = load();
|
|
39
|
-
var pickerActive = false;
|
|
40
|
-
var hoverEl = null;
|
|
41
|
-
|
|
42
|
-
function load() {
|
|
43
|
-
try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; }
|
|
44
|
-
catch (e) { return []; }
|
|
45
|
-
}
|
|
46
|
-
function save() {
|
|
47
|
-
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(items)); }
|
|
48
|
-
catch (e) { /* quota — ignora na POC */ }
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/* ------------------------------------------------------------------ */
|
|
52
|
-
/* Source mapping */
|
|
53
|
-
/* ------------------------------------------------------------------ */
|
|
54
|
-
function getSourceInfo(el) {
|
|
55
|
-
// 1) react-dev-inspector (data attrs no próprio elemento ou ancestral)
|
|
56
|
-
var node = el;
|
|
57
|
-
while (node && node !== document.body) {
|
|
58
|
-
var p = node.getAttribute && (
|
|
59
|
-
node.getAttribute('data-inspector-relative-path') ||
|
|
60
|
-
node.getAttribute('data-source') // convenção alternativa
|
|
61
|
-
);
|
|
62
|
-
if (p) {
|
|
63
|
-
var line = node.getAttribute('data-inspector-line') || '';
|
|
64
|
-
var col = node.getAttribute('data-inspector-column') || '';
|
|
65
|
-
return { file: p, line: line, column: col, via: 'data-attr', ownerHops: node === el ? 0 : 1 };
|
|
66
|
-
}
|
|
67
|
-
node = node.parentElement;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// 2) Fallback: React fiber _debugSource (funciona em dev builds do React <19)
|
|
71
|
-
var fiber = getFiber(el);
|
|
72
|
-
var hops = 0;
|
|
73
|
-
while (fiber && hops < 20) {
|
|
74
|
-
var src = fiber._debugSource;
|
|
75
|
-
if (src && src.fileName) {
|
|
76
|
-
return {
|
|
77
|
-
file: relativize(src.fileName),
|
|
78
|
-
line: src.lineNumber || '',
|
|
79
|
-
column: src.columnNumber || '',
|
|
80
|
-
via: 'fiber',
|
|
81
|
-
componentName: fiberName(fiber)
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
fiber = fiber._debugOwner || fiber.return;
|
|
85
|
-
hops++;
|
|
86
|
-
}
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function getFiber(el) {
|
|
91
|
-
for (var k in el) {
|
|
92
|
-
if (k.indexOf('__reactFiber$') === 0 || k.indexOf('__reactInternalInstance$') === 0) {
|
|
93
|
-
return el[k];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function fiberName(fiber) {
|
|
100
|
-
var f = fiber;
|
|
101
|
-
while (f) {
|
|
102
|
-
var t = f.type;
|
|
103
|
-
if (typeof t === 'function') return t.displayName || t.name || '';
|
|
104
|
-
if (t && typeof t === 'object' && t.displayName) return t.displayName;
|
|
105
|
-
f = f._debugOwner || f.return;
|
|
106
|
-
}
|
|
107
|
-
return '';
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function componentChain(el) {
|
|
111
|
-
// Nome dos 3 componentes React mais próximos, do mais interno pro externo
|
|
112
|
-
var fiber = getFiber(el);
|
|
113
|
-
var names = [];
|
|
114
|
-
var hops = 0;
|
|
115
|
-
while (fiber && names.length < 3 && hops < 30) {
|
|
116
|
-
var t = fiber.type;
|
|
117
|
-
var name = null;
|
|
118
|
-
if (typeof t === 'function') name = t.displayName || t.name;
|
|
119
|
-
else if (t && typeof t === 'object' && t.displayName) name = t.displayName;
|
|
120
|
-
if (name && names.indexOf(name) === -1) names.push(name);
|
|
121
|
-
fiber = fiber._debugOwner || fiber.return;
|
|
122
|
-
hops++;
|
|
123
|
-
}
|
|
124
|
-
return names;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function relativize(fileName) {
|
|
128
|
-
// Corta caminho absoluto até algo legível (src/, app/, pages/, components/)
|
|
129
|
-
var m = fileName.match(/(?:^|\/)((?:src|app|pages|components|lib|features)\/.*)$/);
|
|
130
|
-
return m ? m[1] : fileName;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/* ------------------------------------------------------------------ */
|
|
134
|
-
/* Seletor CSS estável */
|
|
135
|
-
/* ------------------------------------------------------------------ */
|
|
136
|
-
function cssSelector(el) {
|
|
137
|
-
if (el.id) return '#' + CSS.escape(el.id);
|
|
138
|
-
var parts = [];
|
|
139
|
-
var node = el;
|
|
140
|
-
while (node && node.nodeType === 1 && node !== document.body && parts.length < 5) {
|
|
141
|
-
var part = node.tagName.toLowerCase();
|
|
142
|
-
var stableAttrs = ['data-testid', 'data-test', 'aria-label', 'name'];
|
|
143
|
-
var got = false;
|
|
144
|
-
for (var i = 0; i < stableAttrs.length; i++) {
|
|
145
|
-
var v = node.getAttribute(stableAttrs[i]);
|
|
146
|
-
if (v) { part += '[' + stableAttrs[i] + '="' + v + '"]'; got = true; break; }
|
|
147
|
-
}
|
|
148
|
-
if (!got && node.classList.length) {
|
|
149
|
-
// usa até 2 classes que não pareçam geradas (hash/módulo CSS)
|
|
150
|
-
var cls = Array.prototype.filter.call(node.classList, function (c) {
|
|
151
|
-
return !/^[a-z]*[_-][a-zA-Z0-9]{5,}$/.test(c) && !/^\d/.test(c) && c.length < 30;
|
|
152
|
-
}).slice(0, 2);
|
|
153
|
-
if (cls.length) { part += '.' + cls.map(function(c){ return CSS.escape(c); }).join('.'); got = true; }
|
|
154
|
-
}
|
|
155
|
-
if (!got) {
|
|
156
|
-
var idx = 1, sib = node;
|
|
157
|
-
while ((sib = sib.previousElementSibling)) {
|
|
158
|
-
if (sib.tagName === node.tagName) idx++;
|
|
159
|
-
}
|
|
160
|
-
if (idx > 1) part += ':nth-of-type(' + idx + ')';
|
|
161
|
-
}
|
|
162
|
-
parts.unshift(part);
|
|
163
|
-
// se o seletor parcial já é único, para
|
|
164
|
-
try {
|
|
165
|
-
if (document.querySelectorAll(parts.join(' > ')).length === 1) break;
|
|
166
|
-
} catch (e) { /* seletor inválido, segue */ }
|
|
167
|
-
node = node.parentElement;
|
|
168
|
-
}
|
|
169
|
-
return parts.join(' > ');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/* ------------------------------------------------------------------ */
|
|
173
|
-
/* Computed styles curados */
|
|
174
|
-
/* ------------------------------------------------------------------ */
|
|
175
|
-
var BASE_PROPS = ['display', 'position', 'width', 'height', 'margin', 'padding',
|
|
176
|
-
'color', 'background-color', 'font-size', 'font-weight', 'line-height',
|
|
177
|
-
'border', 'border-radius', 'gap', 'flex-direction', 'justify-content',
|
|
178
|
-
'align-items', 'grid-template-columns', 'overflow', 'z-index', 'opacity',
|
|
179
|
-
'box-shadow', 'text-align'];
|
|
180
|
-
|
|
181
|
-
function curatedStyles(el) {
|
|
182
|
-
var cs = getComputedStyle(el);
|
|
183
|
-
var out = {};
|
|
184
|
-
BASE_PROPS.forEach(function (p) {
|
|
185
|
-
var v = cs.getPropertyValue(p);
|
|
186
|
-
if (!v) return;
|
|
187
|
-
// pula valores default barulhentos
|
|
188
|
-
if (v === 'none' || v === 'normal' || v === 'auto' || v === 'rgba(0, 0, 0, 0)' ||
|
|
189
|
-
v === '0px' || v === 'visible' || v === 'static' || v === 'start') return;
|
|
190
|
-
out[p] = v;
|
|
191
|
-
});
|
|
192
|
-
return out;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function truncatedHTML(el, max) {
|
|
196
|
-
var html = el.outerHTML || '';
|
|
197
|
-
if (html.length <= max) return html;
|
|
198
|
-
// mantém a tag de abertura inteira + começo do conteúdo
|
|
199
|
-
return html.slice(0, max) + '… [truncado]';
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/* ------------------------------------------------------------------ */
|
|
203
|
-
/* Captura */
|
|
204
|
-
/* ------------------------------------------------------------------ */
|
|
205
|
-
function capture(el) {
|
|
206
|
-
var rect = el.getBoundingClientRect();
|
|
207
|
-
var source = getSourceInfo(el);
|
|
208
|
-
var item = {
|
|
209
|
-
id: Date.now() + '_' + Math.random().toString(36).slice(2, 7),
|
|
210
|
-
url: location.pathname + location.search + location.hash,
|
|
211
|
-
tag: el.tagName.toLowerCase(),
|
|
212
|
-
selector: cssSelector(el),
|
|
213
|
-
source: source,
|
|
214
|
-
components: componentChain(el),
|
|
215
|
-
text: (el.textContent || '').trim().slice(0, 120),
|
|
216
|
-
html: truncatedHTML(el, 600),
|
|
217
|
-
styles: curatedStyles(el),
|
|
218
|
-
rect: {
|
|
219
|
-
x: Math.round(rect.x), y: Math.round(rect.y),
|
|
220
|
-
w: Math.round(rect.width), h: Math.round(rect.height)
|
|
221
|
-
},
|
|
222
|
-
viewport: window.innerWidth + 'x' + window.innerHeight,
|
|
223
|
-
instruction: '',
|
|
224
|
-
ts: new Date().toISOString()
|
|
225
|
-
};
|
|
226
|
-
items.push(item);
|
|
227
|
-
save();
|
|
228
|
-
renderPanel();
|
|
229
|
-
// foca o textarea do item recém-criado
|
|
230
|
-
setTimeout(function () {
|
|
231
|
-
var ta = panel.querySelector('[data-fbc-ta="' + item.id + '"]');
|
|
232
|
-
if (ta) ta.focus();
|
|
233
|
-
}, 50);
|
|
234
|
-
flashElement(el);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function flashElement(el) {
|
|
238
|
-
var prev = el.style.outline;
|
|
239
|
-
el.style.outline = '3px solid #16a34a';
|
|
240
|
-
setTimeout(function () { el.style.outline = prev; }, 400);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/* ------------------------------------------------------------------ */
|
|
244
|
-
/* Export markdown */
|
|
245
|
-
/* ------------------------------------------------------------------ */
|
|
246
|
-
function toMarkdown() {
|
|
247
|
-
var lines = ['# Backlog de feedback visual', '',
|
|
248
|
-
'Gerado em ' + new Date().toLocaleString() + ' — ' + items.length + ' itens.',
|
|
249
|
-
'Cada item indica o arquivo:linha quando disponível. Execute na ordem, agrupando edits do mesmo arquivo.', ''];
|
|
250
|
-
items.forEach(function (it, i) {
|
|
251
|
-
lines.push('## ' + (i + 1) + '. ' + (it.instruction ? it.instruction.split('\n')[0] : '(sem instrução)'));
|
|
252
|
-
lines.push('');
|
|
253
|
-
if (it.instruction && it.instruction.indexOf('\n') !== -1) {
|
|
254
|
-
lines.push(it.instruction);
|
|
255
|
-
lines.push('');
|
|
256
|
-
}
|
|
257
|
-
lines.push('- **Rota:** `' + it.url + '`');
|
|
258
|
-
if (it.source) {
|
|
259
|
-
var loc = it.source.file + (it.source.line ? ':' + it.source.line : '');
|
|
260
|
-
lines.push('- **Source:** `' + loc + '`' + (it.source.componentName ? ' (componente `' + it.source.componentName + '`)' : ''));
|
|
261
|
-
} else if (it.components && it.components.length) {
|
|
262
|
-
lines.push('- **Componentes (do mais interno):** ' + it.components.map(function (c) { return '`' + c + '`'; }).join(' → '));
|
|
263
|
-
}
|
|
264
|
-
lines.push('- **Elemento:** `<' + it.tag + '>` — seletor: `' + it.selector + '`');
|
|
265
|
-
if (it.text) lines.push('- **Texto visível:** "' + it.text + '"');
|
|
266
|
-
lines.push('- **Posição:** ' + it.rect.w + 'x' + it.rect.h + ' @ (' + it.rect.x + ',' + it.rect.y + '), viewport ' + it.viewport);
|
|
267
|
-
var styleKeys = Object.keys(it.styles);
|
|
268
|
-
if (styleKeys.length) {
|
|
269
|
-
lines.push('- **Styles atuais:** ' + styleKeys.map(function (k) {
|
|
270
|
-
return '`' + k + ': ' + it.styles[k] + '`';
|
|
271
|
-
}).join(', '));
|
|
272
|
-
}
|
|
273
|
-
lines.push('');
|
|
274
|
-
lines.push('```html');
|
|
275
|
-
lines.push(it.html);
|
|
276
|
-
lines.push('```');
|
|
277
|
-
lines.push('');
|
|
278
|
-
});
|
|
279
|
-
return lines.join('\n');
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function copyBacklog() {
|
|
283
|
-
var md = toMarkdown();
|
|
284
|
-
navigator.clipboard.writeText(md).then(function () {
|
|
285
|
-
toast('Backlog copiado (' + items.length + ' itens). Cola no Claude Code.');
|
|
286
|
-
}, function () {
|
|
287
|
-
// fallback: mostra num prompt
|
|
288
|
-
window.prompt('Copie manualmente:', md);
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/* ------------------------------------------------------------------ */
|
|
293
|
-
/* Picker (highlight + clique) */
|
|
294
|
-
/* ------------------------------------------------------------------ */
|
|
295
|
-
var highlight = document.createElement('div');
|
|
296
|
-
highlight.style.cssText = 'position:fixed;pointer-events:none;z-index:2147483645;' +
|
|
297
|
-
'border:2px solid #2563eb;background:rgba(37,99,235,.08);border-radius:2px;' +
|
|
298
|
-
'display:none;transition:all .05s linear;';
|
|
299
|
-
var highlightLabel = document.createElement('div');
|
|
300
|
-
highlightLabel.style.cssText = 'position:absolute;top:-22px;left:0;background:#2563eb;' +
|
|
301
|
-
'color:#fff;font:11px/1.4 ui-monospace,monospace;padding:1px 6px;border-radius:2px;white-space:nowrap;';
|
|
302
|
-
highlight.appendChild(highlightLabel);
|
|
303
|
-
|
|
304
|
-
function onKeyDown(e) {
|
|
305
|
-
if (e.key === 'Alt' && !pickerActive) {
|
|
306
|
-
pickerActive = true;
|
|
307
|
-
document.body.style.cursor = 'crosshair';
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
function onKeyUp(e) {
|
|
311
|
-
if (e.key === 'Alt') deactivatePicker();
|
|
312
|
-
}
|
|
313
|
-
function deactivatePicker() {
|
|
314
|
-
pickerActive = false;
|
|
315
|
-
hoverEl = null;
|
|
316
|
-
highlight.style.display = 'none';
|
|
317
|
-
document.body.style.cursor = '';
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function onMouseMove(e) {
|
|
321
|
-
if (!pickerActive) return;
|
|
322
|
-
var el = document.elementFromPoint(e.clientX, e.clientY);
|
|
323
|
-
if (!el || panel.contains(el) || el === highlight || highlight.contains(el)) {
|
|
324
|
-
highlight.style.display = 'none';
|
|
325
|
-
hoverEl = null;
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
hoverEl = el;
|
|
329
|
-
var r = el.getBoundingClientRect();
|
|
330
|
-
highlight.style.display = 'block';
|
|
331
|
-
highlight.style.left = r.left + 'px';
|
|
332
|
-
highlight.style.top = r.top + 'px';
|
|
333
|
-
highlight.style.width = r.width + 'px';
|
|
334
|
-
highlight.style.height = r.height + 'px';
|
|
335
|
-
var src = getSourceInfo(el);
|
|
336
|
-
highlightLabel.textContent = '<' + el.tagName.toLowerCase() + '>' +
|
|
337
|
-
(src ? ' ' + src.file.split('/').pop() + (src.line ? ':' + src.line : '') : '');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function onClick(e) {
|
|
341
|
-
if (!pickerActive || !hoverEl) return;
|
|
342
|
-
if (panel.contains(e.target)) return;
|
|
343
|
-
e.preventDefault();
|
|
344
|
-
e.stopPropagation();
|
|
345
|
-
capture(hoverEl);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/* ------------------------------------------------------------------ */
|
|
349
|
-
/* Painel */
|
|
350
|
-
/* ------------------------------------------------------------------ */
|
|
351
|
-
var panel = document.createElement('div');
|
|
352
|
-
panel.style.cssText = 'position:fixed;bottom:16px;right:16px;width:340px;max-height:70vh;' +
|
|
353
|
-
'z-index:2147483646;background:#1c1c1e;color:#e5e5e7;border-radius:10px;' +
|
|
354
|
-
'box-shadow:0 8px 30px rgba(0,0,0,.4);font:13px/1.45 system-ui,sans-serif;' +
|
|
355
|
-
'display:flex;flex-direction:column;overflow:hidden;';
|
|
356
|
-
|
|
357
|
-
var toastEl = document.createElement('div');
|
|
358
|
-
toastEl.style.cssText = 'position:fixed;bottom:16px;left:50%;transform:translateX(-50%);' +
|
|
359
|
-
'z-index:2147483647;background:#16a34a;color:#fff;padding:8px 16px;border-radius:8px;' +
|
|
360
|
-
'font:13px system-ui,sans-serif;display:none;box-shadow:0 4px 14px rgba(0,0,0,.3);';
|
|
361
|
-
function toast(msg) {
|
|
362
|
-
toastEl.textContent = msg;
|
|
363
|
-
toastEl.style.display = 'block';
|
|
364
|
-
clearTimeout(toastEl.__t);
|
|
365
|
-
toastEl.__t = setTimeout(function () { toastEl.style.display = 'none'; }, 2500);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
var collapsed = false;
|
|
369
|
-
|
|
370
|
-
function renderPanel() {
|
|
371
|
-
var srcCount = items.filter(function (it) { return it.source; }).length;
|
|
372
|
-
panel.innerHTML =
|
|
373
|
-
'<div data-fbc="header" style="display:flex;align-items:center;gap:8px;padding:10px 12px;' +
|
|
374
|
-
'background:#2c2c2e;cursor:pointer;user-select:none;">' +
|
|
375
|
-
'<span style="font-weight:600;">Feedback</span>' +
|
|
376
|
-
'<span style="background:#2563eb;border-radius:10px;padding:0 8px;font-size:11px;">' + items.length + '</span>' +
|
|
377
|
-
(items.length ? '<span style="font-size:11px;color:#8e8e93;">' + srcCount + ' com source</span>' : '') +
|
|
378
|
-
'<span style="margin-left:auto;color:#8e8e93;font-size:11px;">segure ALT + clique</span>' +
|
|
379
|
-
'<span data-fbc="toggle" style="color:#8e8e93;">' + (collapsed ? '▸' : '▾') + '</span>' +
|
|
380
|
-
'</div>' +
|
|
381
|
-
(collapsed ? '' :
|
|
382
|
-
'<div style="overflow-y:auto;flex:1;padding:8px 12px;display:flex;flex-direction:column;gap:8px;">' +
|
|
383
|
-
(items.length === 0
|
|
384
|
-
? '<div style="color:#8e8e93;padding:12px 0;text-align:center;">Nenhum item. Segure ALT e clique num elemento da página.</div>'
|
|
385
|
-
: items.map(renderItem).join('')) +
|
|
386
|
-
'</div>' +
|
|
387
|
-
'<div style="display:flex;gap:8px;padding:10px 12px;background:#2c2c2e;">' +
|
|
388
|
-
'<button data-fbc="copy" style="flex:1;background:#2563eb;color:#fff;border:0;border-radius:6px;' +
|
|
389
|
-
'padding:8px;font:600 13px system-ui;cursor:pointer;"' + (items.length ? '' : ' disabled style="flex:1;background:#3a3a3c;color:#8e8e93;border:0;border-radius:6px;padding:8px;font:600 13px system-ui;"') + '>Copiar backlog</button>' +
|
|
390
|
-
'<button data-fbc="clear" style="background:#3a3a3c;color:#e5e5e7;border:0;border-radius:6px;' +
|
|
391
|
-
'padding:8px 12px;font:13px system-ui;cursor:pointer;">Limpar</button>' +
|
|
392
|
-
'</div>');
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
function esc(s) {
|
|
396
|
-
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/"/g, '"');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function renderItem(it, i) {
|
|
400
|
-
var loc = it.source
|
|
401
|
-
? it.source.file.split('/').slice(-2).join('/') + (it.source.line ? ':' + it.source.line : '')
|
|
402
|
-
: (it.components && it.components[0] ? '<' + it.components[0] + '>' : it.selector.slice(0, 40));
|
|
403
|
-
return '<div style="background:#2c2c2e;border-radius:8px;padding:8px;">' +
|
|
404
|
-
'<div style="display:flex;align-items:baseline;gap:6px;margin-bottom:6px;">' +
|
|
405
|
-
'<span style="color:#8e8e93;font-size:11px;">#' + (i + 1) + '</span>' +
|
|
406
|
-
'<span style="font-family:ui-monospace,monospace;font-size:11px;color:#64d2ff;overflow:hidden;' +
|
|
407
|
-
'text-overflow:ellipsis;white-space:nowrap;flex:1;" title="' + esc(it.selector) + '">' + esc(loc) + '</span>' +
|
|
408
|
-
'<span data-fbc-del="' + it.id + '" style="cursor:pointer;color:#8e8e93;padding:0 4px;" title="Remover">✕</span>' +
|
|
409
|
-
'</div>' +
|
|
410
|
-
'<textarea data-fbc-ta="' + it.id + '" placeholder="O que mudar aqui?" ' +
|
|
411
|
-
'style="width:100%;box-sizing:border-box;background:#1c1c1e;color:#e5e5e7;border:1px solid #3a3a3c;' +
|
|
412
|
-
'border-radius:6px;padding:6px;font:13px system-ui;resize:vertical;min-height:40px;">' +
|
|
413
|
-
esc(it.instruction) + '</textarea>' +
|
|
414
|
-
'</div>';
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
panel.addEventListener('click', function (e) {
|
|
418
|
-
var t = e.target;
|
|
419
|
-
if (t.getAttribute('data-fbc') === 'copy') { copyBacklog(); return; }
|
|
420
|
-
if (t.getAttribute('data-fbc') === 'clear') {
|
|
421
|
-
if (confirm('Limpar todos os ' + items.length + ' itens?')) {
|
|
422
|
-
items = []; save(); renderPanel();
|
|
423
|
-
}
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
if (t.hasAttribute('data-fbc-del')) {
|
|
427
|
-
var id = t.getAttribute('data-fbc-del');
|
|
428
|
-
items = items.filter(function (it) { return it.id !== id; });
|
|
429
|
-
save(); renderPanel();
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
var header = t.closest('[data-fbc="header"]');
|
|
433
|
-
if (header) { collapsed = !collapsed; renderPanel(); }
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
panel.addEventListener('input', function (e) {
|
|
437
|
-
var t = e.target;
|
|
438
|
-
if (t.hasAttribute('data-fbc-ta')) {
|
|
439
|
-
var id = t.getAttribute('data-fbc-ta');
|
|
440
|
-
var it = items.find(function (x) { return x.id === id; });
|
|
441
|
-
if (it) { it.instruction = t.value; save(); }
|
|
442
|
-
}
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
/* ------------------------------------------------------------------ */
|
|
446
|
-
/* Boot */
|
|
447
|
-
/* ------------------------------------------------------------------ */
|
|
448
|
-
document.addEventListener('keydown', onKeyDown, true);
|
|
449
|
-
document.addEventListener('keyup', onKeyUp, true);
|
|
450
|
-
window.addEventListener('blur', deactivatePicker);
|
|
451
|
-
document.addEventListener('mousemove', onMouseMove, true);
|
|
452
|
-
document.addEventListener('click', onClick, true);
|
|
453
|
-
|
|
454
|
-
document.body.appendChild(highlight);
|
|
455
|
-
document.body.appendChild(panel);
|
|
456
|
-
document.body.appendChild(toastEl);
|
|
457
|
-
renderPanel();
|
|
458
|
-
|
|
459
|
-
console.log('[feedback-collector] ativo. Segure ALT + clique pra capturar. ' +
|
|
460
|
-
items.length + ' item(ns) restaurado(s) do localStorage.');
|
|
461
|
-
})();
|
|
1
|
+
var FeedbackCollector=(function(exports){'use strict';var U=["display","position","width","height","margin","padding","color","background-color","font-size","font-weight","line-height","border","border-radius","gap","flex-direction","justify-content","align-items","grid-template-columns","overflow","z-index","opacity","box-shadow","text-align"],X=new Set(["none","normal","auto","rgba(0, 0, 0, 0)","0px","visible","static","start"]);function F(t){let n=t;for(let e in n)if(e.startsWith("__reactFiber$")||e.startsWith("__reactInternalInstance$"))return n[e];return null}function Z(t){let n=t;for(;n;){let e=n.type;if(typeof n.type=="function")return e?.displayName||e?.name||"";if(e&&typeof e=="object"&&e.displayName)return e.displayName;n=n._debugOwner||n.return;}return ""}function Q(t){return t.match(/(?:^|\/)((?:src|app|pages|components|lib|features)\/.*)$/)?.[1]||t}function ee(t){let n=t.match(/^(.*):(\d+):(\d+)$/);if(n)return {file:n[1]||t,line:n[2]||"",column:n[3]||""};let e=t.match(/^(.*):(\d+)$/);return {file:e?.[1]||t,line:e?.[2]||"",column:""}}function k(t){let n=t,e=0;for(;n&&n!==document.body;){let i=n.getAttribute("data-inspector-relative-path"),a=n.getAttribute("data-source");if(i||a){let u=a?ee(a):null;return {file:i||u?.file||"",line:n.getAttribute("data-inspector-line")||u?.line||"",column:n.getAttribute("data-inspector-column")||u?.column||"",via:"data-attr",ownerHops:e}}n=n.parentElement,e+=1;}let o=F(t),s=0;for(;o&&s<20;){let i=o._debugSource;if(i?.fileName)return {file:Q(i.fileName),line:i.lineNumber||"",column:i.columnNumber||"",via:"fiber",componentName:Z(o)};o=o._debugOwner||o.return||null,s+=1;}return null}function te(t){let n=F(t),e=[],o=0;for(;n&&e.length<3&&o<30;){let s=n.type,i=typeof n.type=="function"?s?.displayName||s?.name:s?.displayName;i&&!e.includes(i)&&e.push(i),n=n._debugOwner||n.return||null,o+=1;}return e}function N(t){return typeof CSS<"u"&&CSS.escape?CSS.escape(t):t.replace(/[^a-zA-Z0-9_-]/g,"\\$&")}function ne(t){return t.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function oe(t){if(t.id)return `#${N(t.id)}`;let n=[],e=t;for(;e&&e!==document.body&&n.length<5;){let o=e.tagName.toLowerCase(),s=false;for(let i of ["data-testid","data-test","aria-label","name"]){let a=e.getAttribute(i);if(a){o+=`[${i}="${ne(a)}"]`,s=true;break}}if(!s&&e.classList.length){let i=Array.from(e.classList).filter(a=>!/^[a-z]*[_-][a-zA-Z0-9]{5,}$/.test(a)&&!/^\d/.test(a)&&a.length<30).slice(0,2);i.length&&(o+=`.${i.map(N).join(".")}`,s=true);}if(!s){let i=1,a=e.previousElementSibling;for(;a;)a.tagName===e.tagName&&(i+=1),a=a.previousElementSibling;i>1&&(o+=`:nth-of-type(${i})`);}n.unshift(o);try{if(document.querySelectorAll(n.join(" > ")).length===1)break}catch{}e=e.parentElement;}return n.join(" > ")}function re(t){let n=getComputedStyle(t),e={};for(let o of U){let s=n.getPropertyValue(o);s&&!X.has(s)&&(e[o]=s);}return e}function ie(t,n){let e=t.outerHTML||"";return e.length<=n?e:`${e.slice(0,n)}\u2026 [truncado]`}function M(t){let n=t.getBoundingClientRect();return {id:`${Date.now()}_${Math.random().toString(36).slice(2,7)}`,url:location.pathname+location.search+location.hash,tag:t.tagName.toLowerCase(),selector:oe(t),source:k(t),components:te(t),text:(t.textContent||"").trim().slice(0,120),html:ie(t,600),styles:re(t),rect:{x:Math.round(n.x),y:Math.round(n.y),w:Math.round(n.width),h:Math.round(n.height)},viewport:`${window.innerWidth}x${window.innerHeight}`,instruction:"",ts:new Date().toISOString()}}function m(t){return String(t).replace(/`/g,"\\`")}function $(t,n=new Date){let e=["# Backlog de feedback visual","",`Gerado em ${n.toLocaleString()} \u2014 ${t.length} itens.`,"Cada item indica o arquivo:linha quando dispon\xEDvel. Execute na ordem, agrupando edits do mesmo arquivo.",""];return t.forEach((o,s)=>{if(e.push(`## ${s+1}. ${o.instruction?o.instruction.split(`
|
|
2
|
+
`)[0]:"(sem instru\xE7\xE3o)"}`),e.push(""),o.instruction.includes(`
|
|
3
|
+
`)&&e.push(o.instruction,""),e.push(`- **Rota:** \`${m(o.url)}\``),o.source){let a=`${o.source.file}${o.source.line?`:${o.source.line}`:""}`,u=o.source.componentName?` (componente \`${m(o.source.componentName)}\`)`:"";e.push(`- **Source:** \`${m(a)}\`${u}`);}else o.components.length&&e.push(`- **Componentes (do mais interno):** ${o.components.map(a=>`\`${m(a)}\``).join(" \u2192 ")}`);e.push(`- **Elemento:** \`<${m(o.tag)}>\` \u2014 seletor: \`${m(o.selector)}\``),o.text&&e.push(`- **Texto vis\xEDvel:** "${o.text}"`),e.push(`- **Posi\xE7\xE3o:** ${o.rect.w}x${o.rect.h} @ (${o.rect.x},${o.rect.y}), viewport ${o.viewport}`);let i=Object.entries(o.styles);i.length&&e.push(`- **Styles atuais:** ${i.map(([a,u])=>`\`${m(a)}: ${m(u)}\``).join(", ")}`),e.push("","```html",o.html,"```","");}),e.join(`
|
|
4
|
+
`)}var P="__fbc_state_v2",ae="__fbc_items_v1";var D=()=>({version:2,items:[]});function z(t){if(!t)return null;try{return JSON.parse(t)}catch{return null}}function se(t){return !!t&&typeof t=="object"&&Array.isArray(t.items)}function le(t,n){let e=z(t);if(se(e)&&e.version===2)return {state:{version:2,items:e.items},migrated:false};let o=z(n);return Array.isArray(o)?{state:{version:2,items:o},migrated:true}:{state:D(),migrated:false}}function _(t,n){try{return t.setItem(P,JSON.stringify(n)),!0}catch{return false}}function j(t){let n=null,e=null;try{n=t.getItem(P),e=t.getItem(ae);}catch{return D()}let o=le(n,e);return o.migrated&&_(t,o.state),o.state}var ce="position:fixed;pointer-events:none;z-index:2147483645;border:2px solid #2563eb;background:rgba(37,99,235,.08);border-radius:2px;display:none;transition:all .05s linear;",ue="position:absolute;top:-22px;left:0;background:#2563eb;color:#fff;font:11px/1.4 ui-monospace,monospace;padding:1px 6px;border-radius:2px;white-space:nowrap;",de="position:fixed;bottom:16px;right:16px;width:340px;max-height:70vh;z-index:2147483646;background:#1c1c1e;color:#e5e5e7;border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.4);font:13px/1.45 system-ui,sans-serif;display:flex;flex-direction:column;overflow:hidden;",pe="position:fixed;bottom:16px;left:50%;transform:translateX(-50%);z-index:2147483647;background:#15803d;color:#fff;padding:8px 16px;border-radius:8px;font:13px system-ui,sans-serif;display:none;box-shadow:0 4px 14px rgba(0,0,0,.3);";function x(t){return String(t).replace(/&/g,"&").replace(/</g,"<").replace(/"/g,""")}function fe(t,n){let e=t.source?`${t.source.file.split("/").slice(-2).join("/")}${t.source.line?`:${t.source.line}`:""}`:t.components[0]?`<${t.components[0]}>`:t.selector.slice(0,40);return `<div style="background:#2c2c2e;border-radius:8px;padding:8px;">
|
|
5
|
+
<div style="display:flex;align-items:baseline;gap:6px;margin-bottom:6px;">
|
|
6
|
+
<span style="color:#a8a8ae;font-size:11px;">#${n+1}</span>
|
|
7
|
+
<span style="font-family:ui-monospace,monospace;font-size:11px;color:#64d2ff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;" title="${x(t.selector)}">${x(e)}</span>
|
|
8
|
+
<span data-fbc-del="${x(t.id)}" style="cursor:pointer;color:#a8a8ae;padding:0 4px;" title="Remover">\u2715</span>
|
|
9
|
+
</div>
|
|
10
|
+
<textarea data-fbc-ta="${x(t.id)}" placeholder="O que mudar aqui?" style="width:100%;box-sizing:border-box;background:#1c1c1e;color:#e5e5e7;border:1px solid #3a3a3c;border-radius:6px;padding:6px;font:13px system-ui;resize:vertical;min-height:40px;">${x(t.instruction)}</textarea>
|
|
11
|
+
</div>`}function H(t={}){if(!document.body)throw new Error("feedback-collector: mount() requer document.body dispon\xEDvel.");let n=t.storage||window.localStorage,e=j(n),o=false,s=null,i=false,a=true,u,v=new Set,B=document.body.style.cursor,c=document.createElement("div");c.dataset.fbcRoot="highlight",c.style.cssText=ce;let S=document.createElement("div");S.style.cssText=ue,c.append(S);let p=document.createElement("div");p.dataset.fbcRoot="panel",p.style.cssText=de;let b=document.createElement("div");b.dataset.fbcRoot="toast",b.style.cssText=pe;function w(){_(n,e);}function G(r){b.textContent=r,b.style.display="block",u&&clearTimeout(u),u=setTimeout(()=>{b.style.display="none";},2500);}function h(){let r=e.items.filter(l=>l.source).length;p.innerHTML=`<div data-fbc="header" style="display:flex;align-items:center;gap:8px;padding:10px 12px;background:#2c2c2e;cursor:pointer;user-select:none;">
|
|
12
|
+
<span style="font-weight:600;">Feedback</span>
|
|
13
|
+
<span style="background:#2563eb;border-radius:10px;padding:0 8px;font-size:11px;">${e.items.length}</span>
|
|
14
|
+
${e.items.length?`<span style="font-size:11px;color:#a8a8ae;">${r} com source</span>`:""}
|
|
15
|
+
<span style="margin-left:auto;color:#a8a8ae;font-size:11px;">segure ALT + clique</span>
|
|
16
|
+
<span data-fbc="toggle" style="color:#a8a8ae;">${i?"\u25B8":"\u25BE"}</span>
|
|
17
|
+
</div>${i?"":`<div style="overflow-y:auto;flex:1;padding:8px 12px;display:flex;flex-direction:column;gap:8px;">
|
|
18
|
+
${e.items.length?e.items.map(fe).join(""):'<div style="color:#a8a8ae;padding:12px 0;text-align:center;">Nenhum item. Segure ALT e clique num elemento da p\xE1gina.</div>'}
|
|
19
|
+
</div>
|
|
20
|
+
<div style="display:flex;gap:8px;padding:10px 12px;background:#2c2c2e;">
|
|
21
|
+
<button data-fbc="copy" ${e.items.length?"":"disabled"} style="flex:1;background:${e.items.length?"#2563eb":"#3a3a3c"};color:${e.items.length?"#fff":"#a8a8ae"};border:0;border-radius:6px;padding:8px;font:600 13px system-ui;cursor:${e.items.length?"pointer":"default"};">Copiar backlog</button>
|
|
22
|
+
<button data-fbc="clear" style="background:#3a3a3c;color:#e5e5e7;border:0;border-radius:6px;padding:8px 12px;font:13px system-ui;cursor:pointer;">Limpar</button>
|
|
23
|
+
</div>`}`;}function E(){o=false,s=null,c.style.display="none",document.body.style.cursor=B;}function K(r){let l=r.style.outline;r.style.outline="3px solid #16a34a";let d=setTimeout(()=>{r.style.outline=l,v.delete(d);},400);v.add(d);}function Y(r){let l=M(r);e.items.push(l),w(),h(),setTimeout(()=>p.querySelector(`[data-fbc-ta="${CSS.escape(l.id)}"]`)?.focus(),50),r instanceof HTMLElement&&K(r);}async function V(){let r=$(e.items);try{await navigator.clipboard.writeText(r),G(`Backlog copiado (${e.items.length} itens). Cola no seu agente de c\xF3digo.`);}catch{window.prompt("Copie manualmente:",r);}}function L(r){r.key==="Alt"&&!o&&(o=true,document.body.style.cursor="crosshair");}function T(r){r.key==="Alt"&&E();}function A(r){if(!o)return;let l=document.elementFromPoint(r.clientX,r.clientY);if(!l||p.contains(l)||c.contains(l)||l===c){c.style.display="none",s=null;return}s=l;let d=l.getBoundingClientRect();c.style.display="block",c.style.left=`${d.left}px`,c.style.top=`${d.top}px`,c.style.width=`${d.width}px`,c.style.height=`${d.height}px`;let f=k(l);S.textContent=`<${l.tagName.toLowerCase()}>${f?` ${f.file.split("/").pop()}${f.line?`:${f.line}`:""}`:""}`;}function O(r){!o||!s||!(r.target instanceof Node)||p.contains(r.target)||(r.preventDefault(),r.stopPropagation(),Y(s));}function I(r){if(!(r.target instanceof Element))return;let l=r.target,d=l.getAttribute("data-fbc");if(d==="copy"){V();return}if(d==="clear"){e.items.length&&window.confirm(`Limpar todos os ${e.items.length} itens?`)&&(e.items=[],w(),h());return}let f=l.getAttribute("data-fbc-del");if(f){e.items=e.items.filter(J=>J.id!==f),w(),h();return}l.closest('[data-fbc="header"]')&&(i=!i,h());}function R(r){if(!(r.target instanceof HTMLTextAreaElement))return;let l=r.target.getAttribute("data-fbc-ta"),d=e.items.find(f=>f.id===l);d&&(d.instruction=r.target.value,w());}document.addEventListener("keydown",L,true),document.addEventListener("keyup",T,true),window.addEventListener("blur",E),document.addEventListener("mousemove",A,true),document.addEventListener("click",O,true),p.addEventListener("click",I),p.addEventListener("input",R),document.body.append(c,p,b),h();let W={get mounted(){return a},get items(){return e.items},exportMarkdown(){return $(e.items)},destroy(){if(a){a=false,E(),document.removeEventListener("keydown",L,true),document.removeEventListener("keyup",T,true),window.removeEventListener("blur",E),document.removeEventListener("mousemove",A,true),document.removeEventListener("click",O,true),p.removeEventListener("click",I),p.removeEventListener("input",R),u&&clearTimeout(u);for(let r of v)clearTimeout(r);v.clear(),c.remove(),p.remove(),b.remove();}}};return console.log(`[feedback-collector] ativo. Segure ALT + clique pra capturar. ${e.items.length} item(ns) restaurado(s) do localStorage.`),W}var g=null,y=null;function C(t={}){if(typeof window>"u"||typeof document>"u")throw new Error("feedback-collector: mount() s\xF3 pode ser chamado no navegador.");if(g?.mounted)return g;let n=window.__FEEDBACK_COLLECTOR__;if(n&&typeof n=="object"&&n.mounted)return g=n,n;if(n===true)throw new Error("feedback-collector: uma vers\xE3o anterior j\xE1 est\xE1 montada nesta p\xE1gina.");return g=H(t),window.__FEEDBACK_COLLECTOR__=g,g}function me(){y&&(document.removeEventListener("DOMContentLoaded",y),y=null),g?.destroy(),g=null,typeof window<"u"&&delete window.__FEEDBACK_COLLECTOR__;}function q(){if(!(typeof window>"u"||typeof document>"u")&&window.__FEEDBACK_COLLECTOR__!==true){if(document.readyState==="loading"){y=()=>{y=null,C();},document.addEventListener("DOMContentLoaded",y,{once:true});return}C();}}q();exports.destroy=me;exports.mount=C;return exports;})({});
|
package/src/index.d.ts
DELETED
package/src/index.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Entry ESM do package. O script em si é um IIFE auto-executável (side effect):
|
|
3
|
-
* importar este módulo injeta o picker/painel na página. Não há API exportada.
|
|
4
|
-
*
|
|
5
|
-
* Guard de SSR: importar este módulo em Node/server (Next SSR, testes) é no-op
|
|
6
|
-
* — o script só carrega quando há `window`. O feedback-collector.js fica sem
|
|
7
|
-
* `export` de propósito, pra também funcionar cru via <script src> em páginas
|
|
8
|
-
* sem bundler (ver "./script" no exports do package.json).
|
|
9
|
-
*/
|
|
10
|
-
if (typeof window !== "undefined") {
|
|
11
|
-
void import("./feedback-collector.js");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export {};
|