Skip to content

feat: Improved serial flasher#183

Open
hhvrc wants to merge 5 commits intodevelopfrom
feature/improved-serial-flasher
Open

feat: Improved serial flasher#183
hhvrc wants to merge 5 commits intodevelopfrom
feature/improved-serial-flasher

Conversation

@hhvrc
Copy link
Copy Markdown
Contributor

@hhvrc hhvrc commented Apr 1, 2026

Summary

  • Add a persistent serial terminal panel with ANSI-colored output, command input with history, and auto-scroll
  • Parse OpenShock ([uptimeMs][LEVEL][tag]) and ESP-IDF (LEVEL (uptimeMs) TAG:) log formats to color output by log level and display device uptime
  • Rework flashtool UI into a guided 4-step wizard (Connect → Channel → Board → Flash)
  • Refactor FlashManager to connect in application mode first, entering bootloader lazily only when flashing
  • Scope FlashManager lifecycle to the component via FlashContext

Details

  • SerialTerminal: new component with 5000-line buffer, copy/clear/reset controls, and monospace rendering of parsed ANSI SGR sequences
  • ansi.ts: lightweight ANSI parser + log line parser that extracts log level, device uptime, and source tag from serial output
  • FlashManager: application-mode connect with serial read loop, proper reader/writer lock cleanup on loop exit, and explicit return from ensureApplication()
  • Stepper UX: step confirmation now tracks both channel and version, step buttons have aria-labels for accessibility

hhvrc and others added 3 commits March 23, 2026 00:05
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>
@hhvrc hhvrc self-assigned this Apr 1, 2026
Copilot AI review requested due to automatic review settings April 1, 2026 05:52
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 1, 2026

Deploying openshockapp with  Cloudflare Pages  Cloudflare Pages

Latest commit: 350330b
Status: ✅  Deploy successful!
Preview URL: https://d4967096.openshockapp.pages.dev
Branch Preview URL: https://feature-improved-serial-flas.openshockapp.pages.dev

View logs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 SerialTerminal component with command history, auto-scroll, and line capping for display.
  • Adds a lightweight ANSI SGR parser to render colored/styled serial output.
  • Refactors FlashManager connection flow to support ConnectApplication() 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 226 to 239
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;
}
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +108 to +114
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);
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
let copyTimeout: ReturnType<typeof setTimeout> | undefined;

async function copyOutput() {
const text = lines.map((l) => stripAnsi(l.text)).join('\n');
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
const text = lines.map((l) => stripAnsi(l.text)).join('\n');
const text = visibleLines.map((l) => stripAnsi(l.text)).join('\n');

Copilot uses AI. Check for mistakes.
Comment on lines 49 to +69
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 {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants