claude-dev-server 1.1.4 → 1.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 +80 -35
- package/dist/assets/dev.html +739 -0
- package/dist/assets/inspect-bridge.js +47 -0
- package/dist/{chunk-RFPIOREI.js → chunk-PAE5WTS2.js} +596 -413
- package/dist/chunk-PAE5WTS2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-RFPIOREI.js.map +0 -1
|
@@ -196,87 +196,77 @@ async function handleInspect(msg, ws, projectRoot) {
|
|
|
196
196
|
// src/client/injection.js
|
|
197
197
|
var CLIENT_STYLES = `
|
|
198
198
|
<style id="claude-dev-server-styles">
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
bottom: 120px;
|
|
202
|
-
right: 20px;
|
|
203
|
-
width: 44px;
|
|
204
|
-
height: 44px;
|
|
205
|
-
background: #1e1e1e;
|
|
206
|
-
border: none;
|
|
207
|
-
border-radius: 50%;
|
|
208
|
-
box-shadow: 0 2px 12px rgba(0,0,0,0.3);
|
|
209
|
-
cursor: pointer;
|
|
210
|
-
z-index: 2147483647;
|
|
211
|
-
display: flex;
|
|
212
|
-
align-items: center;
|
|
213
|
-
justify-content: center;
|
|
214
|
-
font-size: 20px;
|
|
215
|
-
transition: transform 0.2s, background 0.2s;
|
|
199
|
+
* {
|
|
200
|
+
margin: 0;
|
|
216
201
|
padding: 0;
|
|
202
|
+
box-sizing: border-box;
|
|
217
203
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
display: none;
|
|
204
|
+
html, body {
|
|
205
|
+
width: 100%;
|
|
206
|
+
height: 100%;
|
|
207
|
+
overflow: hidden;
|
|
208
|
+
background: transparent;
|
|
224
209
|
}
|
|
225
|
-
.claude-dev-server-
|
|
226
|
-
position: fixed;
|
|
227
|
-
bottom: 174px;
|
|
228
|
-
right: 20px;
|
|
229
|
-
width: 44px;
|
|
230
|
-
height: 44px;
|
|
231
|
-
background: #d97757;
|
|
232
|
-
border: none;
|
|
233
|
-
border-radius: 50%;
|
|
234
|
-
box-shadow: 0 2px 12px rgba(0,0,0,0.3);
|
|
235
|
-
cursor: pointer;
|
|
236
|
-
z-index: 2147483647;
|
|
210
|
+
.claude-dev-server-container {
|
|
237
211
|
display: flex;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
.claude-dev-server-inspect-btn:hover {
|
|
244
|
-
transform: scale(1.1);
|
|
245
|
-
background: #c96a4a;
|
|
212
|
+
width: 100vw;
|
|
213
|
+
height: 100vh;
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
background: transparent;
|
|
246
216
|
}
|
|
247
|
-
.claude-dev-server-
|
|
248
|
-
|
|
217
|
+
.claude-dev-server-left {
|
|
218
|
+
width: 40%;
|
|
219
|
+
min-width: 300px;
|
|
220
|
+
max-width: 60%;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
position: relative;
|
|
223
|
+
background: #fff;
|
|
224
|
+
display: flex;
|
|
225
|
+
flex-direction: column;
|
|
249
226
|
}
|
|
250
|
-
.claude-dev-server-
|
|
251
|
-
|
|
227
|
+
.claude-dev-server-left iframe {
|
|
228
|
+
width: 100%;
|
|
229
|
+
height: 100%;
|
|
230
|
+
border: none;
|
|
252
231
|
}
|
|
253
|
-
.claude-dev-server-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
232
|
+
.claude-dev-server-divider {
|
|
233
|
+
width: 6px;
|
|
234
|
+
background: #3e3e3e;
|
|
235
|
+
position: relative;
|
|
236
|
+
cursor: col-resize;
|
|
237
|
+
transition: background 0.2s;
|
|
257
238
|
flex-shrink: 0;
|
|
258
239
|
}
|
|
259
|
-
.claude-dev-server-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
font-size: 13px;
|
|
269
|
-
box-shadow: -4px 0 20px rgba(0,0,0,0.3);
|
|
270
|
-
transform: translateX(100%);
|
|
271
|
-
transition: transform 0.2s ease;
|
|
272
|
-
z-index: 2147483647;
|
|
240
|
+
.claude-dev-server-divider:hover,
|
|
241
|
+
.claude-dev-server-divider.dragging {
|
|
242
|
+
background: #d97757;
|
|
243
|
+
}
|
|
244
|
+
.claude-dev-server-right {
|
|
245
|
+
flex: 1;
|
|
246
|
+
min-width: 300px;
|
|
247
|
+
overflow: hidden;
|
|
248
|
+
position: relative;
|
|
273
249
|
display: flex;
|
|
274
250
|
flex-direction: column;
|
|
275
251
|
}
|
|
276
|
-
.claude-dev-server-
|
|
277
|
-
|
|
252
|
+
.claude-dev-server-terminal {
|
|
253
|
+
flex: 1;
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
position: relative;
|
|
256
|
+
background: #000;
|
|
278
257
|
}
|
|
279
|
-
.claude-dev-server-
|
|
258
|
+
.claude-dev-server-terminal iframe,
|
|
259
|
+
.claude-dev-server-terminal-iframe {
|
|
260
|
+
width: 100%;
|
|
261
|
+
height: 100%;
|
|
262
|
+
border: none;
|
|
263
|
+
}
|
|
264
|
+
.claude-dev-server-dev-iframe {
|
|
265
|
+
width: 100%;
|
|
266
|
+
height: 100%;
|
|
267
|
+
border: none;
|
|
268
|
+
}
|
|
269
|
+
.claude-dev-server-terminal-header {
|
|
280
270
|
padding: 8px 12px;
|
|
281
271
|
background: #2d2d2d;
|
|
282
272
|
border-bottom: 1px solid #3e3e3e;
|
|
@@ -284,6 +274,7 @@ var CLIENT_STYLES = `
|
|
|
284
274
|
justify-content: space-between;
|
|
285
275
|
align-items: center;
|
|
286
276
|
user-select: none;
|
|
277
|
+
min-height: 40px;
|
|
287
278
|
}
|
|
288
279
|
.claude-dev-server-title {
|
|
289
280
|
font-weight: 600;
|
|
@@ -292,6 +283,7 @@ var CLIENT_STYLES = `
|
|
|
292
283
|
align-items: center;
|
|
293
284
|
gap: 8px;
|
|
294
285
|
font-size: 13px;
|
|
286
|
+
font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;
|
|
295
287
|
}
|
|
296
288
|
.claude-dev-server-title svg {
|
|
297
289
|
width: 14px;
|
|
@@ -323,36 +315,6 @@ var CLIENT_STYLES = `
|
|
|
323
315
|
background: #b85d3f;
|
|
324
316
|
color: #fff;
|
|
325
317
|
}
|
|
326
|
-
.claude-dev-server-close {
|
|
327
|
-
background: none;
|
|
328
|
-
border: none;
|
|
329
|
-
color: #858585;
|
|
330
|
-
cursor: pointer;
|
|
331
|
-
font-size: 18px;
|
|
332
|
-
padding: 0;
|
|
333
|
-
width: 20px;
|
|
334
|
-
height: 20px;
|
|
335
|
-
display: flex;
|
|
336
|
-
align-items: center;
|
|
337
|
-
justify-content: center;
|
|
338
|
-
border-radius: 3px;
|
|
339
|
-
transition: background 0.15s, color 0.15s;
|
|
340
|
-
}
|
|
341
|
-
.claude-dev-server-close:hover {
|
|
342
|
-
background: #3e3e3e;
|
|
343
|
-
color: #fff;
|
|
344
|
-
}
|
|
345
|
-
.claude-dev-server-terminal {
|
|
346
|
-
flex: 1;
|
|
347
|
-
overflow: hidden;
|
|
348
|
-
position: relative;
|
|
349
|
-
background: #000;
|
|
350
|
-
}
|
|
351
|
-
.claude-dev-server-terminal iframe {
|
|
352
|
-
width: 100%;
|
|
353
|
-
height: 100%;
|
|
354
|
-
border: none;
|
|
355
|
-
}
|
|
356
318
|
.claude-dev-server-inspect-overlay {
|
|
357
319
|
position: fixed;
|
|
358
320
|
top: 0;
|
|
@@ -386,39 +348,22 @@ var CLIENT_STYLES = `
|
|
|
386
348
|
font-family: monospace;
|
|
387
349
|
white-space: nowrap;
|
|
388
350
|
}
|
|
389
|
-
/*
|
|
351
|
+
/* Portrait mode */
|
|
390
352
|
@media (orientation: portrait) {
|
|
391
|
-
.claude-dev-server-
|
|
392
|
-
|
|
393
|
-
bottom: 120px;
|
|
394
|
-
right: 10px;
|
|
395
|
-
width: 36px;
|
|
396
|
-
height: 36px;
|
|
353
|
+
.claude-dev-server-container {
|
|
354
|
+
flex-direction: column;
|
|
397
355
|
}
|
|
398
|
-
.claude-dev-server-
|
|
399
|
-
top: auto;
|
|
400
|
-
bottom: 166px;
|
|
401
|
-
right: 10px;
|
|
402
|
-
width: 36px;
|
|
403
|
-
height: 36px;
|
|
404
|
-
}
|
|
405
|
-
.claude-dev-server-panel {
|
|
406
|
-
top: auto;
|
|
407
|
-
bottom: 0;
|
|
408
|
-
right: 0;
|
|
409
|
-
left: 0;
|
|
356
|
+
.claude-dev-server-divider {
|
|
410
357
|
width: 100%;
|
|
411
|
-
height:
|
|
412
|
-
|
|
413
|
-
box-shadow: 0 -4px 20px rgba(0,0,0,0.3);
|
|
414
|
-
}
|
|
415
|
-
.claude-dev-server-panel.open {
|
|
416
|
-
transform: translateY(0);
|
|
358
|
+
height: 6px;
|
|
359
|
+
cursor: row-resize;
|
|
417
360
|
}
|
|
418
|
-
.claude-dev-server-
|
|
419
|
-
|
|
420
|
-
width:
|
|
421
|
-
|
|
361
|
+
.claude-dev-server-right {
|
|
362
|
+
width: 100%;
|
|
363
|
+
min-width: unset;
|
|
364
|
+
max-width: unset;
|
|
365
|
+
height: 50%;
|
|
366
|
+
min-height: 200px;
|
|
422
367
|
}
|
|
423
368
|
}
|
|
424
369
|
</style>
|
|
@@ -426,15 +371,16 @@ var CLIENT_STYLES = `
|
|
|
426
371
|
var CLIENT_SCRIPT = `
|
|
427
372
|
(() => {
|
|
428
373
|
let ws = null
|
|
429
|
-
let
|
|
430
|
-
let
|
|
431
|
-
let inspectBtn = null // \u60AC\u6D6E inspect \u6309\u94AE
|
|
432
|
-
let terminalContainer = null
|
|
433
|
-
let terminalIframe = null
|
|
374
|
+
let ttydIframe = null
|
|
375
|
+
let devIframe = null
|
|
434
376
|
let overlay = null
|
|
435
|
-
let isOpen = false
|
|
436
377
|
let isInspectMode = false
|
|
437
378
|
let ttydWsUrl = null
|
|
379
|
+
let leftPanel = null
|
|
380
|
+
let divider = null
|
|
381
|
+
let rightPanel = null
|
|
382
|
+
let isDragging = false
|
|
383
|
+
let inspectListenersRegistered = false
|
|
438
384
|
|
|
439
385
|
// Fetch the WebSocket port from the server
|
|
440
386
|
async function getWsPort() {
|
|
@@ -449,75 +395,250 @@ var CLIENT_SCRIPT = `
|
|
|
449
395
|
connect(port)
|
|
450
396
|
} catch (err) {
|
|
451
397
|
console.error('[Claude Dev Server] Failed to get port:', err)
|
|
452
|
-
// Retry after 1 second
|
|
453
398
|
setTimeout(initWhenReady, 1000)
|
|
454
399
|
return
|
|
455
400
|
}
|
|
456
|
-
|
|
457
|
-
createInspectBtn()
|
|
401
|
+
// Create overlay first before split layout
|
|
458
402
|
createOverlay()
|
|
459
|
-
|
|
403
|
+
createSplitLayout()
|
|
460
404
|
}
|
|
461
405
|
|
|
462
|
-
function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
406
|
+
function createSplitLayout() {
|
|
407
|
+
// IMPORTANT: Only create layout in the top-level window, not in iframes
|
|
408
|
+
// This prevents infinite nesting when the dev iframe loads
|
|
409
|
+
if (window.top !== window.self) {
|
|
410
|
+
console.log('[Claude Dev Server] Not in top window, skipping layout creation')
|
|
411
|
+
return
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Check if already created
|
|
415
|
+
if (document.querySelector('.claude-dev-server-container')) return
|
|
416
|
+
// Also check if we're already in an injected page
|
|
417
|
+
if (window.__CLAUDE_SPLIT_LAYOUT_CREATED__) return
|
|
418
|
+
window.__CLAUDE_SPLIT_LAYOUT_CREATED__ = true
|
|
419
|
+
|
|
420
|
+
// Get original HTML from window variable (set by server)
|
|
421
|
+
// The HTML is Base64 encoded to avoid any escaping issues
|
|
422
|
+
let originalHtml
|
|
423
|
+
if (window.__CLAUDE_ORIGINAL_HTML_BASE64__) {
|
|
424
|
+
// Decode Base64
|
|
425
|
+
try {
|
|
426
|
+
const decoded = atob(window.__CLAUDE_ORIGINAL_HTML_BASE64__)
|
|
427
|
+
// Handle UTF-8 encoding
|
|
428
|
+
const bytes = new Uint8Array(decoded.length)
|
|
429
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
430
|
+
bytes[i] = decoded.charCodeAt(i)
|
|
431
|
+
}
|
|
432
|
+
originalHtml = new TextDecoder().decode(bytes)
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.error('[Claude Dev Server] Failed to decode Base64 HTML:', e)
|
|
435
|
+
originalHtml = document.documentElement.outerHTML
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
originalHtml = document.documentElement.outerHTML
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Create container
|
|
442
|
+
const container = document.createElement('div')
|
|
443
|
+
container.className = 'claude-dev-server-container'
|
|
444
|
+
|
|
445
|
+
// Left panel - terminal (Claude)
|
|
446
|
+
leftPanel = document.createElement('div')
|
|
447
|
+
leftPanel.className = 'claude-dev-server-left'
|
|
448
|
+
|
|
449
|
+
// Terminal header
|
|
450
|
+
const header = document.createElement('div')
|
|
451
|
+
header.className = 'claude-dev-server-terminal-header'
|
|
452
|
+
header.innerHTML = \`
|
|
453
|
+
<span class="claude-dev-server-title">
|
|
454
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none">
|
|
455
|
+
<path d="M11.376 24L10.776 23.544L10.44 22.8L10.776 21.312L11.16 19.392L11.472 17.856L11.76 15.96L11.928 15.336L11.904 15.288L11.784 15.312L10.344 17.28L8.16 20.232L6.432 22.056L6.024 22.224L5.304 21.864L5.376 21.192L5.784 20.616L8.16 17.568L9.6 15.672L10.536 14.592L10.512 14.448H10.464L4.128 18.576L3 18.72L2.496 18.264L2.568 17.52L2.808 17.28L4.704 15.96L9.432 13.32L9.504 13.08L9.432 12.96H9.192L8.4 12.912L5.712 12.84L3.384 12.744L1.104 12.624L0.528 12.504L0 11.784L0.048 11.424L0.528 11.112L1.224 11.16L2.736 11.28L5.016 11.424L6.672 11.52L9.12 11.784H9.504L9.552 11.616L9.432 11.52L9.336 11.424L6.96 9.84L4.416 8.16L3.072 7.176L2.352 6.672L1.992 6.216L1.848 5.208L2.496 4.488L3.384 4.56L3.6 4.608L4.488 5.304L6.384 6.768L8.88 8.616L9.24 8.904L9.408 8.808V8.736L9.24 8.472L7.896 6.024L6.456 3.528L5.808 2.496L5.64 1.872C5.576 1.656 5.544 1.416 5.544 1.152L6.288 0.144001L6.696 0L7.704 0.144001L8.112 0.504001L8.736 1.92L9.72 4.152L11.28 7.176L11.736 8.088L11.976 8.904L12.072 9.168H12.24V9.024L12.36 7.296L12.6 5.208L12.84 2.52L12.912 1.752L13.296 0.840001L14.04 0.360001L14.616 0.624001L15.096 1.32L15.024 1.752L14.76 3.6L14.184 6.504L13.824 8.472H14.04L14.28 8.208L15.264 6.912L16.92 4.848L17.64 4.032L18.504 3.12L19.056 2.688H20.088L20.832 3.816L20.496 4.992L19.44 6.336L18.552 7.464L17.28 9.168L16.512 10.536L16.584 10.632H16.752L19.608 10.008L21.168 9.744L22.992 9.432L23.832 9.816L23.928 10.2L23.592 11.016L21.624 11.496L19.32 11.952L15.888 12.768L15.84 12.792L15.888 12.864L17.424 13.008L18.096 13.056H19.728L22.752 13.272L23.544 13.8L24 14.424L23.928 14.928L22.704 15.528L21.072 15.144L17.232 14.232L15.936 13.92H15.744V14.016L16.848 15.096L18.84 16.896L21.36 19.224L21.48 19.8L21.168 20.28L20.832 20.232L18.624 18.552L17.76 17.808L15.84 16.2H15.72V16.368L16.152 17.016L18.504 20.544L18.624 21.624L18.456 21.96L17.832 22.176L17.184 22.056L15.792 20.136L14.376 17.952L13.224 16.008L13.104 16.104L12.408 23.352L12.096 23.712L11.376 24Z" fill="#d97757"/>
|
|
456
|
+
</svg>
|
|
457
|
+
Claude Code
|
|
458
|
+
</span>
|
|
459
|
+
<div class="claude-dev-server-actions">
|
|
460
|
+
<button class="claude-dev-server-btn claude-dev-server-btn-inspect" title="Inspect Element">
|
|
461
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
462
|
+
<path d="M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7"/>
|
|
463
|
+
<path d="M12 12l4.166 10 1.48-4.355L22 16.166 12 12z"/>
|
|
464
|
+
<path d="M18 18l3 3"/>
|
|
465
|
+
</svg>
|
|
466
|
+
Inspect
|
|
467
|
+
</button>
|
|
468
|
+
</div>
|
|
469
|
+
\`
|
|
472
470
|
|
|
473
|
-
|
|
474
|
-
if (inspectBtn) return
|
|
475
|
-
inspectBtn = document.createElement('button')
|
|
476
|
-
inspectBtn.className = 'claude-dev-server-inspect-btn'
|
|
477
|
-
inspectBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7"/><path d="M12 12l4.166 10 1.48-4.355L22 16.166 12 12z"/><path d="M18 18l3 3"/></svg>'
|
|
478
|
-
inspectBtn.title = 'Inspect Element (Cmd/Ctrl + Shift + I)'
|
|
479
|
-
inspectBtn.setAttribute('data-dev-tool', 'true')
|
|
471
|
+
const inspectBtn = header.querySelector('.claude-dev-server-btn-inspect')
|
|
480
472
|
inspectBtn.addEventListener('click', () => {
|
|
481
473
|
if (isInspectMode) {
|
|
482
474
|
disableInspectMode()
|
|
483
475
|
} else {
|
|
484
|
-
// \u70B9\u51FB Inspect \u65F6\u5148\u6536\u8D77\u9762\u677F
|
|
485
|
-
if (isOpen) {
|
|
486
|
-
togglePanel(false)
|
|
487
|
-
}
|
|
488
476
|
enableInspectMode()
|
|
489
477
|
}
|
|
490
478
|
})
|
|
491
|
-
document.body.appendChild(inspectBtn)
|
|
492
|
-
}
|
|
493
479
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
480
|
+
// Terminal container
|
|
481
|
+
const terminal = document.createElement('div')
|
|
482
|
+
terminal.className = 'claude-dev-server-terminal'
|
|
483
|
+
|
|
484
|
+
// Ttyd iframe
|
|
485
|
+
ttydIframe = document.createElement('iframe')
|
|
486
|
+
ttydIframe.className = 'claude-dev-server-terminal-iframe'
|
|
487
|
+
ttydIframe.allow = 'clipboard-read; clipboard-write'
|
|
488
|
+
|
|
489
|
+
terminal.appendChild(ttydIframe)
|
|
490
|
+
leftPanel.appendChild(header)
|
|
491
|
+
leftPanel.appendChild(terminal)
|
|
492
|
+
|
|
493
|
+
// Divider - draggable
|
|
494
|
+
divider = document.createElement('div')
|
|
495
|
+
divider.className = 'claude-dev-server-divider'
|
|
496
|
+
setupDraggable(divider)
|
|
497
|
+
|
|
498
|
+
// Right panel - dev server
|
|
499
|
+
rightPanel = document.createElement('div')
|
|
500
|
+
rightPanel.className = 'claude-dev-server-right'
|
|
501
|
+
|
|
502
|
+
// Create dev server iframe with srcdoc
|
|
503
|
+
devIframe = document.createElement('iframe')
|
|
504
|
+
devIframe.className = 'claude-dev-server-dev-iframe'
|
|
505
|
+
// The HTML already has inspect script injected by server
|
|
506
|
+
devIframe.srcdoc = originalHtml
|
|
507
|
+
|
|
508
|
+
rightPanel.appendChild(devIframe)
|
|
509
|
+
|
|
510
|
+
// Assemble layout: Claude (left) | divider | dev server (right)
|
|
511
|
+
container.appendChild(leftPanel)
|
|
512
|
+
container.appendChild(divider)
|
|
513
|
+
container.appendChild(rightPanel)
|
|
514
|
+
|
|
515
|
+
// Replace body content
|
|
516
|
+
document.body.innerHTML = ''
|
|
517
|
+
document.body.appendChild(container)
|
|
498
518
|
document.body.appendChild(overlay)
|
|
519
|
+
|
|
520
|
+
// Wait for iframe to load before setting up communication
|
|
521
|
+
devIframe.onload = () => {
|
|
522
|
+
console.log('[Claude Dev Server] Dev iframe loaded')
|
|
523
|
+
// Setup inspect mode communication
|
|
524
|
+
setupInspectCommunication()
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function setupInspectCommunication() {
|
|
529
|
+
// Inspect mode is now handled entirely in the outer layer
|
|
530
|
+
// We'll access the iframe's document directly
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function setupIframeInspectListeners() {
|
|
534
|
+
if (!devIframe || !devIframe.contentDocument) return
|
|
535
|
+
|
|
536
|
+
// Prevent duplicate listener registration
|
|
537
|
+
if (inspectListenersRegistered) {
|
|
538
|
+
return
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const iframeDoc = devIframe.contentDocument
|
|
542
|
+
const iframeWindow = devIframe.contentWindow
|
|
543
|
+
|
|
544
|
+
const inspectHandler = (e) => {
|
|
545
|
+
if (!isInspectMode) return
|
|
546
|
+
|
|
547
|
+
e.preventDefault()
|
|
548
|
+
e.stopPropagation()
|
|
549
|
+
|
|
550
|
+
if (e.type === 'click') {
|
|
551
|
+
const el = e.target
|
|
552
|
+
const rect = el.getBoundingClientRect()
|
|
553
|
+
const className = el.className ? String(el.className) : ''
|
|
554
|
+
// Limit classnames to first 2-3 to avoid overly long selectors
|
|
555
|
+
const classNames = className.split(' ').filter(c => c).slice(0, 3)
|
|
556
|
+
const selector = el.tagName.toLowerCase() +
|
|
557
|
+
(el.id ? '#' + el.id : '') +
|
|
558
|
+
(classNames.length ? '.' + classNames.join('.') : '')
|
|
559
|
+
|
|
560
|
+
// Highlight element
|
|
561
|
+
if (overlay) {
|
|
562
|
+
overlay.innerHTML = ''
|
|
563
|
+
|
|
564
|
+
// Get iframe position on page
|
|
565
|
+
const iframeRect = devIframe.getBoundingClientRect()
|
|
566
|
+
|
|
567
|
+
// Calculate highlight position relative to main document
|
|
568
|
+
// Element rect is relative to iframe viewport, need to add iframe position
|
|
569
|
+
const highlightTop = iframeRect.top + rect.top
|
|
570
|
+
const highlightLeft = iframeRect.left + rect.left
|
|
571
|
+
|
|
572
|
+
const highlight = document.createElement('div')
|
|
573
|
+
highlight.className = 'claude-dev-server-highlight'
|
|
574
|
+
highlight.style.top = highlightTop + 'px'
|
|
575
|
+
highlight.style.left = highlightLeft + 'px'
|
|
576
|
+
highlight.style.width = rect.width + 'px'
|
|
577
|
+
highlight.style.height = rect.height + 'px'
|
|
578
|
+
highlight.dataset.element = selector
|
|
579
|
+
overlay.appendChild(highlight)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Get source location and send to terminal
|
|
583
|
+
getSourceLocationFromElement(el).then(location => {
|
|
584
|
+
if (location) {
|
|
585
|
+
sendToTerminal(location)
|
|
586
|
+
}
|
|
587
|
+
disableInspectMode()
|
|
588
|
+
})
|
|
589
|
+
} else if (e.type === 'mousemove') {
|
|
590
|
+
const el = e.target
|
|
591
|
+
const rect = el.getBoundingClientRect()
|
|
592
|
+
const className = el.className ? String(el.className) : ''
|
|
593
|
+
// Limit classnames to first 2-3 to avoid overly long selectors
|
|
594
|
+
const classNames = className.split(' ').filter(c => c).slice(0, 3)
|
|
595
|
+
const selector = el.tagName.toLowerCase() +
|
|
596
|
+
(el.id ? '#' + el.id : '') +
|
|
597
|
+
(classNames.length ? '.' + classNames.join('.') : '')
|
|
598
|
+
|
|
599
|
+
if (overlay) {
|
|
600
|
+
overlay.innerHTML = ''
|
|
601
|
+
|
|
602
|
+
// Get iframe position on page
|
|
603
|
+
const iframeRect = devIframe.getBoundingClientRect()
|
|
604
|
+
|
|
605
|
+
// Calculate highlight position relative to main document
|
|
606
|
+
const highlightTop = iframeRect.top + rect.top
|
|
607
|
+
const highlightLeft = iframeRect.left + rect.left
|
|
608
|
+
|
|
609
|
+
const highlight = document.createElement('div')
|
|
610
|
+
highlight.className = 'claude-dev-server-highlight'
|
|
611
|
+
highlight.style.top = highlightTop + 'px'
|
|
612
|
+
highlight.style.left = highlightLeft + 'px'
|
|
613
|
+
highlight.style.width = rect.width + 'px'
|
|
614
|
+
highlight.style.height = rect.height + 'px'
|
|
615
|
+
highlight.dataset.element = selector
|
|
616
|
+
overlay.appendChild(highlight)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Store handler reference for later removal
|
|
622
|
+
iframeDoc._claudeInspectHandler = inspectHandler
|
|
623
|
+
|
|
624
|
+
// Add event listeners in capture phase
|
|
625
|
+
iframeDoc.addEventListener('click', inspectHandler, true)
|
|
626
|
+
iframeDoc.addEventListener('mousemove', inspectHandler, true)
|
|
627
|
+
|
|
628
|
+
// Mark listeners as registered
|
|
629
|
+
inspectListenersRegistered = true
|
|
499
630
|
}
|
|
500
631
|
|
|
501
|
-
function
|
|
502
|
-
|
|
503
|
-
overlay.innerHTML = ''
|
|
504
|
-
const rect = el.getBoundingClientRect()
|
|
505
|
-
const highlight = document.createElement('div')
|
|
506
|
-
highlight.className = 'claude-dev-server-highlight'
|
|
507
|
-
highlight.style.top = rect.top + 'px'
|
|
508
|
-
highlight.style.left = rect.left + 'px'
|
|
509
|
-
highlight.style.width = rect.width + 'px'
|
|
510
|
-
highlight.style.height = rect.height + 'px'
|
|
511
|
-
const className = el.className ? String(el.className) : ''
|
|
512
|
-
highlight.dataset.element = el.tagName.toLowerCase() +
|
|
513
|
-
(el.id ? '#' + el.id : '') +
|
|
514
|
-
(className ? '.' + className.split(' ').join('.') : '')
|
|
515
|
-
overlay.appendChild(highlight)
|
|
632
|
+
async function handleInspectElement(elementInfo) {
|
|
633
|
+
// This is now handled directly in setupIframeInspectListeners
|
|
516
634
|
}
|
|
517
635
|
|
|
518
|
-
async function
|
|
636
|
+
async function getSourceLocationFromElement(element) {
|
|
637
|
+
// Access the iframe's document to detect Next.js chunks
|
|
638
|
+
const iframeDoc = devIframe?.contentDocument || document
|
|
639
|
+
|
|
519
640
|
// Early check: detect Next.js by checking for _next/static chunks
|
|
520
|
-
const hasNextJsChunks = Array.from(
|
|
641
|
+
const hasNextJsChunks = Array.from(iframeDoc.querySelectorAll('script[src]'))
|
|
521
642
|
.some(script => script.getAttribute('src')?.includes('/_next/static/chunks/'))
|
|
522
643
|
|
|
523
644
|
if (hasNextJsChunks) {
|
|
@@ -541,16 +662,37 @@ var CLIENT_SCRIPT = `
|
|
|
541
662
|
const result = await response.json()
|
|
542
663
|
console.log('[Claude Dev Server] Lookup result:', result)
|
|
543
664
|
if (result.file) {
|
|
544
|
-
const elClassName =
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
665
|
+
const elClassName = element.className ? String(element.className) : ''
|
|
666
|
+
// Limit classnames to first 2-3 to avoid overly long selectors
|
|
667
|
+
const classNames = elClassName.split(' ').filter(c => c).slice(0, 3)
|
|
668
|
+
const selector = element.tagName.toLowerCase() +
|
|
669
|
+
(element.id ? '#' + element.id : '') +
|
|
670
|
+
(classNames.length ? '.' + classNames.join('.') : '')
|
|
671
|
+
|
|
672
|
+
// Extract text content from element
|
|
673
|
+
let textContent = ''
|
|
674
|
+
if (element.nodeType === Node.TEXT_NODE) {
|
|
675
|
+
textContent = element.textContent ? element.textContent.trim().substring(0, 50) : ''
|
|
676
|
+
} else if (element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE) {
|
|
677
|
+
textContent = element.childNodes[0].textContent ? element.childNodes[0].textContent.trim().substring(0, 50) : ''
|
|
678
|
+
} else {
|
|
679
|
+
for (const child of element.childNodes) {
|
|
680
|
+
if (child.nodeType === Node.TEXT_NODE && child.textContent && child.textContent.trim()) {
|
|
681
|
+
textContent = child.textContent.trim().substring(0, 50)
|
|
682
|
+
break
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (textContent && textContent.length >= 50) {
|
|
687
|
+
textContent += '...'
|
|
688
|
+
}
|
|
548
689
|
|
|
549
690
|
return {
|
|
550
691
|
url: result.file,
|
|
551
692
|
line: result.line || undefined,
|
|
552
693
|
column: result.column || undefined,
|
|
553
694
|
selector: selector,
|
|
695
|
+
text: textContent,
|
|
554
696
|
hint: 'File: ' + result.file
|
|
555
697
|
}
|
|
556
698
|
}
|
|
@@ -683,12 +825,12 @@ var CLIENT_SCRIPT = `
|
|
|
683
825
|
}
|
|
684
826
|
|
|
685
827
|
// Try React DevTools - handle React 18's randomized suffix
|
|
686
|
-
const elKeys = Object.keys(
|
|
828
|
+
const elKeys = Object.keys(element)
|
|
687
829
|
let fiberKey = elKeys.find(k => k === '__reactFiber__' || k === '__reactInternalInstance' || k.indexOf('__reactFiber') === 0)
|
|
688
830
|
let propsKey = elKeys.find(k => k === '__reactProps__' || k.indexOf('__reactProps') === 0)
|
|
689
831
|
|
|
690
832
|
if (fiberKey) {
|
|
691
|
-
const fiber =
|
|
833
|
+
const fiber = element[fiberKey]
|
|
692
834
|
console.log('[Claude Dev Server] Found fiber at key:', fiberKey)
|
|
693
835
|
|
|
694
836
|
// Log fiber structure for debugging
|
|
@@ -836,7 +978,7 @@ var CLIENT_SCRIPT = `
|
|
|
836
978
|
|
|
837
979
|
// Try Vue component
|
|
838
980
|
if (!sourceFile) {
|
|
839
|
-
const vueComponent =
|
|
981
|
+
const vueComponent = element.__vueParentComponent || element.__vnode
|
|
840
982
|
if (vueComponent) {
|
|
841
983
|
const type = vueComponent.type || vueComponent.component
|
|
842
984
|
if (type && type.__file) {
|
|
@@ -849,7 +991,7 @@ var CLIENT_SCRIPT = `
|
|
|
849
991
|
// Try Vite's HMR source map
|
|
850
992
|
if (!sourceFile) {
|
|
851
993
|
// Look for data-vite-dev-id or similar attributes
|
|
852
|
-
const viteId =
|
|
994
|
+
const viteId = element.getAttribute('data-vite-dev-id')
|
|
853
995
|
if (viteId) {
|
|
854
996
|
// Extract file path from viteId (remove query params if any)
|
|
855
997
|
const queryIndex = viteId.indexOf('?')
|
|
@@ -860,22 +1002,22 @@ var CLIENT_SCRIPT = `
|
|
|
860
1002
|
|
|
861
1003
|
// Check for inline script
|
|
862
1004
|
if (!sourceFile) {
|
|
863
|
-
const inlineScripts =
|
|
1005
|
+
const inlineScripts = iframeDoc.querySelectorAll('script:not([src])')
|
|
864
1006
|
if (inlineScripts.length > 0) {
|
|
865
1007
|
const pathParts = window.location.pathname.split('/')
|
|
866
1008
|
const fileName = pathParts[pathParts.length - 1] || 'index.html'
|
|
867
1009
|
sourceFile = '/' + fileName
|
|
868
1010
|
|
|
869
|
-
const html =
|
|
870
|
-
const elId =
|
|
871
|
-
const elClass =
|
|
1011
|
+
const html = iframeDoc.documentElement.outerHTML
|
|
1012
|
+
const elId = element.id ? element.id : ''
|
|
1013
|
+
const elClass = element.className ? element.className : ''
|
|
872
1014
|
let elPattern
|
|
873
1015
|
if (elId) {
|
|
874
1016
|
elPattern = 'id="' + elId + '"'
|
|
875
1017
|
} else if (elClass) {
|
|
876
1018
|
elPattern = 'class="' + elClass.split(' ')[0] + '"'
|
|
877
1019
|
} else {
|
|
878
|
-
elPattern =
|
|
1020
|
+
elPattern = element.tagName.toLowerCase()
|
|
879
1021
|
}
|
|
880
1022
|
|
|
881
1023
|
const matchIndex = html.indexOf(elPattern)
|
|
@@ -888,7 +1030,7 @@ var CLIENT_SCRIPT = `
|
|
|
888
1030
|
|
|
889
1031
|
// Find main script
|
|
890
1032
|
if (!sourceFile) {
|
|
891
|
-
const scripts =
|
|
1033
|
+
const scripts = iframeDoc.querySelectorAll('script[src]')
|
|
892
1034
|
for (const script of scripts) {
|
|
893
1035
|
const src = script.getAttribute('src')
|
|
894
1036
|
if (src && !src.includes('cdn') && !src.includes('node_modules') &&
|
|
@@ -906,155 +1048,227 @@ var CLIENT_SCRIPT = `
|
|
|
906
1048
|
sourceFile = '/' + fileName
|
|
907
1049
|
}
|
|
908
1050
|
|
|
909
|
-
const elClassName =
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1051
|
+
const elClassName = element.className ? String(element.className) : ''
|
|
1052
|
+
// Limit classnames to first 2-3 to avoid overly long selectors
|
|
1053
|
+
const classNames = elClassName.split(' ').filter(c => c).slice(0, 3)
|
|
1054
|
+
const selector = element.tagName.toLowerCase() +
|
|
1055
|
+
(element.id ? '#' + element.id : '') +
|
|
1056
|
+
(classNames.length ? '.' + classNames.join('.') : '')
|
|
1057
|
+
|
|
1058
|
+
// Get text content for better context
|
|
1059
|
+
let textContent = ''
|
|
1060
|
+
if (element.nodeType === Node.TEXT_NODE) {
|
|
1061
|
+
textContent = element.textContent ? element.textContent.trim().substring(0, 50) : ''
|
|
1062
|
+
} else if (element.childNodes.length === 1 && element.childNodes[0].nodeType === Node.TEXT_NODE) {
|
|
1063
|
+
textContent = element.childNodes[0].textContent ? element.childNodes[0].textContent.trim().substring(0, 50) : ''
|
|
1064
|
+
} else {
|
|
1065
|
+
// Try to get first text node from children
|
|
1066
|
+
for (const child of element.childNodes) {
|
|
1067
|
+
if (child.nodeType === Node.TEXT_NODE && child.textContent && child.textContent.trim()) {
|
|
1068
|
+
textContent = child.textContent.trim().substring(0, 50)
|
|
1069
|
+
break
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (textContent && textContent.length >= 50) {
|
|
1074
|
+
textContent += '...'
|
|
1075
|
+
}
|
|
913
1076
|
|
|
914
1077
|
return {
|
|
915
1078
|
url: sourceFile,
|
|
916
1079
|
line: sourceLine,
|
|
917
1080
|
column: sourceColumn,
|
|
918
1081
|
selector: selector,
|
|
1082
|
+
text: textContent,
|
|
919
1083
|
hint: sourceFile ? 'File: ' + sourceFile : 'Element: ' + selector
|
|
920
1084
|
}
|
|
921
1085
|
}
|
|
922
1086
|
|
|
923
|
-
function
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
//
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1087
|
+
function setupDraggable(divider) {
|
|
1088
|
+
let startX = 0
|
|
1089
|
+
let startWidth = 0
|
|
1090
|
+
let containerWidth = 0
|
|
1091
|
+
|
|
1092
|
+
// Remove existing listeners to avoid duplicates
|
|
1093
|
+
divider.removeEventListener('mousedown', startDrag)
|
|
1094
|
+
divider.removeEventListener('touchstart', startDrag)
|
|
1095
|
+
|
|
1096
|
+
divider.addEventListener('mousedown', startDrag)
|
|
1097
|
+
divider.addEventListener('touchstart', startDrag)
|
|
1098
|
+
|
|
1099
|
+
function startDrag(e) {
|
|
1100
|
+
isDragging = true
|
|
1101
|
+
divider.classList.add('dragging')
|
|
1102
|
+
|
|
1103
|
+
// Disable pointer events on iframes to prevent interference
|
|
1104
|
+
const iframes = document.querySelectorAll('iframe')
|
|
1105
|
+
iframes.forEach(iframe => iframe.style.pointerEvents = 'none')
|
|
1106
|
+
|
|
1107
|
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX
|
|
1108
|
+
startX = clientX
|
|
1109
|
+
|
|
1110
|
+
const container = document.querySelector('.claude-dev-server-container')
|
|
1111
|
+
const leftPanelEl = document.querySelector('.claude-dev-server-left')
|
|
1112
|
+
|
|
1113
|
+
if (container && leftPanelEl) {
|
|
1114
|
+
containerWidth = container.offsetWidth
|
|
1115
|
+
startWidth = leftPanelEl.offsetWidth
|
|
941
1116
|
}
|
|
942
|
-
|
|
1117
|
+
|
|
1118
|
+
e.preventDefault()
|
|
943
1119
|
}
|
|
944
1120
|
|
|
945
|
-
|
|
946
|
-
if (!
|
|
947
|
-
|
|
948
|
-
//
|
|
949
|
-
if (
|
|
950
|
-
|
|
1121
|
+
function drag(e) {
|
|
1122
|
+
if (!isDragging) return
|
|
1123
|
+
|
|
1124
|
+
// Safety check: if mouse button is not pressed, end drag
|
|
1125
|
+
if (e.buttons === 0 && !e.touches) {
|
|
1126
|
+
endDrag()
|
|
1127
|
+
return
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX
|
|
1131
|
+
const deltaX = clientX - startX
|
|
1132
|
+
|
|
1133
|
+
// Calculate new width in pixels, then convert to percentage
|
|
1134
|
+
let newWidth = startWidth + deltaX
|
|
1135
|
+
let percentage = (newWidth / containerWidth) * 100
|
|
1136
|
+
|
|
1137
|
+
// Clamp between 20% and 80%
|
|
1138
|
+
const clamped = Math.max(20, Math.min(80, percentage))
|
|
1139
|
+
|
|
1140
|
+
const leftPanelEl = document.querySelector('.claude-dev-server-left')
|
|
1141
|
+
if (leftPanelEl) {
|
|
1142
|
+
leftPanelEl.style.flex = 'none'
|
|
1143
|
+
leftPanelEl.style.width = clamped + '%'
|
|
951
1144
|
}
|
|
952
1145
|
}
|
|
953
1146
|
|
|
954
|
-
|
|
955
|
-
if (
|
|
956
|
-
|
|
957
|
-
|
|
1147
|
+
function endDrag() {
|
|
1148
|
+
if (isDragging) {
|
|
1149
|
+
isDragging = false
|
|
1150
|
+
divider.classList.remove('dragging')
|
|
958
1151
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
const location = await getSourceLocation(el)
|
|
963
|
-
if (location) {
|
|
964
|
-
await inspectElement(location, el)
|
|
965
|
-
}
|
|
966
|
-
disableInspectMode()
|
|
1152
|
+
// Re-enable pointer events on iframes
|
|
1153
|
+
const iframes = document.querySelectorAll('iframe')
|
|
1154
|
+
iframes.forEach(iframe => iframe.style.pointerEvents = '')
|
|
967
1155
|
}
|
|
968
1156
|
}
|
|
969
1157
|
|
|
970
|
-
|
|
971
|
-
|
|
1158
|
+
// Use capture phase to ensure events are caught
|
|
1159
|
+
// Remove old listeners first to prevent duplicates
|
|
1160
|
+
document.removeEventListener('mousemove', drag, true)
|
|
1161
|
+
document.removeEventListener('touchmove', drag, true)
|
|
1162
|
+
document.removeEventListener('mouseup', endDrag, true)
|
|
1163
|
+
document.removeEventListener('touchend', endDrag, true)
|
|
1164
|
+
|
|
1165
|
+
document.addEventListener('mousemove', drag, true)
|
|
1166
|
+
document.addEventListener('touchmove', drag, { passive: false, capture: true })
|
|
1167
|
+
document.addEventListener('mouseup', endDrag, true)
|
|
1168
|
+
document.addEventListener('touchend', endDrag, true)
|
|
1169
|
+
}
|
|
972
1170
|
|
|
973
|
-
|
|
1171
|
+
function createOverlay() {
|
|
1172
|
+
if (overlay) return
|
|
1173
|
+
overlay = document.createElement('div')
|
|
1174
|
+
overlay.className = 'claude-dev-server-inspect-overlay'
|
|
1175
|
+
document.body.appendChild(overlay)
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function enableInspectMode() {
|
|
1179
|
+
isInspectMode = true
|
|
1180
|
+
if (overlay) overlay.classList.add('active')
|
|
1181
|
+
// Setup inspect listeners on the iframe
|
|
1182
|
+
setupIframeInspectListeners()
|
|
1183
|
+
// Change cursor in iframe
|
|
1184
|
+
if (devIframe && devIframe.contentDocument && devIframe.contentDocument.body) {
|
|
1185
|
+
devIframe.contentDocument.body.style.cursor = 'crosshair'
|
|
1186
|
+
}
|
|
1187
|
+
const btn = document.querySelector('.claude-dev-server-btn-inspect')
|
|
1188
|
+
if (btn) btn.classList.add('active')
|
|
974
1189
|
}
|
|
975
1190
|
|
|
976
1191
|
function disableInspectMode() {
|
|
977
1192
|
isInspectMode = false
|
|
978
|
-
if (overlay)
|
|
979
|
-
|
|
980
|
-
|
|
1193
|
+
if (overlay) {
|
|
1194
|
+
overlay.classList.remove('active')
|
|
1195
|
+
overlay.innerHTML = ''
|
|
1196
|
+
}
|
|
1197
|
+
// Restore cursor in iframe
|
|
1198
|
+
if (devIframe && devIframe.contentDocument && devIframe.contentDocument.body) {
|
|
1199
|
+
devIframe.contentDocument.body.style.cursor = ''
|
|
1200
|
+
}
|
|
1201
|
+
const btn = document.querySelector('.claude-dev-server-btn-inspect')
|
|
1202
|
+
if (btn) btn.classList.remove('active')
|
|
981
1203
|
|
|
982
|
-
//
|
|
983
|
-
|
|
1204
|
+
// Remove inspect listeners to free up resources
|
|
1205
|
+
removeIframeInspectListeners()
|
|
1206
|
+
}
|
|
984
1207
|
|
|
985
|
-
|
|
986
|
-
if (
|
|
1208
|
+
function removeIframeInspectListeners() {
|
|
1209
|
+
if (!devIframe || !devIframe.contentDocument) return
|
|
987
1210
|
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1211
|
+
const iframeDoc = devIframe.contentDocument
|
|
1212
|
+
const existingHandler = iframeDoc._claudeInspectHandler
|
|
1213
|
+
|
|
1214
|
+
if (existingHandler) {
|
|
1215
|
+
iframeDoc.removeEventListener('click', existingHandler, true)
|
|
1216
|
+
iframeDoc.removeEventListener('mousemove', existingHandler, true)
|
|
1217
|
+
delete iframeDoc._claudeInspectHandler
|
|
1218
|
+
inspectListenersRegistered = false
|
|
992
1219
|
}
|
|
993
1220
|
}
|
|
994
1221
|
|
|
995
|
-
async function
|
|
996
|
-
//
|
|
1222
|
+
async function sendToTerminal(location) {
|
|
1223
|
+
// Use format: @filename <selector> "text content" (without line number)
|
|
997
1224
|
const filePath = location.url || location.file || 'unknown'
|
|
998
|
-
const tagName = el.tagName ? el.tagName.toLowerCase() : ''
|
|
999
|
-
const id = el.id ? '#' + el.id : ''
|
|
1000
|
-
|
|
1001
|
-
// \u5BF9\u4E8E Tailwind CSS \u7B49\u5927\u91CF class \u7684\u60C5\u51B5\uFF0C\u53EA\u53D6\u524D 2-3 \u4E2A class
|
|
1002
|
-
let className = ''
|
|
1003
|
-
if (el.className) {
|
|
1004
|
-
const classes = String(el.className).split(' ').filter(c => c)
|
|
1005
|
-
// \u5982\u679C class \u592A\u591A\uFF08\u53EF\u80FD\u662F Tailwind\uFF09\uFF0C\u53EA\u53D6\u524D 2 \u4E2A
|
|
1006
|
-
if (classes.length > 5) {
|
|
1007
|
-
className = '.' + classes.slice(0, 2).join('.')
|
|
1008
|
-
} else {
|
|
1009
|
-
className = '.' + classes.join('.')
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
1225
|
|
|
1013
|
-
|
|
1226
|
+
// Build selector - handle Tailwind CSS classes by limiting
|
|
1227
|
+
const tagName = location.selector ? location.selector.split(/[.#]/)[0] : 'div'
|
|
1228
|
+
let selector = location.selector || tagName
|
|
1014
1229
|
|
|
1015
|
-
//
|
|
1230
|
+
// Get text content for better context
|
|
1016
1231
|
let textContent = ''
|
|
1017
|
-
if (
|
|
1018
|
-
textContent =
|
|
1019
|
-
} else if (
|
|
1020
|
-
|
|
1232
|
+
if (location.text) {
|
|
1233
|
+
textContent = location.text
|
|
1234
|
+
} else if (location.hint && location.hint.includes('File:')) {
|
|
1235
|
+
// No text content available
|
|
1021
1236
|
}
|
|
1022
1237
|
|
|
1023
|
-
//
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1035
|
-
console.log('[Claude Dev Server] Skipping node_modules path:', filePath)
|
|
1036
|
-
} else {
|
|
1037
|
-
const linePart = location.line ? \`:\${location.line}\` : ''
|
|
1038
|
-
prompt = \`@\${filePath}\${linePart} <\${selector}>\`
|
|
1039
|
-
if (textContent) {
|
|
1040
|
-
prompt += \` "\${textContent}"\`
|
|
1041
|
-
}
|
|
1238
|
+
// Format: @filename <selector> "text content" (no line number)
|
|
1239
|
+
let prompt = \`@\${filePath} <\${selector}>\`
|
|
1240
|
+
if (textContent) {
|
|
1241
|
+
prompt += \` "\${textContent}"\`
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
console.log('[Claude Dev Server] Sending to terminal:', prompt)
|
|
1245
|
+
|
|
1246
|
+
// Store the interval ID so we can clear it if needed
|
|
1247
|
+
if (!window._claudeSendRetryInterval) {
|
|
1248
|
+
window._claudeSendRetryInterval = null
|
|
1042
1249
|
}
|
|
1043
1250
|
|
|
1044
|
-
//
|
|
1045
|
-
|
|
1046
|
-
|
|
1251
|
+
// Clear any existing retry interval
|
|
1252
|
+
if (window._claudeSendRetryInterval) {
|
|
1253
|
+
clearInterval(window._claudeSendRetryInterval)
|
|
1254
|
+
window._claudeSendRetryInterval = null
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// Send to terminal with retry logic
|
|
1258
|
+
const sendToTerminalInternal = () => {
|
|
1259
|
+
if (!ttydIframe || !ttydIframe.contentWindow) {
|
|
1047
1260
|
return false
|
|
1048
1261
|
}
|
|
1049
1262
|
|
|
1050
|
-
const win =
|
|
1051
|
-
|
|
1052
|
-
// Check if sendToTerminal function is available
|
|
1263
|
+
const win = ttydIframe.contentWindow
|
|
1053
1264
|
if (win.sendToTerminal) {
|
|
1054
1265
|
win.sendToTerminal(prompt)
|
|
1055
1266
|
console.log('[Claude Dev Server] Sent via sendToTerminal:', prompt)
|
|
1056
|
-
//
|
|
1057
|
-
|
|
1267
|
+
// Clear interval on success
|
|
1268
|
+
if (window._claudeSendRetryInterval) {
|
|
1269
|
+
clearInterval(window._claudeSendRetryInterval)
|
|
1270
|
+
window._claudeSendRetryInterval = null
|
|
1271
|
+
}
|
|
1058
1272
|
return true
|
|
1059
1273
|
}
|
|
1060
1274
|
|
|
@@ -1062,17 +1276,18 @@ var CLIENT_SCRIPT = `
|
|
|
1062
1276
|
}
|
|
1063
1277
|
|
|
1064
1278
|
// Try immediately
|
|
1065
|
-
if (
|
|
1279
|
+
if (sendToTerminalInternal()) {
|
|
1066
1280
|
return
|
|
1067
1281
|
}
|
|
1068
1282
|
|
|
1069
1283
|
// If not ready, wait and retry
|
|
1070
1284
|
let attempts = 0
|
|
1071
1285
|
const maxAttempts = 50
|
|
1072
|
-
|
|
1286
|
+
window._claudeSendRetryInterval = setInterval(() => {
|
|
1073
1287
|
attempts++
|
|
1074
|
-
if (
|
|
1075
|
-
clearInterval(
|
|
1288
|
+
if (sendToTerminalInternal() || attempts >= maxAttempts) {
|
|
1289
|
+
clearInterval(window._claudeSendRetryInterval)
|
|
1290
|
+
window._claudeSendRetryInterval = null
|
|
1076
1291
|
if (attempts >= maxAttempts) {
|
|
1077
1292
|
console.warn('[Claude Dev Server] Could not send to terminal after retries')
|
|
1078
1293
|
}
|
|
@@ -1081,93 +1296,12 @@ var CLIENT_SCRIPT = `
|
|
|
1081
1296
|
}
|
|
1082
1297
|
|
|
1083
1298
|
function loadTerminalIframe(ttydUrl) {
|
|
1084
|
-
if (!
|
|
1299
|
+
if (!ttydIframe) return
|
|
1085
1300
|
ttydWsUrl = ttydUrl
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
terminalIframe = document.createElement('iframe')
|
|
1089
|
-
// Pass the ttyd WebSocket URL as query param
|
|
1090
|
-
terminalIframe.src = '/ttyd/index.html?ws=' + encodeURIComponent(ttydUrl)
|
|
1091
|
-
terminalIframe.allow = 'clipboard-read; clipboard-write'
|
|
1092
|
-
|
|
1093
|
-
// Load event - iframe is ready
|
|
1094
|
-
terminalIframe.onload = () => {
|
|
1301
|
+
ttydIframe.src = '/ttyd/index.html?ws=' + encodeURIComponent(ttydUrl)
|
|
1302
|
+
ttydIframe.onload = () => {
|
|
1095
1303
|
console.log('[Claude Dev Server] Terminal iframe loaded')
|
|
1096
1304
|
}
|
|
1097
|
-
|
|
1098
|
-
terminalContainer.appendChild(terminalIframe)
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
function createPanel() {
|
|
1102
|
-
if (panel) return
|
|
1103
|
-
|
|
1104
|
-
panel = document.createElement('div')
|
|
1105
|
-
panel.className = 'claude-dev-server-panel'
|
|
1106
|
-
panel.innerHTML = \`
|
|
1107
|
-
<div class="claude-dev-server-header">
|
|
1108
|
-
<span class="claude-dev-server-title">
|
|
1109
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none">
|
|
1110
|
-
<path d="M11.376 24L10.776 23.544L10.44 22.8L10.776 21.312L11.16 19.392L11.472 17.856L11.76 15.96L11.928 15.336L11.904 15.288L11.784 15.312L10.344 17.28L8.16 20.232L6.432 22.056L6.024 22.224L5.304 21.864L5.376 21.192L5.784 20.616L8.16 17.568L9.6 15.672L10.536 14.592L10.512 14.448H10.464L4.128 18.576L3 18.72L2.496 18.264L2.568 17.52L2.808 17.28L4.704 15.96L9.432 13.32L9.504 13.08L9.432 12.96H9.192L8.4 12.912L5.712 12.84L3.384 12.744L1.104 12.624L0.528 12.504L0 11.784L0.048 11.424L0.528 11.112L1.224 11.16L2.736 11.28L5.016 11.424L6.672 11.52L9.12 11.784H9.504L9.552 11.616L9.432 11.52L9.336 11.424L6.96 9.84L4.416 8.16L3.072 7.176L2.352 6.672L1.992 6.216L1.848 5.208L2.496 4.488L3.384 4.56L3.6 4.608L4.488 5.304L6.384 6.768L8.88 8.616L9.24 8.904L9.408 8.808V8.736L9.24 8.472L7.896 6.024L6.456 3.528L5.808 2.496L5.64 1.872C5.576 1.656 5.544 1.416 5.544 1.152L6.288 0.144001L6.696 0L7.704 0.144001L8.112 0.504001L8.736 1.92L9.72 4.152L11.28 7.176L11.736 8.088L11.976 8.904L12.072 9.168H12.24V9.024L12.36 7.296L12.6 5.208L12.84 2.52L12.912 1.752L13.296 0.840001L14.04 0.360001L14.616 0.624001L15.096 1.32L15.024 1.752L14.76 3.6L14.184 6.504L13.824 8.472H14.04L14.28 8.208L15.264 6.912L16.92 4.848L17.64 4.032L18.504 3.12L19.056 2.688H20.088L20.832 3.816L20.496 4.992L19.44 6.336L18.552 7.464L17.28 9.168L16.512 10.536L16.584 10.632H16.752L19.608 10.008L21.168 9.744L22.992 9.432L23.832 9.816L23.928 10.2L23.592 11.016L21.624 11.496L19.32 11.952L15.888 12.768L15.84 12.792L15.888 12.864L17.424 13.008L18.096 13.056H19.728L22.752 13.272L23.544 13.8L24 14.424L23.928 14.928L22.704 15.528L21.072 15.144L17.232 14.232L15.936 13.92H15.744V14.016L16.848 15.096L18.84 16.896L21.36 19.224L21.48 19.8L21.168 20.28L20.832 20.232L18.624 18.552L17.76 17.808L15.84 16.2H15.72V16.368L16.152 17.016L18.504 20.544L18.624 21.624L18.456 21.96L17.832 22.176L17.184 22.056L15.792 20.136L14.376 17.952L13.224 16.008L13.104 16.104L12.408 23.352L12.096 23.712L11.376 24Z" fill="#d97757"/>
|
|
1111
|
-
</svg>
|
|
1112
|
-
Claude Code
|
|
1113
|
-
</span>
|
|
1114
|
-
<div class="claude-dev-server-actions">
|
|
1115
|
-
<button class="claude-dev-server-btn claude-dev-server-btn-inspect" title="Inspect Element">
|
|
1116
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1117
|
-
<path d="M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7"/>
|
|
1118
|
-
<path d="M12 12l4.166 10 1.48-4.355L22 16.166 12 12z"/>
|
|
1119
|
-
<path d="M18 18l3 3"/>
|
|
1120
|
-
</svg>
|
|
1121
|
-
Inspect
|
|
1122
|
-
</button>
|
|
1123
|
-
</div>
|
|
1124
|
-
<button class="claude-dev-server-close" aria-label="Close" title="Minimize to icon">×</button>
|
|
1125
|
-
</div>
|
|
1126
|
-
<div class="claude-dev-server-terminal"></div>
|
|
1127
|
-
\`
|
|
1128
|
-
|
|
1129
|
-
document.body.appendChild(panel)
|
|
1130
|
-
|
|
1131
|
-
terminalContainer = panel.querySelector('.claude-dev-server-terminal')
|
|
1132
|
-
const closeBtn = panel.querySelector('.claude-dev-server-close')
|
|
1133
|
-
const inspectBtn = panel.querySelector('.claude-dev-server-btn-inspect')
|
|
1134
|
-
|
|
1135
|
-
closeBtn && closeBtn.addEventListener('click', () => togglePanel(false))
|
|
1136
|
-
|
|
1137
|
-
inspectBtn && inspectBtn.addEventListener('click', () => {
|
|
1138
|
-
if (isInspectMode) {
|
|
1139
|
-
disableInspectMode()
|
|
1140
|
-
} else {
|
|
1141
|
-
// \u70B9\u51FB Inspect \u65F6\u5148\u6536\u8D77\u9762\u677F
|
|
1142
|
-
if (isOpen) {
|
|
1143
|
-
togglePanel(false)
|
|
1144
|
-
}
|
|
1145
|
-
enableInspectMode()
|
|
1146
|
-
}
|
|
1147
|
-
})
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
function togglePanel(force) {
|
|
1151
|
-
createPanel()
|
|
1152
|
-
if (typeof force === 'boolean') {
|
|
1153
|
-
isOpen = force
|
|
1154
|
-
} else {
|
|
1155
|
-
isOpen = !isOpen
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
if (panel) panel.classList.toggle('open', isOpen)
|
|
1159
|
-
if (toggleBtn) toggleBtn.classList.toggle('hidden', isOpen)
|
|
1160
|
-
|
|
1161
|
-
if (isOpen) {
|
|
1162
|
-
// Focus iframe when panel opens
|
|
1163
|
-
if (terminalIframe && terminalIframe.contentWindow) {
|
|
1164
|
-
setTimeout(() => {
|
|
1165
|
-
if (terminalIframe.contentWindow.terminalInstance) {
|
|
1166
|
-
terminalIframe.contentWindow.terminalInstance.focus()
|
|
1167
|
-
}
|
|
1168
|
-
}, 100)
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
1305
|
}
|
|
1172
1306
|
|
|
1173
1307
|
function connect(port) {
|
|
@@ -1184,10 +1318,6 @@ var CLIENT_SCRIPT = `
|
|
|
1184
1318
|
console.log('[Claude Dev Server] Received message:', msg.type, msg)
|
|
1185
1319
|
if (msg.type === 'ready' && msg.ttydUrl) {
|
|
1186
1320
|
loadTerminalIframe(msg.ttydUrl)
|
|
1187
|
-
} else if (msg.type === 'inspectResult') {
|
|
1188
|
-
if (msg.location) {
|
|
1189
|
-
showContextPanel(msg.location, null, false)
|
|
1190
|
-
}
|
|
1191
1321
|
}
|
|
1192
1322
|
} catch (err) {
|
|
1193
1323
|
console.error('[Claude Dev Server] Message parse error:', err)
|
|
@@ -1207,12 +1337,13 @@ var CLIENT_SCRIPT = `
|
|
|
1207
1337
|
initWhenReady()
|
|
1208
1338
|
|
|
1209
1339
|
document.addEventListener('keydown', (e) => {
|
|
1210
|
-
if ((e.metaKey || e.ctrlKey) && e.
|
|
1340
|
+
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'I') {
|
|
1211
1341
|
e.preventDefault()
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1342
|
+
if (isInspectMode) {
|
|
1343
|
+
disableInspectMode()
|
|
1344
|
+
} else {
|
|
1345
|
+
enableInspectMode()
|
|
1346
|
+
}
|
|
1216
1347
|
}
|
|
1217
1348
|
if (e.key === 'Escape' && isInspectMode) {
|
|
1218
1349
|
disableInspectMode()
|
|
@@ -1501,21 +1632,59 @@ async function handleTurbopackLookup(projectRoot, pagePath, targetPort) {
|
|
|
1501
1632
|
return { error: String(err) };
|
|
1502
1633
|
}
|
|
1503
1634
|
}
|
|
1635
|
+
function isHtmlPageRequest(req) {
|
|
1636
|
+
const accept = req.headers.accept || "";
|
|
1637
|
+
const url = req.url || "";
|
|
1638
|
+
if (!accept.includes("text/html")) {
|
|
1639
|
+
return false;
|
|
1640
|
+
}
|
|
1641
|
+
if (url.startsWith("/@") || url.startsWith("/_next/") || url.startsWith("/ttyd") || url.startsWith("/dev.html")) {
|
|
1642
|
+
return false;
|
|
1643
|
+
}
|
|
1644
|
+
return true;
|
|
1645
|
+
}
|
|
1504
1646
|
function createProxyServer(targetPort, wsPort, projectRoot) {
|
|
1505
1647
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
1506
1648
|
const assetsPath = join(moduleDir, "assets");
|
|
1507
1649
|
let ttydHtml;
|
|
1508
1650
|
let ttydBridgeJs;
|
|
1651
|
+
let devHtml;
|
|
1509
1652
|
try {
|
|
1510
1653
|
ttydHtml = readFileSync(join(assetsPath, "ttyd-terminal.html"), "utf-8");
|
|
1511
1654
|
ttydBridgeJs = readFileSync(join(assetsPath, "ttyd-bridge.js"), "utf-8");
|
|
1655
|
+
devHtml = readFileSync(join(assetsPath, "dev.html"), "utf-8");
|
|
1512
1656
|
} catch (e) {
|
|
1513
|
-
console.error("[Claude Dev Server] Failed to read
|
|
1657
|
+
console.error("[Claude Dev Server] Failed to read assets from", assetsPath);
|
|
1514
1658
|
console.error("[Claude Dev Server] moduleDir:", moduleDir);
|
|
1515
1659
|
console.error("[Claude Dev Server] Error:", e.message);
|
|
1516
|
-
throw new Error("
|
|
1660
|
+
throw new Error("Assets not found. Please run `npm run build` first.");
|
|
1517
1661
|
}
|
|
1518
1662
|
const server = http.createServer((req, res) => {
|
|
1663
|
+
const referer = req.headers.referer || "";
|
|
1664
|
+
const isFromDevPage = referer.includes("dev.html");
|
|
1665
|
+
if (req.url?.startsWith("/dev.html")) {
|
|
1666
|
+
const urlParams = new URL(req.url || "", `http://${req.headers.host}`);
|
|
1667
|
+
const originalPath = urlParams.searchParams.get("path") || "/";
|
|
1668
|
+
const host = req.headers.host || "localhost:3000";
|
|
1669
|
+
const origin = `http://${host}`;
|
|
1670
|
+
const modifiedDevHtml = devHtml.replace(
|
|
1671
|
+
/__CLAUDE_IFRAME_SRC__/g,
|
|
1672
|
+
`${origin}${originalPath}`
|
|
1673
|
+
).replace(
|
|
1674
|
+
/__CLAUDE_ORIGINAL_PATH__/g,
|
|
1675
|
+
originalPath
|
|
1676
|
+
);
|
|
1677
|
+
res.setHeader("Content-Type", "text/html");
|
|
1678
|
+
res.end(modifiedDevHtml);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
if (!isFromDevPage && isHtmlPageRequest(req)) {
|
|
1682
|
+
const currentPath = req.url || "/";
|
|
1683
|
+
const devPageUrl = `/dev.html?path=${encodeURIComponent(currentPath)}`;
|
|
1684
|
+
res.writeHead(302, { "Location": devPageUrl });
|
|
1685
|
+
res.end();
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1519
1688
|
if (req.url === "/@claude-port") {
|
|
1520
1689
|
res.setHeader("Content-Type", "application/json");
|
|
1521
1690
|
res.end(JSON.stringify({ port: wsPort }));
|
|
@@ -1577,6 +1746,7 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
|
|
|
1577
1746
|
}
|
|
1578
1747
|
const proxyHeaders = { ...req.headers };
|
|
1579
1748
|
delete proxyHeaders["accept-encoding"];
|
|
1749
|
+
const shouldInject = !isFromDevPage;
|
|
1580
1750
|
const options = {
|
|
1581
1751
|
hostname: "localhost",
|
|
1582
1752
|
port: targetPort,
|
|
@@ -1585,7 +1755,7 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
|
|
|
1585
1755
|
headers: proxyHeaders
|
|
1586
1756
|
};
|
|
1587
1757
|
const proxyReq = http.request(options, (proxyRes) => {
|
|
1588
|
-
if (proxyRes.headers["content-type"]?.includes("text/html")) {
|
|
1758
|
+
if (proxyRes.headers["content-type"]?.includes("text/html") && shouldInject) {
|
|
1589
1759
|
const body = [];
|
|
1590
1760
|
proxyRes.on("data", (chunk) => body.push(chunk));
|
|
1591
1761
|
proxyRes.on("end", () => {
|
|
@@ -1667,20 +1837,33 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
|
|
|
1667
1837
|
return server;
|
|
1668
1838
|
}
|
|
1669
1839
|
function injectScripts(html, wsPort, projectRoot) {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
);
|
|
1840
|
+
if (html.includes("claude-dev-server-container") || html.includes("__CLAUDE_ORIGINAL_HTML__")) {
|
|
1841
|
+
console.log("[Claude Dev Server] HTML already injected, returning as-is");
|
|
1842
|
+
return html;
|
|
1843
|
+
}
|
|
1844
|
+
const hasOurScripts = html.includes("CLIENT_SCRIPT") || html.includes("claude-dev-server-container");
|
|
1845
|
+
if (hasOurScripts) {
|
|
1846
|
+
console.log("[Claude Dev Server] Warning: Original HTML already has our scripts");
|
|
1847
|
+
}
|
|
1848
|
+
const modifiedHtml = html;
|
|
1849
|
+
console.log("[Claude Dev Server] Creating split layout, original HTML length:", html.length);
|
|
1850
|
+
const base64Html = Buffer.from(modifiedHtml, "utf-8").toString("base64");
|
|
1851
|
+
const projectRootScript = `<script>window.__CLAUDE_PROJECT_ROOT__ = ${JSON.stringify(projectRoot)};window.__CLAUDE_ORIGINAL_HTML_BASE64__ = "${base64Html}";</script>`;
|
|
1852
|
+
return `<!DOCTYPE html>
|
|
1853
|
+
<html lang="en">
|
|
1854
|
+
<head>
|
|
1855
|
+
<meta charset="UTF-8">
|
|
1856
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1857
|
+
<title>Claude Dev Server</title>
|
|
1858
|
+
${CLIENT_STYLES}
|
|
1859
|
+
${projectRootScript}
|
|
1860
|
+
</head>
|
|
1861
|
+
<body>
|
|
1862
|
+
<script type="module">${CLIENT_SCRIPT.replace(/wsPort:\s*\d+/, `wsPort: ${wsPort}`)}</script>
|
|
1863
|
+
</body>
|
|
1864
|
+
</html>`;
|
|
1682
1865
|
}
|
|
1683
1866
|
|
|
1684
1867
|
export { startUniversalServer };
|
|
1685
|
-
//# sourceMappingURL=chunk-
|
|
1686
|
-
//# sourceMappingURL=chunk-
|
|
1868
|
+
//# sourceMappingURL=chunk-PAE5WTS2.js.map
|
|
1869
|
+
//# sourceMappingURL=chunk-PAE5WTS2.js.map
|