Conversation
Resolve conflicts: keep stepper UI and ANSI terminal from branch, adopt lucide v1 icon names from develop. Fix lint errors in SerialTerminal (unused import, bare expression, missing each keys). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflicts in flashtool: keep stepper UI and ANSI terminal from feature branch, adopt scoped serial/flash lifecycle contexts from develop. Update FlashContext to use ConnectApplication. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deploying openshockapp with
|
| Latest commit: |
350330b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d4967096.openshockapp.pages.dev |
| Branch Preview URL: | https://feature-improved-serial-flas.openshockapp.pages.dev |
There was a problem hiding this comment.
Pull request overview
Adds an improved serial console experience to the flashtool page, including ANSI-colored output rendering and a persistent terminal panel, while adjusting FlashManager to support connecting in “application mode” first and entering bootloader mode lazily.
Changes:
- Introduces a new
SerialTerminalcomponent with command history, auto-scroll, and line capping for display. - Adds a lightweight ANSI SGR parser to render colored/styled serial output.
- Refactors
FlashManagerconnection flow to supportConnectApplication()and starts an application serial read loop on connect.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/routes/flashtool/SerialTerminal.svelte |
New terminal UI component rendering segmented ANSI output and command input. |
src/routes/flashtool/FlashManager.ts |
Adds application-mode connect/read-loop path and bootloader connect rename. |
src/routes/flashtool/flash-context.svelte.ts |
Switches connect flow to ConnectApplication(). |
src/routes/flashtool/ansi.ts |
New ANSI parser/stripper for styled terminal output. |
src/routes/flashtool/+page.svelte |
Reworks UI into a stepper flow and integrates persistent terminal + ANSI parsing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| async ensureBootloader(forceReset?: boolean) { | ||
| if (!this.serialPort) return false; | ||
| if (this.loader && !forceReset) return true; | ||
|
|
||
| const serialPort = await this._cycleTransport(); | ||
|
|
||
| const loader = await setupESPLoader(serialPort, this.terminal); | ||
| if (loader) { | ||
| // success (if a failure occurs we're left disconnected) | ||
| this.serialPort = serialPort; | ||
| this.loader = loader; | ||
| this.chip = loader.chip.CHIP_NAME; | ||
| } | ||
| } |
There was a problem hiding this comment.
ensureBootloader returns false/true in the early branches but has no return statement after attempting setupESPLoader(). That makes the resolved value undefined on the success/failure path and hides whether entering bootloader actually worked (and on failure leaves the instance in the disconnected state after _cycleTransport()). Return an explicit boolean (e.g., true when loader is set, otherwise false) and consider restoring/handling the disconnected state on failure so callers can react appropriately.
| async function copyOutput() { | ||
| const text = lines.map((l) => stripAnsi(l.text)).join('\n'); | ||
| await navigator.clipboard.writeText(text); | ||
| copied = true; | ||
| clearTimeout(copyTimeout); | ||
| copyTimeout = setTimeout(() => (copied = false), 2000); | ||
| } |
There was a problem hiding this comment.
copyOutput() assumes navigator.clipboard.writeText will always succeed. In practice it can throw/reject (permission denied, insecure context, clipboard API unavailable), which would create an unhandled rejection and leave the UI in a confusing state. Wrap the clipboard call in try/catch and surface a failure state (or at least avoid toggling copied when the write fails).
| let copyTimeout: ReturnType<typeof setTimeout> | undefined; | ||
|
|
||
| async function copyOutput() { | ||
| const text = lines.map((l) => stripAnsi(l.text)).join('\n'); |
There was a problem hiding this comment.
copyOutput() copies from lines, but the UI renders visibleLines (capped to MAX_LINES). If the parent ever passes more than MAX_LINES, the copied output will include lines the user can’t see and could be unexpectedly large. Consider copying from visibleLines (or explicitly documenting that copy includes the full backlog).
| const text = lines.map((l) => stripAnsi(l.text)).join('\n'); | |
| const text = visibleLines.map((l) => stripAnsi(l.text)).join('\n'); |
| writeLine: (data: string) => { | ||
| terminalText += data + '\n'; | ||
| const newLines = [...terminalLines, makeTerminalLine(data)]; | ||
| terminalLines = newLines.length > MAX_LINES ? newLines.slice(-MAX_LINES) : newLines; | ||
| }, | ||
| write: (data: string) => { | ||
| terminalText += data; | ||
| if (terminalLines.length > 0) { | ||
| const last = terminalLines[terminalLines.length - 1]; | ||
| const newText = last.text + data; | ||
| const parsed = parseLogLine(newText); | ||
| terminalLines = [ | ||
| ...terminalLines.slice(0, -1), | ||
| { | ||
| ...last, | ||
| text: newText, | ||
| segments: parseAnsi(newText), | ||
| logLevel: parsed?.logLevel ?? null, | ||
| deviceUptime: parsed?.deviceUptime ?? null, | ||
| logTag: parsed?.tag ?? null, | ||
| }, | ||
| ]; | ||
| } else { |
There was a problem hiding this comment.
Terminal output updates clone/slice the entire terminalLines array on every writeLine/write call. With high-rate serial logs this becomes O(n) per chunk and can noticeably degrade UI responsiveness even with MAX_LINES = 5000. Prefer mutating the $state array in-place (and trimming in-place), or use a ring buffer approach to avoid allocating/copying thousands of entries per incoming line.
Summary
[uptimeMs][LEVEL][tag]) and ESP-IDF (LEVEL (uptimeMs) TAG:) log formats to color output by log level and display device uptimeDetails
ensureApplication()