adhdev 0.1.54 → 0.4.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/dist/cli-entrypoint.js +29083 -0
- package/dist/cli-entrypoint.js.map +1 -0
- package/dist/index.js +26970 -11054
- package/dist/index.js.map +1 -0
- package/package.json +19 -16
- package/providers/_builtin/COMPATIBILITY.md +217 -0
- package/providers/_builtin/acp/agentpool/provider.json +54 -0
- package/providers/_builtin/acp/amp/provider.json +52 -0
- package/providers/_builtin/acp/auggie/provider.json +57 -0
- package/providers/_builtin/acp/autodev/provider.json +54 -0
- package/providers/_builtin/acp/autohand/provider.json +52 -0
- package/providers/_builtin/acp/blackbox-ai/provider.json +54 -0
- package/providers/_builtin/acp/claude-agent/provider.json +57 -0
- package/providers/_builtin/acp/cline-acp/provider.json +54 -0
- package/providers/_builtin/acp/codebuddy/provider.json +54 -0
- package/providers/_builtin/acp/codex-cli/provider.json +57 -0
- package/providers/_builtin/acp/corust-agent/provider.json +52 -0
- package/providers/_builtin/acp/crow-cli/provider.json +54 -0
- package/providers/_builtin/acp/cursor-acp/provider.json +54 -0
- package/providers/_builtin/acp/deepagents/provider.json +52 -0
- package/providers/_builtin/acp/dimcode/provider.json +54 -0
- package/providers/_builtin/acp/docker-cagent/provider.json +57 -0
- package/providers/_builtin/acp/factory-droid/provider.json +60 -0
- package/providers/_builtin/acp/fast-agent/provider.json +52 -0
- package/providers/_builtin/acp/gemini-cli/provider.json +114 -0
- package/providers/_builtin/acp/github-copilot/provider.json +54 -0
- package/providers/_builtin/acp/goose/provider.json +57 -0
- package/providers/_builtin/acp/junie/provider.json +52 -0
- package/providers/_builtin/acp/kilo/provider.json +54 -0
- package/providers/_builtin/acp/kimi-cli/provider.json +57 -0
- package/providers/_builtin/acp/minion-code/provider.json +52 -0
- package/providers/_builtin/acp/mistral-vibe/provider.json +57 -0
- package/providers/_builtin/acp/nova/provider.json +54 -0
- package/providers/_builtin/acp/openclaw/provider.json +54 -0
- package/providers/_builtin/acp/opencode/provider.json +52 -0
- package/providers/_builtin/acp/openhands/provider.json +54 -0
- package/providers/_builtin/acp/pi-acp/provider.json +52 -0
- package/providers/_builtin/acp/qoder/provider.json +54 -0
- package/providers/_builtin/acp/qwen-code/provider.json +60 -0
- package/providers/_builtin/acp/stakpak/provider.json +54 -0
- package/providers/_builtin/acp/vtcode/provider.json +54 -0
- package/providers/_builtin/cli/claude-cli/provider.json +100 -0
- package/providers/_builtin/cli/codex-cli/provider.json +89 -0
- package/providers/_builtin/cli/gemini-cli/provider.json +93 -0
- package/providers/_builtin/docs/CDP_SELECTOR_GUIDE.md +370 -0
- package/providers/_builtin/docs/PROVIDER_GUIDE.md +916 -0
- package/providers/_builtin/extension/cline/provider.json +35 -0
- package/providers/_builtin/extension/cline/scripts/open_panel.js +1 -1
- package/providers/_builtin/extension/cline/{provider.js → scripts.js} +29 -55
- package/providers/_builtin/extension/roo-code/provider.json +35 -0
- package/providers/_builtin/extension/roo-code/{provider.js → scripts.js} +27 -97
- package/providers/_builtin/ide/antigravity/provider.json +63 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_models.js +38 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/list_modes.js +48 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/scripts.js +64 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_mode.js +34 -0
- package/providers/_builtin/ide/antigravity/scripts/legacy/set_model.js +47 -0
- package/providers/_builtin/ide/antigravity/scripts/list_models.js +31 -8
- package/providers/_builtin/ide/antigravity/scripts/list_modes.js +37 -13
- package/providers/_builtin/ide/antigravity/scripts/set_mode.js +49 -16
- package/providers/_builtin/ide/antigravity/scripts/set_model.js +47 -22
- package/providers/_builtin/ide/antigravity/scripts.js +67 -0
- package/providers/_builtin/ide/cursor/provider.json +59 -0
- package/providers/_builtin/ide/cursor/{provider.js → scripts.js} +47 -79
- package/providers/_builtin/ide/kiro/provider.json +60 -0
- package/providers/_builtin/ide/kiro/scripts.js +62 -0
- package/providers/_builtin/ide/pearai/provider.json +60 -0
- package/providers/_builtin/ide/pearai/scripts.js +74 -0
- package/providers/_builtin/ide/trae/provider.json +59 -0
- package/providers/_builtin/ide/trae/scripts.js +57 -0
- package/providers/_builtin/ide/vscode/provider.json +57 -0
- package/providers/_builtin/ide/vscode-insiders/provider.json +55 -0
- package/providers/_builtin/ide/vscodium/provider.json +56 -0
- package/providers/_builtin/ide/windsurf/provider.json +46 -0
- package/providers/_builtin/ide/windsurf/scripts.js +57 -0
- package/README.md +0 -43
- package/dist/dev-console-monaco.js +0 -176
- package/dist/dev-console.css +0 -326
- package/dist/dev-console.html +0 -148
- package/dist/dev-console.js +0 -1165
- package/dist/index.d.ts +0 -2
- package/providers/_builtin/acp/agentpool/provider.js +0 -59
- package/providers/_builtin/acp/amp/provider.js +0 -61
- package/providers/_builtin/acp/auggie/provider.js +0 -60
- package/providers/_builtin/acp/autodev/provider.js +0 -59
- package/providers/_builtin/acp/autohand/provider.js +0 -59
- package/providers/_builtin/acp/blackbox-ai/provider.js +0 -59
- package/providers/_builtin/acp/claude-agent/provider.js +0 -61
- package/providers/_builtin/acp/cline-acp/provider.js +0 -62
- package/providers/_builtin/acp/code-assistant/provider.js +0 -59
- package/providers/_builtin/acp/codebuddy/provider.js +0 -59
- package/providers/_builtin/acp/codex-cli/provider.js +0 -64
- package/providers/_builtin/acp/corust-agent/provider.js +0 -59
- package/providers/_builtin/acp/crow-cli/provider.js +0 -59
- package/providers/_builtin/acp/cursor-acp/provider.js +0 -59
- package/providers/_builtin/acp/deepagents/provider.js +0 -59
- package/providers/_builtin/acp/dimcode/provider.js +0 -58
- package/providers/_builtin/acp/docker-cagent/provider.js +0 -59
- package/providers/_builtin/acp/factory-droid/provider.js +0 -59
- package/providers/_builtin/acp/fast-agent/provider.js +0 -59
- package/providers/_builtin/acp/fount/provider.js +0 -59
- package/providers/_builtin/acp/gemini-cli/provider.js +0 -104
- package/providers/_builtin/acp/github-copilot/provider.js +0 -60
- package/providers/_builtin/acp/goose/provider.js +0 -64
- package/providers/_builtin/acp/junie/provider.js +0 -62
- package/providers/_builtin/acp/kilo/provider.js +0 -59
- package/providers/_builtin/acp/kimi-cli/provider.js +0 -63
- package/providers/_builtin/acp/kiro-cli/provider.js +0 -59
- package/providers/_builtin/acp/minion-code/provider.js +0 -59
- package/providers/_builtin/acp/mistral-vibe/provider.js +0 -63
- package/providers/_builtin/acp/nova/provider.js +0 -59
- package/providers/_builtin/acp/openclaw/provider.js +0 -59
- package/providers/_builtin/acp/opencode/provider.js +0 -60
- package/providers/_builtin/acp/openhands/provider.js +0 -59
- package/providers/_builtin/acp/pi-acp/provider.js +0 -59
- package/providers/_builtin/acp/qoder/provider.js +0 -58
- package/providers/_builtin/acp/qwen-code/provider.js +0 -61
- package/providers/_builtin/acp/stakpak/provider.js +0 -59
- package/providers/_builtin/acp/vtcode/provider.js +0 -59
- package/providers/_builtin/cli/claude-cli/provider.js +0 -128
- package/providers/_builtin/cli/codex-cli/provider.js +0 -80
- package/providers/_builtin/cli/gemini-cli/provider.js +0 -124
- package/providers/_builtin/ide/antigravity/provider.js +0 -114
- package/providers/_builtin/ide/kiro/provider.js +0 -90
- package/providers/_builtin/ide/pearai/provider.js +0 -100
- package/providers/_builtin/ide/trae/provider.js +0 -83
- package/providers/_builtin/ide/vscode/provider.js +0 -36
- package/providers/_builtin/ide/vscode-insiders/provider.js +0 -27
- package/providers/_builtin/ide/vscodium/provider.js +0 -27
- package/providers/_builtin/ide/windsurf/provider.js +0 -76
- package/providers/_helpers/index.js +0 -188
package/dist/dev-console.js
DELETED
|
@@ -1,1165 +0,0 @@
|
|
|
1
|
-
const API = 'http://127.0.0.1:19280';
|
|
2
|
-
let currentProvider = '';
|
|
3
|
-
|
|
4
|
-
// ─── Init ───
|
|
5
|
-
async function init() {
|
|
6
|
-
await refreshProviders();
|
|
7
|
-
await refreshStatus();
|
|
8
|
-
await refreshTargets();
|
|
9
|
-
setInterval(refreshStatus, 5000);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function api(path, method = 'GET', body = null) {
|
|
13
|
-
const opts = { method, headers: { 'Content-Type': 'application/json' } };
|
|
14
|
-
if (body) opts.body = JSON.stringify(body);
|
|
15
|
-
const res = await fetch(API + path, opts);
|
|
16
|
-
return res.json();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function apiBinary(path) {
|
|
20
|
-
const res = await fetch(API + path);
|
|
21
|
-
return res;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ─── Providers ───
|
|
25
|
-
async function refreshProviders() {
|
|
26
|
-
try {
|
|
27
|
-
const data = await api('/api/providers');
|
|
28
|
-
const sel = document.getElementById('providerSelect');
|
|
29
|
-
sel.innerHTML = '<option value="">— Select Provider —</option>';
|
|
30
|
-
for (const p of data.providers || []) {
|
|
31
|
-
sel.innerHTML += `<option value="${p.type}">${p.category === 'ide' ? '💻' : p.category === 'extension' ? '🧩' : '⌨️'} ${p.name} (${p.type})</option>`;
|
|
32
|
-
}
|
|
33
|
-
sel.onchange = () => {
|
|
34
|
-
currentProvider = sel.value;
|
|
35
|
-
updateScriptMenu();
|
|
36
|
-
exitEditMode();
|
|
37
|
-
};
|
|
38
|
-
} catch (e) {
|
|
39
|
-
log('❌ Cannot reach DevServer: ' + e.message, 'error');
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function refreshTargets() {
|
|
44
|
-
try {
|
|
45
|
-
const data = await api('/api/cdp/targets');
|
|
46
|
-
const sel = document.getElementById('ideTargetSelect');
|
|
47
|
-
sel.innerHTML = '<option value="">Auto</option>';
|
|
48
|
-
for (const t of data.targets || []) {
|
|
49
|
-
const dot = t.connected ? '🟢' : '🔴';
|
|
50
|
-
sel.innerHTML += `<option value="${t.ide}">${dot} ${t.ide} (:${t.port})</option>`;
|
|
51
|
-
}
|
|
52
|
-
} catch {}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ─── Script Menu ───
|
|
56
|
-
function updateScriptMenu() {
|
|
57
|
-
const dropdown = document.getElementById('scriptDropdown');
|
|
58
|
-
dropdown.innerHTML = '';
|
|
59
|
-
if (!currentProvider) return;
|
|
60
|
-
|
|
61
|
-
api('/api/providers').then(data => {
|
|
62
|
-
const provider = (data.providers || []).find(p => p.type === currentProvider);
|
|
63
|
-
if (!provider) return;
|
|
64
|
-
for (const script of provider.scripts || []) {
|
|
65
|
-
const item = document.createElement('div');
|
|
66
|
-
item.className = 'item';
|
|
67
|
-
item.style.cssText = 'display:flex;justify-content:space-between;align-items:center;';
|
|
68
|
-
const left = document.createElement('span');
|
|
69
|
-
left.textContent = script;
|
|
70
|
-
left.style.cursor = 'pointer';
|
|
71
|
-
left.onclick = () => loadProviderScript(script);
|
|
72
|
-
left.title = 'Load into editor';
|
|
73
|
-
const right = document.createElement('div');
|
|
74
|
-
right.style.cssText = 'display:flex;gap:4px;';
|
|
75
|
-
// Quick run button (no params)
|
|
76
|
-
const runBtn = document.createElement('button');
|
|
77
|
-
runBtn.textContent = '▶';
|
|
78
|
-
runBtn.title = 'Quick run (no params)';
|
|
79
|
-
runBtn.style.cssText = 'padding:2px 6px;font-size:10px;background:var(--accent);color:#000;border:none;border-radius:3px;cursor:pointer;';
|
|
80
|
-
runBtn.onclick = (e) => { e.stopPropagation(); dropdown.classList.remove('show'); runProviderScript(script); };
|
|
81
|
-
right.appendChild(runBtn);
|
|
82
|
-
// Params run button
|
|
83
|
-
const paramsBtn = document.createElement('button');
|
|
84
|
-
paramsBtn.textContent = '⚙ params';
|
|
85
|
-
paramsBtn.title = 'Run with custom JSON params';
|
|
86
|
-
paramsBtn.style.cssText = 'padding:2px 8px;font-size:10px;background:var(--bg-input);border:1px solid var(--border);color:var(--text-dim);border-radius:3px;cursor:pointer;display:flex;align-items:center;gap:4px;';
|
|
87
|
-
paramsBtn.onmouseover = () => { paramsBtn.style.color = 'var(--text)'; paramsBtn.style.borderColor = 'var(--text-dim)'; };
|
|
88
|
-
paramsBtn.onmouseout = () => { paramsBtn.style.color = 'var(--text-dim)'; paramsBtn.style.borderColor = 'var(--border)'; };
|
|
89
|
-
paramsBtn.onclick = (e) => { e.stopPropagation(); dropdown.classList.remove('show'); showParamsPrompt(script); };
|
|
90
|
-
right.appendChild(paramsBtn);
|
|
91
|
-
item.appendChild(left);
|
|
92
|
-
item.appendChild(right);
|
|
93
|
-
dropdown.appendChild(item);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function showParamsPrompt(scriptName) {
|
|
99
|
-
const raw = prompt(`Params for "${scriptName}" (JSON object):\n\nExamples:\n {"text": "hello"}\n {"buttonText": "Accept"}\n {"index": 0, "button": "Restart"}`, '{}');
|
|
100
|
-
if (raw === null) return;
|
|
101
|
-
try {
|
|
102
|
-
const params = JSON.parse(raw);
|
|
103
|
-
runProviderScript(scriptName, params);
|
|
104
|
-
} catch (e) {
|
|
105
|
-
log('⚠ Invalid JSON: ' + e.message, 'warn');
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
document.getElementById('scriptBtn').onclick = (e) => {
|
|
110
|
-
e.stopPropagation();
|
|
111
|
-
const d = document.getElementById('scriptDropdown');
|
|
112
|
-
d.classList.toggle('show');
|
|
113
|
-
};
|
|
114
|
-
document.addEventListener('click', () => {
|
|
115
|
-
document.getElementById('scriptDropdown').classList.remove('show');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
async function loadProviderScript(scriptName) {
|
|
119
|
-
document.getElementById('scriptDropdown').classList.remove('show');
|
|
120
|
-
const editor = document.getElementById('editor');
|
|
121
|
-
activeScript = scriptName; // track which script is being edited
|
|
122
|
-
|
|
123
|
-
// Show script editing mode indicator
|
|
124
|
-
const tabHeader = document.querySelector('.editor-header');
|
|
125
|
-
document.querySelectorAll('.editor-header .tab').forEach(t => t.classList.remove('active'));
|
|
126
|
-
|
|
127
|
-
// Add/update script mode indicator (replaces Editor tab with script name + close button)
|
|
128
|
-
let scriptTab = document.getElementById('scriptModeTab');
|
|
129
|
-
if (!scriptTab) {
|
|
130
|
-
scriptTab = document.createElement('div');
|
|
131
|
-
scriptTab.id = 'scriptModeTab';
|
|
132
|
-
scriptTab.style.cssText = 'display:inline-flex;align-items:center;gap:6px;padding:4px 10px;background:rgba(88,166,255,0.15);color:var(--accent);font-weight:600;border-radius:4px;margin-left:4px;font-size:12px;';
|
|
133
|
-
}
|
|
134
|
-
scriptTab.innerHTML = `✏️ Editing: <b>${scriptName}</b> <button id="scriptModeClose" style="background:none;border:1px solid var(--border);color:var(--text-dim);cursor:pointer;border-radius:3px;padding:1px 6px;font-size:10px;margin-left:4px" title="Exit edit mode and restore editor">✕ Close</button>`;
|
|
135
|
-
tabHeader.appendChild(scriptTab);
|
|
136
|
-
document.getElementById('scriptModeClose').onclick = () => {
|
|
137
|
-
activeScript = null;
|
|
138
|
-
scriptTab.remove();
|
|
139
|
-
editor.value = editor.dataset.backup || '';
|
|
140
|
-
document.querySelector('.editor-header .tab[data-tab="editor"]').classList.add('active');
|
|
141
|
-
updateSaveBtn();
|
|
142
|
-
editor.style.borderLeft = '';
|
|
143
|
-
document.getElementById('saveBtn').style.display = 'none';
|
|
144
|
-
log('✏️ Script edit mode closed, editor restored.', 'log');
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// 탭 클릭했을 때 복구를 위한 임시 저장
|
|
148
|
-
scriptTab.onclick = (e) => {
|
|
149
|
-
// 닫기 버튼이 아닌 여백/글씨 영역을 클릭했을 때
|
|
150
|
-
if (e.target.id !== 'scriptModeClose') {
|
|
151
|
-
document.querySelectorAll('.editor-header .tab, #scriptModeTab').forEach(t => t.classList.remove('active'));
|
|
152
|
-
scriptTab.classList.add('active');
|
|
153
|
-
document.getElementById('editor').value = scriptTab.dataset.scriptCode || '';
|
|
154
|
-
document.getElementById('saveBtn').style.display = 'inline-block';
|
|
155
|
-
document.getElementById('editor').style.borderLeft = '3px solid var(--accent)';
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
scriptTab.classList.add('active');
|
|
159
|
-
|
|
160
|
-
try {
|
|
161
|
-
const result = await api(`/api/providers/${currentProvider}/source`);
|
|
162
|
-
const source = result.source || '';
|
|
163
|
-
// Extract function body using brace counting (regex fails with template literals)
|
|
164
|
-
const fnStart = source.indexOf(scriptName + '(');
|
|
165
|
-
if (fnStart >= 0) {
|
|
166
|
-
const braceStart = source.indexOf('{', fnStart);
|
|
167
|
-
if (braceStart >= 0) {
|
|
168
|
-
let depth = 1, i = braceStart + 1;
|
|
169
|
-
let inTmpl = false, inStr = null;
|
|
170
|
-
while (i < source.length && depth > 0) {
|
|
171
|
-
const ch = source[i], prev = source[i-1] || '';
|
|
172
|
-
if (!inStr && !inTmpl && ch === '`') inTmpl = true;
|
|
173
|
-
else if (inTmpl && ch === '`' && prev !== '\\') inTmpl = false;
|
|
174
|
-
else if (!inStr && !inTmpl && (ch === "'" || ch === '"')) inStr = ch;
|
|
175
|
-
else if (inStr && ch === inStr && prev !== '\\') inStr = null;
|
|
176
|
-
else if (!inStr && !inTmpl) { if (ch === '{') depth++; else if (ch === '}') depth--; }
|
|
177
|
-
if (depth > 0) i++;
|
|
178
|
-
}
|
|
179
|
-
if (depth === 0) {
|
|
180
|
-
const body = source.slice(braceStart + 1, i).trim();
|
|
181
|
-
// Extract CDP code from template literal: return `...code...`;
|
|
182
|
-
const tmplMatch = body.match(/return\s*`([\s\S]*)`;?\s*$/);
|
|
183
|
-
if (tmplMatch) {
|
|
184
|
-
// Unescape template literal chars
|
|
185
|
-
editor.value = tmplMatch[1].replace(/\\`/g, '`').replace(/\\\$\{/g, '${');
|
|
186
|
-
} else if (body.includes('return null')) {
|
|
187
|
-
editor.value = getScriptTemplate(scriptName);
|
|
188
|
-
} else {
|
|
189
|
-
editor.value = body;
|
|
190
|
-
}
|
|
191
|
-
} else {
|
|
192
|
-
editor.value = getScriptTemplate(scriptName);
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
editor.value = getScriptTemplate(scriptName);
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
editor.value = getScriptTemplate(scriptName);
|
|
199
|
-
}
|
|
200
|
-
} catch {
|
|
201
|
-
editor.value = getScriptTemplate(scriptName);
|
|
202
|
-
}
|
|
203
|
-
updateSaveBtn();
|
|
204
|
-
document.getElementById('saveBtn').style.display = 'inline-block';
|
|
205
|
-
editor.style.borderLeft = '3px solid var(--accent)';
|
|
206
|
-
scriptTab.dataset.scriptCode = editor.value; // 백업용 저장
|
|
207
|
-
log(`✏️ Script edit mode: ${scriptName} — Edit the CDP JS below, click ▶ Run to test, 💾 Save to update provider.js`, 'info');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
let activeScript = null; // current script being edited
|
|
211
|
-
|
|
212
|
-
function getScriptTemplate(script) {
|
|
213
|
-
const templates = {
|
|
214
|
-
readChat: `(() => {
|
|
215
|
-
try {
|
|
216
|
-
const messages = [];
|
|
217
|
-
let status = 'idle';
|
|
218
|
-
// TODO: Find your message elements
|
|
219
|
-
// Use 🔍 DOM to discover selectors
|
|
220
|
-
// const msgEls = document.querySelectorAll('[data-message-id]');
|
|
221
|
-
return JSON.stringify({ messages, status, id: 'active', title: document.title });
|
|
222
|
-
} catch(e) {
|
|
223
|
-
return JSON.stringify({ messages: [], status: 'error', error: e.message });
|
|
224
|
-
}
|
|
225
|
-
})()`,
|
|
226
|
-
sendMessage: `(async () => {
|
|
227
|
-
const msg = \${TEXT};
|
|
228
|
-
// TODO: Type and send message
|
|
229
|
-
// Return needsTypeAndSend if using CDP type method
|
|
230
|
-
return JSON.stringify({ sent: false, needsTypeAndSend: true, selector: '[contenteditable]' });
|
|
231
|
-
})()`,
|
|
232
|
-
listSessions: `(() => {
|
|
233
|
-
const sessions = [];
|
|
234
|
-
// TODO: Find session list elements
|
|
235
|
-
return JSON.stringify(sessions);
|
|
236
|
-
})()`,
|
|
237
|
-
|
|
238
|
-
openPanel: `(() => {
|
|
239
|
-
// TODO: Open the chat/agent panel if hidden
|
|
240
|
-
const sidebar = document.getElementById('workbench.parts.auxiliarybar');
|
|
241
|
-
if (sidebar && sidebar.offsetWidth > 0) return 'visible';
|
|
242
|
-
// Try clicking a toggle button
|
|
243
|
-
return 'not_found';
|
|
244
|
-
})()`,
|
|
245
|
-
};
|
|
246
|
-
return templates[script] || `(() => {\n // TODO: Implement ${script}\n return JSON.stringify({});\n})()`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function updateSaveBtn() {
|
|
250
|
-
const saveBtn = document.getElementById('saveBtn');
|
|
251
|
-
if (activeScript) {
|
|
252
|
-
saveBtn.textContent = '💾 Save Script';
|
|
253
|
-
saveBtn.title = `Save ${activeScript} to provider.js`;
|
|
254
|
-
} else {
|
|
255
|
-
saveBtn.textContent = '💾 Save & Run';
|
|
256
|
-
saveBtn.title = 'Save provider.js and re-run last script';
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
async function runProviderScript(scriptName, params) {
|
|
261
|
-
if (scriptName && !params) lastRunScript = scriptName; // track for Save & Run
|
|
262
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
263
|
-
const start = Date.now();
|
|
264
|
-
try {
|
|
265
|
-
const result = await api(`/api/providers/${currentProvider}/script`, 'POST', {
|
|
266
|
-
script: scriptName,
|
|
267
|
-
params: params || {},
|
|
268
|
-
ideType: ideTarget,
|
|
269
|
-
});
|
|
270
|
-
const elapsed = Date.now() - start;
|
|
271
|
-
document.getElementById('execTime').textContent = `${elapsed}ms`;
|
|
272
|
-
// null 또는 undefined 처리
|
|
273
|
-
const resValue = result.result !== undefined ? result.result : result;
|
|
274
|
-
showResult(resValue, result.error ? 'error' : 'ok');
|
|
275
|
-
} catch (e) {
|
|
276
|
-
showResult({ error: e.message }, 'error');
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// ─── Run Script ───
|
|
281
|
-
document.getElementById('runBtn').onclick = runEditor;
|
|
282
|
-
// CM handles Ctrl+Enter via keymap; expose runEditor for CM module
|
|
283
|
-
window.runEditor = runEditor;
|
|
284
|
-
|
|
285
|
-
// ─── Helper runtime injection ───
|
|
286
|
-
const HELPER_PREAMBLE = `
|
|
287
|
-
var __logs = [];
|
|
288
|
-
function log() { var args = Array.prototype.slice.call(arguments); __logs.push(args.map(function(a) { return typeof a === 'object' ? JSON.stringify(a) : String(a); }).join(' ')); }
|
|
289
|
-
function queryAll(sel, limit) {
|
|
290
|
-
var els = Array.from(document.querySelectorAll(sel)).slice(0, limit || 20);
|
|
291
|
-
return els.map(function(el, i) {
|
|
292
|
-
var r = el.getBoundingClientRect();
|
|
293
|
-
return { index: i, tag: el.tagName.toLowerCase(), id: el.id || undefined, text: (el.textContent||'').trim().substring(0,100), visible: el.offsetWidth > 0, bounds: { top: Math.round(r.top), left: Math.round(r.left), w: Math.round(r.width), h: Math.round(r.height) } };
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
function click(sel) {
|
|
297
|
-
var el = document.querySelector(sel);
|
|
298
|
-
if (!el) return false;
|
|
299
|
-
el.click();
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
function getStyle(el, prop) { return getComputedStyle(el)[prop]; }
|
|
303
|
-
function sleep(ms) { return new Promise(function(r) { setTimeout(r, ms); }); }
|
|
304
|
-
function waitFor(sel, timeout) {
|
|
305
|
-
timeout = timeout || 5000;
|
|
306
|
-
return new Promise(function(resolve) {
|
|
307
|
-
var start = Date.now();
|
|
308
|
-
(function check() {
|
|
309
|
-
var el = document.querySelector(sel);
|
|
310
|
-
if (el) return resolve(el);
|
|
311
|
-
if (Date.now() - start > timeout) return resolve(null);
|
|
312
|
-
setTimeout(check, 200);
|
|
313
|
-
})();
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
`;
|
|
317
|
-
|
|
318
|
-
function wrapWithHelpers(code) {
|
|
319
|
-
// If the code is an IIFE or expression, prepend "return await" to catch its result.
|
|
320
|
-
// Otherwise, run it directly (assuming user will use "return" for raw scripts, or just wants to run statements).
|
|
321
|
-
const isIIFE = /^\s*\(?\s*(async\s*)?\(\s*\)\s*=>\s*\{/.test(code);
|
|
322
|
-
const wrappedBody = isIIFE ? `return await ${code};` : code;
|
|
323
|
-
|
|
324
|
-
return `(async () => {
|
|
325
|
-
${HELPER_PREAMBLE}
|
|
326
|
-
try {
|
|
327
|
-
const __result = await (async () => {
|
|
328
|
-
${wrappedBody}
|
|
329
|
-
})();
|
|
330
|
-
return JSON.stringify({ __helpers: true, logs: __logs, result: __result });
|
|
331
|
-
} catch(e) {
|
|
332
|
-
return JSON.stringify({ __helpers: true, logs: __logs, error: Object.getOwnPropertyNames(e).reduce((a, k) => { a[k] = e[k]; return a; }, {}) });
|
|
333
|
-
}
|
|
334
|
-
})()`;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async function runEditor() {
|
|
338
|
-
const code = document.getElementById('editor').value.trim();
|
|
339
|
-
if (!code) return;
|
|
340
|
-
|
|
341
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
342
|
-
const wrappedCode = wrapWithHelpers(code);
|
|
343
|
-
const start = Date.now();
|
|
344
|
-
try {
|
|
345
|
-
const result = await api('/api/cdp/evaluate', 'POST', {
|
|
346
|
-
expression: wrappedCode,
|
|
347
|
-
timeout: 30000,
|
|
348
|
-
ideType: ideTarget,
|
|
349
|
-
});
|
|
350
|
-
const elapsed = Date.now() - start;
|
|
351
|
-
document.getElementById('execTime').textContent = `${elapsed}ms`;
|
|
352
|
-
|
|
353
|
-
// Parse helper wrapper result
|
|
354
|
-
let raw = result.result !== undefined ? result.result : result;
|
|
355
|
-
let parsed = null;
|
|
356
|
-
if (typeof raw === 'object' && raw !== null && raw.__helpers) {
|
|
357
|
-
parsed = raw;
|
|
358
|
-
} else if (typeof raw === 'string') {
|
|
359
|
-
try { parsed = JSON.parse(raw); } catch(e) {}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (parsed && parsed.__helpers) {
|
|
363
|
-
// Show logs first
|
|
364
|
-
for (const l of (parsed.logs || [])) {
|
|
365
|
-
appendOutput(l, 'log');
|
|
366
|
-
}
|
|
367
|
-
// Show result or error
|
|
368
|
-
if (parsed.error) {
|
|
369
|
-
appendOutput(parsed.error, 'error');
|
|
370
|
-
setBadge('error');
|
|
371
|
-
} else {
|
|
372
|
-
const val = parsed.result;
|
|
373
|
-
// Try to parse if JSON string
|
|
374
|
-
let display = val;
|
|
375
|
-
try {
|
|
376
|
-
if (typeof val === 'string') display = JSON.parse(val);
|
|
377
|
-
} catch(e) {}
|
|
378
|
-
appendOutput(typeof display === 'object' ? JSON.stringify(display, null, 2) : String(display ?? 'undefined'), 'result');
|
|
379
|
-
setBadge('ok');
|
|
380
|
-
}
|
|
381
|
-
} else {
|
|
382
|
-
// Fallback: non-helper result
|
|
383
|
-
showResult(raw, result.error ? 'error' : 'ok');
|
|
384
|
-
}
|
|
385
|
-
} catch (e) {
|
|
386
|
-
appendOutput(e.message, 'error');
|
|
387
|
-
setBadge('error');
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
// ─── Screenshot ───
|
|
393
|
-
document.getElementById('screenshotBtn').onclick = takeScreenshot;
|
|
394
|
-
async function takeScreenshot() {
|
|
395
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || '';
|
|
396
|
-
try {
|
|
397
|
-
const res = await apiBinary('/api/cdp/screenshot' + (ideTarget ? '?ideType=' + ideTarget : ''));
|
|
398
|
-
if (!res.ok) {
|
|
399
|
-
const err = await res.json();
|
|
400
|
-
log('❌ Screenshot failed: ' + err.error, 'error');
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
const blob = await res.blob();
|
|
404
|
-
const url = URL.createObjectURL(blob);
|
|
405
|
-
const img = document.getElementById('screenshotImg');
|
|
406
|
-
img.src = url;
|
|
407
|
-
img.style.display = 'block';
|
|
408
|
-
document.getElementById('screenshotPlaceholder').style.display = 'none';
|
|
409
|
-
} catch (e) {
|
|
410
|
-
log('❌ Screenshot: ' + e.message, 'error');
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// ─── DOM Inspector (click-to-inspect) ───
|
|
415
|
-
// object-fit: contain 보정: 이미지 실제 표시 영역 계산
|
|
416
|
-
function getContainedImageRect(img) {
|
|
417
|
-
const containerW = img.clientWidth;
|
|
418
|
-
const containerH = img.clientHeight;
|
|
419
|
-
const natW = img.naturalWidth || 1;
|
|
420
|
-
const natH = img.naturalHeight || 1;
|
|
421
|
-
const scale = Math.min(containerW / natW, containerH / natH);
|
|
422
|
-
const renderedW = natW * scale;
|
|
423
|
-
const renderedH = natH * scale;
|
|
424
|
-
const offsetX = (containerW - renderedW) / 2;
|
|
425
|
-
const offsetY = (containerH - renderedH) / 2;
|
|
426
|
-
return { offsetX, offsetY, renderedW, renderedH, scale };
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
document.getElementById('screenshotImg').addEventListener('click', async (e) => {
|
|
430
|
-
const img = e.target;
|
|
431
|
-
const imgRect = img.getBoundingClientRect();
|
|
432
|
-
const { offsetX, offsetY, renderedW, renderedH, scale } = getContainedImageRect(img);
|
|
433
|
-
// 클릭 좌표를 이미지 내부 좌표로 변환
|
|
434
|
-
const clickX = e.clientX - imgRect.left - offsetX;
|
|
435
|
-
const clickY = e.clientY - imgRect.top - offsetY;
|
|
436
|
-
// 이미지 밖 클릭 무시
|
|
437
|
-
if (clickX < 0 || clickY < 0 || clickX > renderedW || clickY > renderedH) return;
|
|
438
|
-
const px = Math.round(clickX / scale);
|
|
439
|
-
const py = Math.round(clickY / scale);
|
|
440
|
-
const ch = document.getElementById('inspectCrosshair');
|
|
441
|
-
ch.style.display = 'block';
|
|
442
|
-
ch.style.left = (offsetX + clickX) + 'px';
|
|
443
|
-
ch.style.top = (offsetY + clickY) + 'px';
|
|
444
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
445
|
-
try {
|
|
446
|
-
const result = await api('/api/cdp/dom/inspect', 'POST', { x: px, y: py, ideType: ideTarget });
|
|
447
|
-
if (result.error) { showResult({ error: result.error }, 'error'); return; }
|
|
448
|
-
renderInspector(result);
|
|
449
|
-
} catch (err) {
|
|
450
|
-
showResult({ error: 'Inspect failed: ' + err.message }, 'error');
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
async function inspectSelector(selector) {
|
|
455
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
456
|
-
try {
|
|
457
|
-
const result = await api('/api/cdp/dom/inspect', 'POST', { selector, ideType: ideTarget });
|
|
458
|
-
if (result.error) { showResult({ error: result.error }, 'error'); return; }
|
|
459
|
-
renderInspector(result);
|
|
460
|
-
} catch (err) {
|
|
461
|
-
showResult({ error: 'Inspect failed: ' + err.message }, 'error');
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function htmlEsc(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
|
466
|
-
function jsEsc(s) { return (s||'').replace(/\\/g,'\\\\').replace(/'/g,"\\'"); }
|
|
467
|
-
function insertSelector(sel) {
|
|
468
|
-
const ed = document.getElementById('editor');
|
|
469
|
-
const pos = ed.selectionStart || ed.value.length;
|
|
470
|
-
ed.value = ed.value.slice(0, pos) + "document.querySelector('" + sel + "')" + ed.value.slice(pos);
|
|
471
|
-
ed.focus();
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function renderInspector(data) {
|
|
475
|
-
const { element, ancestors, children } = data;
|
|
476
|
-
const out = document.getElementById('output');
|
|
477
|
-
out.innerHTML = '';
|
|
478
|
-
const badge = document.getElementById('outputBadge');
|
|
479
|
-
badge.style.display = 'inline-block';
|
|
480
|
-
badge.textContent = '🔍';
|
|
481
|
-
badge.className = 'badge ok';
|
|
482
|
-
|
|
483
|
-
// Breadcrumb
|
|
484
|
-
const bc = document.createElement('div');
|
|
485
|
-
bc.style.cssText = 'padding:6px 10px;background:var(--bg-input);border-radius:6px;margin-bottom:8px;font-size:12px;overflow-x:auto;white-space:nowrap;';
|
|
486
|
-
let bcHTML = '';
|
|
487
|
-
for (const a of ancestors) {
|
|
488
|
-
bcHTML += '<span class="bc-node" data-sel="' + htmlEsc(a.selector) + '" style="cursor:pointer;color:var(--text-dim)">' + htmlEsc(a.tag);
|
|
489
|
-
if (a.cls.length) bcHTML += '<span style="color:var(--accent)">.' + htmlEsc(a.cls[0]) + '</span>';
|
|
490
|
-
bcHTML += '</span> <span style="color:var(--text-dim)">›</span> ';
|
|
491
|
-
}
|
|
492
|
-
bcHTML += '<span style="color:var(--accent);font-weight:600">' + htmlEsc(element.tag);
|
|
493
|
-
if (element.cls.length) bcHTML += '.' + htmlEsc(element.cls[0]);
|
|
494
|
-
bcHTML += '</span>';
|
|
495
|
-
bc.innerHTML = bcHTML;
|
|
496
|
-
bc.querySelectorAll('.bc-node').forEach(n => {
|
|
497
|
-
n.onmouseover = () => n.style.color = 'var(--accent)';
|
|
498
|
-
n.onmouseout = () => n.style.color = 'var(--text-dim)';
|
|
499
|
-
n.onclick = () => inspectSelector(n.dataset.sel);
|
|
500
|
-
});
|
|
501
|
-
out.appendChild(bc);
|
|
502
|
-
|
|
503
|
-
// Element details
|
|
504
|
-
const det = document.createElement('div');
|
|
505
|
-
det.style.cssText = 'padding:10px;background:var(--bg-input);border-radius:6px;margin-bottom:8px;font-size:12px;';
|
|
506
|
-
let h = '<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">';
|
|
507
|
-
h += '<span style="font-weight:700;color:var(--accent);font-size:14px"><' + htmlEsc(element.tag) + '></span>';
|
|
508
|
-
if (element.rect) h += '<span style="color:var(--text-dim)">' + element.rect.w + '×' + element.rect.h + ' @ (' + element.rect.x + ',' + element.rect.y + ')</span>';
|
|
509
|
-
h += '<span style="color:var(--text-dim)">' + element.childCount + ' children</span></div>';
|
|
510
|
-
// Full selector + copy/insert
|
|
511
|
-
h += '<div style="margin-bottom:6px;display:flex;align-items:center;gap:6px">';
|
|
512
|
-
h += '<code style="background:#000;color:var(--accent);padding:3px 8px;border-radius:4px;font-size:11px;flex:1;overflow:hidden;text-overflow:ellipsis">' + htmlEsc(element.fullSelector) + '</code>';
|
|
513
|
-
h += '<button onclick="navigator.clipboard.writeText(\'' + jsEsc(element.fullSelector) + '\');this.textContent=\'✓\';setTimeout(()=>this.textContent=\'📋\',800)" style="padding:2px 6px;font-size:11px;background:transparent;border:1px solid var(--border);border-radius:3px;cursor:pointer;color:var(--text-dim)">📋</button>';
|
|
514
|
-
h += '<button onclick="insertSelector(\'' + jsEsc(element.fullSelector) + '\')" style="padding:2px 6px;font-size:11px;background:var(--accent);color:#000;border:none;border-radius:3px;cursor:pointer;font-weight:600">→ Editor</button>';
|
|
515
|
-
h += '</div>';
|
|
516
|
-
// Classes
|
|
517
|
-
if (element.cls.length) {
|
|
518
|
-
h += '<div style="margin-bottom:4px"><span style="color:var(--text-dim)">classes:</span> ';
|
|
519
|
-
h += element.cls.map(c => '<span style="background:rgba(0,255,136,0.15);color:var(--accent);padding:1px 6px;border-radius:3px;font-size:11px;margin-right:3px">.' + htmlEsc(c) + '</span>').join('');
|
|
520
|
-
h += '</div>';
|
|
521
|
-
}
|
|
522
|
-
// Attributes
|
|
523
|
-
const attrKeys = Object.keys(element.attrs || {});
|
|
524
|
-
if (attrKeys.length) {
|
|
525
|
-
h += '<div style="margin-bottom:4px"><span style="color:var(--text-dim)">attrs:</span> ';
|
|
526
|
-
h += attrKeys.map(k => '<span style="color:#ff9f43">' + htmlEsc(k) + '</span>=<span style="color:#a29bfe">"' + htmlEsc(element.attrs[k]) + '"</span>').join(' ');
|
|
527
|
-
h += '</div>';
|
|
528
|
-
}
|
|
529
|
-
// Direct text
|
|
530
|
-
if (element.directText) {
|
|
531
|
-
h += '<div><span style="color:var(--text-dim)">text:</span> <span style="color:#ddd">"' + htmlEsc(element.directText.substring(0, 80)) + '"</span></div>';
|
|
532
|
-
}
|
|
533
|
-
det.innerHTML = h;
|
|
534
|
-
out.appendChild(det);
|
|
535
|
-
|
|
536
|
-
// Children tree
|
|
537
|
-
if (children && children.length) {
|
|
538
|
-
const tree = document.createElement('div');
|
|
539
|
-
tree.style.cssText = 'font-size:12px;';
|
|
540
|
-
const tt = document.createElement('div');
|
|
541
|
-
tt.style.cssText = 'font-weight:600;margin-bottom:6px;color:var(--text-dim);padding:0 4px;';
|
|
542
|
-
tt.textContent = 'Children (' + children.length + ')';
|
|
543
|
-
tree.appendChild(tt);
|
|
544
|
-
for (const child of children) {
|
|
545
|
-
if (!child) continue;
|
|
546
|
-
const row = document.createElement('div');
|
|
547
|
-
row.style.cssText = 'padding:4px 8px;border-left:2px solid var(--border);margin-left:8px;margin-bottom:2px;cursor:pointer;border-radius:0 4px 4px 0;transition:background 0.15s;';
|
|
548
|
-
row.onmouseover = () => row.style.background = 'rgba(255,255,255,0.05)';
|
|
549
|
-
row.onmouseout = () => row.style.background = 'transparent';
|
|
550
|
-
let lbl = '<span style="color:var(--accent)"><' + htmlEsc(child.tag) + '></span>';
|
|
551
|
-
if (child.cls.length) lbl += '<span style="color:var(--text-dim)">.' + htmlEsc(child.cls[0]) + '</span>';
|
|
552
|
-
if (child.childCount > 0) lbl += ' <span style="color:var(--text-dim);font-size:10px">(' + child.childCount + ')</span>';
|
|
553
|
-
if (child.directText) lbl += ' <span style="color:#888;font-size:11px">"' + htmlEsc(child.directText.substring(0, 40)) + '"</span>';
|
|
554
|
-
const hints = Object.entries(child.attrs || {}).slice(0, 2).map(([k,v]) => k + '="' + (v+'').substring(0, 20) + '"').join(' ');
|
|
555
|
-
if (hints) lbl += ' <span style="color:#666;font-size:10px">' + htmlEsc(hints) + '</span>';
|
|
556
|
-
row.innerHTML = lbl;
|
|
557
|
-
row.onclick = () => inspectSelector(child.selector);
|
|
558
|
-
tree.appendChild(row);
|
|
559
|
-
}
|
|
560
|
-
out.appendChild(tree);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// ─── Selector Query ───
|
|
565
|
-
document.getElementById('selectorBtn').onclick = querySel;
|
|
566
|
-
document.getElementById('selectorInput').addEventListener('keydown', (e) => {
|
|
567
|
-
if (e.key === 'Enter') querySel();
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
async function querySel() {
|
|
571
|
-
const sel = document.getElementById('selectorInput').value.trim();
|
|
572
|
-
if (!sel) return;
|
|
573
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
574
|
-
try {
|
|
575
|
-
const result = await api('/api/cdp/dom/query', 'POST', { selector: sel, limit: 20, ideType: ideTarget });
|
|
576
|
-
document.getElementById('selectorCount').textContent = (result.total || 0) + ' matches';
|
|
577
|
-
|
|
578
|
-
let output = `🔍 Selector: ${sel}\nTotal matches: ${result.total || 0}\n\n`;
|
|
579
|
-
for (const item of (result.results || [])) {
|
|
580
|
-
const vis = item.visible ? '✅' : '❌hidden';
|
|
581
|
-
const rect = item.rect ? `${item.rect.x},${item.rect.y} ${item.rect.w}×${item.rect.h}` : '';
|
|
582
|
-
output += `[${item.index}] <${item.tag}> ${vis} ${rect}\n`;
|
|
583
|
-
if (item.id) output += ` id: ${item.id}\n`;
|
|
584
|
-
if (item.class) output += ` class: ${item.class.slice(0, 80)}\n`;
|
|
585
|
-
if (item.text) output += ` text: "${item.text.slice(0, 80)}"\n`;
|
|
586
|
-
if (item.role) output += ` role: ${item.role}\n`;
|
|
587
|
-
output += '\n';
|
|
588
|
-
}
|
|
589
|
-
showResult(output, result.total > 0 ? 'ok' : 'error');
|
|
590
|
-
|
|
591
|
-
// Draw overlays on screenshot
|
|
592
|
-
drawOverlays(result.results || []);
|
|
593
|
-
} catch (e) {
|
|
594
|
-
showResult({ error: e.message }, 'error');
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function drawOverlays(items) {
|
|
599
|
-
const panel = document.getElementById('screenshotPanel');
|
|
600
|
-
// Remove old overlays
|
|
601
|
-
panel.querySelectorAll('.overlay').forEach(el => el.remove());
|
|
602
|
-
|
|
603
|
-
const img = document.getElementById('screenshotImg');
|
|
604
|
-
if (!img || img.style.display === 'none') return;
|
|
605
|
-
|
|
606
|
-
const imgRect = img.getBoundingClientRect();
|
|
607
|
-
const panelRect = panel.getBoundingClientRect();
|
|
608
|
-
|
|
609
|
-
// We need to know the actual image dimensions to scale
|
|
610
|
-
const scaleX = imgRect.width / (img.naturalWidth || 1);
|
|
611
|
-
const scaleY = imgRect.height / (img.naturalHeight || 1);
|
|
612
|
-
const offsetX = imgRect.left - panelRect.left;
|
|
613
|
-
const offsetY = imgRect.top - panelRect.top;
|
|
614
|
-
|
|
615
|
-
const colors = ['#58a6ff', '#3fb950', '#d29922', '#f85149', '#bc8cff'];
|
|
616
|
-
|
|
617
|
-
items.forEach((item, i) => {
|
|
618
|
-
if (!item.rect || !item.visible) return;
|
|
619
|
-
const div = document.createElement('div');
|
|
620
|
-
div.className = 'overlay';
|
|
621
|
-
const color = colors[i % colors.length];
|
|
622
|
-
div.style.borderColor = color;
|
|
623
|
-
div.style.left = (offsetX + item.rect.x * scaleX) + 'px';
|
|
624
|
-
div.style.top = (offsetY + item.rect.y * scaleY) + 'px';
|
|
625
|
-
div.style.width = (item.rect.w * scaleX) + 'px';
|
|
626
|
-
div.style.height = (item.rect.h * scaleY) + 'px';
|
|
627
|
-
|
|
628
|
-
const label = document.createElement('span');
|
|
629
|
-
label.className = 'label';
|
|
630
|
-
label.style.background = color;
|
|
631
|
-
label.textContent = `<${item.tag}>${item.id ? '#' + item.id : item.class ? '.' + item.class.split(' ')[0].slice(0, 20) : ''}`;
|
|
632
|
-
div.appendChild(label);
|
|
633
|
-
|
|
634
|
-
panel.appendChild(div);
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// ─── Reload ───
|
|
639
|
-
document.getElementById('reloadBtn').onclick = async () => {
|
|
640
|
-
try {
|
|
641
|
-
const result = await api('/api/providers/reload', 'POST');
|
|
642
|
-
if (result.reloaded) {
|
|
643
|
-
log(`🔄 Reloaded: ${result.providers?.length || 0} providers`, 'ok');
|
|
644
|
-
await refreshProviders();
|
|
645
|
-
} else {
|
|
646
|
-
log('❌ Reload failed: ' + (result.error || 'unknown'), 'error');
|
|
647
|
-
}
|
|
648
|
-
} catch (e) {
|
|
649
|
-
log('❌ ' + e.message, 'error');
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
|
-
// ─── Clear ───
|
|
654
|
-
document.getElementById('clearBtn').onclick = () => {
|
|
655
|
-
document.getElementById('output').innerHTML = '';
|
|
656
|
-
document.getElementById('outputBadge').style.display = 'none';
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
// ─── Status ───
|
|
660
|
-
async function refreshStatus() {
|
|
661
|
-
try {
|
|
662
|
-
const data = await api('/api/status');
|
|
663
|
-
const bar = document.getElementById('statusBar');
|
|
664
|
-
const hasCdp = Object.values(data.cdp || {}).some(c => c.connected);
|
|
665
|
-
bar.innerHTML = `<span class="dot ${hasCdp ? 'on' : 'off'}"></span> ${hasCdp ? 'CDP Connected' : 'No CDP'} · ${data.providers?.length || 0} providers`;
|
|
666
|
-
} catch {
|
|
667
|
-
document.getElementById('statusBar').innerHTML = '<span class="dot off"></span> DevServer offline';
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// ─── Output helpers ───
|
|
672
|
-
const OUTPUT_ICONS = { log: '📝', result: '✅', error: '❌', warn: '⚠️', query: '🔍' };
|
|
673
|
-
|
|
674
|
-
function getTimestamp() {
|
|
675
|
-
const d = new Date();
|
|
676
|
-
return d.toTimeString().split(' ')[0].substring(0, 8);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function appendOutput(text, type = 'log') {
|
|
680
|
-
const output = document.getElementById('output');
|
|
681
|
-
const entry = document.createElement('div');
|
|
682
|
-
entry.className = `output-entry type-${type}`;
|
|
683
|
-
entry.innerHTML = `<span class="ts">${getTimestamp()}</span><span class="icon">${OUTPUT_ICONS[type] || '•'}</span><span class="content"></span>`;
|
|
684
|
-
entry.querySelector('.content').textContent = text;
|
|
685
|
-
output.appendChild(entry);
|
|
686
|
-
output.scrollTop = output.scrollHeight;
|
|
687
|
-
// Apply current filter
|
|
688
|
-
const filter = document.getElementById('outputSearch').value.toLowerCase();
|
|
689
|
-
if (filter && !text.toLowerCase().includes(filter)) entry.classList.add('hidden');
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function setBadge(type) {
|
|
693
|
-
const badge = document.getElementById('outputBadge');
|
|
694
|
-
badge.style.display = 'inline';
|
|
695
|
-
badge.className = 'badge ' + (type === 'error' ? 'err' : 'ok');
|
|
696
|
-
badge.textContent = type === 'error' ? 'ERROR' : 'OK';
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function showResult(data, type = 'ok') {
|
|
700
|
-
let text;
|
|
701
|
-
if (typeof data === 'string') {
|
|
702
|
-
text = data;
|
|
703
|
-
} else {
|
|
704
|
-
try { text = JSON.stringify(data, null, 2); } catch { text = String(data); }
|
|
705
|
-
}
|
|
706
|
-
appendOutput(text, type === 'error' ? 'error' : 'result');
|
|
707
|
-
setBadge(type);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
function log(msg, type = 'ok') {
|
|
711
|
-
appendOutput(msg, type === 'error' ? 'error' : type === 'warn' ? 'warn' : 'log');
|
|
712
|
-
setBadge(type);
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// Output search filter
|
|
716
|
-
document.getElementById('outputSearch').addEventListener('input', (e) => {
|
|
717
|
-
const filter = e.target.value.toLowerCase();
|
|
718
|
-
document.querySelectorAll('.output-entry').forEach(entry => {
|
|
719
|
-
const text = entry.querySelector('.content')?.textContent?.toLowerCase() || '';
|
|
720
|
-
entry.classList.toggle('hidden', filter && !text.includes(filter));
|
|
721
|
-
});
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
// ─── Template tab ───
|
|
725
|
-
const TEMPLATE = `/**
|
|
726
|
-
* MyAgent — Provider Template
|
|
727
|
-
*
|
|
728
|
-
* Category: 'ide' | 'extension'
|
|
729
|
-
* @type {import('../../../src/providers/contracts').ProviderModule}
|
|
730
|
-
*/
|
|
731
|
-
module.exports = {
|
|
732
|
-
type: 'my-agent',
|
|
733
|
-
name: 'My Agent',
|
|
734
|
-
category: 'ide', // or 'extension'
|
|
735
|
-
|
|
736
|
-
// Extension only:
|
|
737
|
-
// extensionId: 'publisher.extension-name',
|
|
738
|
-
// extensionIdPattern: /extensionId=publisher\.extension-name/i,
|
|
739
|
-
|
|
740
|
-
inputMethod: 'cdp-type-and-send',
|
|
741
|
-
inputSelector: '[contenteditable="true"][role="textbox"]',
|
|
742
|
-
|
|
743
|
-
scripts: {
|
|
744
|
-
readChat() {
|
|
745
|
-
return \`(() => {
|
|
746
|
-
// TODO: Parse chat messages from DOM
|
|
747
|
-
const messages = [];
|
|
748
|
-
let status = 'idle';
|
|
749
|
-
|
|
750
|
-
return JSON.stringify({
|
|
751
|
-
id: 'active_session',
|
|
752
|
-
status,
|
|
753
|
-
title: document.title || 'Session',
|
|
754
|
-
messages,
|
|
755
|
-
inputContent: '',
|
|
756
|
-
activeModal: null,
|
|
757
|
-
});
|
|
758
|
-
})()\`;
|
|
759
|
-
},
|
|
760
|
-
|
|
761
|
-
sendMessage(text) {
|
|
762
|
-
return \`(async () => {
|
|
763
|
-
const msg = \${JSON.stringify(text)};
|
|
764
|
-
// TODO: Find input, type text, press Enter
|
|
765
|
-
return JSON.stringify({ sent: false, needsTypeAndSend: true, selector: '[contenteditable]' });
|
|
766
|
-
})()\`;
|
|
767
|
-
},
|
|
768
|
-
|
|
769
|
-
listSessions() {
|
|
770
|
-
return \`(async () => {
|
|
771
|
-
// TODO: Parse session list
|
|
772
|
-
return JSON.stringify([]);
|
|
773
|
-
})()\`;
|
|
774
|
-
},
|
|
775
|
-
|
|
776
|
-
openPanel() {
|
|
777
|
-
return \`(() => {
|
|
778
|
-
// TODO: Open chat/agent panel if hidden
|
|
779
|
-
// Try clicking toggle button or using keyboard shortcut
|
|
780
|
-
const sidebar = document.getElementById('workbench.parts.auxiliarybar');
|
|
781
|
-
if (sidebar && sidebar.offsetWidth > 0) return 'visible';
|
|
782
|
-
// Try toggle button
|
|
783
|
-
const btns = Array.from(document.querySelectorAll('li.action-item a, button'));
|
|
784
|
-
const toggle = btns.find(b => /agent|chat|composer|panel/i.test(b.textContent || b.getAttribute('aria-label') || ''));
|
|
785
|
-
if (toggle) { toggle.click(); return 'opened'; }
|
|
786
|
-
return 'not_found';
|
|
787
|
-
})()\`;
|
|
788
|
-
},
|
|
789
|
-
},
|
|
790
|
-
};
|
|
791
|
-
`;
|
|
792
|
-
|
|
793
|
-
const CONTRACTS = `// ═══ Output Contracts Reference ═══
|
|
794
|
-
// All script functions must return JSON.stringify(result)
|
|
795
|
-
|
|
796
|
-
// ─── readChat() ───────────────────
|
|
797
|
-
// Returns: { messages, status, ... }
|
|
798
|
-
{
|
|
799
|
-
messages: [
|
|
800
|
-
{ role: 'user' | 'assistant' | 'system',
|
|
801
|
-
content: string, // message text
|
|
802
|
-
id?: string, // optional unique ID
|
|
803
|
-
index?: number }, // optional order
|
|
804
|
-
],
|
|
805
|
-
status: 'idle' | 'generating' | 'waiting_approval' | 'error' | 'streaming',
|
|
806
|
-
id?: string, // session ID
|
|
807
|
-
title?: string, // session title
|
|
808
|
-
activeModal?: { // if approval dialog is shown
|
|
809
|
-
message: string,
|
|
810
|
-
buttons: ['Accept', 'Reject'],
|
|
811
|
-
} | null,
|
|
812
|
-
inputContent?: string, // current text in input box
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// ─── sendMessage(text) ────────────
|
|
816
|
-
// Returns: { sent, error?, needsTypeAndSend?, selector? }
|
|
817
|
-
{
|
|
818
|
-
sent: true, // message was sent
|
|
819
|
-
}
|
|
820
|
-
// OR if input requires typeAndSend:
|
|
821
|
-
{
|
|
822
|
-
sent: false,
|
|
823
|
-
needsTypeAndSend: true, // daemon will use CDP Input API
|
|
824
|
-
selector: '[contenteditable]',
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
// ─── listSessions() ──────────────
|
|
828
|
-
// Returns: array of sessions
|
|
829
|
-
[
|
|
830
|
-
{ id: 'abc123', title: 'Session 1', time?: '2m ago' },
|
|
831
|
-
]
|
|
832
|
-
|
|
833
|
-
// ─── switchSession(sessionId) ────
|
|
834
|
-
// Returns: { switched: true }
|
|
835
|
-
{
|
|
836
|
-
switched: true,
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
// ─── resolveAction({ action, button? }) ────
|
|
840
|
-
// Returns: { resolved: true, clicked: 'Accept' }
|
|
841
|
-
{
|
|
842
|
-
resolved: true, clicked: 'Accept',
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
// ─── openPanel() ─────────────────
|
|
846
|
-
// Returns: string
|
|
847
|
-
'visible' // panel was already visible
|
|
848
|
-
'opened' // panel was opened
|
|
849
|
-
'not_found' // could not find toggle button
|
|
850
|
-
`;
|
|
851
|
-
|
|
852
|
-
document.querySelectorAll('.editor-header .tab').forEach(tab => {
|
|
853
|
-
tab.addEventListener('click', () => {
|
|
854
|
-
document.querySelectorAll('.editor-header .tab').forEach(t => t.classList.remove('active'));
|
|
855
|
-
tab.classList.add('active');
|
|
856
|
-
const editor = document.getElementById('editor');
|
|
857
|
-
if (tab.dataset.tab === 'template') {
|
|
858
|
-
editor.dataset.backup = editor.value;
|
|
859
|
-
editor.value = TEMPLATE;
|
|
860
|
-
} else if (tab.dataset.tab === 'contracts') {
|
|
861
|
-
editor.dataset.backup = editor.value;
|
|
862
|
-
editor.value = CONTRACTS;
|
|
863
|
-
} else {
|
|
864
|
-
if (editor.dataset.backup) {
|
|
865
|
-
editor.value = editor.dataset.backup;
|
|
866
|
-
delete editor.dataset.backup;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
});
|
|
871
|
-
|
|
872
|
-
// ─── Watch Mode (SSE) ───
|
|
873
|
-
let watching = false;
|
|
874
|
-
let eventSource = null;
|
|
875
|
-
|
|
876
|
-
document.getElementById('watchBtn').onclick = async () => {
|
|
877
|
-
if (watching) {
|
|
878
|
-
// Stop
|
|
879
|
-
await api('/api/watch/stop', 'POST');
|
|
880
|
-
watching = false;
|
|
881
|
-
document.getElementById('watchBtn').textContent = '👁 Watch';
|
|
882
|
-
document.getElementById('watchIndicator').classList.remove('active');
|
|
883
|
-
if (eventSource) { eventSource.close(); eventSource = null; }
|
|
884
|
-
return;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
if (!currentProvider) {
|
|
888
|
-
log('⚠ Select a provider first', 'warn');
|
|
889
|
-
return;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Start SSE
|
|
893
|
-
eventSource = new EventSource(API + '/api/watch/events');
|
|
894
|
-
eventSource.onmessage = (e) => {
|
|
895
|
-
try {
|
|
896
|
-
const data = JSON.parse(e.data);
|
|
897
|
-
if (data.type === 'watch_result') {
|
|
898
|
-
document.getElementById('execTime').textContent = `${data.elapsed}ms`;
|
|
899
|
-
showResult(data.result, 'ok');
|
|
900
|
-
} else if (data.type === 'watch_error') {
|
|
901
|
-
showResult({ error: data.error }, 'error');
|
|
902
|
-
} else if (data.type === 'watch_stopped') {
|
|
903
|
-
watching = false;
|
|
904
|
-
document.getElementById('watchBtn').textContent = '👁 Watch';
|
|
905
|
-
document.getElementById('watchIndicator').classList.remove('active');
|
|
906
|
-
}
|
|
907
|
-
} catch {}
|
|
908
|
-
};
|
|
909
|
-
|
|
910
|
-
// Start watch
|
|
911
|
-
await api('/api/watch/start', 'POST', {
|
|
912
|
-
type: currentProvider,
|
|
913
|
-
script: 'readChat',
|
|
914
|
-
interval: 3000,
|
|
915
|
-
});
|
|
916
|
-
watching = true;
|
|
917
|
-
document.getElementById('watchBtn').textContent = '⏹ Stop';
|
|
918
|
-
document.getElementById('watchIndicator').classList.add('active');
|
|
919
|
-
log(`👁 Watching: ${currentProvider} → readChat (every 3s)`, 'ok');
|
|
920
|
-
};
|
|
921
|
-
|
|
922
|
-
// ─── Source View ───
|
|
923
|
-
document.getElementById('sourceBtn').onclick = async () => {
|
|
924
|
-
if (!currentProvider) {
|
|
925
|
-
log('⚠ Select a provider first', 'warn');
|
|
926
|
-
return;
|
|
927
|
-
}
|
|
928
|
-
try {
|
|
929
|
-
const result = await api(`/api/providers/${currentProvider}/source`);
|
|
930
|
-
if (result.error) {
|
|
931
|
-
log('❌ ' + result.error, 'error');
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
const editor = document.getElementById('editor');
|
|
935
|
-
editor.dataset.editMode = 'true';
|
|
936
|
-
editor.dataset.editType = currentProvider;
|
|
937
|
-
editor.dataset.editPath = result.path;
|
|
938
|
-
editor.dataset.backup = editor.value;
|
|
939
|
-
editor.value = result.source;
|
|
940
|
-
|
|
941
|
-
// Show save button, highlight editor
|
|
942
|
-
document.getElementById('saveBtn').style.display = 'inline-block';
|
|
943
|
-
editor.style.borderLeft = '3px solid var(--accent-green)';
|
|
944
|
-
|
|
945
|
-
// Switch to editor tab
|
|
946
|
-
document.querySelectorAll('.editor-header .tab').forEach(t => t.classList.remove('active'));
|
|
947
|
-
document.querySelector('[data-tab="editor"]').classList.add('active');
|
|
948
|
-
log(`📄 Editing: ${result.path} — make changes and click 💾 Save`, 'ok');
|
|
949
|
-
} catch (e) {
|
|
950
|
-
log('❌ ' + e.message, 'error');
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
|
|
954
|
-
function exitEditMode() {
|
|
955
|
-
const editor = document.getElementById('editor');
|
|
956
|
-
editor.dataset.editMode = '';
|
|
957
|
-
editor.style.borderLeft = '';
|
|
958
|
-
document.getElementById('saveBtn').style.display = 'none';
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
let lastRunScript = 'readChat'; // Track last-run script for Save & Run
|
|
962
|
-
|
|
963
|
-
// Save & Run handler
|
|
964
|
-
document.getElementById('saveBtn').onclick = async () => {
|
|
965
|
-
const editor = document.getElementById('editor');
|
|
966
|
-
|
|
967
|
-
// Script edit mode — save individual script to provider.js
|
|
968
|
-
if (activeScript && currentProvider) {
|
|
969
|
-
const code = editor.value.trim();
|
|
970
|
-
if (!code) { log('⚠ Editor is empty', 'warn'); return; }
|
|
971
|
-
try {
|
|
972
|
-
const result = await api(`/api/providers/${currentProvider}/script-save`, 'POST', {
|
|
973
|
-
script: activeScript,
|
|
974
|
-
code: code,
|
|
975
|
-
});
|
|
976
|
-
if (result.error) {
|
|
977
|
-
log('❌ Script save failed: ' + result.error, 'error');
|
|
978
|
-
return;
|
|
979
|
-
}
|
|
980
|
-
log(`💾 Saved: ${activeScript} → provider.js — Testing...`, 'ok');
|
|
981
|
-
await refreshProviders();
|
|
982
|
-
document.getElementById('providerSelect').value = currentProvider;
|
|
983
|
-
updateScriptMenu();
|
|
984
|
-
// Auto-run the saved script via provider
|
|
985
|
-
runProviderScript(activeScript);
|
|
986
|
-
} catch (e) {
|
|
987
|
-
log('❌ ' + e.message, 'error');
|
|
988
|
-
}
|
|
989
|
-
return;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// Edit Source mode — save whole provider.js
|
|
993
|
-
const type = editor.dataset.editType;
|
|
994
|
-
if (!type) {
|
|
995
|
-
log('⚠ No provider loaded for editing', 'warn');
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
try {
|
|
999
|
-
const result = await api(`/api/providers/${type}/save`, 'POST', { source: editor.value });
|
|
1000
|
-
if (result.error) {
|
|
1001
|
-
log('❌ Save failed: ' + result.error, 'error');
|
|
1002
|
-
return;
|
|
1003
|
-
}
|
|
1004
|
-
log(`💾 Saved: ${result.path} (${result.chars} chars) — running ${lastRunScript}...`, 'ok');
|
|
1005
|
-
await refreshProviders();
|
|
1006
|
-
document.getElementById('providerSelect').value = type;
|
|
1007
|
-
currentProvider = type;
|
|
1008
|
-
updateScriptMenu();
|
|
1009
|
-
// Auto-run last script
|
|
1010
|
-
runProviderScript(lastRunScript);
|
|
1011
|
-
} catch (e) {
|
|
1012
|
-
log('❌ ' + e.message, 'error');
|
|
1013
|
-
}
|
|
1014
|
-
};
|
|
1015
|
-
|
|
1016
|
-
// 🔍 DOM Debug — analyze inputs, buttons, textareas, editables
|
|
1017
|
-
document.getElementById('domDebugBtn').onclick = async () => {
|
|
1018
|
-
const ideTarget = document.getElementById('ideTargetSelect').value || undefined;
|
|
1019
|
-
const start = Date.now();
|
|
1020
|
-
try {
|
|
1021
|
-
const debugExpr = `(() => {
|
|
1022
|
-
const r = { url: location.href, title: document.title,
|
|
1023
|
-
viewport: { w: innerWidth, h: innerHeight },
|
|
1024
|
-
inputs: [], textareas: [], editables: [], textboxes: [], buttons: [], iframes: [] };
|
|
1025
|
-
document.querySelectorAll('input[type="text"],input:not([type])').forEach((el,i) => {
|
|
1026
|
-
if(i>=10) return;
|
|
1027
|
-
r.inputs.push({ id:el.id||null, class:(el.className||'').toString().slice(0,100),
|
|
1028
|
-
placeholder:el.getAttribute('placeholder')||null, visible:el.offsetParent!==null });
|
|
1029
|
-
});
|
|
1030
|
-
document.querySelectorAll('textarea').forEach((el,i) => {
|
|
1031
|
-
if(i>=10) return;
|
|
1032
|
-
r.textareas.push({ id:el.id||null, class:(el.className||'').toString().slice(0,100),
|
|
1033
|
-
placeholder:el.getAttribute('placeholder')||null, rows:el.rows, visible:el.offsetParent!==null });
|
|
1034
|
-
});
|
|
1035
|
-
document.querySelectorAll('[contenteditable="true"]').forEach((el,i) => {
|
|
1036
|
-
if(i>=10) return;
|
|
1037
|
-
r.editables.push({ tag:el.tagName?.toLowerCase(), id:el.id||null,
|
|
1038
|
-
class:(el.className||'').toString().slice(0,100), role:el.getAttribute('role')||null,
|
|
1039
|
-
text:(el.textContent||'').trim().slice(0,80), visible:el.offsetParent!==null });
|
|
1040
|
-
});
|
|
1041
|
-
document.querySelectorAll('[role="textbox"]').forEach((el,i) => {
|
|
1042
|
-
if(i>=10) return;
|
|
1043
|
-
r.textboxes.push({ tag:el.tagName?.toLowerCase(), id:el.id||null,
|
|
1044
|
-
class:(el.className||'').toString().slice(0,80), visible:el.offsetParent!==null });
|
|
1045
|
-
});
|
|
1046
|
-
const bk=/send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
|
|
1047
|
-
document.querySelectorAll('button,[role="button"]').forEach((el,i) => {
|
|
1048
|
-
const t=(el.textContent||el.getAttribute('aria-label')||'').trim();
|
|
1049
|
-
if(i<30&&(t.length<30||bk.test(t)))
|
|
1050
|
-
r.buttons.push({ tag:el.tagName?.toLowerCase(), text:t.slice(0,60),
|
|
1051
|
-
disabled:el.disabled||el.getAttribute('disabled')!==null, visible:el.offsetParent!==null });
|
|
1052
|
-
});
|
|
1053
|
-
document.querySelectorAll('iframe,webview').forEach((el,i) => {
|
|
1054
|
-
if(i>=20) return;
|
|
1055
|
-
r.iframes.push({ tag:el.tagName?.toLowerCase(), src:el.getAttribute('src')?.slice(0,200)||null,
|
|
1056
|
-
title:el.getAttribute('title')||null });
|
|
1057
|
-
});
|
|
1058
|
-
return JSON.stringify(r);
|
|
1059
|
-
})()`;
|
|
1060
|
-
const result = await api('/api/cdp/evaluate', 'POST', {
|
|
1061
|
-
expression: debugExpr, timeout: 10000, ideType: ideTarget,
|
|
1062
|
-
});
|
|
1063
|
-
const data = typeof result.result === 'string' ? JSON.parse(result.result) : result.result;
|
|
1064
|
-
const elapsed = Date.now() - start;
|
|
1065
|
-
document.getElementById('execTime').textContent = `${elapsed}ms`;
|
|
1066
|
-
|
|
1067
|
-
let output = `🔍 DOM Debug — ${data.title || 'N/A'}\n`;
|
|
1068
|
-
output += `URL: ${data.url || 'N/A'}\n`;
|
|
1069
|
-
output += `Viewport: ${data.viewport?.w}×${data.viewport?.h}\n\n`;
|
|
1070
|
-
|
|
1071
|
-
const sections = [
|
|
1072
|
-
{ key: 'inputs', label: '📝 Inputs' },
|
|
1073
|
-
{ key: 'textareas', label: '📝 Textareas' },
|
|
1074
|
-
{ key: 'editables', label: '✏️ ContentEditable' },
|
|
1075
|
-
{ key: 'textboxes', label: '🔤 [role=textbox]' },
|
|
1076
|
-
{ key: 'buttons', label: '🔘 Buttons' },
|
|
1077
|
-
{ key: 'iframes', label: '🖼️ Iframes' },
|
|
1078
|
-
];
|
|
1079
|
-
for (const s of sections) {
|
|
1080
|
-
const items = data[s.key] || [];
|
|
1081
|
-
if (items.length === 0) continue;
|
|
1082
|
-
output += `${s.label} (${items.length})\n`;
|
|
1083
|
-
for (const item of items) {
|
|
1084
|
-
const vis = item.visible ? '✅' : '❌';
|
|
1085
|
-
const parts = [vis];
|
|
1086
|
-
if (item.tag) parts.push(`<${item.tag}>`);
|
|
1087
|
-
if (item.id) parts.push(`#${item.id}`);
|
|
1088
|
-
if (item.text) parts.push(`"${item.text}"`);
|
|
1089
|
-
if (item.role) parts.push(`role=${item.role}`);
|
|
1090
|
-
if (item.placeholder) parts.push(`placeholder="${item.placeholder}"`);
|
|
1091
|
-
if (item.class) parts.push(`.${item.class.split(' ')[0].slice(0,30)}`);
|
|
1092
|
-
output += ` ${parts.join(' ')}\n`;
|
|
1093
|
-
}
|
|
1094
|
-
output += '\n';
|
|
1095
|
-
}
|
|
1096
|
-
showResult(output, 'ok');
|
|
1097
|
-
} catch (e) {
|
|
1098
|
-
showResult({ error: e.message }, 'error');
|
|
1099
|
-
}
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
// Scaffold
|
|
1103
|
-
document.getElementById('newBtn').onclick = () => {
|
|
1104
|
-
document.getElementById('scaffoldModal').style.display = 'flex';
|
|
1105
|
-
document.getElementById('scaffoldType').value = '';
|
|
1106
|
-
document.getElementById('scaffoldName').value = '';
|
|
1107
|
-
document.getElementById('scaffoldType').focus();
|
|
1108
|
-
};
|
|
1109
|
-
|
|
1110
|
-
function toggleScaffoldFields() {
|
|
1111
|
-
const cat = document.getElementById('scaffoldCategory').value;
|
|
1112
|
-
document.getElementById('scaffoldIdeFields').style.display = (cat === 'ide' || cat === 'extension') ? '' : 'none';
|
|
1113
|
-
document.getElementById('scaffoldExtFields').style.display = cat === 'extension' ? '' : 'none';
|
|
1114
|
-
document.getElementById('scaffoldCliFields').style.display = cat === 'cli' ? '' : 'none';
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
async function doScaffold() {
|
|
1118
|
-
const type = document.getElementById('scaffoldType').value.trim();
|
|
1119
|
-
const name = document.getElementById('scaffoldName').value.trim();
|
|
1120
|
-
const category = document.getElementById('scaffoldCategory').value;
|
|
1121
|
-
if (!type || !name) {
|
|
1122
|
-
log('⚠ Type and Name are required', 'warn');
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
const body = { type, name, category, location: 'user' };
|
|
1126
|
-
// CDP ports
|
|
1127
|
-
const p1 = document.getElementById('scaffoldCdpPort1').value;
|
|
1128
|
-
const p2 = document.getElementById('scaffoldCdpPort2').value;
|
|
1129
|
-
if (p1) body.cdpPorts = [parseInt(p1), parseInt(p2 || p1) + 1];
|
|
1130
|
-
// CLI
|
|
1131
|
-
const cli = document.getElementById('scaffoldCli').value.trim();
|
|
1132
|
-
if (cli) body.cli = cli;
|
|
1133
|
-
// Process name
|
|
1134
|
-
const proc = document.getElementById('scaffoldProcess').value.trim();
|
|
1135
|
-
if (proc) body.processName = proc;
|
|
1136
|
-
// Install path
|
|
1137
|
-
const iPath = document.getElementById('scaffoldInstallPath').value.trim();
|
|
1138
|
-
if (iPath) body.installPath = iPath;
|
|
1139
|
-
// Extension ID
|
|
1140
|
-
const extId = document.getElementById('scaffoldExtId').value.trim();
|
|
1141
|
-
if (extId) body.extensionId = extId;
|
|
1142
|
-
// Binary (CLI)
|
|
1143
|
-
const binary = document.getElementById('scaffoldBinary').value.trim();
|
|
1144
|
-
if (binary) body.binary = binary;
|
|
1145
|
-
|
|
1146
|
-
try {
|
|
1147
|
-
const result = await api('/api/scaffold', 'POST', body);
|
|
1148
|
-
document.getElementById('scaffoldModal').style.display = 'none';
|
|
1149
|
-
if (result.error) {
|
|
1150
|
-
log('❌ ' + result.error, 'error');
|
|
1151
|
-
return;
|
|
1152
|
-
}
|
|
1153
|
-
log(`✅ Created: ${result.path}`, 'ok');
|
|
1154
|
-
await api('/api/providers/reload', 'POST');
|
|
1155
|
-
await refreshProviders();
|
|
1156
|
-
document.getElementById('providerSelect').value = type;
|
|
1157
|
-
currentProvider = type;
|
|
1158
|
-
updateScriptMenu();
|
|
1159
|
-
document.getElementById('sourceBtn').click();
|
|
1160
|
-
} catch (e) {
|
|
1161
|
-
log('❌ ' + e.message, 'error');
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
init();
|