Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 11, 2025

Summary by CodeRabbit

  • New Features

    • Window title support: apps can set a custom terminal window title; demos demonstrate title usage and lifecycle behavior.
  • Bug Fixes / Reliability

    • Improved title restore and fallback handling for terminals without title stacks to ensure consistent behavior.
    • Ensured screen resources are reliably stopped on exit in demos to prevent orphaned terminal state.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds Screen.setTitle(string) to the public API, implements title storage and OSC/DSC emission and restoration in TtyScreen across start/draw/stop, and updates demos to call setTitle and add scope(exit) cleanup for stopping the screen.

Changes

Cohort / File(s) Summary
Interface Addition
source/dcell/screen.d
Adds public method void setTitle(string) to the Screen interface with documentation about windowing-emulator applicability.
Tty implementation
source/dcell/ttyscreen.d
Adds private string title field and void setTitle(string) implementation; changes Vt.restoreTitle composition; emits OSC/DSC title sequences during start, draw, and stop; gates emission on vt capabilities and started state; minor cursor/VT call refactors.
Mouse demo
demos/mouse/source/mouse.d
Calls s.setTitle("Dcell Event Demo"); and adds scope(exit) to ensure s.stop() is invoked on exit.
Colors demo
demos/colors/source/colors.d
Inserts scope(exit) to call s.stop() immediately after screen creation (early cleanup path).
Hello demo
demos/hello/source/hello.d
Adds scope(exit) to ensure ts.stop() is invoked on scope exit after null-check and before start.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Demo as Demo (mouse/colors/hello)
    participant ScreenIface as Screen
    participant Tty as TtyScreen
    participant Terminal as Terminal Emulator

    Demo->>ScreenIface: setTitle(title)
    ScreenIface->>Tty: setTitle(title)  -- store title
    Note right of Tty: internal title state updated
    alt on start or draw when started and title set
        Tty->>Terminal: emit OSC set-title sequence
    else on start/draw when no title or on restore
        Tty->>Terminal: emit OSC restore-title sequence
    end
    alt on stop
        Tty->>Terminal: emit OSC restore-title sequence
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect OSC/DSC escape sequence correctness and concatenation in Vt.restoreTitle.
  • Verify gating logic around vt capability checks (e.g., vt.setTitle, vt.enterURL) and started/stopped lifecycle.
  • Ensure Screen interface change is safely implemented across other Screen implementations (search for other implementors).
  • Review demo scope(exit) placements for unintended early stop semantics (notably demos/colors/source/colors.d where stop may occur before initialization completes).

Possibly related PRs

Poem

🐰 I tapped the terminal brim,
A tiny OSC whisper, slim.
"Dcell Event Demo" takes the stage,
A ribbon of text upon the page.
πŸ₯•βœ¨

Pre-merge checks and finishing touches

βœ… Passed checks (3 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title accurately describes the main feature addition: implementing window title manipulation functionality via the new setTitle() method across the Screen interface and implementations.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gdamore/window-title

πŸ“œ Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 4077086 and 7fcdcdc.

πŸ“’ Files selected for processing (5)
  • demos/colors/source/colors.d (1 hunks)
  • demos/hello/source/hello.d (1 hunks)
  • demos/mouse/source/mouse.d (2 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • source/dcell/screen.d
  • demos/mouse/source/mouse.d
🧰 Additional context used
🧠 Learnings (2)
πŸ“š Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
πŸ“š Learning: 2025-12-08T19:17:33.678Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:448-494
Timestamp: 2025-12-08T19:17:33.678Z
Learning: In source/dcell/ttyscreen.d, Color.reset is defined as a value above the range of valid colors. In the sendColors() function, after emitting the reset sequence (vt.resetFgBg), Color.reset values naturally skip all subsequent color-setting logic (isRGB checks and palette range checks < 8, < 256) because they are out of range, which is the correct behavior by design.

Applied to files:

  • demos/colors/source/colors.d
πŸ”‡ Additional comments (10)
demos/colors/source/colors.d (1)

130-133: Defensive cleanup pattern is safe, though note the redundant call on normal exit.

The scope(exit) ensures s.stop() is called on exceptional exits (exceptions, early returns). Since stop() at line 177 is also called in the normal flow, s.stop() will be invoked twice on normal exit. This is safe because TtyScreen.stop() guards with if (!started) and returns early on subsequent calls.

demos/hello/source/hello.d (1)

78-81: Exception-safety pattern is sound, though note limited utility in the normal flow.

The scope(exit) provides cleanup on exceptional exits (e.g., exceptions during initialization or the event loop). However, the normal exit path calls ts.stop() explicitly in handleEvent (line 58) followed by exit(0) (line 59), which terminates the process immediately without unwinding the stackβ€”so the scope(exit) block won't run in that case. Still, this defensive pattern is safe and useful for exceptional scenarios.

source/dcell/ttyscreen.d (8)

102-108: Comment typo corrected and title restoration fallback is well-designed.

The typo "FOr" β†’ "For" noted in previous reviews has been corrected. The updated restoreTitle sequence now concatenates an OSC 2 empty-title sequence with the stack-restore command, providing a graceful fallback for terminals that support title changes but not the title stack.


240-248: Using null for unsupported legacy terminal features is clearer than empty strings.

Setting OSC-related fields to null instead of empty strings explicitly indicates "not supported" rather than "supported but empty," improving code clarity and making null-checks more meaningful (see line 728).


268-279: Startup sequence reordering and conditional title emission look correct.

The reordered operations (hiding cursor, disabling auto-margin, enabling CSI-u) now occur after establishing the alternate screen but before saving the title stack, which is a logical progression. The conditional title emission at lines 276-279 correctly checks both that a title is set and that vt.setTitle is available before emitting the sequence.


298-298: Title restoration in stop() completes the lifecycle correctly.

Adding puts(vt.restoreTitle) ensures the original terminal title is restored when the screen is stopped, matching the saveTitle call in start() and providing proper cleanup.


440-448: setTitle() implementation correctly addresses previous review concerns.

The implementation now properly:

  • Includes the flush() call (line 446) when emitting the title sequence while started
  • Avoids premature title-stack manipulationβ€”it simply stores this.title when not started, deferring stack operations to start() (lines 276-279)

The pattern matches other control methods like setSize() and handles the legacy case correctly via the null-check.


532-532: Title field addition is straightforward.

The title field stores the current window title for use in setTitle() and during start().


643-643: Instance reference is more consistent with the file's predominant pattern.

Changing from Vt.setCursorPosition to vt.setCursorPosition improves consistency. While both forms work for enum string constants, instance references (vt.) are used throughout most of the file for accessing terminal capabilities, making this change a minor style improvement.


728-728: Null-check is appropriate given the updated legacy handling.

The change from == "" to !is null aligns with the updated legacy terminal handling (lines 240-241) where enterURL is now set to null rather than an empty string, making the null-check semantically correct.


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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/dcell/ttyscreen.d (1)

252-286: Add vt.restoreTitle call to stop() to balance the title stack

start() saves the window title via vt.saveTitle (and conditionally restores/resaves when no custom title is set), but stop() never emits vt.restoreTitle. This leaves the saved title on the terminal's title stack and can prevent the original window title from being explicitly restored on exit. The implementation of setTitle() shows the developer understood this stack behavior (with its comment "restore previously saved title, but also make sure it remains on the stack"), making the omission in stop() appear unintentional.

Add the restore sequence early in stop(), guarded by a capability check:

    void stop()
    {
        if (!started)
            return;

+       if (vt.restoreTitle.length)
+       {
+           puts(vt.restoreTitle);
+       }
+
        puts(vt.enableAutoMargin);
        puts(vt.resetFgBg);
        ...

While xterm, VTE, and kitty all support the standard CSI 23;2t sequence, terminal behavior with title stacks varies by emulator version and configuration (e.g., xterm's allowTitleOps setting). Test the placement and interaction with alternate-screen cleanup across your target terminals to ensure the title is restored correctly.

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between f01d9fc and ff86c86.

πŸ“’ Files selected for processing (3)
  • demos/mouse/source/mouse.d (1 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
πŸ“š Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
πŸ”‡ Additional comments (2)
demos/mouse/source/mouse.d (1)

107-112: Title-setting call placement looks good

Calling s.setTitle("Dcell Event Demo"); right after start() and input-mode setup cleanly demonstrates the new API and ensures the title is applied while the screen is active. No issues here.

source/dcell/screen.d (1)

150-155: New Screen.setTitle API looks fine; consider clarifying semantics and impact

The interface addition is reasonable and matches existing naming conventions (setSize, setStyle), but a couple of clarifications could help:

  • This is a breaking change for any external Screen implementations; they'll now need to add a setTitle stub. Consider noting that in release notes or appropriate version markers.
  • The documentation could specify when it's valid to call setTitle (e.g., allowed before start(), after stop(), or only while active) and explicitly note that implementations on non-windowed or legacy terminals are free to treat it as a no-op.

This guidance will make it easier for alternative backends to implement the method consistently.

@gdamore gdamore force-pushed the gdamore/window-title branch from ff86c86 to 3777731 Compare December 11, 2025 18:05
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
source/dcell/ttyscreen.d (1)

445-459: Missing flush() call when started is true.

The issues from the previous review remain unaddressed. When started is true, a flush() call should follow the puts() to match the pattern in setSize() (line 420) and sendPasteEnable() (line 834). Additionally, the pre-start stack manipulation is redundant since start() already handles title stack management.

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between ff86c86 and 3777731.

πŸ“’ Files selected for processing (3)
  • demos/mouse/source/mouse.d (1 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • demos/mouse/source/mouse.d
  • source/dcell/screen.d
🧰 Additional context used
🧠 Learnings (1)
πŸ“š Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
πŸ”‡ Additional comments (3)
source/dcell/ttyscreen.d (3)

276-284: LGTM: Title handling in start() is well-structured.

The conditional logic correctly handles both cases: applying a pre-set title or maintaining the original terminal title via the save/restore stack.


303-303: LGTM: Restoring title on stop() ensures proper cleanup.

This correctly restores the original window title when the screen stops.


543-543: LGTM: Appropriate state variable for title management.

string exitURL = "\x1b]8;;\x1b\\";
string setWindowSize = "\x1b[8;%d;%dt";
// Some terminals do not support the title stack, but do support
// changing the title. FOr those we set the title back to the
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

Typo: "FOr" should be "For".

-        // changing the title.  FOr those we set the title back to the
+        // changing the title.  For those we set the title back to the
πŸ“ 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
// changing the title. FOr those we set the title back to the
// changing the title. For those we set the title back to the
πŸ€– Prompt for AI Agents
In source/dcell/ttyscreen.d around line 103, the comment contains a typo: "FOr"
should be corrected to "For"; update the comment text to use proper
capitalization ("For those we set the title back to the") and save the file.

@gdamore gdamore force-pushed the gdamore/window-title branch from 3777731 to bf746f9 Compare December 11, 2025 18:16
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/dcell/ttyscreen.d (1)

238-248: Guard methods against empty vt. format strings to avoid FormatException on legacy terminals*

In the legacy path (lines 238–248), several vt.* attributes are set to "" (empty strings). This creates a critical issue in methods that pass arguments to format(): calling format("", arg) in D's std.format throws FormatException because the empty format string cannot accept arguments.

The following methods are affected and will crash:

  • setTitle() (line 451): format(vt.setTitle, title)
  • enterURL() (line 481): format(vt.enterURL, url)
  • exitURL() (line 489): format(vt.exitURL, url)
  • setWindowSize() (line 497): format(vt.setWindowSize, width, height)

Add length checks before format calls to make these methods harmless on legacy terminals:

    void setTitle(string title)
    {
        this.title = title;
-        if (started)
-        {
-            puts(format(vt.setTitle, title));
-            flush();
-        }
+        if (started && vt.setTitle.length)
+        {
+            puts(format(vt.setTitle, title));
+            flush();
+        }
    }

Apply the same pattern to enterURL(), exitURL(), and setWindowSize().

Note: Methods without format arguments (saveTitle, restoreTitle, enableCsiU, disableCsiU) will not throw but will produce no output, which is acceptable; however, applying the guard for consistency is recommended.

♻️ Duplicate comments (1)
source/dcell/ttyscreen.d (1)

102-107: Fix comment typo (β€œFOr” β†’ β€œFor”)

Minor nit: Line 103 reads FOr instead of For; worth correcting while you’re here.

-        // Some terminals do not support the title stack, but do support
-        // changing the title.  FOr those we set the title back to the
+        // Some terminals do not support the title stack, but do support
+        // changing the title.  For those we set the title back to the
🧹 Nitpick comments (1)
source/dcell/screen.d (1)

150-155: Clarify semantics of setTitle("") in the interface contract

The method is well-placed, but the doc doesn’t specify what an empty title string should mean (clear/unset vs restore original vs β€œno-op”). Since TtyScreen already treats empty differently before/after start(), it’d be good to document the intended semantics here so other backends and callers can align.

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 3777731 and bf746f9.

πŸ“’ Files selected for processing (3)
  • demos/mouse/source/mouse.d (1 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • demos/mouse/source/mouse.d
🧰 Additional context used
🧠 Learnings (1)
πŸ“š Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/screen.d
  • source/dcell/ttyscreen.d
πŸ”‡ Additional comments (1)
source/dcell/ttyscreen.d (1)

271-284: Title save/restore around start() / stop() looks consistent

The new logic to saveTitle on start, apply either the explicit title or restoreTitle+saveTitle when no title is set, and then restoreTitle on stop appears balanced and should behave correctly across multiple start/stop cycles for both stack-capable and simpler terminals.

Also applies to: 303-303

@gdamore gdamore force-pushed the gdamore/window-title branch from bf746f9 to 4077086 Compare December 11, 2025 22:06
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
source/dcell/ttyscreen.d (1)

276-284: Clarify null vs empty string semantics for title field.

In D, if (title) distinguishes between null (never set) and "" (explicitly empty). If setTitle("") is called, the condition is true and an empty title is emitted, whereas null takes the else path. If this distinction is intentional, a brief comment would help API consumers understand the behavior.

+        // Note: null means no title set (use original), "" means explicitly empty title
         if (title)
         {
             puts(format(vt.setTitle, title));
         }
πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between bf746f9 and 4077086.

πŸ“’ Files selected for processing (3)
  • demos/mouse/source/mouse.d (1 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • source/dcell/screen.d
  • demos/mouse/source/mouse.d
🧰 Additional context used
🧠 Learnings (2)
πŸ““ Common learnings
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").
πŸ“š Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
πŸ”‡ Additional comments (4)
source/dcell/ttyscreen.d (4)

102-107: LGTM - Typo fixed, fallback strategy is sound.

The dual-sequence approach in restoreTitle (OSC 2 empty + XTWINOPS pop) gracefully handles both stack-supporting and non-stack terminals. The typo from the previous review has been corrected.


303-303: LGTM - Proper title restoration on stop.

Restoring the title before exiting the alternate screen and other cleanup is the correct sequencing for terminal state restoration.


445-453: LGTM - Previous concerns addressed.

The implementation now correctly flushes after emitting the title sequence when started, and properly defers to start() for title handling when not yet started. This aligns with the pattern used by setSize() and sendPasteEnable().


537-537: LGTM!

Appropriate field declaration for tracking title state.

Changing to the alternate screen apparently breaks kitty protocol
on kitty, because the alt screen has different values there.  We
need to switch to it before doing other things.
Using a scope exit ensures that we put things back properly.
@gdamore gdamore force-pushed the gdamore/window-title branch from 4077086 to 7fcdcdc Compare December 12, 2025 03:24
@gdamore gdamore merged commit acf50c4 into main Dec 12, 2025
3 checks passed
@gdamore gdamore deleted the gdamore/window-title branch December 12, 2025 03:42
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