dev3000 0.0.91 → 0.0.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/screencast-manager.d.ts +1 -0
- package/dist/screencast-manager.d.ts.map +1 -1
- package/dist/screencast-manager.js +29 -4
- package/dist/screencast-manager.js.map +1 -1
- package/dist/src/tui-interface-impl.tsx +22 -13
- package/dist/tui-interface-impl.d.ts.map +1 -1
- package/dist/tui-interface-impl.js +19 -11
- package/dist/tui-interface-impl.js.map +1 -1
- package/mcp-server/.next/BUILD_ID +1 -1
- package/mcp-server/.next/build-manifest.json +2 -2
- package/mcp-server/.next/fallback-build-manifest.json +2 -2
- package/mcp-server/.next/prerender-manifest.json +3 -3
- package/mcp-server/.next/server/app/_global-error.html +2 -2
- package/mcp-server/.next/server/app/_global-error.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.html +1 -1
- package/mcp-server/.next/server/app/_not-found.rsc +1 -1
- package/mcp-server/.next/server/app/index.html +1 -1
- package/mcp-server/.next/server/app/index.rsc +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js +6 -6
- package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js.map +1 -1
- package/mcp-server/.next/server/server-reference-manifest.js +1 -1
- package/mcp-server/.next/server/server-reference-manifest.json +1 -1
- package/mcp-server/app/mcp/tools.ts +151 -24
- package/package.json +1 -1
- package/src/tui-interface-impl.tsx +22 -13
- /package/mcp-server/.next/static/{eVL_05d0pOH_qw2twMoct → _qXVPjMKmdbmLNrnd_3zK}/_buildManifest.js +0 -0
- /package/mcp-server/.next/static/{eVL_05d0pOH_qw2twMoct → _qXVPjMKmdbmLNrnd_3zK}/_clientMiddlewareManifest.json +0 -0
- /package/mcp-server/.next/static/{eVL_05d0pOH_qw2twMoct → _qXVPjMKmdbmLNrnd_3zK}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"
|
|
1
|
+
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"lZgibe2ziYqyEAkZ7RFH62f938aB/wxVlceTcZMnbR8=\"\n}"
|
|
@@ -669,7 +669,13 @@ export async function fixMyApp({
|
|
|
669
669
|
)
|
|
670
670
|
}
|
|
671
671
|
|
|
672
|
-
|
|
672
|
+
const triggerLabel =
|
|
673
|
+
jankResult.captureTrigger === "navigation"
|
|
674
|
+
? "Navigation complete"
|
|
675
|
+
: jankResult.captureTrigger === "load"
|
|
676
|
+
? "Load complete"
|
|
677
|
+
: "View all frames"
|
|
678
|
+
results.push(`📹 **${triggerLabel}**: ${videoUrl}`)
|
|
673
679
|
results.push(`🎞️ **Session ID**: ${jankResult.sessionId} (${jankResult.totalFrames} frames)`)
|
|
674
680
|
results.push("")
|
|
675
681
|
|
|
@@ -2122,48 +2128,128 @@ export async function getMcpCapabilities({
|
|
|
2122
2128
|
* - Layout shifts: Elements move to new positions (top region changes while bottom stays same)
|
|
2123
2129
|
* - Content changes: Same regions change in-place (image loads with pixels appearing)
|
|
2124
2130
|
*/
|
|
2125
|
-
function detectLayoutShiftVsContentChange(
|
|
2131
|
+
function detectLayoutShiftVsContentChange(
|
|
2132
|
+
prevPng: PNG,
|
|
2133
|
+
currPng: PNG
|
|
2134
|
+
): { isLayoutShift: boolean; shiftScore: number; isOverlayNoise: boolean } {
|
|
2126
2135
|
const width = prevPng.width
|
|
2127
2136
|
const height = prevPng.height
|
|
2128
2137
|
|
|
2129
|
-
//
|
|
2130
|
-
const
|
|
2131
|
-
const bands = Array(8).fill(0)
|
|
2138
|
+
// Track changes at row-level for detecting correlated shifts
|
|
2139
|
+
const rowChangeCounts = new Array(height).fill(0)
|
|
2132
2140
|
|
|
2133
|
-
// Count changed pixels
|
|
2141
|
+
// Count changed pixels per row (for correlation analysis)
|
|
2134
2142
|
for (let y = 0; y < height; y++) {
|
|
2135
|
-
const bandIndex = Math.min(Math.floor(y / bandHeight), 7)
|
|
2136
2143
|
for (let x = 0; x < width; x++) {
|
|
2137
2144
|
const idx = (width * y + x) << 2
|
|
2138
2145
|
const rDiff = Math.abs(prevPng.data[idx] - currPng.data[idx])
|
|
2139
2146
|
const gDiff = Math.abs(prevPng.data[idx + 1] - currPng.data[idx + 1])
|
|
2140
2147
|
const bDiff = Math.abs(prevPng.data[idx + 2] - currPng.data[idx + 2])
|
|
2141
2148
|
|
|
2142
|
-
// If any channel differs significantly, count as changed pixel
|
|
2143
2149
|
if (rDiff > 30 || gDiff > 30 || bDiff > 30) {
|
|
2144
|
-
|
|
2150
|
+
rowChangeCounts[y]++
|
|
2145
2151
|
}
|
|
2146
2152
|
}
|
|
2147
2153
|
}
|
|
2148
2154
|
|
|
2149
|
-
// Calculate percentage of pixels changed
|
|
2155
|
+
// Calculate percentage of pixels changed per row
|
|
2156
|
+
const rowChangePercents = rowChangeCounts.map((count) => (count / width) * 100)
|
|
2157
|
+
|
|
2158
|
+
// Detect consecutive rows with high change (indicates shift boundary)
|
|
2159
|
+
// True CLS: Many consecutive rows change together (content moved as a block)
|
|
2160
|
+
let maxConsecutiveHighChangeRows = 0
|
|
2161
|
+
let currentConsecutive = 0
|
|
2162
|
+
|
|
2163
|
+
for (let i = 0; i < height; i++) {
|
|
2164
|
+
if (rowChangePercents[i] > 50) {
|
|
2165
|
+
// >50% of row changed
|
|
2166
|
+
currentConsecutive++
|
|
2167
|
+
maxConsecutiveHighChangeRows = Math.max(maxConsecutiveHighChangeRows, currentConsecutive)
|
|
2168
|
+
} else {
|
|
2169
|
+
currentConsecutive = 0
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// Detect isolated hotspots (fixed/absolute overlay noise)
|
|
2174
|
+
// Pattern: low change → spike → low change (element appearing in place)
|
|
2175
|
+
let isolatedHotspots = 0
|
|
2176
|
+
const windowSize = 5
|
|
2177
|
+
|
|
2178
|
+
for (let i = windowSize; i < height - windowSize; i++) {
|
|
2179
|
+
// Calculate average change in windows before, during, and after
|
|
2180
|
+
const before = rowChangePercents.slice(i - windowSize, i).reduce((a, b) => a + b, 0) / windowSize
|
|
2181
|
+
const during = rowChangePercents[i]
|
|
2182
|
+
const after = rowChangePercents.slice(i + 1, i + windowSize + 1).reduce((a, b) => a + b, 0) / windowSize
|
|
2183
|
+
|
|
2184
|
+
// Isolated spike: calm before/after, high during
|
|
2185
|
+
if (before < 10 && during > 60 && after < 10) {
|
|
2186
|
+
isolatedHotspots++
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
// Detect narrow fixed elements (toolbars, indicators)
|
|
2191
|
+
// Pattern: Many rows with LOW percentage change (5-25%) = narrow element across many rows
|
|
2192
|
+
// This catches toolbars/indicators that are thin but tall
|
|
2193
|
+
let narrowChangeRows = 0
|
|
2194
|
+
for (let i = 0; i < height; i++) {
|
|
2195
|
+
// Low but consistent change (narrow element)
|
|
2196
|
+
if (rowChangePercents[i] > 5 && rowChangePercents[i] < 25) {
|
|
2197
|
+
narrowChangeRows++
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// If many rows have narrow changes, this is likely a fixed toolbar/sidebar
|
|
2202
|
+
const hasNarrowFixedElement = narrowChangeRows > height * 0.3 // >30% of rows have narrow changes
|
|
2203
|
+
|
|
2204
|
+
// Calculate band-based metrics for backward compatibility
|
|
2205
|
+
const bandHeight = Math.floor(height / 8)
|
|
2206
|
+
const bands = Array(8).fill(0)
|
|
2207
|
+
|
|
2208
|
+
for (let y = 0; y < height; y++) {
|
|
2209
|
+
const bandIndex = Math.min(Math.floor(y / bandHeight), 7)
|
|
2210
|
+
bands[bandIndex] += rowChangeCounts[y]
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2150
2213
|
const pixelsPerBand = width * bandHeight
|
|
2151
2214
|
const bandPercentages = bands.map((count) => (count / pixelsPerBand) * 100)
|
|
2152
|
-
|
|
2153
|
-
// Layout shift pattern: High change in top bands (nav/header area), low change in bottom
|
|
2154
|
-
// Content change pattern: Evenly distributed or contained to specific regions
|
|
2155
2215
|
const topBandChange = (bandPercentages[0] + bandPercentages[1]) / 2
|
|
2156
2216
|
const bottomBandChange = (bandPercentages[6] + bandPercentages[7]) / 2
|
|
2157
2217
|
|
|
2158
|
-
//
|
|
2159
|
-
const
|
|
2160
|
-
|
|
2161
|
-
|
|
2218
|
+
// Calculate variance to detect if changes are uniform (shift) or scattered (overlay)
|
|
2219
|
+
const meanChange = bandPercentages.reduce((a, b) => a + b, 0) / bandPercentages.length
|
|
2220
|
+
const variance = bandPercentages.reduce((sum, val) => sum + (val - meanChange) ** 2, 0) / bandPercentages.length
|
|
2221
|
+
|
|
2222
|
+
// Determine if this is a layout shift or overlay noise
|
|
2223
|
+
// True layout shift indicators:
|
|
2224
|
+
// 1. Many consecutive rows changed (>20 rows = significant shift)
|
|
2225
|
+
// 2. Top heavy change pattern (topBandChange > bottomBandChange)
|
|
2226
|
+
// 3. Low variance (uniform change across bands)
|
|
2227
|
+
// 4. Few isolated hotspots
|
|
2228
|
+
|
|
2229
|
+
const hasConsecutiveShift = maxConsecutiveHighChangeRows > 20
|
|
2230
|
+
const hasTopHeavyPattern = topBandChange > 5 && bottomBandChange < 2 && topBandChange > bottomBandChange * 2
|
|
2231
|
+
const hasUniformChange = variance < 200 && meanChange > 10
|
|
2232
|
+
const hasIsolatedHotspots = isolatedHotspots >= 3
|
|
2233
|
+
|
|
2234
|
+
// Overlay noise indicators:
|
|
2235
|
+
// 1. High variance (scattered changes)
|
|
2236
|
+
// 2. Multiple isolated hotspots
|
|
2237
|
+
// 3. Few consecutive rows changed
|
|
2238
|
+
// 4. Narrow fixed element (toolbar/indicator pattern)
|
|
2239
|
+
const isOverlayNoise =
|
|
2240
|
+
hasNarrowFixedElement || // Narrow element like toolbar
|
|
2241
|
+
(hasIsolatedHotspots && !hasConsecutiveShift && (variance > 500 || meanChange < 10))
|
|
2242
|
+
|
|
2243
|
+
// Layout shift: Either consecutive shift pattern OR traditional top-heavy pattern
|
|
2244
|
+
// But NOT if it looks like overlay noise
|
|
2245
|
+
const isLayoutShift = !isOverlayNoise && (hasConsecutiveShift || hasTopHeavyPattern || hasUniformChange)
|
|
2246
|
+
|
|
2247
|
+
// Calculate shift score
|
|
2162
2248
|
const totalChanged = bands.reduce((sum, count) => sum + count, 0)
|
|
2163
2249
|
const totalPixels = width * height
|
|
2164
|
-
const shiftScore = (totalChanged / totalPixels) * 0.1
|
|
2250
|
+
const shiftScore = (totalChanged / totalPixels) * 0.1
|
|
2165
2251
|
|
|
2166
|
-
return { isLayoutShift, shiftScore }
|
|
2252
|
+
return { isLayoutShift, shiftScore, isOverlayNoise }
|
|
2167
2253
|
}
|
|
2168
2254
|
|
|
2169
2255
|
/**
|
|
@@ -2186,6 +2272,7 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
|
|
|
2186
2272
|
totalFrames: number
|
|
2187
2273
|
screenshotDir: string
|
|
2188
2274
|
realCLS?: { score: number; grade: string }
|
|
2275
|
+
captureTrigger?: "navigation" | "load"
|
|
2189
2276
|
}> {
|
|
2190
2277
|
const screenshotDir = process.env.SCREENSHOT_DIR || join(tmpdir(), "dev3000-mcp-deps", "public", "screenshots")
|
|
2191
2278
|
|
|
@@ -2224,13 +2311,20 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
|
|
|
2224
2311
|
| {
|
|
2225
2312
|
score: number
|
|
2226
2313
|
grade: string
|
|
2227
|
-
shifts: Array<{
|
|
2314
|
+
shifts: Array<{
|
|
2315
|
+
score: number
|
|
2316
|
+
timestamp: number
|
|
2317
|
+
sources?: Array<{ node?: string; position?: string | null }>
|
|
2318
|
+
}>
|
|
2228
2319
|
}
|
|
2229
2320
|
| undefined
|
|
2321
|
+
let captureTrigger: "navigation" | "load" | undefined
|
|
2230
2322
|
|
|
2231
2323
|
if (existsSync(metadataPath)) {
|
|
2232
2324
|
try {
|
|
2233
2325
|
const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"))
|
|
2326
|
+
// Capture the trigger type for use in output messages
|
|
2327
|
+
captureTrigger = metadata.captureTrigger
|
|
2234
2328
|
// Set realCLSData even if there are zero shifts - this tells us Chrome ran and found nothing
|
|
2235
2329
|
if (metadata.layoutShifts !== undefined) {
|
|
2236
2330
|
realCLSData = {
|
|
@@ -2329,15 +2423,27 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
|
|
|
2329
2423
|
continue
|
|
2330
2424
|
}
|
|
2331
2425
|
|
|
2332
|
-
// Detect if this is a layout shift vs content change
|
|
2426
|
+
// Detect if this is a layout shift vs content change vs overlay noise
|
|
2333
2427
|
const shiftAnalysis = detectLayoutShiftVsContentChange(prevPng, currPng)
|
|
2334
2428
|
|
|
2335
|
-
//
|
|
2429
|
+
// Skip if this looks like overlay noise (fixed/absolute elements like Next.js dev indicator or Vercel toolbar)
|
|
2430
|
+
if (shiftAnalysis.isOverlayNoise) {
|
|
2431
|
+
logToDevFile(
|
|
2432
|
+
`Pixel Diff Hydration: Skipping frame ${i} - detected overlay noise (fixed/absolute elements), not true CLS`
|
|
2433
|
+
)
|
|
2434
|
+
continue
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
// If we detect a true layout shift (not just content loading or overlay noise), flag it
|
|
2336
2438
|
if (shiftAnalysis.isLayoutShift) {
|
|
2337
2439
|
foundHydrationShift = true
|
|
2338
2440
|
const timeMatch = earlyFrames[i].match(/-(\d+)ms\.png$/)
|
|
2339
2441
|
const timeSinceStart = timeMatch ? parseInt(timeMatch[1], 10) : 0
|
|
2340
2442
|
|
|
2443
|
+
logToDevFile(
|
|
2444
|
+
`Pixel Diff Hydration: Detected true layout shift at ${timeSinceStart}ms (score: ${shiftAnalysis.shiftScore.toFixed(4)})`
|
|
2445
|
+
)
|
|
2446
|
+
|
|
2341
2447
|
const mcpPort = process.env.MCP_PORT || "3684"
|
|
2342
2448
|
jankDetections.push({
|
|
2343
2449
|
timestamp: `${timeSinceStart}ms`,
|
|
@@ -2378,9 +2484,29 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
|
|
|
2378
2484
|
}
|
|
2379
2485
|
|
|
2380
2486
|
// Process actual layout shifts detected by Chrome
|
|
2381
|
-
// Trust Chrome's Layout Instability API
|
|
2487
|
+
// Trust Chrome's Layout Instability API - BUT ONLY if we can identify the culprit element
|
|
2488
|
+
// and verify it's not a fixed/absolute positioned overlay
|
|
2382
2489
|
realCLSData.shifts.forEach((shift) => {
|
|
2383
2490
|
const element = shift.sources?.[0]?.node || "unknown"
|
|
2491
|
+
const position = shift.sources?.[0]?.position
|
|
2492
|
+
|
|
2493
|
+
// FILTER: Skip shifts where we couldn't identify the element
|
|
2494
|
+
// Chrome sometimes reports CLS for fixed overlays but fails to identify the element
|
|
2495
|
+
if (!shift.sources?.[0] || element === "unknown" || position === null || position === undefined) {
|
|
2496
|
+
logToDevFile(
|
|
2497
|
+
`Chrome CLS: Skipping unidentified shift (score: ${shift.score.toFixed(4)}) - cannot verify if it's a true CLS or fixed overlay noise`
|
|
2498
|
+
)
|
|
2499
|
+
return // Skip this shift - can't verify it's real
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// FILTER: Skip fixed/absolute positioned elements - these are overlays, not true CLS
|
|
2503
|
+
if (position === "fixed" || position === "absolute") {
|
|
2504
|
+
logToDevFile(
|
|
2505
|
+
`Chrome CLS: Filtering out ${element} shift (position: ${position}) - fixed/absolute elements don't cause true layout shifts`
|
|
2506
|
+
)
|
|
2507
|
+
return // Skip this shift
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2384
2510
|
const isCriticalElement = ["NAV", "HEADER", "BUTTON", "A"].includes(element.toUpperCase())
|
|
2385
2511
|
const isDuringLoad = shift.timestamp < 1000 // First second
|
|
2386
2512
|
|
|
@@ -2479,7 +2605,8 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
|
|
|
2479
2605
|
detections: jankDetections,
|
|
2480
2606
|
sessionId: latestSessionId,
|
|
2481
2607
|
totalFrames: sessionFiles.length,
|
|
2482
|
-
screenshotDir
|
|
2608
|
+
screenshotDir,
|
|
2609
|
+
captureTrigger
|
|
2483
2610
|
}
|
|
2484
2611
|
}
|
|
2485
2612
|
|
package/package.json
CHANGED
|
@@ -45,8 +45,9 @@ const TUIApp = ({
|
|
|
45
45
|
const [initStatus, setInitStatus] = useState<string | null>("Initializing...")
|
|
46
46
|
const [appPort, setAppPort] = useState<string>(initialAppPort)
|
|
47
47
|
const logIdCounter = useRef(0)
|
|
48
|
+
const [clearFromLogId, setClearFromLogId] = useState<number>(0) // Track log ID to clear from
|
|
48
49
|
const { stdout } = useStdout()
|
|
49
|
-
const ctrlCMessageDefault = "^C quit"
|
|
50
|
+
const ctrlCMessageDefault = "^L clear ^C quit"
|
|
50
51
|
const [ctrlCMessage, setCtrlCMessage] = useState(ctrlCMessageDefault)
|
|
51
52
|
|
|
52
53
|
const [terminalSize, setTerminalSize] = useState(() => ({
|
|
@@ -228,12 +229,19 @@ const TUIApp = ({
|
|
|
228
229
|
if (key.ctrl && input === "c") {
|
|
229
230
|
// Send SIGINT to trigger main process shutdown handler
|
|
230
231
|
process.kill(process.pid, "SIGINT")
|
|
232
|
+
} else if (key.ctrl && input === "l") {
|
|
233
|
+
// Ctrl-L: Clear logs box - set clear point to last log ID
|
|
234
|
+
const lastLogId = logs.length > 0 ? logs[logs.length - 1].id : logIdCounter.current
|
|
235
|
+
setClearFromLogId(lastLogId)
|
|
236
|
+
setScrollOffset(0) // Reset scroll to bottom
|
|
231
237
|
} else if (key.upArrow) {
|
|
232
|
-
|
|
238
|
+
const filteredCount = logs.filter((log) => log.id > clearFromLogId).length
|
|
239
|
+
setScrollOffset((prev) => Math.min(prev + 1, Math.max(0, filteredCount - maxVisibleLogs)))
|
|
233
240
|
} else if (key.downArrow) {
|
|
234
241
|
setScrollOffset((prev) => Math.max(0, prev - 1))
|
|
235
242
|
} else if (key.pageUp) {
|
|
236
|
-
|
|
243
|
+
const filteredCount = logs.filter((log) => log.id > clearFromLogId).length
|
|
244
|
+
setScrollOffset((prev) => Math.min(prev + maxVisibleLogs, Math.max(0, filteredCount - maxVisibleLogs)))
|
|
237
245
|
} else if (key.pageDown) {
|
|
238
246
|
setScrollOffset((prev) => Math.max(0, prev - maxVisibleLogs))
|
|
239
247
|
} else if (input === "g" && key.shift) {
|
|
@@ -241,12 +249,17 @@ const TUIApp = ({
|
|
|
241
249
|
setScrollOffset(0)
|
|
242
250
|
} else if (input === "g" && !key.shift) {
|
|
243
251
|
// g to go to beginning
|
|
244
|
-
|
|
252
|
+
const filteredCount = logs.filter((log) => log.id > clearFromLogId).length
|
|
253
|
+
setScrollOffset(Math.max(0, filteredCount - maxVisibleLogs))
|
|
245
254
|
}
|
|
246
255
|
})
|
|
247
256
|
|
|
248
|
-
// Calculate visible logs
|
|
249
|
-
const
|
|
257
|
+
// Calculate visible logs - filter to only show logs after the clear point
|
|
258
|
+
const filteredLogs = logs.filter((log) => log.id > clearFromLogId)
|
|
259
|
+
const visibleLogs = filteredLogs.slice(
|
|
260
|
+
Math.max(0, filteredLogs.length - maxVisibleLogs - scrollOffset),
|
|
261
|
+
filteredLogs.length - scrollOffset
|
|
262
|
+
)
|
|
250
263
|
|
|
251
264
|
// Render compact header for small terminals
|
|
252
265
|
const renderCompactHeader = () => (
|
|
@@ -331,7 +344,7 @@ const TUIApp = ({
|
|
|
331
344
|
<Box flexDirection="column" borderStyle="single" borderColor="gray" paddingX={1} flexGrow={1} minHeight={0}>
|
|
332
345
|
{!isVeryCompact && (
|
|
333
346
|
<Text color="gray" dimColor>
|
|
334
|
-
Logs ({
|
|
347
|
+
Logs ({filteredLogs.length} total{scrollOffset > 0 && `, scrolled up ${scrollOffset} lines`})
|
|
335
348
|
</Text>
|
|
336
349
|
)}
|
|
337
350
|
|
|
@@ -427,10 +440,6 @@ const TUIApp = ({
|
|
|
427
440
|
// Single space after type
|
|
428
441
|
const typeSpacing = ""
|
|
429
442
|
|
|
430
|
-
// For alignment: if no type tag, add spacing equivalent to a tag
|
|
431
|
-
// This aligns SERVER logs without tags with those that have tags
|
|
432
|
-
const alignmentSpacing = !type ? " " : "" // ~7 chars for average tag like [GET]
|
|
433
|
-
|
|
434
443
|
return (
|
|
435
444
|
<Text key={log.id} wrap="truncate-end">
|
|
436
445
|
<Text dimColor>[{timestamp}]</Text>
|
|
@@ -438,14 +447,14 @@ const TUIApp = ({
|
|
|
438
447
|
<Text color={sourceColor} bold>
|
|
439
448
|
[{source}]
|
|
440
449
|
</Text>
|
|
441
|
-
<Text>{sourceSpacing} </Text>
|
|
442
450
|
{type ? (
|
|
443
451
|
<>
|
|
452
|
+
<Text>{sourceSpacing} </Text>
|
|
444
453
|
<Text color={typeColors[type] || "#A0A0A0"}>[{type}]</Text>
|
|
445
454
|
<Text>{typeSpacing} </Text>
|
|
446
455
|
</>
|
|
447
456
|
) : (
|
|
448
|
-
<Text>
|
|
457
|
+
<Text> </Text>
|
|
449
458
|
)}
|
|
450
459
|
<Text>{message}</Text>
|
|
451
460
|
</Text>
|
/package/mcp-server/.next/static/{eVL_05d0pOH_qw2twMoct → _qXVPjMKmdbmLNrnd_3zK}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/mcp-server/.next/static/{eVL_05d0pOH_qw2twMoct → _qXVPjMKmdbmLNrnd_3zK}/_ssgManifest.js
RENAMED
|
File without changes
|