Skip to content

Add serial console with draggable windows and ZMK connection fallback#30

Open
Copilot wants to merge 8 commits into
mainfrom
copilot/add-serial-console-feature-again
Open

Add serial console with draggable windows and ZMK connection fallback#30
Copilot wants to merge 8 commits into
mainfrom
copilot/add-serial-console-feature-again

Conversation

Copilot AI commented Feb 4, 2026

Copy link
Copy Markdown
Contributor

Implements Web Serial API integration for monitoring serial devices alongside ZMK Studio connections. USB connections attempt ZMK protocol first, falling back to serial console on connection failure with seamless port reuse.

Architecture

Console Management

  • ConsoleContext provides global state for multiple console instances
  • Snap positions persist across tab transitions (Console tab → window mode when navigating away, restored on return)
  • useSerialPort hook wraps Web Serial API lifecycle (read/write streams, cleanup) with support for existing ports

Window System

  • Floating console windows render at App level, visible across all tabs
  • Snapped consoles render within ConsolePage
  • DraggableWindow component handles drag/resize with 8 resize handles
  • Z-index stacking on focus, position tracking
  • Snap zones at edges (50px threshold) with equal space division for multiple windows

Console Features

  • Regex line filtering and sed-style word replacement applied to stream
  • Configurable baud rates (9600-921600)
  • Real-time text display with auto-scroll

Connection Flow

// DeviceConnection.tsx
const handleConnectWithFallback = async (method: ConnectionMethod) => {
  if (method !== "serial") {
    // Non-serial connections work as before
    return true;
  }
  
  // Use reusable transport for serial
  const transport = await connectReusableSerial();
  await zmkApp.connect(async () => transport);
  
  // Check error state to determine if ZMK connection succeeded
  if (zmkApp.state.error) {
    // ZMK failed, release port and open serial console
    const port = transport.release();
    if (port) {
      consoleContext.addConsoleFromPort(port);
    }
    return false;
  }
  
  return true;
};

The connection fallback checks zmkApp.state.error after the connection attempt to reliably detect ZMK protocol failures. When ZMK negotiation fails, the ReusableRpcTransport releases the serial port without closing it, allowing the serial console to reuse the same connection without requiring the user to select the port again.

Files

New:

  • src/hooks/useSerialPort.ts - Web Serial API wrapper with existing port support
  • src/components/SerialConsole.tsx - Console UI with filtering
  • src/components/DraggableWindow.tsx - Window management
  • src/pages/ConsolePage.tsx - Multi-console orchestration (snapped consoles only)
  • src/contexts/ConsoleContext.tsx - Global state
  • src/types/webserial.d.ts - Type definitions
  • src/lib/transport/reusableSerial.ts - Reusable transport wrapper

Modified:

  • src/App.tsx - Renders floating consoles globally, Console tab, tab transition handling
  • src/components/DeviceConnection.tsx - ReusableSerial integration, error-based fallback detection
  • src/components/SplashScreen.tsx - Fallback method binding
Original prompt

Please implement serial console feature.
The feature supports connecting to web-serial with custom baud rate and shows text messages sent from it.
It also supports filtering lines with regex based keyword search, sed based word replacement, which is placed on header of web-serial view.
It also supports sending text message.

As for relation to ZMK studio connection feature, requirements are like below:

  • Serial console should be shown as if draggable/resizeable window in DYA studio and other features should not be interrupted.
  • Multiple serial console windows are supported to monitor multiple targets at the same time.
  • A new tab "Console" should be created. User can add new serial console connection from button in the tab.
    • The console tab should contain feature to snap console window. If console window is dragged-and-dropped to left/top/right/bottom of the snap area, the console should move to the snap area.
      If multiple consoles are dragged, the area is divided into equal size and each consoles fits to within the area.
      Then, the console's header shows button to snap-out. If snap-out button clicked, it backs to window mode. You can image windows10's window snapping.
    • If user left "Console" tab, all the consoles changes to window mode and the connection and contents should be kept.
    • If user go back to console tab, the previous snap status restored.
  • Finally, USB connection button in splash screen should work to connect either zmk-studio protocol or serial console.
    • You should try connect as zmk-studio protocol first. If the connection failed (=first request to get device status timed-out), the connection should be re-used as serial console and new serial console window should be shown. In this case, user can retry to connect to new device in splash screen and it results in the same process.

To re-use connection when zmk-studio protocol negotiation failed, you will have to properly wrap RPCConnection interface to avoid the connection be closed by react-zmk-studio library.
But once after connection properly worked, the full-control should be delegated to react-zmk-studio library.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 6 commits February 4, 2026 15:53
Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
- Created useSerialPort hook for Web Serial API integration
- Created SerialConsole component with filtering and text replacement
- Created DraggableWindow component for window management
- Added ConsolePage with snap functionality
- Added Console tab to main navigation

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
- Created ConsoleContext to manage console windows globally
- Implemented tab state persistence for snap positions
- Updated App.tsx to handle tab transitions
- Updated ConsolePage to use context
- Added automatic snap state restoration when switching tabs

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
- Modified DeviceConnection to support fallback to serial console
- Added onConnectWithFallback method with timeout
- Updated SplashScreen to use fallback connection
- Created reusableSerial transport wrapper (not yet in use)
- When ZMK connection fails, automatically opens serial console

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
- Created webserial.d.ts with Web Serial API type definitions
- Fixed import type declarations
- Fixed timeout type in reusableSerial
- Added addConsoleFromPort to interface
- Build now succeeds without errors

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
- Fix unsafe type casting in reusableSerial (use undefined instead of null)
- Improve console ID display (show "#1" instead of "console-1")
- All tests passing

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement serial console feature with custom baud rate Add serial console with draggable windows and ZMK connection fallback Feb 4, 2026
Copilot AI requested a review from cormoran February 4, 2026 16:16
@github-actions

github-actions Bot commented Feb 4, 2026

Copy link
Copy Markdown

🚀 Cloudflare Workers Preview

Preview URL: https://e6c0676e-dya-studio.cormoran707.workers.dev

Deployed from commit 60f3c83

@cormoran cormoran left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@copilot please check comments

Comment thread src/pages/ConsolePage.tsx Outdated
consoleId={console.id}
onClose={() => removeConsole(console.id)}
/>
</DraggableWindow>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

DraggableWindow should be not in ConsolePage. It should be in global space (under body?) So that it's shown even if other tab is selected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 472c779. DraggableWindow now renders at App.tsx level so floating consoles are visible across all tabs. Snapped consoles remain in ConsolePage.

Comment thread src/components/DeviceConnection.tsx Outdated
Comment thread src/components/DeviceConnection.tsx Outdated
- Move DraggableWindow to global space (App.tsx) so consoles visible across all tabs
- Fix ZMK connection fallback to check zmkApp.state.error instead of timeout
- Implement ReusableSerial to reuse connection when ZMK negotiation fails
- Add connectWithPort to useSerialPort for existing ports
- Update SerialConsole to accept existingPort prop
- Remove floating console logic from ConsolePage (now in App.tsx)
- Add ConsoleProvider to DeviceConnection tests
- All tests passing

Co-authored-by: cormoran <7994064+cormoran@users.noreply.github.com>

Copilot AI commented Feb 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please check comments

All feedback addressed in commit 472c779:

  • Floating consoles now render globally at App level (visible across tabs)
  • Connection fallback checks error state instead of timeout
  • ReusableSerial implemented to reuse connection when ZMK fails
    All tests passing ✅

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

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