adhdev 0.1.41 → 0.1.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,176 @@
1
+ require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs' } });
2
+
3
+ require(['vs/editor/editor.main'], function() {
4
+ // ─── Custom Dark Theme ───
5
+ monaco.editor.defineTheme('adhdev-dark', {
6
+ base: 'vs-dark',
7
+ inherit: true,
8
+ rules: [
9
+ { token: 'comment', foreground: '6a9955', fontStyle: 'italic' },
10
+ { token: 'keyword', foreground: 'c586c0' },
11
+ { token: 'string', foreground: 'ce9178' },
12
+ { token: 'number', foreground: 'b5cea8' },
13
+ { token: 'type', foreground: '4ec9b0' },
14
+ { token: 'identifier', foreground: '9cdcfe' },
15
+ { token: 'delimiter', foreground: 'd4d4d4' },
16
+ ],
17
+ colors: {
18
+ 'editor.background': '#0d1117',
19
+ 'editor.foreground': '#c9d1d9',
20
+ 'editor.lineHighlightBackground': '#161b2280',
21
+ 'editorLineNumber.foreground': '#484f58',
22
+ 'editorLineNumber.activeForeground': '#8b949e',
23
+ 'editor.selectionBackground': '#264f7840',
24
+ 'editorCursor.foreground': '#58a6ff',
25
+ 'editor.inactiveSelectionBackground': '#264f7820',
26
+ 'editorGutter.background': '#0d1117',
27
+ 'minimap.background': '#0d1117',
28
+ 'editorWidget.background': '#161b22',
29
+ 'editorSuggestWidget.background': '#161b22',
30
+ 'editorSuggestWidget.border': '#30363d',
31
+ 'editorSuggestWidget.selectedBackground': '#1f6feb40',
32
+ },
33
+ });
34
+
35
+ // ─── Helper Function Typedefs (autocomplete) ───
36
+ const helperDefs = `
37
+ /** Log a message to DevConsole output */
38
+ declare function log(message: any, ...args: any[]): void;
39
+ /** Probe an element — returns bounds, styles, attributes */
40
+ declare function probe(el: Element, opts?: { bounds?: boolean, styles?: string[], attributes?: boolean }): { tag: string, id: string, classes: string[], bounds?: DOMRect, styles?: Record<string, string>, attributes?: Record<string, string> };
41
+ /** Query all elements matching selector, returns summary array */
42
+ declare function queryAll(selector: string, limit?: number): Array<{ index: number, tag: string, text: string, visible: boolean, bounds: DOMRect }>;
43
+ /** Click an element by selector */
44
+ declare function click(selector: string): boolean;
45
+ /** Wait for an element to appear (polls) */
46
+ declare function waitFor(selector: string, timeoutMs?: number): Promise<Element | null>;
47
+ /** Get computed style value */
48
+ declare function getStyle(el: Element, prop: string): string;
49
+ /** Sleep for ms */
50
+ declare function sleep(ms: number): Promise<void>;
51
+ `;
52
+ monaco.languages.typescript.javascriptDefaults.addExtraLib(helperDefs, 'helpers.d.ts');
53
+ monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
54
+ noSemanticValidation: true,
55
+ noSyntaxValidation: false,
56
+ });
57
+ monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
58
+ target: monaco.languages.typescript.ScriptTarget.ES2020,
59
+ allowNonTsExtensions: true,
60
+ allowJs: true,
61
+ });
62
+
63
+ const defaultCode = `// Write JS to evaluate via CDP — Ctrl+Enter to run
64
+ // Helpers: log(), probe(), queryAll(), click(), waitFor()
65
+
66
+ (() => {
67
+ const title = document.title;
68
+ log('Page title:', title);
69
+ return title;
70
+ })()`;
71
+
72
+ const container = document.getElementById('editor-container');
73
+ const monacoEditor = monaco.editor.create(container, {
74
+ value: defaultCode,
75
+ language: 'javascript',
76
+ theme: 'adhdev-dark',
77
+ fontFamily: "'JetBrains Mono', monospace",
78
+ fontSize: 13,
79
+ lineHeight: 22,
80
+ minimap: { enabled: true, scale: 1, showSlider: 'mouseover' },
81
+ scrollBeyondLastLine: false,
82
+ renderLineHighlight: 'all',
83
+ cursorBlinking: 'smooth',
84
+ cursorSmoothCaretAnimation: 'on',
85
+ smoothScrolling: true,
86
+ bracketPairColorization: { enabled: true },
87
+ autoIndent: 'full',
88
+ formatOnPaste: true,
89
+ tabSize: 2,
90
+ wordWrap: 'on',
91
+ padding: { top: 8 },
92
+ suggest: {
93
+ showKeywords: true,
94
+ showSnippets: true,
95
+ showFunctions: true,
96
+ },
97
+ quickSuggestions: true,
98
+ parameterHints: { enabled: true },
99
+ automaticLayout: true,
100
+ });
101
+
102
+ // Ctrl+Enter → Run
103
+ monacoEditor.addAction({
104
+ id: 'run-script',
105
+ label: 'Run Script',
106
+ keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
107
+ run: () => window.runEditor?.(),
108
+ });
109
+
110
+ // ─── Proxy textarea.value → Monaco ───
111
+ const ta = document.getElementById('editor');
112
+ Object.defineProperty(ta, 'value', {
113
+ get() { return monacoEditor.getValue(); },
114
+ set(v) { monacoEditor.setValue(v); },
115
+ });
116
+ ta.focus = () => monacoEditor.focus();
117
+
118
+ window.monacoEditor = monacoEditor;
119
+
120
+ // ─── Snippets ───
121
+ monaco.languages.registerCompletionItemProvider('javascript', {
122
+ provideCompletionItems: (model, position) => {
123
+ const word = model.getWordUntilPosition(position);
124
+ const range = {
125
+ startLineNumber: position.lineNumber,
126
+ endLineNumber: position.lineNumber,
127
+ startColumn: word.startColumn,
128
+ endColumn: word.endColumn,
129
+ };
130
+ return {
131
+ suggestions: [
132
+ {
133
+ label: 'iife',
134
+ kind: monaco.languages.CompletionItemKind.Snippet,
135
+ insertText: '(() => {\n ${1:// code}\n return ${2:result};\n})()',
136
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
137
+ documentation: 'Immediately Invoked Function Expression (CDP pattern)',
138
+ range,
139
+ },
140
+ {
141
+ label: 'aiife',
142
+ kind: monaco.languages.CompletionItemKind.Snippet,
143
+ insertText: '(async () => {\n ${1:// async code}\n return ${2:result};\n})()',
144
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
145
+ documentation: 'Async IIFE (for await/Promise)',
146
+ range,
147
+ },
148
+ {
149
+ label: 'qsa',
150
+ kind: monaco.languages.CompletionItemKind.Snippet,
151
+ insertText: "[...document.querySelectorAll('${1:selector}')].map(el => ({\n tag: el.tagName,\n text: el.textContent?.trim().substring(0, 100),\n visible: el.offsetWidth > 0,\n}))",
152
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
153
+ documentation: 'QuerySelectorAll with element mapping',
154
+ range,
155
+ },
156
+ {
157
+ label: 'readchat',
158
+ kind: monaco.languages.CompletionItemKind.Snippet,
159
+ insertText: "(() => {\n const msgs = [];\n document.querySelectorAll('${1:.message}').forEach((el, i) => {\n msgs.push({\n role: el.classList.contains('${2:user}') ? 'user' : 'assistant',\n content: el.textContent.trim().substring(0, 6000),\n index: i,\n });\n });\n return JSON.stringify({ messages: msgs, status: 'idle' });\n})()",
160
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
161
+ documentation: 'readChat template',
162
+ range,
163
+ },
164
+ {
165
+ label: 'probe',
166
+ kind: monaco.languages.CompletionItemKind.Snippet,
167
+ insertText: "(() => {\n const el = document.querySelector('${1:selector}');\n if (!el) return JSON.stringify({ error: 'not found' });\n const r = el.getBoundingClientRect();\n return JSON.stringify({\n tag: el.tagName, id: el.id,\n classes: [...el.classList],\n text: el.textContent?.trim().substring(0, 200),\n bounds: { top: r.top, left: r.left, width: r.width, height: r.height },\n styles: { color: getComputedStyle(el).color, bg: getComputedStyle(el).background },\n attrs: Object.fromEntries([...el.attributes].map(a => [a.name, a.value])),\n });\n})()",
168
+ insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
169
+ documentation: 'Probe element details (bounds, styles, attrs)',
170
+ range,
171
+ },
172
+ ],
173
+ };
174
+ },
175
+ });
176
+ });
@@ -0,0 +1,326 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Inter:wght@400;500;600&display=swap');
2
+
3
+ :root {
4
+ --bg: #0d1117;
5
+ --bg-panel: #161b22;
6
+ --bg-input: #0d1117;
7
+ --border: #30363d;
8
+ --text: #c9d1d9;
9
+ --text-dim: #8b949e;
10
+ --accent: #58a6ff;
11
+ --accent-green: #3fb950;
12
+ --accent-red: #f85149;
13
+ --accent-yellow: #d29922;
14
+ --accent-purple: #bc8cff;
15
+ }
16
+
17
+ * { margin: 0; padding: 0; box-sizing: border-box; }
18
+ body {
19
+ font-family: 'Inter', sans-serif;
20
+ background: var(--bg);
21
+ color: var(--text);
22
+ height: 100vh;
23
+ display: flex;
24
+ flex-direction: column;
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* ─── Toolbar ─── */
29
+ .toolbar {
30
+ display: flex;
31
+ align-items: center;
32
+ gap: 8px;
33
+ padding: 8px 16px;
34
+ background: var(--bg-panel);
35
+ border-bottom: 1px solid var(--border);
36
+ flex-shrink: 0;
37
+ }
38
+ .toolbar .logo {
39
+ font-weight: 600;
40
+ font-size: 14px;
41
+ color: var(--accent);
42
+ margin-right: 12px;
43
+ white-space: nowrap;
44
+ }
45
+ .toolbar select, .toolbar button {
46
+ font-family: 'Inter', sans-serif;
47
+ font-size: 12px;
48
+ border: 1px solid var(--border);
49
+ border-radius: 6px;
50
+ padding: 6px 12px;
51
+ background: var(--bg-input);
52
+ color: var(--text);
53
+ cursor: pointer;
54
+ transition: all 0.15s;
55
+ }
56
+ .toolbar button:hover { border-color: var(--accent); background: rgba(88,166,255,0.1); }
57
+ .toolbar button.primary {
58
+ background: var(--accent);
59
+ color: #000;
60
+ border-color: var(--accent);
61
+ font-weight: 600;
62
+ }
63
+ .toolbar button.primary:hover { background: #79c0ff; }
64
+ .toolbar button.danger { border-color: var(--accent-red); color: var(--accent-red); }
65
+ .toolbar button.danger:hover { background: rgba(248,81,73,0.1); }
66
+ .toolbar .spacer { flex: 1; }
67
+ .toolbar .status {
68
+ font-size: 11px;
69
+ color: var(--text-dim);
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 4px;
73
+ }
74
+ .toolbar .status .dot {
75
+ width: 6px; height: 6px; border-radius: 50%;
76
+ display: inline-block;
77
+ }
78
+ .toolbar .status .dot.on { background: var(--accent-green); }
79
+ .toolbar .status .dot.off { background: var(--accent-red); }
80
+
81
+ /* ─── Main Layout ─── */
82
+ .main {
83
+ flex: 1;
84
+ display: flex;
85
+ overflow: hidden;
86
+ }
87
+
88
+ /* Left: Editor */
89
+ .editor-panel {
90
+ width: 50%;
91
+ display: flex;
92
+ flex-direction: column;
93
+ border-right: 1px solid var(--border);
94
+ }
95
+ .editor-header {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 6px;
99
+ padding: 6px 12px;
100
+ background: var(--bg-panel);
101
+ border-bottom: 1px solid var(--border);
102
+ font-size: 12px;
103
+ color: var(--text-dim);
104
+ }
105
+ .editor-header .tab {
106
+ padding: 4px 10px;
107
+ border-radius: 4px;
108
+ cursor: pointer;
109
+ font-size: 11px;
110
+ }
111
+ .editor-header .tab.active {
112
+ background: rgba(88,166,255,0.15);
113
+ color: var(--accent);
114
+ }
115
+ .editor-header .tab:hover { background: rgba(255,255,255,0.05); }
116
+ #editor-container {
117
+ flex: 1;
118
+ overflow: hidden;
119
+ background: var(--bg);
120
+ }
121
+ /* Hidden textarea for backward compat */
122
+ #editor { display: none; }
123
+
124
+ /* Right: Preview + Output */
125
+ .right-panel {
126
+ width: 50%;
127
+ display: flex;
128
+ flex-direction: column;
129
+ }
130
+
131
+ /* Screenshot */
132
+ .screenshot-panel {
133
+ flex: 0 0 50%;
134
+ position: relative;
135
+ overflow: hidden;
136
+ border-bottom: 1px solid var(--border);
137
+ background: #000;
138
+ }
139
+ .screenshot-panel img {
140
+ width: 100%;
141
+ height: 100%;
142
+ object-fit: contain;
143
+ }
144
+ .screenshot-panel .placeholder {
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ height: 100%;
149
+ color: var(--text-dim);
150
+ font-size: 13px;
151
+ }
152
+ .screenshot-panel .overlay {
153
+ position: absolute;
154
+ border: 2px solid var(--accent);
155
+ background: rgba(88,166,255,0.08);
156
+ pointer-events: none;
157
+ transition: all 0.2s;
158
+ }
159
+ .screenshot-panel .overlay .label {
160
+ position: absolute;
161
+ top: -18px;
162
+ left: 0;
163
+ font-size: 10px;
164
+ background: var(--accent);
165
+ color: #000;
166
+ padding: 1px 4px;
167
+ border-radius: 2px;
168
+ white-space: nowrap;
169
+ font-family: 'JetBrains Mono', monospace;
170
+ }
171
+
172
+ /* Output */
173
+ .output-panel {
174
+ flex: 1;
175
+ display: flex;
176
+ flex-direction: column;
177
+ overflow: hidden;
178
+ }
179
+ .output-header {
180
+ display: flex;
181
+ align-items: center;
182
+ gap: 8px;
183
+ padding: 6px 12px;
184
+ background: var(--bg-panel);
185
+ border-bottom: 1px solid var(--border);
186
+ font-size: 12px;
187
+ color: var(--text-dim);
188
+ }
189
+ .output-header .badge {
190
+ font-size: 10px;
191
+ padding: 1px 6px;
192
+ border-radius: 8px;
193
+ font-weight: 600;
194
+ }
195
+ .output-header .badge.ok { background: rgba(63,185,80,0.2); color: var(--accent-green); }
196
+ .output-header .badge.err { background: rgba(248,81,73,0.2); color: var(--accent-red); }
197
+ .output-search {
198
+ font-family: 'JetBrains Mono', monospace;
199
+ font-size: 11px;
200
+ border: 1px solid var(--border);
201
+ border-radius: 4px;
202
+ padding: 2px 8px;
203
+ background: var(--bg-input);
204
+ color: var(--text);
205
+ width: 140px;
206
+ outline: none;
207
+ }
208
+ .output-search:focus { border-color: var(--accent); }
209
+ #output {
210
+ flex: 1;
211
+ overflow-y: auto;
212
+ padding: 4px 0;
213
+ font-family: 'JetBrains Mono', monospace;
214
+ font-size: 12px;
215
+ line-height: 1.5;
216
+ }
217
+ .output-entry {
218
+ padding: 4px 12px;
219
+ border-bottom: 1px solid rgba(48,54,61,0.3);
220
+ display: flex;
221
+ gap: 8px;
222
+ align-items: flex-start;
223
+ transition: background 0.15s;
224
+ }
225
+ .output-entry:hover { background: rgba(255,255,255,0.02); }
226
+ .output-entry .ts {
227
+ color: var(--text-dim);
228
+ font-size: 10px;
229
+ flex-shrink: 0;
230
+ min-width: 60px;
231
+ padding-top: 1px;
232
+ }
233
+ .output-entry .icon {
234
+ flex-shrink: 0;
235
+ width: 16px;
236
+ text-align: center;
237
+ font-size: 11px;
238
+ }
239
+ .output-entry .content {
240
+ flex: 1;
241
+ white-space: pre-wrap;
242
+ word-break: break-all;
243
+ }
244
+ .output-entry.type-log .icon { color: var(--text-dim); }
245
+ .output-entry.type-result .icon { color: var(--accent-green); }
246
+ .output-entry.type-error .icon { color: var(--accent-red); }
247
+ .output-entry.type-error .content { color: var(--accent-red); }
248
+ .output-entry.type-warn .icon { color: var(--accent-yellow); }
249
+ .output-entry.type-warn .content { color: var(--accent-yellow); }
250
+ .output-entry.type-query .icon { color: var(--accent-purple); }
251
+ .output-entry.hidden { display: none; }
252
+
253
+ /* Watch indicator */
254
+ .watch-indicator {
255
+ display: none;
256
+ align-items: center;
257
+ gap: 4px;
258
+ font-size: 11px;
259
+ color: var(--accent-green);
260
+ animation: pulse 2s infinite;
261
+ }
262
+ .watch-indicator.active { display: flex; }
263
+ @keyframes pulse {
264
+ 0%, 100% { opacity: 1; }
265
+ 50% { opacity: 0.4; }
266
+ }
267
+
268
+ /* Script list dropdown */
269
+ .script-menu {
270
+ position: relative;
271
+ }
272
+ .script-dropdown {
273
+ display: none;
274
+ position: absolute;
275
+ top: 100%;
276
+ left: 0;
277
+ background: var(--bg-panel);
278
+ border: 1px solid var(--border);
279
+ border-radius: 6px;
280
+ padding: 4px;
281
+ min-width: 200px;
282
+ z-index: 100;
283
+ box-shadow: 0 8px 24px rgba(0,0,0,0.4);
284
+ }
285
+ .script-dropdown.show { display: block; }
286
+ .script-dropdown .item {
287
+ padding: 6px 10px;
288
+ border-radius: 4px;
289
+ cursor: pointer;
290
+ font-size: 12px;
291
+ display: flex;
292
+ justify-content: space-between;
293
+ }
294
+ .script-dropdown .item:hover { background: rgba(255,255,255,0.05); }
295
+ .script-dropdown .item .hint { color: var(--text-dim); font-size: 11px; }
296
+
297
+ /* Selector tester */
298
+ .selector-bar {
299
+ display: flex;
300
+ gap: 6px;
301
+ padding: 4px 8px;
302
+ background: var(--bg-panel);
303
+ border-bottom: 1px solid var(--border);
304
+ }
305
+ .selector-bar input {
306
+ flex: 1;
307
+ font-family: 'JetBrains Mono', monospace;
308
+ font-size: 12px;
309
+ border: 1px solid var(--border);
310
+ border-radius: 4px;
311
+ padding: 4px 8px;
312
+ background: var(--bg-input);
313
+ color: var(--text);
314
+ outline: none;
315
+ }
316
+ .selector-bar input:focus { border-color: var(--accent); }
317
+ .selector-bar button {
318
+ font-size: 11px;
319
+ padding: 4px 8px;
320
+ border: 1px solid var(--border);
321
+ border-radius: 4px;
322
+ background: var(--bg-input);
323
+ color: var(--text);
324
+ cursor: pointer;
325
+ }
326
+ .selector-bar .count { font-size: 11px; color: var(--text-dim); line-height: 28px; }
@@ -0,0 +1,148 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ADHDev DevConsole — Script Development</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.52.2/min/vs/loader.min.js"></script>
8
+ <link rel="stylesheet" href="/static/dev-console.css">
9
+ </head>
10
+ <body>
11
+
12
+ <!-- Toolbar -->
13
+ <div class="toolbar">
14
+ <span class="logo">🔧 ADHDev DevConsole</span>
15
+ <select id="providerSelect"><option value="">Loading...</option></select>
16
+ <button id="newBtn" title="Create new provider">+ New</button>
17
+ <select id="ideTargetSelect" title="CDP Target IDE"><option value="">Auto</option></select>
18
+ <div class="script-menu">
19
+ <button id="scriptBtn">📜 Scripts ▾</button>
20
+ <div class="script-dropdown" id="scriptDropdown"></div>
21
+ </div>
22
+ <button class="primary" id="runBtn" title="Ctrl+Enter">▶ Run</button>
23
+
24
+ <button id="screenshotBtn">📸</button>
25
+ <button id="domDebugBtn" title="Analyze inputs, buttons, textareas">🔍 DOM</button>
26
+ <button id="reloadBtn">🔄</button>
27
+ <button id="watchBtn">👁 Watch</button>
28
+ <button id="sourceBtn">📄 Edit Source</button>
29
+ <button id="saveBtn" style="display:none;background:var(--accent-green);color:#000;border-color:var(--accent-green);font-weight:600">💾 Save & Run</button>
30
+ <div class="spacer"></div>
31
+ <div class="watch-indicator" id="watchIndicator">● Watch</div>
32
+ <div class="status" id="statusBar"><span class="dot off"></span> Connecting...</div>
33
+ </div>
34
+
35
+ <!-- Scaffold Modal -->
36
+ <div id="scaffoldModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:200;display:none;align-items:center;justify-content:center">
37
+ <div style="background:var(--bg-panel);border:1px solid var(--border);border-radius:12px;padding:24px;width:460px;box-shadow:0 20px 40px rgba(0,0,0,0.6);max-height:85vh;overflow-y:auto">
38
+ <h3 style="margin:0 0 16px;font-size:16px;color:var(--accent)">+ New Provider</h3>
39
+ <div style="display:flex;flex-direction:column;gap:10px">
40
+ <div style="display:flex;gap:8px">
41
+ <div style="flex:1">
42
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Type ID</label>
43
+ <input id="scaffoldType" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="zed">
44
+ </div>
45
+ <div style="flex:1">
46
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Display Name</label>
47
+ <input id="scaffoldName" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="Zed">
48
+ </div>
49
+ </div>
50
+ <div>
51
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Category</label>
52
+ <select id="scaffoldCategory" onchange="toggleScaffoldFields()" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px">
53
+ <option value="ide">💻 IDE (main window CDP)</option>
54
+ <option value="extension">🧩 Extension (webview CDP)</option>
55
+ <option value="cli">⌨️ CLI (terminal agent)</option>
56
+ </select>
57
+ </div>
58
+ <!-- IDE/Extension fields -->
59
+ <div id="scaffoldIdeFields">
60
+ <div style="display:flex;gap:8px;margin-bottom:8px">
61
+ <div style="flex:1">
62
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">CDP Port (primary)</label>
63
+ <input id="scaffoldCdpPort1" type="number" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="9222">
64
+ </div>
65
+ <div style="flex:1">
66
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">CDP Port (fallback)</label>
67
+ <input id="scaffoldCdpPort2" type="number" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="9223">
68
+ </div>
69
+ </div>
70
+ <div style="display:flex;gap:8px">
71
+ <div style="flex:1">
72
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">CLI Command</label>
73
+ <input id="scaffoldCli" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="cursor">
74
+ </div>
75
+ <div style="flex:1">
76
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Process Name</label>
77
+ <input id="scaffoldProcess" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="Cursor">
78
+ </div>
79
+ </div>
80
+ </div>
81
+ <!-- Extension-only field -->
82
+ <div id="scaffoldExtFields" style="display:none">
83
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Extension ID</label>
84
+ <input id="scaffoldExtId" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="publisher.extension-name">
85
+ </div>
86
+ <!-- CLI-only field -->
87
+ <div id="scaffoldCliFields" style="display:none">
88
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Binary Path</label>
89
+ <input id="scaffoldBinary" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="gemini-cli">
90
+ </div>
91
+ <div>
92
+ <label style="font-size:11px;color:var(--text-dim);display:block;margin-bottom:4px">Install Path (optional)</label>
93
+ <input id="scaffoldInstallPath" style="width:100%;padding:8px;background:var(--bg-input);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px" placeholder="/Applications/MyIDE.app">
94
+ </div>
95
+ <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:8px">
96
+ <button onclick="document.getElementById('scaffoldModal').style.display='none'" style="padding:8px 16px;background:transparent;border:1px solid var(--border);border-radius:6px;color:var(--text-dim);cursor:pointer">Cancel</button>
97
+ <button onclick="doScaffold()" style="padding:8px 16px;background:var(--accent);color:#000;border:none;border-radius:6px;font-weight:600;cursor:pointer">Create</button>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </div>
102
+
103
+ <!-- Main -->
104
+ <div class="main">
105
+ <!-- Left: Editor -->
106
+ <div class="editor-panel">
107
+ <div class="editor-header">
108
+ <span class="tab active" data-tab="editor" title="Write and run CDP scripts on the target IDE">🖥 Editor</span>
109
+ <span class="tab" data-tab="template" title="Provider scaffold template — copy & customize for a new provider">📋 New Provider Template</span>
110
+ <span class="tab" data-tab="contracts" title="Reference: required JSON output format for each script function">📑 Output Spec</span>
111
+ <div style="flex:1"></div>
112
+ <span id="execTime" style="font-size:11px;color:var(--text-dim)"></span>
113
+ </div>
114
+ <textarea id="editor" style="display:none"></textarea>
115
+ <div id="editor-container"></div>
116
+ </div>
117
+
118
+ <!-- Right: Screenshot + Output -->
119
+ <div class="right-panel">
120
+ <div class="screenshot-panel" id="screenshotPanel" style="position:relative">
121
+ <div class="placeholder" id="screenshotPlaceholder">Click 📸 Screenshot to capture IDE<br><small style="color:var(--text-dim)">Then click on the screenshot to inspect elements</small></div>
122
+ <img id="screenshotImg" style="display:none;cursor:crosshair" />
123
+ <div id="inspectCrosshair" style="display:none;position:absolute;width:20px;height:20px;border:2px solid var(--accent);border-radius:50%;pointer-events:none;transform:translate(-50%,-50%);z-index:10;box-shadow:0 0 8px var(--accent)"></div>
124
+ </div>
125
+
126
+ <div class="selector-bar">
127
+ <input id="selectorInput" placeholder="CSS selector to test..." />
128
+ <button id="selectorBtn">Query</button>
129
+ <span class="count" id="selectorCount"></span>
130
+ </div>
131
+
132
+ <div class="output-panel">
133
+ <div class="output-header">
134
+ <span>Output</span>
135
+ <span class="badge" id="outputBadge" style="display:none"></span>
136
+ <div style="flex:1"></div>
137
+ <input class="output-search" id="outputSearch" placeholder="Filter..." />
138
+ <button style="font-size:11px;padding:2px 6px;border:1px solid var(--border);border-radius:3px;background:transparent;color:var(--text-dim);cursor:pointer" id="clearBtn">Clear</button>
139
+ </div>
140
+ <div id="output"></div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ <script src="/static/dev-console.js"></script>
146
+ <script src="/static/dev-console-monaco.js"></script>
147
+ </body>
148
+ </html>