Skip to content

feat(qemu): default a VM to a window on a graphical host - #28

Merged
NovusEdge merged 1 commit into
mainfrom
tui-default-window
Aug 9, 2026
Merged

feat(qemu): default a VM to a window on a graphical host#28
NovusEdge merged 1 commit into
mainfrom
tui-default-window

Conversation

@NovusEdge

@NovusEdge NovusEdge commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Flips the display default. auto (and an empty display) now resolves to a qemu window whenever the host can draw one, for every mode and installed state. Before, only the uninstalled-disk installer console got a window; live, cloud, and installed VMs went to VNC, so a provisioned desktop VM showed nothing on the host.

DisplayKind drops to two facts:

func DisplayKind(pref string, graphical bool) string {
    if !graphical { return DisplayVNC }   // host veto, no display server
    if pref == "vnc" { return DisplayVNC } // explicit opt-out
    return DisplayWindow                    // auto / "" / window
}

mode and installed no longer affect the surface, so they left the signature; NeedsWindow, core.DisplayKind, and the two detail.go call sites were updated. The graphical veto stays unconditional and first, so a window preference still degrades to VNC on a headless host rather than crashing qemu's -display gtk.

display = "vnc" opts a single VM back to headless; STOAT_GRAPHICAL=0 does it host-wide.

Swept the now-false prose: the "cloud VMs get no window" comments (config, password, core, cloudinit), the installer-console-specific printDisplay wording, the detail console-password row (it reported "over vnc" unconditionally, now reports the real surface), the form hint, and the docs (troubleshooting, installation, cli, access-and-auth).

Tests: DisplayKind table now expects a window for live/cloud/installed on a graphical host, VNC on a headless one; several TUI/CLI tests pin STOAT_GRAPHICAL explicitly since the sandbox reads as graphical. just check and just test pass.

Summary by CodeRabbit

  • New Features

    • VMs now open in graphical QEMU windows by default on hosts with a graphical session.
    • Explicit VNC mode remains available for headless operation.
    • Hosts without a graphical session automatically use VNC, with instructions for connecting.
    • Console access guidance now identifies whether credentials are used through QEMU or VNC.
  • Documentation

    • Updated CLI, installation, access, and troubleshooting guidance for display behavior and VNC fallback.

@NovusEdge NovusEdge added the enhancement New feature label Aug 9, 2026
@NovusEdge NovusEdge self-assigned this Aug 9, 2026
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@NovusEdge, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c06ac6e-f3e2-4752-805b-6e611ad8af7a

📥 Commits

Reviewing files that changed from the base of the PR and between 9b66f2b and cc4cbb4.

📒 Files selected for processing (3)
  • internal/tui/detail.go
  • internal/tui/detail_test.go
  • internal/tui/form.go

Walkthrough

VM display selection now defaults to QEMU windows on graphical hosts. Explicit VNC preferences and hosts without graphical sessions use VNC. CLI, TUI, tests, comments, and documentation reflect the updated behavior.

Changes

VM display behavior

Layer / File(s) Summary
Display selection rule
internal/qemu/args.go, internal/core/display.go, internal/qemu/*_test.go, internal/core/*_test.go
Display selection now depends on the VM preference and host graphical state. Graphical hosts use QEMU windows by default.
CLI and TUI display surfaces
internal/cli/*, internal/tui/*
CLI and TUI output now identifies QEMU windows or VNC surfaces and reports headless fallback behavior.
Display behavior documentation
docs/*, internal/cloudinit/*, internal/config/*, internal/core/core.go
Documentation and comments describe default QEMU windows, explicit VNC mode, headless fallback, and console login paths.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • NovusEdge/stoat#16: Shares display behavior concerns for installed and uninstalled disk VMs, but changes Alpine disk VM auto-installation.
  • NovusEdge/stoat#17: Shares disk-install VM visibility concerns, but adds serial logging and an installation banner.

Suggested labels: tui

Poem

I’m a rabbit by the QEMU light,
Windows bloom when hosts are bright.
VNC waits when screens are bare,
Explicit choices guide the hare.
Console paths now plainly show—
Where every VM screen will go.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: graphical hosts now default VMs to a QEMU window.
Docstring Coverage ✅ Passed Docstring coverage is 88.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tui-default-window

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

DisplayKind's "auto" default no longer singles out the uninstalled
disk-mode install console. Every mode and installed state now opens a
real qemu window on a graphical host; display="vnc" opts a VM out, and
a headless host still vetoes the window unconditionally.

DisplayKind drops the now-unused mode and installed parameters. The
change touches few call sites (qemu.NeedsWindow, core.DisplayKind,
tui/detail.go), so dropping them was cleaner than keeping dead
arguments around.

Updates the doc comments, TUI hints, and docs/ prose that described
"auto" as the installer-console default, and fixes the console-password
row in the TUI to name the surface the password is actually reached
at instead of hardcoding VNC.
@NovusEdge
NovusEdge force-pushed the tui-default-window branch from 9b66f2b to cc4cbb4 Compare August 9, 2026 15:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/concepts/access-and-auth.md`:
- Line 133: Update the console-access documentation in
docs/concepts/access-and-auth.md at lines 133-133 to state that VNC is used when
the host is headless or display = "vnc"; apply the same wording and condition to
the cloud-console comment in internal/core/core.go at lines 250-251 so both
copies remain aligned.

In `@docs/reference/cli.md`:
- Line 157: Update the shell example code fences at the referenced documentation
locations to include the sh language identifier on both opening fences,
resolving markdownlint MD040 without changing the example contents.

In `@internal/config/config.go`:
- Around line 48-53: Update the Display field comment to state that "auto" uses
a QEMU window unless there is no usable graphical session or STOAT_GRAPHICAL=0
disables windows, in which cases it falls back to VNC. Keep the existing value
descriptions and legacy empty-value behavior unchanged.

In `@internal/qemu/args.go`:
- Around line 22-26: Update the DisplayKind comment to reflect that “window”, ""
and “auto” request a window only when the host is graphical; document that
DisplayKind returns VNC for non-graphical hosts, including when display is set
to “window”.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 00f25820-3af4-4192-b061-db8840b59eef

📥 Commits

Reviewing files that changed from the base of the PR and between eb8889b and 9b66f2b.

📒 Files selected for processing (20)
  • docs/concepts/access-and-auth.md
  • docs/getting-started/installation.md
  • docs/reference/cli.md
  • docs/troubleshooting.md
  • internal/cli/display_test.go
  • internal/cli/run_vm.go
  • internal/cli/wire/dto_test.go
  • internal/cloudinit/cloudinit.go
  • internal/config/config.go
  • internal/config/password.go
  • internal/core/core.go
  • internal/core/display.go
  • internal/core/display_test.go
  • internal/qemu/args.go
  • internal/qemu/args_test.go
  • internal/qemu/display_test.go
  • internal/tui/creds_test.go
  • internal/tui/detail.go
  • internal/tui/detail_test.go
  • internal/tui/form.go

```
ssh stoat@127.0.0.1:2202
console stoat / stoat (qemu window only)
console stoat / stoat (over the qemu window, or vnc on a headless host)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document both VNC routes for console access.

The text limits VNC to the headless fallback, but display = "vnc" also selects VNC on graphical hosts. Keep both copies aligned.

  • docs/concepts/access-and-auth.md#L133-L133: mention VNC when the host is headless or display = "vnc".
  • internal/core/core.go#L250-L251: apply the same condition to the cloud-console comment.
📍 Affects 2 files
  • docs/concepts/access-and-auth.md#L133-L133 (this comment)
  • internal/core/core.go#L250-L251
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/concepts/access-and-auth.md` at line 133, Update the console-access
documentation in docs/concepts/access-and-auth.md at lines 133-133 to state that
VNC is used when the host is headless or display = "vnc"; apply the same wording
and condition to the cloud-console comment in internal/core/core.go at lines
250-251 so both copies remain aligned.

Comment thread docs/reference/cli.md
Exactly one kind of VM gets a real QEMU window: a **disk-mode VM that is not yet installed**, on a host with a graphical session. Its OS installer draws to VGA and a human has to drive it, so `up` says so:
A VM gets a real QEMU window by default, on a host with a graphical session. Set `display = "vnc"` in `vm.toml` (or cycle it with the `d` key in the TUI) to keep a VM headless instead. QEMU then starts with `-display none` and a VNC server bound to a unix socket in the VM's directory; `-display none` cannot be undone on a running QEMU, so binding VNC at launch keeps a misbehaving guest recoverable.

```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add language identifiers to the shell examples.

markdownlint reports MD040 at Line 157 and Line 175. Add sh to both opening fences so the documentation lint passes.

Also applies to: 175-175

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 157-157: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/cli.md` at line 157, Update the shell example code fences at
the referenced documentation locations to include the sh language identifier on
both opening fences, resolving markdownlint MD040 without changing the example
contents.

Source: Linters/SAST tools

Comment thread internal/config/config.go
Comment on lines 48 to +53
// Display is the user's screen preference: "" or "auto" (default),
// "window", or "vnc". core.validateDisplay is the single place that
// checks the value; empty means an old vm.toml predates this field, so
// it must read the same as "auto". qemu.DisplayKind is the rule that
// turns this into DisplayWindow or DisplayVNC.
// "window", or "vnc". "auto" opens a real qemu window on a graphical
// host, and falls back to VNC only when the host has no display server;
// set "vnc" to opt out of a window on a graphical host. core.validateDisplay
// is the single place that checks the value; empty means an old vm.toml
// predates this field, so it must read the same as "auto".

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include the host override in the Display contract.

The comment says "auto" falls back only when the host has no display server. STOAT_GRAPHICAL=0 also forces VNC on graphical hosts. Describe both conditions, such as no usable graphical session or an override that disables windows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/config/config.go` around lines 48 - 53, Update the Display field
comment to state that "auto" uses a QEMU window unless there is no usable
graphical session or STOAT_GRAPHICAL=0 disables windows, in which cases it falls
back to VNC. Keep the existing value descriptions and legacy empty-value
behavior unchanged.

Comment thread internal/qemu/args.go
Comment on lines +22 to +26
// DisplayKind is the rule, stated over the two facts it actually depends on.
//
// pref is config.VM.Display: "window" or "vnc" pins the surface outright,
// "" or "auto" runs the installer-console default below. Only an uninstalled
// disk-mode VM wants a window under that default, because its OS installer
// draws to VGA rather than the serial console and a human has to drive it.
// live, cloud and installed disk VMs reach ssh with no console interaction,
// so their screen goes to the VNC socket unless pref overrides it.
// pref is config.VM.Display: "window" pins a window, "vnc" pins the socket,
// "" or "auto" defaults to a window. Every mode and installed state defaults
// the same way; a VM opts into VNC by setting display="vnc", not by its mode.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the headless-host rule in this comment.

Lines 24-25 imply that "window" always selects a window. DisplayKind returns VNC when graphical is false, including for "window".

State that "window", "", and "auto" request a window only on a graphical host.

Proposed fix
-// pref is config.VM.Display: "window" pins a window, "vnc" pins the socket,
-// "" or "auto" defaults to a window. Every mode and installed state defaults
+// pref is config.VM.Display: "vnc" selects the socket. "window", "" and
+// "auto" select a window on a graphical host. Every mode and installed state defaults
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// DisplayKind is the rule, stated over the two facts it actually depends on.
//
// pref is config.VM.Display: "window" or "vnc" pins the surface outright,
// "" or "auto" runs the installer-console default below. Only an uninstalled
// disk-mode VM wants a window under that default, because its OS installer
// draws to VGA rather than the serial console and a human has to drive it.
// live, cloud and installed disk VMs reach ssh with no console interaction,
// so their screen goes to the VNC socket unless pref overrides it.
// pref is config.VM.Display: "window" pins a window, "vnc" pins the socket,
// "" or "auto" defaults to a window. Every mode and installed state defaults
// the same way; a VM opts into VNC by setting display="vnc", not by its mode.
// DisplayKind is the rule, stated over the two facts it actually depends on.
//
// pref is config.VM.Display: "vnc" selects the socket. "window", "" and
// "auto" select a window on a graphical host. Every mode and installed state defaults
// the same way; a VM opts into VNC by setting display="vnc", not by its mode.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/qemu/args.go` around lines 22 - 26, Update the DisplayKind comment
to reflect that “window”, "" and “auto” request a window only when the host is
graphical; document that DisplayKind returns VNC for non-graphical hosts,
including when display is set to “window”.

@NovusEdge
NovusEdge merged commit 4250d87 into main Aug 9, 2026
7 of 8 checks passed
@NovusEdge
NovusEdge deleted the tui-default-window branch August 9, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant