Skip to content

TUI fixups#203

Merged
dobesv merged 8 commits into
mainfrom
pending-message-in-editor
Apr 6, 2026
Merged

TUI fixups#203
dobesv merged 8 commits into
mainfrom
pending-message-in-editor

Conversation

@dobesv

@dobesv dobesv commented Apr 6, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Shift+Enter inserts a newline into the draft without submitting.
    • External-editor integration improved to preserve and restore terminal state during edits.
  • Improvements

    • Draft input is preserved when messages are queued while the model is busy.
    • Input area now wraps and sizes correctly based on available width.
    • Enhanced keyboard support and panic-safe terminal cleanup.
    • REPL command handling refined for clearer exit/resume semantics.
  • Tests

    • Added test for Shift+Enter newline behavior.

dobesv added 2 commits April 5, 2026 17:16
Signed-off-by: Dobes Vandermeer <dobes.vandermeer@newsela.com>
@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Added editor hooks and terminal panic-guard; migrated textarea and crossterm deps; preserved visible input when queuing messages; made input-height wrapping-aware; introduced REPL CommandOutcome to refine command control flow.

Changes

Cohort / File(s) Summary
Dependency updates
Cargo.toml
Bumped TUI deps: reedline, ratatui (with features = ["crossterm_0_29"]), replaced tui-textarearatatui-textarea, aligned crossterm to 0.29.
Config editor hooks
src/config/mod.rs
Removed derive Debug/Clone in favor of manual impls; added tui_before_editor/tui_after_editor (#[serde(skip)]), set_tui_editor_hooks(), edit_with_tui_hooks() and routed editor callers through it; edit_config now takes &mut.
TUI lifecycle & terminal safety
src/tui/lifecycle.rs, src/tui/terminal.rs
Added centralized cleanup_terminal_state(); added PanicTerminalHookGuard to install/restore panic hook that calls cleanup; run flow installs guard and wires external-editor bridge to set editor hooks.
Input queuing & submission
src/tui/input.rs, src/tui/tests.rs
Queueing while LLM busy preserves visible app.input; consuming pending message now clears input on take and dispatches command vs prompt; tests updated and new shift+enter newline test added.
Rendering & sizing
src/tui/render.rs, src/tui/types.rs
Added last_known_input_width: u16 to App; Tui::draw captures input width; Tui::input_height now accepts available_width and computes wrapping-aware height via textwrap.
Module/import adjustments
src/tui/mod.rs, src/tui/*
Switched imports from tui_textarearatatui_textarea; added crossterm keyboard enhancement flag imports and adjusted std imports.
REPL flow & completer small tweaks
src/repl/mod.rs, src/repl/completer.rs
Added pub enum CommandOutcome { Continue, Exit, ResumeHandled }; changed run_repl_command and async-resume returns to CommandOutcome; create_suggestion now sets display_override and match_indices to None.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TUI
    participant App
    participant Repl
    participant Editor

    User->>TUI: Press Enter (LLM busy)
    TUI->>App: queue pending_message (keep app.input visible)

    Note right of App: pending_message stored, input unchanged

    alt pending_message processed
        App->>TUI: take pending_message (clear app.input)
        TUI->>TUI: push user transcript
        alt message starts with "."
            TUI->>Repl: run_repl_command -> CommandOutcome
        else
            TUI->>Repl: start_prompt
        end
    end

    User->>TUI: Open external editor
    TUI->>App: edit_with_tui_hooks(before, edit_op, after)
    App->>Editor: tui_before_editor (cleanup terminal / leave alt screen)
    Editor-->>App: editor closed
    App->>Editor: tui_after_editor (re-enable alternate screen / raw mode)
Loading
sequenceDiagram
    participant TuiSetup
    participant Terminal
    participant PanicGuard
    participant Cleanup

    TuiSetup->>Terminal: enable raw mode, alt screen, push keyboard flags
    TuiSetup->>PanicGuard: install PanicTerminalHookGuard (wrap panic hook)
    TuiSetup->>Terminal: run event loop

    alt panic occurs
        PanicGuard->>Cleanup: cleanup_terminal_state()
        Cleanup->>Terminal: disable raw mode, pop keyboard flags, disable mouse, leave alt screen
    else normal exit
        TuiSetup->>Cleanup: restore_terminal() -> cleanup_terminal_state()
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇
I tuck your draft and guard the screen,
Hooks hop out, then hop back in between,
Panic caught, the terminal breathes light,
Wrapped lines fold tidy into sight,
A rabbit cheers — the TUI sleeps right.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'TUI fixups' is vague and generic, using non-descriptive language that fails to convey the actual scope of changes across multiple files and systems. Consider a more specific title that highlights the main changes, such as 'Migrate TUI to ratatui-textarea and add editor hook support' or 'Update TUI dependencies and implement external editor lifecycle hooks'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pending-message-in-editor

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (1)
src/tui/terminal.rs (1)

59-73: ⚠️ Potential issue | 🟠 Major

Convert to async or use blocking wrapper for I/O operations.

The setup_terminal() function performs synchronous I/O operations (enable_raw_mode, execute, Terminal::new) but is called directly from an async context in lifecycle.rs. According to coding guidelines, all I/O operations must use async fn and .await via the Tokio runtime. Wrap the blocking operations with tokio::task::block_in_place() or spawn_blocking() to prevent blocking the async executor.

The keyboard enhancement flags themselves are correctly configured—the conditional check and flag combinations are appropriate—but the synchronous I/O handling must be addressed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tui/terminal.rs` around lines 59 - 73, The setup_terminal function
performs blocking I/O but is invoked from async code; make it safe by converting
it to async fn setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>>
and run the existing synchronous body inside tokio::task::block_in_place (or use
tokio::task::spawn_blocking if you prefer returning a JoinHandle) so the
blocking calls (enable_raw_mode, stdout.execute, Terminal::new) run off the
async executor; keep the current logic and flags inside the closure and return
the constructed Terminal from that closure.
🧹 Nitpick comments (1)
Cargo.toml (1)

82-82: Use crates.io release instead of tracking moving branch.

ratatui-textarea version 0.8.0 is available on crates.io and is the latest stable release. Tracking branch = "main" makes the dependency non-reproducible across lockfile regenerations; switch to the published crate.

📦 Suggested manifest change
-ratatui-textarea = { git = "https://github.com/ratatui/ratatui-textarea", branch = "main", version = "0.8.0" }
+ratatui-textarea = "0.8.0"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` at line 82, The Cargo.toml currently pulls ratatui-textarea from
a git branch which makes builds non-reproducible; update the dependency
declaration for ratatui-textarea to use the published crates.io version (remove
git = ... and branch = ... and set version = "0.8.0") so Cargo pulls the
released crate instead of tracking main, ensuring reproducible lockfiles and
builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config/mod.rs`:
- Around line 1385-1392: Create a shared helper method edit_with_tui_hooks that
suspends/restores the TUI around editor invocations (mirroring the behavior
currently in edit_session which wraps edit_file) and use it everywhere edit_file
is currently called directly: replace edit_file() calls in edit_config,
upsert_agent, and new_macro with self.edit_with_tui_hooks(...), and for the
async edit_rag_docs create an async variant or awaitable wrapper so it also runs
under the same before/after hooks; ensure the helper accepts a closure or async
block to invoke edit_file (and preserves signatures/return values) and keep the
existing set_tui_editor_hooks, tui_before_editor, and tui_after_editor fields as
the hook sources.

In `@src/tui/input.rs`:
- Around line 68-72: Queued dot-commands placed into self.app.pending_message
while self.app.llm_busy are later replayed via start_prompt, causing commands
like .info/.set to be sent to the model instead of executed; update the replay
logic in the Finished and Errored arms (and the similar blocks at the other
occurrences) to check pending_message and if it starts with a dot command invoke
run_repl_command(pending) instead of start_prompt(pending), then clear
pending_message and refresh_input_chrome() as currently done so the command is
executed locally rather than sent to the LLM.

In `@src/tui/lifecycle.rs`:
- Around line 170-172: install_external_editor_bridge currently disables the
editor hooks by calling config.write().set_tui_editor_hooks(None, None) and
ignores the provided Terminal, which prevents edit_session from
suspending/restoring terminal state; instead, set tui_before_editor and
tui_after_editor callbacks via
config.write().set_tui_editor_hooks(Some(before_cb), Some(after_cb)) where
before_cb captures a clone or weak ref to the Terminal/CrosstermBackend and
calls the appropriate suspend/cleanup methods (e.g., disable raw mode, leave
alternate screen, show cursor) and after_cb restores terminal state (re-enable
raw mode, enter alternate screen, hide cursor) so edit_session can safely launch
the external editor; replace the unused _terminal parameter by using it (or a
clone) inside these callbacks and reference install_external_editor_bridge,
edit_session, and set_tui_editor_hooks when making the change.

In `@src/tui/render.rs`:
- Around line 186-206: The input_height() math can overflow when narrowing to
u16; change the accumulator so body_lines is computed as a usize (not u16) while
wrapping lines using body_width, then when returning convert safely by clamping
the final value to u16::MAX before adding the extra padding: compute total =
min(body_lines, (u16::MAX as usize).saturating_sub(2)) + 2 and return total as
u16. Update references in input_height (variables body_lines, wrapped,
body_width, last_known_input_width, lines) so all intermediate arithmetic stays
in usize and only the final value is narrowed with a safe clamp.

In `@src/tui/terminal.rs`:
- Around line 20-33: The panic hook capture/restore in
PanicTerminalHookGuard::install is incorrect because multiple panic::take_hook()
calls remove and lose the original hook; change the logic to take the original
hook once, wrap it in a shared owner (e.g., Arc<dyn
Fn(&panic::PanicHookInfo<'_>) + Sync + Send + 'static>) and clone that Arc into
the new hook closure so the closure can call the original hook after calling
cleanup_terminal_state(), then store that Arc in self.previous_hook for Drop to
restore the original hook; update the type of previous_hook and the install
function to use the Arc-wrapped hook and ensure panic::set_hook is only called
once with the new chained closure that calls the Arc-cloned original.
- Line 54: The code references a non-existent WrapMode and set_wrap_mode() on
the TextArea/input: remove the WrapMode import from the ratatui_textarea import
list (the Input as TextInput, Key, TextArea import) and delete the call
input.set_wrap_mode(WrapMode::WordOrGlyph) in terminal.rs; alternatively, if you
intend to use configurable wrap modes, upgrade the ratatui-textarea dependency
in Cargo.toml to a version that exposes WrapMode and set_wrap_mode(), then
update imports to match that newer API (ensuring the symbol names
Input/TextArea/WrapMode match the upgraded crate).

---

Outside diff comments:
In `@src/tui/terminal.rs`:
- Around line 59-73: The setup_terminal function performs blocking I/O but is
invoked from async code; make it safe by converting it to async fn
setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>> and run the
existing synchronous body inside tokio::task::block_in_place (or use
tokio::task::spawn_blocking if you prefer returning a JoinHandle) so the
blocking calls (enable_raw_mode, stdout.execute, Terminal::new) run off the
async executor; keep the current logic and flags inside the closure and return
the constructed Terminal from that closure.

---

Nitpick comments:
In `@Cargo.toml`:
- Line 82: The Cargo.toml currently pulls ratatui-textarea from a git branch
which makes builds non-reproducible; update the dependency declaration for
ratatui-textarea to use the published crates.io version (remove git = ... and
branch = ... and set version = "0.8.0") so Cargo pulls the released crate
instead of tracking main, ensuring reproducible lockfiles and builds.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7550aa23-b0dc-4d57-a90d-646e9f163a7b

📥 Commits

Reviewing files that changed from the base of the PR and between 0e8437c and 00ec69d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • src/config/mod.rs
  • src/repl/completer.rs
  • src/tui/input.rs
  • src/tui/lifecycle.rs
  • src/tui/mod.rs
  • src/tui/render.rs
  • src/tui/terminal.rs
  • src/tui/tests.rs
  • src/tui/types.rs

Comment thread src/config/mod.rs
Comment thread src/tui/input.rs
Comment on lines 68 to 72
if self.app.llm_busy {
// Queue the message to send when LLM finishes
// Keep the text in input so user can see/edit it
self.app.pending_message = Some(text);
self.app.input = Self::new_input();
self.refresh_input_chrome();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Queued dot-commands are still replayed as chat prompts.

When Enter is pressed while llm_busy, .info, .set, etc. are stored in pending_message. Both replay paths then call start_prompt(pending), so the queued command goes to the model instead of run_repl_command(...).

🪄 Suggested fix
+    async fn replay_pending_message(&mut self, pending: String) -> Result<()> {
+        self.app.input = Self::new_input();
+        self.app
+            .transcript
+            .push(TranscriptEntry::User(pending.clone()));
+        self.pin_transcript_to_bottom();
+        if pending.trim_start().starts_with('.') {
+            self.run_repl_command(&pending).await?;
+            self.refresh_input_chrome();
+        } else {
+            self.start_prompt(pending).await?;
+        }
+        Ok(())
+    }
+
...
-                if let Some(pending) = self.app.pending_message.take() {
-                    // Clear input now that the pending message is actually being submitted
-                    self.app.input = Self::new_input();
-                    self.app
-                        .transcript
-                        .push(TranscriptEntry::User(pending.clone()));
-                    self.pin_transcript_to_bottom();
-                    self.start_prompt(pending).await?;
-                }
+                if let Some(pending) = self.app.pending_message.take() {
+                    self.replay_pending_message(pending).await?;
+                }

Apply the same replacement in both the Finished and Errored arms.

Also applies to: 197-205, 214-221

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tui/input.rs` around lines 68 - 72, Queued dot-commands placed into
self.app.pending_message while self.app.llm_busy are later replayed via
start_prompt, causing commands like .info/.set to be sent to the model instead
of executed; update the replay logic in the Finished and Errored arms (and the
similar blocks at the other occurrences) to check pending_message and if it
starts with a dot command invoke run_repl_command(pending) instead of
start_prompt(pending), then clear pending_message and refresh_input_chrome() as
currently done so the command is executed locally rather than sent to the LLM.

Comment thread src/tui/lifecycle.rs Outdated
Comment thread src/tui/render.rs Outdated
Comment thread src/tui/terminal.rs
Comment thread src/tui/terminal.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config/mod.rs`:
- Around line 1673-1678: The call to edit_file() inside the async
edit_rag_docs() blocks the Tokio worker; replace the direct blocking edit_file()
call in the config.write().edit_with_tui_hooks closure with an async wrapper
that spawns a dedicated OS thread (std::thread::spawn) to run edit_file(editor,
path) and use a tokio::sync::oneshot channel to notify the async context when
the editor exits. Specifically, change the code around edit_rag_docs() /
edit_with_tui_hooks / editor() so the closure captures editor and temp_file
(converted to owned types) and sends completion via the oneshot sender from the
spawned thread, then await the oneshot receiver in the async function instead of
calling edit_file() directly.

In `@src/repl/mod.rs`:
- Around line 273-276: The REPL loop is being starved because
process_pending_async_resume() returns CommandOutcome::Continue both for
"nothing to resume" and for "resume completed", and the current matches(...) {
continue; } prevents read_line() from ever running; change the logic in the loop
around process_pending_async_resume() (the call to
process_pending_async_resume().await? and the matches on
CommandOutcome::Continue) to use a distinct "resume_handled" signal (or restore
the previous bool) that only causes continue when a resume was actually
processed, and explicitly handle CommandOutcome::Exit (do not let it fall
through to read_line()); apply the same fix to the other occurrence that mirrors
this logic (the block around lines 319-353) so exit and normal read_line() paths
behave correctly.

In `@src/tui/render.rs`:
- Line 95: The height calculation in input_height() is using
self.app.last_known_input_width which is only updated later in draw(), causing
incorrect clamping on first render; update input_height() (and the similar logic
around the other occurrences noted) to use the current frame width (size.width
or the equivalent local variable passed into draw()) instead of
last_known_input_width, and keep last_known_input_width only as a stored value
updated after layout (e.g., in draw()) so the immediate height measurement uses
the current frame's width.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 10ed629f-eafd-49e4-b661-0d882b444844

📥 Commits

Reviewing files that changed from the base of the PR and between 00ec69d and 2fc123c.

📒 Files selected for processing (7)
  • src/config/mod.rs
  • src/repl/mod.rs
  • src/tui/input.rs
  • src/tui/lifecycle.rs
  • src/tui/mod.rs
  • src/tui/render.rs
  • src/tui/terminal.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/tui/input.rs
  • src/tui/mod.rs
  • src/tui/terminal.rs

Comment thread src/config/mod.rs
Comment thread src/repl/mod.rs Outdated
Comment thread src/tui/render.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config/mod.rs`:
- Around line 1673-1687: The problem is the closure passed to
edit_with_tui_hooks spawns the editor thread and returns immediately, allowing
the after-hook to run before the editor exits; fix by making the editor run
synchronously inside the closure instead of spawning a thread: in the block
where you call config_write.edit_with_tui_hooks(|_| { ... }), remove
std::thread::spawn and call edit_file(&editor, &temp_file_path) directly, send
its Result over the oneshot tx, then return Ok(()) so the closure only returns
after edit_file completes (this ensures before()/after() brackets the full
editor lifetime).
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 452e7784-5480-4703-943c-1d6d4f11ab0f

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc123c and af2035e.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • src/config/mod.rs
  • src/repl/mod.rs
  • src/tui/render.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • Cargo.toml

Comment thread src/config/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/config/mod.rs (1)

1673-1685: The oneshot channel is now unnecessary.

Since edit_file runs synchronously within the closure (no std::thread::spawn), the channel adds complexity without benefit. The result is sent immediately and awaited right after the lock is released.

♻️ Suggested simplification
-        let (tx, rx) = tokio::sync::oneshot::channel();
-        {
-            let mut config_write = config.write();
-            let editor = config_write.editor()?;
-            let temp_file_path = temp_file.clone();
-            config_write.edit_with_tui_hooks(|_| {
-                let result = edit_file(&editor, &temp_file_path);
-                let _ = tx.send(result);
-                Ok(())
-            })?;
-        }
-        rx.await
-            .map_err(|_| anyhow!("Editor hook completion channel unexpectedly closed"))??;
+        {
+            let mut config_write = config.write();
+            let editor = config_write.editor()?;
+            let temp_file_path = temp_file.clone();
+            config_write.edit_with_tui_hooks(|_| {
+                edit_file(&editor, &temp_file_path)
+            })?;
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config/mod.rs` around lines 1673 - 1685, The oneshot channel (tx/rx) is
unnecessary because edit_file is called synchronously inside the
edit_with_tui_hooks closure; remove the tokio::sync::oneshot::channel creation
and usage, call edit_file(&editor, &temp_file) directly inside the closure (or
capture its result and return it via the closure's Result), propagate its Result
out of edit_with_tui_hooks and handle errors the same way as before (keep the
config.write()/editor()/edit_with_tui_hooks sequence but return the edit_file
result directly instead of sending it through tx and awaiting rx).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/config/mod.rs`:
- Around line 1673-1685: The oneshot channel (tx/rx) is unnecessary because
edit_file is called synchronously inside the edit_with_tui_hooks closure; remove
the tokio::sync::oneshot::channel creation and usage, call edit_file(&editor,
&temp_file) directly inside the closure (or capture its result and return it via
the closure's Result), propagate its Result out of edit_with_tui_hooks and
handle errors the same way as before (keep the
config.write()/editor()/edit_with_tui_hooks sequence but return the edit_file
result directly instead of sending it through tx and awaiting rx).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cf2d3916-e437-40dd-b61a-8c2f531469d5

📥 Commits

Reviewing files that changed from the base of the PR and between af2035e and 0068ccd.

📒 Files selected for processing (1)
  • src/config/mod.rs

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.

1 participant