claude-dev-server 1.0.2

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,1166 @@
1
+ import { spawn } from 'child_process';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { dirname, join, resolve } from 'path';
4
+ import http from 'http';
5
+ import { fileURLToPath } from 'url';
6
+ import { WebSocketServer } from 'ws';
7
+ import { SourceMapConsumer } from 'source-map';
8
+ import { readFile } from 'fs/promises';
9
+
10
+ // src/universal/index.ts
11
+ function spawnClaudeCode(options) {
12
+ const port = 5e4 + Math.floor(Math.random() * 1e4);
13
+ const ttydProc = spawn("ttyd", [
14
+ "--port",
15
+ String(port),
16
+ "--interface",
17
+ "127.0.0.1",
18
+ "--writable",
19
+ options.claudePath,
20
+ ...options.args
21
+ ], {
22
+ cwd: options.cwd,
23
+ env: {
24
+ ...process.env,
25
+ ...options.env,
26
+ TERM: "xterm-256color",
27
+ FORCE_COLOR: "1"
28
+ }
29
+ });
30
+ ttydProc.on("exit", (code) => {
31
+ console.log(`[claude-dev-server] ttyd exited - code: ${code}`);
32
+ });
33
+ ttydProc.on("error", (err) => {
34
+ console.error(`[claude-dev-server] ttyd error: ${err.message}`);
35
+ });
36
+ return new Promise((resolve2, reject) => {
37
+ ttydProc.stdout?.on("data", (data) => {
38
+ const msg = data.toString();
39
+ console.log(`[ttyd] ${msg}`);
40
+ });
41
+ ttydProc.stderr?.on("data", (data) => {
42
+ const msg = data.toString();
43
+ console.error(`[ttyd stderr] ${msg}`);
44
+ });
45
+ setTimeout(() => {
46
+ resolve2({
47
+ wsUrl: `ws://127.0.0.1:${port}`,
48
+ process: ttydProc,
49
+ port
50
+ });
51
+ }, 500);
52
+ ttydProc.on("error", reject);
53
+ });
54
+ }
55
+ var sourceMapCache = /* @__PURE__ */ new Map();
56
+ async function parseSourceMap(sourceMapUrl, content) {
57
+ try {
58
+ const consumer = await new SourceMapConsumer(content);
59
+ sourceMapCache.set(sourceMapUrl, consumer);
60
+ } catch (err) {
61
+ console.error("Failed to parse source map:", err);
62
+ }
63
+ }
64
+ async function findOriginalPosition(generatedFile, line, column) {
65
+ const consumer = sourceMapCache.get(generatedFile);
66
+ if (!consumer) {
67
+ return null;
68
+ }
69
+ try {
70
+ const position = consumer.originalPositionFor({ line, column });
71
+ if (position.source) {
72
+ return {
73
+ source: position.source,
74
+ line: position.line || 1,
75
+ column: position.column || 0,
76
+ name: position.name
77
+ };
78
+ }
79
+ } catch (err) {
80
+ console.error("Failed to find original position:", err);
81
+ }
82
+ return null;
83
+ }
84
+ function formatCodeContext(location) {
85
+ return `
86
+ \u{1F4CD} Code Location:
87
+ File: ${location.file}
88
+ Line: ${location.line}
89
+ Column: ${location.column}
90
+ `;
91
+ }
92
+ function createWebSocketServer(options) {
93
+ let wss = null;
94
+ let port = 0;
95
+ let ttydInfo = null;
96
+ const start = async () => {
97
+ console.log("[claude-dev-server] Starting ttyd...");
98
+ ttydInfo = await spawnClaudeCode({
99
+ cwd: options.projectRoot,
100
+ claudePath: options.claudePath,
101
+ args: options.claudeArgs
102
+ });
103
+ console.log(`[claude-dev-server] ttyd running at ${ttydInfo.wsUrl}`);
104
+ return new Promise((resolve2, reject) => {
105
+ wss = new WebSocketServer({ port, host: "127.0.0.1" });
106
+ wss.on("listening", () => {
107
+ const address = wss.address();
108
+ if (address && typeof address === "object") {
109
+ port = address.port;
110
+ resolve2({ wsPort: port, ttydPort: ttydInfo.port });
111
+ }
112
+ });
113
+ wss.on("error", (err) => {
114
+ reject(err);
115
+ });
116
+ wss.on("connection", (ws) => {
117
+ ws.on("message", async (message) => {
118
+ try {
119
+ const msg = JSON.parse(message.toString());
120
+ if (msg.type === "inspect") {
121
+ await handleInspect(msg, ws, options.projectRoot);
122
+ } else if (msg.type === "loadSourceMap") {
123
+ await handleLoadSourceMap(msg, ws, options.projectRoot);
124
+ }
125
+ } catch (err) {
126
+ }
127
+ });
128
+ ws.send(JSON.stringify({
129
+ type: "ready",
130
+ ttydUrl: ttydInfo.wsUrl
131
+ }));
132
+ });
133
+ });
134
+ };
135
+ const stop = () => {
136
+ ttydInfo?.process.kill();
137
+ wss?.close();
138
+ };
139
+ return { start, stop };
140
+ }
141
+ async function handleLoadSourceMap(msg, ws, projectRoot) {
142
+ const { sourceMapUrl } = msg;
143
+ try {
144
+ const mapPath = resolve(projectRoot, sourceMapUrl.replace(/^\//, ""));
145
+ const content = await readFile(mapPath, "utf-8");
146
+ await parseSourceMap(sourceMapUrl, content);
147
+ ws.send(JSON.stringify({
148
+ type: "sourceMapLoaded",
149
+ sourceMapUrl,
150
+ success: true
151
+ }));
152
+ } catch (err) {
153
+ ws.send(JSON.stringify({
154
+ type: "sourceMapLoaded",
155
+ sourceMapUrl,
156
+ success: false,
157
+ error: err.message
158
+ }));
159
+ }
160
+ }
161
+ async function handleInspect(msg, ws, projectRoot) {
162
+ const { url, line, column, sourceMapUrl } = msg;
163
+ let location = null;
164
+ if (sourceMapUrl) {
165
+ const original = await findOriginalPosition(sourceMapUrl, line, column);
166
+ if (original) {
167
+ location = {
168
+ file: resolve(projectRoot, original.source),
169
+ line: original.line,
170
+ column: original.column
171
+ };
172
+ }
173
+ }
174
+ if (!location) {
175
+ const match = url.match(/\/@fs\/(.+?)(?:\?|$)|\/@vite\/(.+?)(?:\?|$)/);
176
+ if (match) {
177
+ location = {
178
+ file: decodeURIComponent(match[1] || match[2]),
179
+ line,
180
+ column
181
+ };
182
+ }
183
+ }
184
+ ws.send(JSON.stringify({
185
+ type: "inspectResult",
186
+ location: location ? {
187
+ file: location.file,
188
+ line: location.line,
189
+ column: location.column,
190
+ context: formatCodeContext(location)
191
+ } : null
192
+ }));
193
+ }
194
+
195
+ // src/client/injection.js
196
+ var CLIENT_STYLES = `
197
+ <style id="claude-dev-server-styles">
198
+ .claude-dev-server-toggle {
199
+ position: fixed;
200
+ top: 20px;
201
+ right: 20px;
202
+ width: 44px;
203
+ height: 44px;
204
+ background: #1e1e1e;
205
+ border: none;
206
+ border-radius: 50%;
207
+ box-shadow: 0 2px 12px rgba(0,0,0,0.3);
208
+ cursor: pointer;
209
+ z-index: 999999;
210
+ display: flex;
211
+ align-items: center;
212
+ justify-content: center;
213
+ font-size: 20px;
214
+ transition: transform 0.2s, background 0.2s;
215
+ }
216
+ .claude-dev-server-toggle:hover {
217
+ transform: scale(1.1);
218
+ background: #2d2d2d;
219
+ }
220
+ .claude-dev-server-toggle.hidden {
221
+ display: none;
222
+ }
223
+ .claude-dev-server-inspect-btn {
224
+ position: fixed;
225
+ top: 74px;
226
+ right: 20px;
227
+ width: 44px;
228
+ height: 44px;
229
+ background: #1e1e1e;
230
+ border: none;
231
+ border-radius: 50%;
232
+ box-shadow: 0 2px 12px rgba(0,0,0,0.3);
233
+ cursor: pointer;
234
+ z-index: 999999;
235
+ display: flex;
236
+ align-items: center;
237
+ justify-content: center;
238
+ font-size: 16px;
239
+ transition: transform 0.2s, background 0.2s;
240
+ }
241
+ .claude-dev-server-inspect-btn:hover {
242
+ transform: scale(1.1);
243
+ background: #2d2d2d;
244
+ }
245
+ .claude-dev-server-inspect-btn.active {
246
+ background: #007acc;
247
+ }
248
+ .claude-dev-server-inspect-btn.hidden {
249
+ display: none;
250
+ }
251
+ .claude-dev-server-toggle svg,
252
+ .claude-dev-server-inspect-btn svg {
253
+ width: 20px;
254
+ height: 20px;
255
+ flex-shrink: 0;
256
+ }
257
+ .claude-dev-server-panel {
258
+ position: fixed;
259
+ top: 0;
260
+ right: 0;
261
+ width: 650px;
262
+ height: 100vh;
263
+ background: #1e1e1e;
264
+ color: #d4d4d4;
265
+ font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;
266
+ font-size: 13px;
267
+ box-shadow: -4px 0 20px rgba(0,0,0,0.3);
268
+ transform: translateX(100%);
269
+ transition: transform 0.2s ease;
270
+ z-index: 999999;
271
+ display: flex;
272
+ flex-direction: column;
273
+ }
274
+ .claude-dev-server-panel.open {
275
+ transform: translateX(0);
276
+ }
277
+ .claude-dev-server-header {
278
+ padding: 8px 12px;
279
+ background: #2d2d2d;
280
+ border-bottom: 1px solid #3e3e3e;
281
+ display: flex;
282
+ justify-content: space-between;
283
+ align-items: center;
284
+ user-select: none;
285
+ }
286
+ .claude-dev-server-title {
287
+ font-weight: 600;
288
+ color: #fff;
289
+ display: flex;
290
+ align-items: center;
291
+ gap: 8px;
292
+ font-size: 13px;
293
+ }
294
+ .claude-dev-server-title::before {
295
+ content: '\u{1F916}';
296
+ font-size: 14px;
297
+ }
298
+ .claude-dev-server-actions {
299
+ display: flex;
300
+ gap: 6px;
301
+ }
302
+ .claude-dev-server-btn {
303
+ background: #3e3e3e;
304
+ border: none;
305
+ color: #d4d4d4;
306
+ cursor: pointer;
307
+ font-family: inherit;
308
+ font-size: 11px;
309
+ padding: 4px 10px;
310
+ border-radius: 3px;
311
+ transition: background 0.15s;
312
+ display: flex;
313
+ align-items: center;
314
+ gap: 4px;
315
+ }
316
+ .claude-dev-server-btn:hover {
317
+ background: #4e4e4e;
318
+ }
319
+ .claude-dev-server-btn.active {
320
+ background: #007acc;
321
+ color: #fff;
322
+ }
323
+ .claude-dev-server-close {
324
+ background: none;
325
+ border: none;
326
+ color: #858585;
327
+ cursor: pointer;
328
+ font-size: 18px;
329
+ padding: 0;
330
+ width: 20px;
331
+ height: 20px;
332
+ display: flex;
333
+ align-items: center;
334
+ justify-content: center;
335
+ border-radius: 3px;
336
+ transition: background 0.15s, color 0.15s;
337
+ }
338
+ .claude-dev-server-close:hover {
339
+ background: #3e3e3e;
340
+ color: #fff;
341
+ }
342
+ .claude-dev-server-terminal {
343
+ flex: 1;
344
+ overflow: hidden;
345
+ position: relative;
346
+ background: #000;
347
+ }
348
+ .claude-dev-server-terminal iframe {
349
+ width: 100%;
350
+ height: 100%;
351
+ border: none;
352
+ }
353
+ .claude-dev-server-inspect-overlay {
354
+ position: fixed;
355
+ top: 0;
356
+ left: 0;
357
+ right: 0;
358
+ bottom: 0;
359
+ pointer-events: none;
360
+ z-index: 999998;
361
+ display: none;
362
+ }
363
+ .claude-dev-server-inspect-overlay.active {
364
+ display: block;
365
+ }
366
+ .claude-dev-server-highlight {
367
+ position: absolute;
368
+ border: 2px solid #007acc;
369
+ background: rgba(0, 122, 204, 0.1);
370
+ pointer-events: none;
371
+ transition: all 0.1s ease;
372
+ }
373
+ .claude-dev-server-highlight::after {
374
+ content: attr(data-element);
375
+ position: absolute;
376
+ top: -20px;
377
+ left: 0;
378
+ background: #007acc;
379
+ color: #fff;
380
+ font-size: 10px;
381
+ padding: 2px 4px;
382
+ border-radius: 2px 2px 0 0;
383
+ font-family: monospace;
384
+ white-space: nowrap;
385
+ }
386
+ /* Responsive layout for portrait mode */
387
+ @media (orientation: portrait) {
388
+ .claude-dev-server-toggle {
389
+ top: auto;
390
+ bottom: 20px;
391
+ right: 10px;
392
+ width: 36px;
393
+ height: 36px;
394
+ }
395
+ .claude-dev-server-inspect-btn {
396
+ top: auto;
397
+ bottom: 66px;
398
+ right: 10px;
399
+ width: 36px;
400
+ height: 36px;
401
+ }
402
+ .claude-dev-server-panel {
403
+ top: auto;
404
+ bottom: 0;
405
+ right: 0;
406
+ left: 0;
407
+ width: 100%;
408
+ height: 80vh;
409
+ transform: translateY(100%);
410
+ box-shadow: 0 -4px 20px rgba(0,0,0,0.3);
411
+ }
412
+ .claude-dev-server-panel.open {
413
+ transform: translateY(0);
414
+ }
415
+ .claude-dev-server-toggle svg,
416
+ .claude-dev-server-inspect-btn svg {
417
+ width: 16px;
418
+ height: 16px;
419
+ }
420
+ }
421
+ </style>
422
+ `;
423
+ var CLIENT_SCRIPT = `
424
+ (() => {
425
+ let ws = null
426
+ let panel = null
427
+ let toggleBtn = null
428
+ let inspectBtn = null // \u60AC\u6D6E inspect \u6309\u94AE
429
+ let terminalContainer = null
430
+ let terminalIframe = null
431
+ let overlay = null
432
+ let isOpen = false
433
+ let isInspectMode = false
434
+ let ttydWsUrl = null
435
+
436
+ // Fetch the WebSocket port from the server
437
+ async function getWsPort() {
438
+ const res = await fetch('/@claude-port')
439
+ const data = await res.json()
440
+ return data.port
441
+ }
442
+
443
+ async function initWhenReady() {
444
+ try {
445
+ const port = await getWsPort()
446
+ connect(port)
447
+ } catch (err) {
448
+ console.error('[Claude Dev Server] Failed to get port:', err)
449
+ // Retry after 1 second
450
+ setTimeout(initWhenReady, 1000)
451
+ return
452
+ }
453
+ createToggleBtn()
454
+ createInspectBtn()
455
+ createOverlay()
456
+ createPanel()
457
+ }
458
+
459
+ function createToggleBtn() {
460
+ if (toggleBtn) return
461
+ toggleBtn = document.createElement('button')
462
+ toggleBtn.className = 'claude-dev-server-toggle'
463
+ toggleBtn.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24" fill="none"><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"/></svg>'
464
+ toggleBtn.title = 'Open Claude Code (Cmd/Ctrl + \`)'
465
+ toggleBtn.setAttribute('data-dev-tool', 'true')
466
+ toggleBtn.addEventListener('click', () => togglePanel(true))
467
+ document.body.appendChild(toggleBtn)
468
+ }
469
+
470
+ function createInspectBtn() {
471
+ if (inspectBtn) return
472
+ inspectBtn = document.createElement('button')
473
+ inspectBtn.className = 'claude-dev-server-inspect-btn'
474
+ 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>'
475
+ inspectBtn.title = 'Inspect Element (Cmd/Ctrl + Shift + I)'
476
+ inspectBtn.setAttribute('data-dev-tool', 'true')
477
+ inspectBtn.addEventListener('click', () => {
478
+ if (isInspectMode) {
479
+ disableInspectMode()
480
+ } else {
481
+ // \u70B9\u51FB Inspect \u65F6\u5148\u6536\u8D77\u9762\u677F
482
+ if (isOpen) {
483
+ togglePanel(false)
484
+ }
485
+ enableInspectMode()
486
+ }
487
+ })
488
+ document.body.appendChild(inspectBtn)
489
+ }
490
+
491
+ function createOverlay() {
492
+ if (overlay) return
493
+ overlay = document.createElement('div')
494
+ overlay.className = 'claude-dev-server-inspect-overlay'
495
+ document.body.appendChild(overlay)
496
+ }
497
+
498
+ function highlightElement(el) {
499
+ if (!overlay) return
500
+ overlay.innerHTML = ''
501
+ const rect = el.getBoundingClientRect()
502
+ const highlight = document.createElement('div')
503
+ highlight.className = 'claude-dev-server-highlight'
504
+ highlight.style.top = rect.top + 'px'
505
+ highlight.style.left = rect.left + 'px'
506
+ highlight.style.width = rect.width + 'px'
507
+ highlight.style.height = rect.height + 'px'
508
+ const className = el.className ? String(el.className) : ''
509
+ highlight.dataset.element = el.tagName.toLowerCase() +
510
+ (el.id ? '#' + el.id : '') +
511
+ (className ? '.' + className.split(' ').join('.') : '')
512
+ overlay.appendChild(highlight)
513
+ }
514
+
515
+ async function getSourceLocation(el) {
516
+ let sourceFile = null
517
+ let sourceLine = 1
518
+ let sourceColumn = 1
519
+
520
+ // Try to get original line number using source map
521
+ async function getOriginalLine(generatedFile, generatedLine, generatedColumn) {
522
+ try {
523
+ // Find the script tag that corresponds to this file
524
+ const scripts = Array.from(document.querySelectorAll('script[src]'))
525
+ for (const script of scripts) {
526
+ const src = script.getAttribute('src')
527
+ if (src && (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {
528
+ // Try to fetch and parse the source map
529
+ const response = await fetch(src + '.map')
530
+ if (response.ok) {
531
+ const map = await response.json()
532
+ // Use the source map to find the original position
533
+ const consumer = await new (window.SourceMap || window.sourceMap).SourceMapConsumer(map)
534
+ const original = consumer.originalPositionFor({
535
+ line: generatedLine,
536
+ column: generatedColumn
537
+ })
538
+ consumer.destroy()
539
+ if (original.source && original.line !== null) {
540
+ return { line: original.line, column: original.column || 1 }
541
+ }
542
+ }
543
+ }
544
+ }
545
+ } catch (e) {
546
+ console.log('[Claude Dev Server] Source map lookup failed:', e.message)
547
+ }
548
+ return null
549
+ }
550
+
551
+ // Try React DevTools - handle React 18's randomized suffix
552
+ const elKeys = Object.keys(el)
553
+ let fiberKey = elKeys.find(k => k === '__reactFiber__' || k === '__reactInternalInstance' || k.indexOf('__reactFiber') === 0)
554
+ let propsKey = elKeys.find(k => k === '__reactProps__' || k.indexOf('__reactProps') === 0)
555
+
556
+ if (fiberKey) {
557
+ const fiber = el[fiberKey]
558
+ console.log('[Claude Dev Server] Found fiber at key:', fiberKey)
559
+
560
+ // Try to get _debugSource from different locations
561
+ const debugSource = fiber._debugSource || fiber.elementType?._debugSource || fiber.type?._debugSource || fiber.alternate?._debugSource
562
+ if (debugSource && debugSource.fileName) {
563
+ sourceFile = debugSource.fileName
564
+ sourceLine = debugSource.lineNumber || 1
565
+ sourceColumn = debugSource.columnNumber || 1
566
+
567
+ // Try to use source map to get the original line number
568
+ const original = await getOriginalLine(sourceFile, sourceLine, sourceColumn)
569
+ if (original) {
570
+ sourceLine = original.line
571
+ sourceColumn = original.column
572
+ console.log('[Claude Dev Server] Original line from source map:', sourceLine)
573
+ }
574
+
575
+ // Convert absolute path to relative path using project root
576
+ if (sourceFile.startsWith('/')) {
577
+ const projectRoot = window.__CLAUDE_PROJECT_ROOT__
578
+ if (projectRoot && sourceFile.startsWith(projectRoot)) {
579
+ sourceFile = sourceFile.substring(projectRoot.length + 1)
580
+ if (sourceFile.startsWith('/')) {
581
+ sourceFile = sourceFile.substring(1)
582
+ }
583
+ }
584
+ }
585
+ console.log('[Claude Dev Server] Found React source:', sourceFile, sourceLine)
586
+ } else {
587
+ // Try going up the fiber tree
588
+ let currentFiber = fiber
589
+ let depth = 0
590
+ while (currentFiber && depth < 20) {
591
+ const ds = currentFiber._debugSource || currentFiber.elementType?._debugSource || currentFiber.type?._debugSource
592
+ if (ds && ds.fileName) {
593
+ sourceFile = ds.fileName
594
+ sourceLine = ds.lineNumber || 1
595
+ sourceColumn = ds.columnNumber || 1
596
+ // Convert absolute path to relative path using project root
597
+ if (sourceFile.startsWith('/')) {
598
+ const projectRoot = window.__CLAUDE_PROJECT_ROOT__
599
+ if (projectRoot && sourceFile.startsWith(projectRoot)) {
600
+ sourceFile = sourceFile.substring(projectRoot.length + 1)
601
+ if (sourceFile.startsWith('/')) {
602
+ sourceFile = sourceFile.substring(1)
603
+ }
604
+ }
605
+ }
606
+ console.log('[Claude Dev Server] Found React source at depth', depth, ':', sourceFile, sourceLine)
607
+ break
608
+ }
609
+ currentFiber = currentFiber.return || currentFiber.alternate
610
+ depth++
611
+ }
612
+ }
613
+ }
614
+
615
+ // Try Vue component
616
+ if (!sourceFile) {
617
+ const vueComponent = el.__vueParentComponent || el.__vnode
618
+ if (vueComponent) {
619
+ const type = vueComponent.type || vueComponent.component
620
+ if (type && type.__file) {
621
+ sourceFile = type.__file
622
+ console.log('[Claude Dev Server] Found Vue source:', sourceFile)
623
+ }
624
+ }
625
+ }
626
+
627
+ // Try Vite's HMR source map
628
+ if (!sourceFile) {
629
+ // Look for data-vite-dev-id or similar attributes
630
+ const viteId = el.getAttribute('data-vite-dev-id')
631
+ if (viteId) {
632
+ // Extract file path from viteId (remove query params if any)
633
+ const queryIndex = viteId.indexOf('?')
634
+ sourceFile = '/' + (queryIndex >= 0 ? viteId.substring(0, queryIndex) : viteId)
635
+ console.log('[Claude Dev Server] Found Vite ID:', viteId)
636
+ }
637
+ }
638
+
639
+ // Check for inline script
640
+ if (!sourceFile) {
641
+ const inlineScripts = document.querySelectorAll('script:not([src])')
642
+ if (inlineScripts.length > 0) {
643
+ const pathParts = window.location.pathname.split('/')
644
+ const fileName = pathParts[pathParts.length - 1] || 'index.html'
645
+ sourceFile = '/' + fileName
646
+
647
+ const html = document.documentElement.outerHTML
648
+ const elId = el.id ? el.id : ''
649
+ const elClass = el.className ? el.className : ''
650
+ let elPattern
651
+ if (elId) {
652
+ elPattern = 'id="' + elId + '"'
653
+ } else if (elClass) {
654
+ elPattern = 'class="' + elClass.split(' ')[0] + '"'
655
+ } else {
656
+ elPattern = el.tagName.toLowerCase()
657
+ }
658
+
659
+ const matchIndex = html.indexOf(elPattern)
660
+ if (matchIndex !== -1) {
661
+ const beforeElement = html.substring(0, matchIndex)
662
+ sourceLine = beforeElement.split('\\n').length
663
+ }
664
+ }
665
+ }
666
+
667
+ // Find main script
668
+ if (!sourceFile) {
669
+ const scripts = document.querySelectorAll('script[src]')
670
+ for (const script of scripts) {
671
+ const src = script.getAttribute('src')
672
+ if (src && !src.includes('cdn') && !src.includes('node_modules') &&
673
+ (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {
674
+ sourceFile = src
675
+ break
676
+ }
677
+ }
678
+ }
679
+
680
+ // Fallback
681
+ if (!sourceFile) {
682
+ const pathParts = window.location.pathname.split('/')
683
+ const fileName = pathParts[pathParts.length - 1] || 'index.html'
684
+ sourceFile = '/' + fileName
685
+ }
686
+
687
+ const elClassName = el.className ? String(el.className) : ''
688
+ const selector = el.tagName.toLowerCase() +
689
+ (el.id ? '#' + el.id : '') +
690
+ (elClassName ? '.' + elClassName.split(' ').filter(c => c).join('.') : '')
691
+
692
+ return {
693
+ url: sourceFile,
694
+ line: sourceLine,
695
+ column: sourceColumn,
696
+ selector: selector,
697
+ hint: sourceFile ? 'File: ' + sourceFile : 'Element: ' + selector
698
+ }
699
+ }
700
+
701
+ function enableInspectMode() {
702
+ isInspectMode = true
703
+ overlay && overlay.classList.add('active')
704
+ document.body.style.cursor = 'crosshair'
705
+
706
+ // \u66F4\u65B0\u60AC\u6D6E inspect \u6309\u94AE\u72B6\u6001
707
+ if (inspectBtn) inspectBtn.classList.add('active')
708
+
709
+ // \u68C0\u67E5\u5143\u7D20\u662F\u5426\u5C5E\u4E8E dev tools
710
+ function isDevToolElement(el) {
711
+ if (!el) return false
712
+ // \u68C0\u67E5\u5143\u7D20\u672C\u8EAB
713
+ if (el.dataset && el.dataset.devTool === 'true') return true
714
+ // \u68C0\u67E5\u7236\u5143\u7D20
715
+ let parent = el.parentElement
716
+ while (parent) {
717
+ if (parent.dataset && parent.dataset.devTool === 'true') return true
718
+ parent = parent.parentElement
719
+ }
720
+ return false
721
+ }
722
+
723
+ const onMouseMove = async (e) => {
724
+ if (!isInspectMode) return
725
+ const el = document.elementFromPoint(e.clientX, e.clientY)
726
+ // \u5FFD\u7565 dev tools \u5143\u7D20\u548C overlay
727
+ if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {
728
+ highlightElement(el)
729
+ }
730
+ }
731
+
732
+ const onClick = async (e) => {
733
+ if (!isInspectMode) return
734
+ e.preventDefault()
735
+ e.stopPropagation()
736
+
737
+ const el = document.elementFromPoint(e.clientX, e.clientY)
738
+ // \u5FFD\u7565 dev tools \u5143\u7D20\u548C overlay
739
+ if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {
740
+ const location = await getSourceLocation(el)
741
+ if (location) {
742
+ await inspectElement(location, el)
743
+ }
744
+ disableInspectMode()
745
+ }
746
+ }
747
+
748
+ document.addEventListener('mousemove', onMouseMove, true)
749
+ document.addEventListener('click', onClick, true)
750
+
751
+ if (overlay) overlay._inspectHandlers = { onMouseMove, onClick }
752
+ }
753
+
754
+ function disableInspectMode() {
755
+ isInspectMode = false
756
+ if (overlay) overlay.classList.remove('active')
757
+ document.body.style.cursor = ''
758
+ if (overlay) overlay.innerHTML = ''
759
+
760
+ // \u66F4\u65B0\u60AC\u6D6E inspect \u6309\u94AE\u72B6\u6001
761
+ if (inspectBtn) inspectBtn.classList.remove('active')
762
+
763
+ const btn = panel && panel.querySelector('.claude-dev-server-btn-inspect')
764
+ if (btn) btn.classList.remove('active')
765
+
766
+ const handlers = overlay && overlay._inspectHandlers
767
+ if (handlers) {
768
+ document.removeEventListener('mousemove', handlers.onMouseMove, true)
769
+ document.removeEventListener('click', handlers.onClick, true)
770
+ }
771
+ }
772
+
773
+ async function inspectElement(location, el) {
774
+ // \u6784\u5EFA\u683C\u5F0F\u5316\u7684 prompt
775
+ const filePath = location.url || location.file || 'unknown'
776
+ const tagName = el.tagName ? el.tagName.toLowerCase() : ''
777
+ const id = el.id ? '#' + el.id : ''
778
+ const className = el.className ? '.' + String(el.className).split(' ').filter(c => c).join('.') : ''
779
+ const selector = tagName + id + className
780
+
781
+ // \u83B7\u53D6\u5143\u7D20\u7684\u6587\u672C\u5185\u5BB9\uFF08\u5982\u679C\u662F\u6587\u672C\u8282\u70B9\u6216\u77ED\u6587\u672C\u5143\u7D20\uFF09
782
+ let textContent = ''
783
+ if (el.nodeType === Node.TEXT_NODE) {
784
+ textContent = el.textContent ? el.textContent.trim().substring(0, 50) : ''
785
+ } else if (el.childNodes.length === 1 && el.childNodes[0].nodeType === Node.TEXT_NODE) {
786
+ textContent = el.childNodes[0].textContent ? el.childNodes[0].textContent.trim().substring(0, 50) : ''
787
+ }
788
+
789
+ // \u683C\u5F0F: @filename:line <selector> "text content"
790
+ let prompt = \`@\${filePath}:\${location.line} <\${selector}>\`
791
+ if (textContent) {
792
+ prompt += \` "\${textContent}"\`
793
+ }
794
+
795
+ // \u53D1\u9001\u6210\u529F\u540E\u5C55\u5F00\u9762\u677F
796
+ const sendToTerminal = () => {
797
+ if (!terminalIframe || !terminalIframe.contentWindow) {
798
+ return false
799
+ }
800
+
801
+ const win = terminalIframe.contentWindow
802
+
803
+ // Check if sendToTerminal function is available
804
+ if (win.sendToTerminal) {
805
+ win.sendToTerminal(prompt)
806
+ console.log('[Claude Dev Server] Sent via sendToTerminal:', prompt)
807
+ // \u53D1\u9001\u6210\u529F\u540E\u5C55\u5F00\u9762\u677F
808
+ setTimeout(() => togglePanel(true), 100)
809
+ return true
810
+ }
811
+
812
+ return false
813
+ }
814
+
815
+ // Try immediately
816
+ if (sendToTerminal()) {
817
+ return
818
+ }
819
+
820
+ // If not ready, wait and retry
821
+ let attempts = 0
822
+ const maxAttempts = 50
823
+ const retryInterval = setInterval(() => {
824
+ attempts++
825
+ if (sendToTerminal() || attempts >= maxAttempts) {
826
+ clearInterval(retryInterval)
827
+ if (attempts >= maxAttempts) {
828
+ console.warn('[Claude Dev Server] Could not send to terminal after retries')
829
+ }
830
+ }
831
+ }, 100)
832
+ }
833
+
834
+ function loadTerminalIframe(ttydUrl) {
835
+ if (!terminalContainer || terminalIframe) return
836
+ ttydWsUrl = ttydUrl
837
+
838
+ // Create iframe pointing to ttyd/index.html with ttyd WebSocket URL
839
+ terminalIframe = document.createElement('iframe')
840
+ // Pass the ttyd WebSocket URL as query param
841
+ terminalIframe.src = '/ttyd/index.html?ws=' + encodeURIComponent(ttydUrl)
842
+ terminalIframe.allow = 'clipboard-read; clipboard-write'
843
+
844
+ // Load event - iframe is ready
845
+ terminalIframe.onload = () => {
846
+ console.log('[Claude Dev Server] Terminal iframe loaded')
847
+ }
848
+
849
+ terminalContainer.appendChild(terminalIframe)
850
+ }
851
+
852
+ function createPanel() {
853
+ if (panel) return
854
+
855
+ panel = document.createElement('div')
856
+ panel.className = 'claude-dev-server-panel'
857
+ panel.innerHTML = \`
858
+ <div class="claude-dev-server-header">
859
+ <span class="claude-dev-server-title">Claude Code</span>
860
+ <div class="claude-dev-server-actions">
861
+ <button class="claude-dev-server-btn claude-dev-server-btn-inspect" title="Inspect Element">
862
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
863
+ <path d="M2 12h20M12 2v20M4.93 4.93l14.14 14.14M19.07 4.93L4.93 19.07"/>
864
+ </svg>
865
+ Inspect
866
+ </button>
867
+ </div>
868
+ <button class="claude-dev-server-close" aria-label="Close" title="Minimize to icon">&times;</button>
869
+ </div>
870
+ <div class="claude-dev-server-terminal"></div>
871
+ \`
872
+
873
+ document.body.appendChild(panel)
874
+
875
+ terminalContainer = panel.querySelector('.claude-dev-server-terminal')
876
+ const closeBtn = panel.querySelector('.claude-dev-server-close')
877
+ const inspectBtn = panel.querySelector('.claude-dev-server-btn-inspect')
878
+
879
+ closeBtn && closeBtn.addEventListener('click', () => togglePanel(false))
880
+
881
+ inspectBtn && inspectBtn.addEventListener('click', () => {
882
+ if (isInspectMode) {
883
+ disableInspectMode()
884
+ } else {
885
+ // \u70B9\u51FB Inspect \u65F6\u5148\u6536\u8D77\u9762\u677F
886
+ if (isOpen) {
887
+ togglePanel(false)
888
+ }
889
+ enableInspectMode()
890
+ }
891
+ })
892
+ }
893
+
894
+ function togglePanel(force) {
895
+ createPanel()
896
+ if (typeof force === 'boolean') {
897
+ isOpen = force
898
+ } else {
899
+ isOpen = !isOpen
900
+ }
901
+
902
+ if (panel) panel.classList.toggle('open', isOpen)
903
+ if (toggleBtn) toggleBtn.classList.toggle('hidden', isOpen)
904
+
905
+ if (isOpen) {
906
+ // Focus iframe when panel opens
907
+ if (terminalIframe && terminalIframe.contentWindow) {
908
+ setTimeout(() => {
909
+ if (terminalIframe.contentWindow.terminalInstance) {
910
+ terminalIframe.contentWindow.terminalInstance.focus()
911
+ }
912
+ }, 100)
913
+ }
914
+ }
915
+ }
916
+
917
+ function connect(port) {
918
+ const WS_URL = 'ws://localhost:' + port
919
+ ws = new WebSocket(WS_URL)
920
+
921
+ ws.onopen = () => {
922
+ console.log('[Claude Dev Server] Connected to control server')
923
+ }
924
+
925
+ ws.onmessage = (e) => {
926
+ try {
927
+ const msg = JSON.parse(e.data)
928
+ console.log('[Claude Dev Server] Received message:', msg.type, msg)
929
+ if (msg.type === 'ready' && msg.ttydUrl) {
930
+ loadTerminalIframe(msg.ttydUrl)
931
+ } else if (msg.type === 'inspectResult') {
932
+ if (msg.location) {
933
+ showContextPanel(msg.location, null, false)
934
+ }
935
+ }
936
+ } catch (err) {
937
+ console.error('[Claude Dev Server] Message parse error:', err)
938
+ }
939
+ }
940
+
941
+ ws.onclose = () => {
942
+ console.log('[Claude Dev Server] Control WebSocket disconnected, reconnecting...')
943
+ setTimeout(() => connect(port), 2000)
944
+ }
945
+
946
+ ws.onerror = (err) => {
947
+ console.error('[Claude Dev Server] Control WebSocket error:', err)
948
+ }
949
+ }
950
+
951
+ initWhenReady()
952
+
953
+ document.addEventListener('keydown', (e) => {
954
+ if ((e.metaKey || e.ctrlKey) && e.code === 'Backquote') {
955
+ e.preventDefault()
956
+ togglePanel()
957
+ }
958
+ if (e.key === 'Escape' && isOpen) {
959
+ togglePanel(false)
960
+ }
961
+ if (e.key === 'Escape' && isInspectMode) {
962
+ disableInspectMode()
963
+ }
964
+ })
965
+ })()
966
+ `;
967
+
968
+ // src/universal/index.ts
969
+ var __filename$1 = fileURLToPath(import.meta.url);
970
+ dirname(__filename$1);
971
+ async function startUniversalServer(options = {}) {
972
+ const cwd = options.cwd || process.cwd();
973
+ const port = options.port || 3e3;
974
+ const projectType = options.server?.type || detectProjectType(cwd);
975
+ const targetCommand = options.server?.command || getDefaultCommand(projectType);
976
+ console.log(`[Claude Dev Server] Detected project type: ${projectType}`);
977
+ console.log(`[Claude Dev Server] Starting target server...`);
978
+ const targetServer = spawnTargetServer(targetCommand, cwd);
979
+ console.log(`[Claude Dev Server] Waiting for target server to start (PID: ${targetServer.pid})...`);
980
+ const targetPort = await detectServerPort(targetServer, 3e4);
981
+ if (!targetPort) {
982
+ throw new Error("Failed to detect target server port. Please check if the dev server started successfully.");
983
+ }
984
+ console.log(`[Claude Dev Server] Target server is running on port ${targetPort}`);
985
+ const wsServer = createWebSocketServer({
986
+ port: 0,
987
+ // 自动分配端口
988
+ projectRoot: cwd,
989
+ claudePath: options.claudePath || "claude",
990
+ claudeArgs: options.claudeArgs || []
991
+ });
992
+ const { wsPort, ttydPort } = await wsServer.start();
993
+ console.log(`[Claude Dev Server] Control server running on ws://localhost:${wsPort}`);
994
+ console.log(`[Claude Dev Server] ttyd running on ws://localhost:${ttydPort}`);
995
+ const proxyServer = createProxyServer(targetPort, wsPort, cwd);
996
+ proxyServer.listen(port);
997
+ console.log(`[Claude Dev Server] Proxy server running on http://localhost:${port}`);
998
+ console.log(`
999
+ \u{1F680} Ready! Open http://localhost:${port} in your browser`);
1000
+ const cleanup = () => {
1001
+ console.log("[Claude Dev Server] Shutting down...");
1002
+ targetServer.kill();
1003
+ wsServer.stop();
1004
+ proxyServer.close();
1005
+ process.exit(0);
1006
+ };
1007
+ process.on("SIGINT", cleanup);
1008
+ process.on("SIGTERM", cleanup);
1009
+ return { proxyServer, targetServer, wsServer };
1010
+ }
1011
+ function detectProjectType(cwd) {
1012
+ if (existsSync(join(cwd, "vite.config.ts")) || existsSync(join(cwd, "vite.config.js"))) {
1013
+ return "vite";
1014
+ }
1015
+ if (existsSync(join(cwd, "next.config.js")) || existsSync(join(cwd, "next.config.mjs"))) {
1016
+ return "next";
1017
+ }
1018
+ if (existsSync(join(cwd, "webpack.config.js")) || existsSync(join(cwd, "webpack.config.ts"))) {
1019
+ return "webpack";
1020
+ }
1021
+ return "custom";
1022
+ }
1023
+ function getDefaultCommand(projectType) {
1024
+ switch (projectType) {
1025
+ case "vite":
1026
+ return "npm run dev";
1027
+ case "next":
1028
+ return "npm run dev";
1029
+ case "webpack":
1030
+ return "npm run dev";
1031
+ default:
1032
+ return "npm run dev";
1033
+ }
1034
+ }
1035
+ function spawnTargetServer(command, cwd) {
1036
+ const [cmd, ...args] = command.split(" ");
1037
+ const child = spawn(cmd, args, {
1038
+ cwd,
1039
+ stdio: "pipe",
1040
+ env: process.env
1041
+ });
1042
+ child.stdout?.pipe(process.stdout);
1043
+ child.stderr?.pipe(process.stderr);
1044
+ return child;
1045
+ }
1046
+ async function detectServerPort(childProcess, timeout) {
1047
+ return new Promise((resolve2) => {
1048
+ const timeoutId = setTimeout(() => {
1049
+ cleanup();
1050
+ resolve2(null);
1051
+ }, timeout);
1052
+ let stdout = "";
1053
+ const onData = (chunk) => {
1054
+ stdout += chunk.toString();
1055
+ const patterns = [
1056
+ /localhost:(\d{4,5})/,
1057
+ /127\.0\.0\.1:(\d{4,5})/,
1058
+ /0\.0\.0\.0:(\d{4,5})/
1059
+ ];
1060
+ for (const pattern of patterns) {
1061
+ const match = stdout.match(pattern);
1062
+ if (match) {
1063
+ const port = parseInt(match[1], 10);
1064
+ console.log(`[Claude Dev Server] Detected port from stdout: ${port}`);
1065
+ cleanup();
1066
+ resolve2(port);
1067
+ return;
1068
+ }
1069
+ }
1070
+ };
1071
+ const cleanup = () => {
1072
+ clearTimeout(timeoutId);
1073
+ childProcess.stdout?.off("data", onData);
1074
+ };
1075
+ childProcess.stdout?.on("data", onData);
1076
+ });
1077
+ }
1078
+ function createProxyServer(targetPort, wsPort, projectRoot) {
1079
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
1080
+ const assetsPath = join(moduleDir, "assets");
1081
+ let ttydHtml;
1082
+ let ttydBridgeJs;
1083
+ try {
1084
+ ttydHtml = readFileSync(join(assetsPath, "ttyd-terminal.html"), "utf-8");
1085
+ ttydBridgeJs = readFileSync(join(assetsPath, "ttyd-bridge.js"), "utf-8");
1086
+ } catch (e) {
1087
+ console.error("[Claude Dev Server] Failed to read ttyd assets from", assetsPath);
1088
+ console.error("[Claude Dev Server] moduleDir:", moduleDir);
1089
+ console.error("[Claude Dev Server] Error:", e.message);
1090
+ throw new Error("ttyd assets not found. Please run `npm run build` first.");
1091
+ }
1092
+ return http.createServer((req, res) => {
1093
+ if (req.url === "/@claude-port") {
1094
+ res.setHeader("Content-Type", "application/json");
1095
+ res.end(JSON.stringify({ port: wsPort }));
1096
+ return;
1097
+ }
1098
+ if (req.url?.startsWith("/ttyd/")) {
1099
+ const urlPath = req.url.split("?")[0];
1100
+ if (urlPath === "/ttyd/index.html" || urlPath === "/ttyd/") {
1101
+ res.setHeader("Content-Type", "text/html");
1102
+ res.end(ttydHtml);
1103
+ return;
1104
+ }
1105
+ if (urlPath === "/ttyd/ttyd-bridge.js") {
1106
+ res.setHeader("Content-Type", "application/javascript");
1107
+ res.end(ttydBridgeJs);
1108
+ return;
1109
+ }
1110
+ res.statusCode = 404;
1111
+ res.end("Not found");
1112
+ return;
1113
+ }
1114
+ const options = {
1115
+ hostname: "localhost",
1116
+ port: targetPort,
1117
+ path: req.url,
1118
+ method: req.method,
1119
+ headers: req.headers
1120
+ };
1121
+ const proxyReq = http.request(options, (proxyRes) => {
1122
+ if (proxyRes.headers["content-type"]?.includes("text/html")) {
1123
+ const body = [];
1124
+ proxyRes.on("data", (chunk) => body.push(chunk));
1125
+ proxyRes.on("end", () => {
1126
+ const html = Buffer.concat(body).toString("utf8");
1127
+ const injected = injectScripts(html, wsPort, projectRoot);
1128
+ const statusCode = proxyRes.statusCode || 200;
1129
+ res.writeHead(statusCode, {
1130
+ ...proxyRes.headers,
1131
+ "content-length": Buffer.byteLength(injected)
1132
+ });
1133
+ res.end(injected);
1134
+ });
1135
+ } else {
1136
+ const statusCode = proxyRes.statusCode || 200;
1137
+ res.writeHead(statusCode, proxyRes.headers);
1138
+ proxyRes.pipe(res);
1139
+ }
1140
+ });
1141
+ proxyReq.on("error", (err) => {
1142
+ console.error("[Claude Dev Server] Proxy error:", err);
1143
+ res.statusCode = 502;
1144
+ res.end("Bad Gateway");
1145
+ });
1146
+ req.pipe(proxyReq);
1147
+ });
1148
+ }
1149
+ function injectScripts(html, wsPort, projectRoot) {
1150
+ const projectRootScript = `<script>window.__CLAUDE_PROJECT_ROOT__ = ${JSON.stringify(projectRoot)}</script>`;
1151
+ return html.replace(
1152
+ "</head>",
1153
+ `<!-- Claude Dev Server -->
1154
+ ${CLIENT_STYLES}
1155
+ ${projectRootScript}
1156
+ <script type="module">${CLIENT_SCRIPT}</script>
1157
+ </head>`
1158
+ ).replace(
1159
+ /wsPort:\s*\d+/,
1160
+ `wsPort: ${wsPort}`
1161
+ );
1162
+ }
1163
+
1164
+ export { startUniversalServer };
1165
+ //# sourceMappingURL=chunk-DOLSA776.js.map
1166
+ //# sourceMappingURL=chunk-DOLSA776.js.map