Skip to content

Conversation

@togo01
Copy link
Contributor

@togo01 togo01 commented Nov 5, 2025

Summary

This PR includes three terminal input improvements that enhance the experience when using interactive CLI tools and IME input:

  1. Shift+Enter newline support: Enable Shift+Enter to insert newlines by default
  2. Image paste support: Allow pasting images by saving them as temporary files and pasting the file path
  3. IME duplicate input fix: Fix duplicate text when switching input methods during composition

Motivation

Shift+Enter for newlines

Currently, pressing Shift+Enter in the terminal behaves the same as Enter, making it difficult to input multi-line commands or text in interactive CLI tools. This change enables Shift+Enter to insert newlines by default, matching common terminal emulator behavior.

Image paste support

Interactive AI tools like Claude Code support receiving images through file paths, but Wave Terminal currently doesn't support pasting images. This change implements image paste functionality similar to iTerm2's behavior: when an image is pasted, it's saved to a temporary file and the path is pasted into the terminal.

IME duplicate input fix

When using Chinese/Japanese/Korean IME in the terminal, switching input methods with Capslock during composition causes the composed text to be sent twice, resulting in duplicate output (e.g., "你好" becomes "你好你好"). This issue severely impacts users who frequently switch between languages.

Changes

Shift+Enter newline (frontend/app/view/term/term-model.ts)

  • Change default shiftenternewline config from false to true
  • Send standard newline character (\n) instead of escape sequence (\^[\n)

Image paste (frontend/app/view/term/term-model.ts, frontend/app/view/term/termwrap.ts)

  • Add handlePaste() method to intercept Cmd+Shift+V paste events
  • Add handleImagePasteBlob() to save images to /tmp and paste the file path
  • Detect image data in clipboard using both ClipboardEvent.clipboardData and Clipboard API
  • Support both screenshot paste and file copy scenarios
  • Add 5MB size limit for pasted images
  • Temporary files are created with format: waveterm_paste_[timestamp].[ext]

IME duplicate input fix (frontend/app/view/term/termwrap.ts, frontend/app/view/term/term-model.ts)

IME Composition Handling:

  • Track composition state (isComposing, composingData, etc.) in TermWrap
  • Register compositionstart/update/end event listeners on xterm.js textarea
  • Block all data sends during composition, only allow after compositionend
  • Prevents xterm.js from sending intermediate data during compositionupdate phase

Deduplication Logic:

  • Implement 50ms time window deduplication for both IME and paste operations
  • Track first send after composition, block duplicate sends from Capslock switching
  • Ensure Ctrl+Space and Fn switching work correctly (single send only)

Edge Case Handling:

  • Add blur event handler to reset composition state on focus loss
  • Add Escape key handling to cancel composition in progress

Testing

Shift+Enter

  1. Open a terminal in Wave
  2. Press Shift+Enter
  3. Verify that a newline is inserted instead of executing the command

Image paste

  1. Take a screenshot and copy it to clipboard (or copy an image file in Finder)
  2. In a terminal running Claude Code, paste the image (Cmd+V or Cmd+Shift+V)
  3. Verify that the image path appears and Claude Code recognizes it

IME Input Testing

IME Input:

  • macOS Zhuyin IME + Capslock switching - no duplicate output ✅
  • macOS Zhuyin IME + Ctrl+Space switching - normal single input ✅
  • macOS Zhuyin IME + Fn switching - normal single input ✅

Regression Testing:

  • English keyboard input - normal operation ✅
  • Shift+Enter multiline input - works correctly ✅
  • Text paste (Cmd+Shift+V) - no duplicates ✅
  • Image paste - works correctly ✅
  • Basic command execution (ls, echo, etc.) - normal ✅
  • Cmd+K clear terminal - works correctly ✅
  • Copy selected text (Cmd+Shift+C) - works correctly ✅

Demo

2025-11-05.15-32-58.mp4
2025-11-07.04-09-29.mp4
2025-11-07.04-10-26.mp4

All features have been tested successfully on macOS with Claude Code in Wave Terminal.

@CLAassistant
Copy link

CLAassistant commented Nov 5, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 5, 2025

Walkthrough

Adds image-first clipboard paste support to the terminal UI and a backend RPC to create temporary files. Frontend changes introduce unified paste handling that prefers image clipboard items, convert image Blobs to temp files via a new RPC, and paste the created temp file path; text-paste fallbacks remain. IME composition state tracking, paste deduplication, and Escape-key handling during composition were added. Ctrl+Shift+V now invokes the unified paste flow and Shift+Enter default newline behavior was changed. New RPC types and handlers (GetTempDirCommand / CommandGetTempDirData) let the server return os.TempDir() optionally joined with a requested filename.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Files to focus on:

    • frontend/app/view/term/term-model.ts
    • frontend/app/view/term/termwrap.ts
    • frontend/app/view/term/termutil.ts
    • frontend/app/store/wshclientapi.ts
    • frontend/types/gotypes.d.ts
    • pkg/wshrpc/wshrpctypes.go
    • pkg/wshrpc/wshclient/wshclient.go
    • pkg/wshrpc/wshserver/wshserver.go
  • Areas needing extra attention:

    • Consistency of RPC contract (command name, JSON field filename, return type string) between TypeScript and Go.
    • IME composition handling, paste deduplication logic, and Escape-key interactions to avoid input loss or double-sends.
    • Blob size limit, MIME→extension mapping, filename generation, and potential edge cases for file paths.
    • Asynchronous paste lifecycle, error paths, and UI/state updates during image-to-temp-file creation.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(terminal): improve input handling for interactive CLI tools' clearly and concisely summarizes the main changes: three input improvements (Shift+Enter, image paste, IME handling) for terminal interaction with CLI tools.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering all three features (Shift+Enter support, image paste, IME duplicate input fix), motivation, implementation details, testing results, and demo videos.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 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 1cf388f and 80403e1.

📒 Files selected for processing (1)
  • pkg/wshrpc/wshserver/wshserver.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/wshrpc/wshserver/wshserver.go

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Error: unknown linters: 'unusedfunc,unusedparams', run 'golangci-lint help linters' to see the list of supported linters
The command is terminated due to an error: unknown linters: 'unusedfunc,unusedparams', run 'golangci-lint help linters' to see the list of supported linters


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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c5c82f and 8e7579a.

📒 Files selected for processing (2)
  • frontend/app/view/term/term-model.ts (2 hunks)
  • frontend/app/view/term/termwrap.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/app/view/term/termwrap.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (602-602)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (602-602)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: merge-gatekeeper

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

🧹 Nitpick comments (3)
frontend/app/view/term/termwrap.ts (3)

11-11: Remove unused import.

The stringToBase64 import appears unused. The image paste implementation relies on FileReader.readAsDataURL, which already produces base64-encoded output.

Apply this diff:

-import { base64ToArray, base64ToString, fireAndForget, stringToBase64 } from "@/util/util";
+import { base64ToArray, base64ToString, fireAndForget } from "@/util/util";

712-720: Consider simplifying the wrapper.

This method simply extracts a blob and delegates to handleImagePasteBlob. Since it's only called once (line 461), you could inline it.

However, keeping the wrapper maintains clear separation between the Clipboard API path and the blob processing logic, which may be preferred for readability.


707-709: Consider user feedback for paste failures.

Errors during image paste are logged but provide no user-facing feedback. Users may not understand why their paste didn't work.

Consider displaying a toast notification or status message when image paste fails, especially for the size limit (line 678).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e7579a and 4b9b678.

📒 Files selected for processing (1)
  • frontend/app/view/term/termwrap.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/app/view/term/termwrap.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (602-602)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (2)
frontend/app/view/term/termwrap.ts (2)

424-486: LGTM! Clipboard handling is robust.

The async paste handler correctly prioritizes image data, implements multiple fallback paths, and properly manages the pasteActive flag. The use of handleImagePasteBlob avoids the stack overflow issue mentioned in previous reviews.


667-672: LGTM!

The method correctly enables image paste support for all terminals by returning true unconditionally.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9b678 and e553cf5.

📒 Files selected for processing (2)
  • frontend/app/view/term/term-model.ts (2 hunks)
  • frontend/app/view/term/termwrap.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/app/view/term/termwrap.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (602-602)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (602-602)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)

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

🧹 Nitpick comments (2)
frontend/app/view/term/termwrap.ts (1)

696-698: Consider stronger random generation for filename uniqueness.

Math.random() provides weak randomness and could lead to collisions in high-frequency paste scenarios. Consider using crypto.randomUUID() or a cryptographically secure random string.

Apply this diff:

-            const timestamp = Date.now();
-            const random = Math.random().toString(36).substring(2, 8);
-            const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
+            const timestamp = Date.now();
+            const random = crypto.randomUUID().split('-')[0]; // or use full UUID
+            const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
frontend/app/view/term/term-model.ts (1)

463-466: Consider stronger random generation for filename uniqueness.

Similar to the implementation in termwrap.ts, Math.random() provides weak randomness. Consider using crypto.randomUUID() for better uniqueness and to avoid potential collisions.

Apply this diff:

-            const timestamp = Date.now();
-            const random = Math.random().toString(36).substring(2, 8);
-            const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
+            const timestamp = Date.now();
+            const random = crypto.randomUUID().split('-')[0];
+            const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e553cf5 and 016385c.

📒 Files selected for processing (6)
  • frontend/app/store/wshclientapi.ts (1 hunks)
  • frontend/app/view/term/term-model.ts (2 hunks)
  • frontend/app/view/term/termwrap.ts (2 hunks)
  • pkg/wshrpc/wshclient/wshclient.go (1 hunks)
  • pkg/wshrpc/wshrpctypes.go (2 hunks)
  • pkg/wshrpc/wshserver/wshserver.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-22T01:28:41.417Z
Learnt from: esimkowitz
Repo: wavetermdev/waveterm PR: 1790
File: pkg/remote/fileshare/wshfs/wshfs.go:122-122
Timestamp: 2025-01-22T01:28:41.417Z
Learning: The RpcClient in pkg/remote/fileshare/wshfs/wshfs.go is initialized and handled downstream by either main-server or wshcmd-connserver, as documented in the package comment.

Applied to files:

  • frontend/app/store/wshclientapi.ts
  • pkg/wshrpc/wshclient/wshclient.go
🧬 Code graph analysis (6)
frontend/app/store/wshclientapi.ts (2)
frontend/app/store/wshclient.ts (1)
  • WshClient (159-159)
pkg/wshrpc/wshrpctypes.go (1)
  • RpcOpts (329-335)
pkg/wshrpc/wshrpctypes.go (2)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (256-258)
pkg/wshrpc/wshclient/wshclient.go (1)
  • GetTempDirCommand (313-315)
pkg/wshrpc/wshserver/wshserver.go (2)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (256-258)
pkg/wshrpc/wshclient/wshclient.go (1)
  • GetTempDirCommand (313-315)
frontend/app/view/term/termwrap.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (607-607)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
pkg/wshrpc/wshclient/wshclient.go (3)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (256-258)
pkg/wshutil/wshrpc.go (1)
  • WshRpc (47-61)
pkg/wshrpc/wshrpctypes.go (1)
  • RpcOpts (329-335)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (607-607)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (7)
frontend/app/view/term/termwrap.ts (1)

700-702: Verify path separator handling across platforms.

Line 702 uses a forward slash / to join the temp directory and filename. While this typically works on Windows when handled by Node.js/Electron file APIs, ensure the backend RPC FileWriteCommand normalizes paths correctly on all platforms.

pkg/wshrpc/wshserver/wshserver.go (1)

447-449: LGTM!

The implementation correctly uses os.TempDir() which is cross-platform and returns the appropriate temporary directory for each OS (e.g., /tmp on Unix-like systems, %TEMP% on Windows).

frontend/app/store/wshclientapi.ts (1)

255-258: LGTM!

The RPC client method follows the established pattern for commands with no input data, correctly passing null for the data parameter and returning a Promise<string>.

pkg/wshrpc/wshrpctypes.go (1)

86-86: LGTM!

The command constant and interface method are correctly defined and follow the established naming conventions and patterns in the codebase.

Also applies to: 216-216

pkg/wshrpc/wshclient/wshclient.go (1)

312-315: LGTM!

The client wrapper correctly follows the pattern for commands with no input data. Note that this file is generated code (line 4), so any issues should be addressed in the code generator rather than here.

frontend/app/view/term/term-model.ts (2)

602-608: Verify Shift+Enter behavior change is documented.

The default value for shiftEnterNewlineEnabled has changed from false to true (line 602), and the implementation now sends a standard newline \n (line 604) instead of the previous escape sequence. This is a breaking change in default behavior that users may notice.

Ensure this behavior change is:

  1. Documented in release notes or changelog
  2. Communicated to users who may have workflows dependent on the old behavior
  3. Tested with Claude Code and other interactive CLI tools mentioned in the PR objectives

468-470: Verify path separator handling.

Line 470 uses a forward slash / to join paths. While this typically works cross-platform in Node.js/Electron, verify that the FileWriteCommand RPC properly handles path separators on Windows.

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

🧹 Nitpick comments (2)
frontend/app/view/term/term-model.ts (2)

475-475: Validate data URL format before splitting.

The code assumes the data URL will always contain a comma separator. While this should always be true for valid FileReader output, defensive validation would improve robustness.

             // Extract base64 data from data URL (remove "data:image/png;base64," prefix)
-            const base64Data = dataUrl.split(',')[1];
+            const parts = dataUrl.split(',');
+            if (parts.length < 2) {
+                throw new Error("Invalid data URL format");
+            }
+            const base64Data = parts[1];

445-455: Consider extracting MIME type mapping as a constant.

The MIME-to-extension mapping is comprehensive but could be extracted as a class-level or module-level constant for reusability and maintainability, especially if other components need similar mappings.

+    private static readonly MIME_TO_EXT: Record<string, string> = {
+        "image/png": "png",
+        "image/jpeg": "jpg",
+        "image/jpg": "jpg",
+        "image/gif": "gif",
+        "image/webp": "webp",
+        "image/bmp": "bmp",
+        "image/svg+xml": "svg",
+        "image/tiff": "tiff",
+    };
+
     async handleImagePasteBlob(blob: Blob): Promise<void> {
         try {
             // Check size limit (5MB)
             if (blob.size > 5 * 1024 * 1024) {
                 console.error("Image too large (>5MB):", blob.size);
                 return;
             }
 
-            // Map MIME types to file extensions
-            const mimeToExt: Record<string, string> = {
-                "image/png": "png",
-                "image/jpeg": "jpg",
-                "image/jpg": "jpg",
-                "image/gif": "gif",
-                "image/webp": "webp",
-                "image/bmp": "bmp",
-                "image/svg+xml": "svg",
-                "image/tiff": "tiff",
-            };
-            const ext = mimeToExt[blob.type] || "png";
+            const ext = TermViewModel.MIME_TO_EXT[blob.type] || "png";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 016385c and 5cb6834.

📒 Files selected for processing (1)
  • frontend/app/view/term/term-model.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (607-607)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (2)
frontend/app/view/term/term-model.ts (2)

394-427: LGTM! Clean clipboard handling with proper fallbacks.

The implementation correctly prioritizes image paste for supported terminals, falls back to text paste, and includes robust error handling. The previous debug logging statements have been successfully removed.


596-598: LGTM! Shift+Enter and paste handling correctly implemented.

The changes successfully implement the PR objectives:

  • Shift+Enter default changed to true (line 596), enabling newline insertion by default
  • Newline character \n is sent correctly (line 598)
  • Paste trigger properly integrated with new handlePaste() method (line 605)

Also applies to: 605-605

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

♻️ Duplicate comments (1)
frontend/app/view/term/term-model.ts (1)

37-46: Consider extracting duplicated MIME_TO_EXT constant.

This mapping is identical to the one in frontend/app/view/term/termwrap.ts (lines 330-339). See the comment on that file for a suggested refactor to share this constant.

🧹 Nitpick comments (2)
frontend/app/view/term/termwrap.ts (1)

330-339: Consider extracting MIME_TO_EXT to a shared constant.

This exact mapping is duplicated in frontend/app/view/term/term-model.ts (lines 37-46). Consider extracting it to a shared utility module to maintain a single source of truth.

For example, create a new file frontend/app/view/term/termutil.ts:

export const MIME_TO_EXT: Record<string, string> = {
    "image/png": "png",
    "image/jpeg": "jpg",
    "image/jpg": "jpg",
    "image/gif": "gif",
    "image/webp": "webp",
    "image/bmp": "bmp",
    "image/svg+xml": "svg",
    "image/tiff": "tiff",
};

Then import it in both files. However, this is optional since the duplication is minimal and localized.

frontend/app/view/term/term-model.ts (1)

447-493: Consider extracting shared image paste logic.

This implementation is nearly identical to termwrap.ts lines 685-731 (only difference: this.termRef.current?.terminal.paste() vs this.terminal.paste()). Since both classes handle image pasting the same way, consider extracting the core logic into a shared utility function to reduce duplication and maintenance burden.

Example refactor:

// In termutil.ts or similar
export async function createTempFileFromBlob(
    blob: Blob,
    MIME_TO_EXT: Record<string, string>,
    client: WshClient
): Promise<string> {
    if (blob.size > 5 * 1024 * 1024) {
        throw new Error("Image too large (>5MB)");
    }
    
    const ext = MIME_TO_EXT[blob.type] || "png";
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2, 8);
    const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
    
    const tempPath = await RpcApi.GetTempDirCommand(client, { filename });
    
    const dataUrl = await new Promise<string>((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result as string);
        reader.onerror = reject;
        reader.readAsDataURL(blob);
    });
    
    const parts = dataUrl.split(",");
    if (parts.length < 2) {
        throw new Error("Invalid data URL format");
    }
    
    await RpcApi.FileWriteCommand(client, {
        info: { path: tempPath },
        data64: parts[1],
    });
    
    return tempPath;
}

Then both classes can call this shared function and just handle the terminal paste differently.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5cb6834 and 3efc03e.

📒 Files selected for processing (7)
  • frontend/app/store/wshclientapi.ts (1 hunks)
  • frontend/app/view/term/term-model.ts (3 hunks)
  • frontend/app/view/term/termwrap.ts (3 hunks)
  • frontend/types/gotypes.d.ts (1 hunks)
  • pkg/wshrpc/wshclient/wshclient.go (1 hunks)
  • pkg/wshrpc/wshrpctypes.go (3 hunks)
  • pkg/wshrpc/wshserver/wshserver.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/wshrpc/wshclient/wshclient.go
  • pkg/wshrpc/wshrpctypes.go
🧰 Additional context used
🧬 Code graph analysis (5)
frontend/types/gotypes.d.ts (1)
pkg/wshrpc/wshrpctypes.go (1)
  • CommandGetTempDirData (609-611)
pkg/wshrpc/wshserver/wshserver.go (3)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (306-308)
pkg/wshrpc/wshclient/wshclient.go (1)
  • GetTempDirCommand (372-375)
pkg/wshrpc/wshrpctypes.go (1)
  • CommandGetTempDirData (609-611)
frontend/app/store/wshclientapi.ts (1)
pkg/wshrpc/wshrpctypes.go (2)
  • CommandGetTempDirData (609-611)
  • RpcOpts (329-335)
frontend/app/view/term/termwrap.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (607-607)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (607-607)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (11)
frontend/types/gotypes.d.ts (1)

259-262: LGTM! Type definition correctly mirrors backend struct.

The generated TypeScript type properly represents the Go struct with an optional filename field, enabling platform-aware temp file path construction.

frontend/app/store/wshclientapi.ts (1)

305-308: LGTM! RPC command follows established patterns.

The GetTempDirCommand implementation is consistent with other RPC methods in the file and correctly typed.

pkg/wshrpc/wshserver/wshserver.go (1)

447-453: LGTM! Platform-aware temp directory implementation.

The implementation correctly uses os.TempDir() and filepath.Join() to ensure cross-platform compatibility, addressing the Windows path separator concerns from previous reviews.

frontend/app/view/term/termwrap.ts (4)

435-497: LGTM! Robust paste handling with proper fallbacks.

The async paste handler correctly:

  • Prioritizes image detection via ClipboardEvent.clipboardData (Electron compatibility)
  • Falls back to the Clipboard API for modern browsers
  • Includes text paste fallback on errors
  • Properly manages pasteActive state with a finally block

678-683: LGTM! Clear and well-documented.

The method appropriately returns true for all terminals with a clear comment explaining the design decision.


685-731: LGTM! Comprehensive image paste implementation.

This implementation correctly addresses all previous review concerns:

  • ✅ 5 MB size limit prevents excessive memory usage
  • ✅ Unique filename with timestamp + random component prevents collisions
  • ✅ Platform-aware temp path via GetTempDirCommand backend RPC
  • ✅ Proper base64 conversion using FileReader
  • ✅ Error handling with console.error logging
  • ✅ UX: Trailing space after path for easier command composition

733-741: LGTM! Clean wrapper with error handling.

The method is a straightforward adapter that extracts the blob and delegates to handleImagePasteBlob.

frontend/app/view/term/term-model.ts (4)

405-438: LGTM! Clean paste handling without debug statements.

The implementation:

  • Correctly prioritizes image pastes over text
  • Includes proper error handling with double fallback (clipboard.read → clipboard.readText)
  • No debug console.log statements (addressed previous review feedback)

440-445: LGTM! Consistent with termwrap implementation.

The method has the same clear design as the termwrap version.


600-602: Verify Shift+Enter default change is intentional.

The default value for shiftenternewline has changed from false (implicit) to true (explicit via ?? true). This means Shift+Enter will now insert newlines by default instead of executing commands, which is a breaking behavioral change for existing users.

Please confirm this is the intended behavior per the PR objectives. If so, consider:

  1. Documenting this change in release notes
  2. Adding a migration notice for users who relied on the old behavior
  3. Ensuring the config setting term:shiftenternewline allows users to opt back into the old behavior

609-609: LGTM! Paste trigger correctly updated.

The change from direct text paste to calling handlePaste() enables the new image paste functionality while maintaining text paste as a fallback.

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

♻️ Duplicate comments (1)
frontend/app/view/term/termwrap.ts (1)

668-684: Code duplication noted.

These methods are duplicated from term-model.ts. See the refactoring suggestion in the review of term-model.ts lines 430-446.

🧹 Nitpick comments (1)
frontend/app/view/term/term-model.ts (1)

430-446: Consider extracting duplicated methods to reduce maintenance burden.

The supportsImageInput() and handleImagePasteBlob() methods are duplicated identically in termwrap.ts (lines 668-684). Consider extracting these to a shared utility module or base class to maintain consistency and reduce duplication.

For example, you could move these methods to termutil.ts:

export function supportsImageInput(): boolean {
    return true;
}

export async function handleImagePasteBlob(
    blob: Blob, 
    client: WshClient, 
    terminal: { paste: (text: string) => void }
): Promise<void> {
    try {
        const tempPath = await createTempFileFromBlob(blob, client);
        terminal.paste(tempPath + " ");
    } catch (err) {
        console.error("Error pasting image:", err);
    }
}

Then import and use them in both files.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3efc03e and ef67e4f.

📒 Files selected for processing (3)
  • frontend/app/view/term/term-model.ts (3 hunks)
  • frontend/app/view/term/termutil.ts (1 hunks)
  • frontend/app/view/term/termwrap.ts (3 hunks)
🔇 Additional comments (7)
frontend/app/view/term/termutil.ts (2)

41-50: LGTM! Good coverage of common image formats.

The MIME type mapping covers all the common image formats users are likely to paste.


62-101: Excellent implementation with proper safeguards.

The function correctly:

  • Enforces a size limit to prevent abuse
  • Generates unique filenames using timestamp and random components
  • Obtains platform-appropriate temp paths from the backend (addressing previous review feedback)
  • Handles errors gracefully
frontend/app/view/term/term-model.ts (3)

395-428: Well-structured paste handling with proper prioritization.

The implementation correctly prioritizes image data over text and includes comprehensive error handling with a text-only fallback. The use of the Clipboard API is appropriate for this use case.


551-560: Shift+Enter behavior updated as documented.

The changes correctly implement the feature described in the PR:

  • Default shiftenternewline changed to true
  • Shift+Enter now sends a standard newline (\n) instead of the escape sequence

561-565: Good integration of image paste support with existing keyboard shortcuts.

Updating Ctrl+Shift+V to use handlePaste() enables image pasting via keyboard while maintaining backward compatibility for text pasting.

frontend/app/view/term/termwrap.ts (2)

425-487: Robust clipboard handling with excellent cross-platform support.

The async paste handler elegantly handles multiple clipboard APIs:

  1. ClipboardEvent.clipboardData (Electron)
  2. Clipboard API (modern browsers)
  3. Multiple fallback layers for error cases

The finally block ensures proper cleanup of the pasteActive flag. All previous review concerns about call stack issues and hardcoded paths have been addressed.


686-694: Clean helper method for Clipboard API integration.

The handleImagePaste() method provides a clean abstraction for processing ClipboardItem objects and properly delegates to the shared handleImagePasteBlob() logic.

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)
frontend/app/view/term/termwrap.ts (1)

429-491: Consider whether 30ms is sufficient for async image processing.

The finally block clears pasteActive after 30ms, but if handleImagePasteBlob() takes longer (e.g., large image, slow I/O), the flag might be cleared before the paste operation completes. This could affect the multi-input callback behavior triggered by pasteActive in handleTermData (line 570).

Consider either:

  • Awaiting the image paste operation and clearing pasteActive immediately after
  • Increasing the timeout if empirical testing shows 30ms is insufficient
  • Using a more deterministic completion signal
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef67e4f and 1188a63.

📒 Files selected for processing (3)
  • frontend/app/view/term/term-model.ts (3 hunks)
  • frontend/app/view/term/termutil.ts (1 hunks)
  • frontend/app/view/term/termwrap.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/app/view/term/termutil.ts
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/app/view/term/termwrap.ts (1)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (8)
frontend/app/view/term/termwrap.ts (3)

21-25: LGTM! Clean import additions.

The imports from termutil are appropriately scoped and used by the new image paste handling methods below.


672-690: LGTM! Clean delegation to termutil.

The methods appropriately delegate image handling to the utility layer and provide the terminal paste callback. Error handling in handleImagePaste is properly included.


676-679: 5 MB size limit is properly enforced.

Verification confirms that handleImagePasteBlobUtil calls createTempFileFromBlob (in termutil.ts, line 127), which includes the 5 MB validation at line 64 with the check if (blob.size > 5 * 1024 * 1024) and throws an error with appropriate user feedback: "Image too large (>5MB)".

frontend/app/view/term/term-model.ts (5)

32-38: LGTM! Import additions align with new functionality.

The extended imports from termutil provide the necessary utilities for image paste handling and are appropriately used in the methods below.


401-434: LGTM! Clean image-first paste handling.

The implementation properly prioritizes image paste over text and includes appropriate fallbacks. The use of Clipboard API (without ClipboardData) differs from termwrap.ts but is appropriate for this context where there's no ClipboardEvent available.


436-444: LGTM! Consistent delegation pattern.

The methods appropriately delegate to termutil and provide the terminal paste callback through termRef.current, maintaining consistency with the termwrap implementation.


551-553: LGTM! Shift+Enter behavior updated as intended.

The default for shiftenternewline changed to true, and the newline character is now standard \n instead of \u001b\n. This aligns with the PR objectives to improve input handling for interactive CLI tools.


560-560: LGTM! Keyboard shortcut integrated with new paste flow.

The Ctrl+Shift+V handler now delegates to handlePaste(), enabling the image-first paste behavior through the keyboard shortcut.

@sawka
Copy link
Member

sawka commented Nov 5, 2025

@togo01 thanks for working on this. i think you're right that we should default the Shift+Enter case to work better with claude code rather than have it default to the less friendly version. but i'm not sure about this change:

-                this.sendDataToController("\u001b\n");
+                this.sendDataToController("\n");

i think sending the ESC + "\n" is right (basically sends an Alt+Enter). When I test with Claude Code, the ESC+\n inserts the newline correctly without submitting (also the same way that VSCode solved it with /term-setup). Are you seeing something different?

@togo01
Copy link
Contributor Author

togo01 commented Nov 6, 2025

@togo01 thanks for working on this. i think you're right that we should default the Shift+Enter case to work better with claude code rather than have it default to the less friendly version. but i'm not sure about this change:

-                this.sendDataToController("\u001b\n");
+                this.sendDataToController("\n");

i think sending the ESC + "\n" is right (basically sends an Alt+Enter). When I test with Claude Code, the ESC+\n inserts the newline correctly without submitting (also the same way that VSCode solved it with /term-setup). Are you seeing something different?

Thanks for the feedback! You're absolutely right - I've reverted the change back to use \u001b\n (ESC + newline).
After reviewing the original implementation and testing again, I can confirm that \u001b\n is the correct approach. It properly inserts a newline in Claude Code without submitting the command, just like VSCode's /term-setup solution.
The feature works perfectly with this configuration. Thanks for catching that!

togo01 added 11 commits November 7, 2025 14:06
Enable Shift+Enter to insert newlines in terminal by default, improving
multi-line input support for interactive CLI tools like Claude Code.

Changes:
- Change default shiftenternewline config from false to true
- Send standard newline character (\n) instead of escape sequence (\u001b\n)

This allows users to input multi-line commands and text without requiring
manual configuration changes.
Add support for pasting images in terminal by saving them as temporary
files and pasting the file path, similar to iTerm2 behavior.

This enables AI tools like Claude Code to receive and process pasted
images by reading the temporary file path.

Changes:
- Add handlePaste() method to intercept Cmd+Shift+V paste events
- Add handleImagePasteBlob() to save images to /tmp and paste path
- Detect image data in clipboard using ClipboardEvent and Clipboard API
- Support both screenshot paste and file copy scenarios
- Add 5MB size limit for pasted images

The implementation creates temporary files in /tmp with format:
waveterm_paste_[timestamp].[ext]
Reuse handleImagePasteBlob() method instead of manually converting
blob to base64 with String.fromCharCode(), which causes RangeError
for images larger than ~64KB.

This ensures consistent behavior and prevents stack overflow errors
when pasting large screenshots.

Fixes: CodeRabbit review feedback
Address CodeRabbit review feedback:

- Use MIME type lookup table instead of split() to handle complex types
  like image/svg+xml correctly
- Add random component to filename to prevent collisions when pasting
  multiple images in quick succession
- Remove unused stringToBase64 import from termwrap.ts
- Add TODO comment documenting cross-platform temp directory limitation

The /tmp/ path currently works on macOS/Linux. Windows support would
require backend API to provide platform-appropriate temp directory.
Add GetTempDirCommand RPC to provide platform-appropriate temp directory
paths from the backend, replacing hardcoded /tmp/ paths.

This ensures image paste works correctly on:
- macOS: /tmp or /var/folders/...
- Linux: /tmp or $TMPDIR
- Windows: %TEMP% or %LOCALAPPDATA%\Temp

Backend changes:
- Add GetTempDirCommand to wshrpc interface (wshrpctypes.go)
- Implement GetTempDirCommand using os.TempDir() (wshserver.go)
- Add client wrapper in wshclient.go

Frontend changes:
- Add GetTempDirCommand to RpcApi (wshclientapi.ts)
- Update handleImagePasteBlob to use GetTempDirCommand in term-model.ts
- Update handleImagePasteBlob to use GetTempDirCommand in termwrap.ts

Addresses CodeRabbit review feedback about cross-platform compatibility.
Remove temporary debug logging from handlePaste() method:
- Remove handlePaste() called log
- Remove clipboard items count log
- Remove clipboard item types log
- Remove found image processing log
- Remove pasting text length log

Preserve console.error statements for actual error reporting.

Addresses CodeRabbit review feedback.
Address three critical review items:

1. Extract MIME type mapping as class constants
   - Add MIME_TO_EXT static readonly in TermViewModel and TermWrap
   - Improves reusability and maintainability

2. Validate data URL format before splitting
   - Add defensive check for comma separator in data URL
   - Throw error if format is invalid
   - Prevents potential undefined access

3. Fix cross-platform path joining issue
   - Extend GetTempDirCommand to accept optional filename parameter
   - Backend now joins paths using filepath.Join (platform-aware)
   - Eliminates hardcoded '/' separator that breaks on Windows

Backend changes:
- Add CommandGetTempDirData struct with optional FileName field
- Update GetTempDirCommand signature and implementation
- Use filepath.Join for cross-platform path handling

Frontend changes:
- Extract MIME type mappings to static constants
- Pass filename to GetTempDirCommand instead of manual joining
- Add data URL format validation with error handling

Addresses CodeRabbit PR review feedback.
Extract MIME type mapping and image paste logic to shared termutil module:
- Move MIME_TO_EXT constant from TermViewModel and TermWrap to termutil.ts
- Create createTempFileFromBlob() utility function to handle image blob processing
- Refactor both classes to use shared utilities, reducing code duplication
- Maintain all functionality: size validation, format detection, temp file creation

This addresses CodeRabbit review feedback about code duplication.
Further reduce code duplication by extracting supportsImageInput() and
handleImagePasteBlob() methods to termutil.ts:
- Add supportsImageInput() utility function
- Add handleImagePasteBlob() utility with callback for terminal paste
- Update both TermViewModel and TermWrap to delegate to shared utilities
- Maintain all functionality while eliminating duplicate method implementations

This addresses additional CodeRabbit review feedback about method duplication.
Use escape sequence + newline (\u001b\n) instead of plain newline (\n)
for Shift+Enter, following the terminal standard and matching VSCode's
/term-setup implementation.

This ensures proper multi-line input support in Claude Code and other
AI coding tools without accidentally submitting incomplete commands.
Fix duplicate text input when switching input methods via Capslock
during Chinese/Japanese/Korean IME composition.

## Problem
When using Chinese Zhuyin IME in terminal, switching to English mode
with Capslock during composition causes the composed text to be sent
twice, resulting in duplicate output.

## Solution

### IME Composition Handling
- Add IME composition state tracking in TermWrap (isComposing, etc.)
- Register compositionstart/update/end event listeners on xterm textarea
- Block all data sends during composition, only allow after compositionend
- Prevents xterm.js from sending data during compositionupdate phase

### Deduplication Logic
- Implement 50ms time window deduplication mechanism
- Track first send, block duplicate send from Capslock switching
- Ensure Ctrl+Space and Fn switching work correctly (single send)

### Paste Deduplication
- Fix duplicate paste issue caused by xterm.js paste() method
- paste() triggers onData event, causing handleTermData to be called twice
- Use pasteActive flag and time window to allow only first send

### Edge Cases
- Add blur event handler to reset composition state on focus loss
- Add Escape key handling in term-model.ts to cancel composition

## Test Results
✅ macOS Zhuyin + Capslock switching - no duplicate
✅ macOS Zhuyin + Ctrl+Space switching - normal input
✅ macOS Zhuyin + Fn switching - normal input
✅ English input - normal
✅ Shift+Enter multiline - normal
✅ Text paste - no duplicate
✅ Image paste - normal
✅ Basic commands - normal
@togo01 togo01 force-pushed the feature/terminal-input-improvements branch from 7343252 to 1cf388f Compare November 7, 2025 06:07
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

🧹 Nitpick comments (4)
frontend/app/view/term/term-model.ts (1)

32-38: Remove unused import.

createTempFileFromBlob isn’t used in this file.

 import {
     computeTheme,
-    createTempFileFromBlob,
     DefaultTermTheme,
     handleImagePasteBlob as handleImagePasteBlobUtil,
     supportsImageInput as supportsImageInputUtil,
 } from "./termutil";
frontend/app/view/term/termutil.ts (2)

41-50: Broaden MIME coverage.

Add common types like AVIF/HEIC/HEIF/ICO for better cross-platform paste coverage.

 export const MIME_TO_EXT: Record<string, string> = {
   "image/png": "png",
   "image/jpeg": "jpg",
   "image/jpg": "jpg",
   "image/gif": "gif",
   "image/webp": "webp",
   "image/bmp": "bmp",
   "image/svg+xml": "svg",
   "image/tiff": "tiff",
+  "image/avif": "avif",
+  "image/heic": "heic",
+  "image/heif": "heif",
+  "image/x-icon": "ico",
+  "image/vnd.microsoft.icon": "ico",
 };

62-101: Use stronger unique ID for filenames.

Prefer crypto.randomUUID() when available to avoid rare collisions.

-  const timestamp = Date.now();
-  const random = Math.random().toString(36).substring(2, 8);
-  const filename = `waveterm_paste_${timestamp}_${random}.${ext}`;
+  const timestamp = Date.now();
+  const rand =
+    typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
+      ? crypto.randomUUID()
+      : Math.random().toString(36).slice(2, 10);
+  const filename = `waveterm_paste_${timestamp}_${rand}.${ext}`;
frontend/app/view/term/termwrap.ts (1)

637-687: IME and paste dedup logic is sensible.

Blocking during composition, 50 ms dedup windows for paste and IME duplicates are reasonable defaults.

Consider centralizing DEDUP_WINDOW_MS in a module constant shared with related handlers for consistency.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7343252 and 1cf388f.

📒 Files selected for processing (8)
  • frontend/app/store/wshclientapi.ts (1 hunks)
  • frontend/app/view/term/term-model.ts (4 hunks)
  • frontend/app/view/term/termutil.ts (1 hunks)
  • frontend/app/view/term/termwrap.ts (7 hunks)
  • frontend/types/gotypes.d.ts (1 hunks)
  • pkg/wshrpc/wshclient/wshclient.go (1 hunks)
  • pkg/wshrpc/wshrpctypes.go (3 hunks)
  • pkg/wshrpc/wshserver/wshserver.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/app/store/wshclientapi.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-01-22T01:28:41.417Z
Learnt from: esimkowitz
Repo: wavetermdev/waveterm PR: 1790
File: pkg/remote/fileshare/wshfs/wshfs.go:122-122
Timestamp: 2025-01-22T01:28:41.417Z
Learning: The RpcClient in pkg/remote/fileshare/wshfs/wshfs.go is initialized and handled downstream by either main-server or wshcmd-connserver, as documented in the package comment.

Applied to files:

  • pkg/wshrpc/wshclient/wshclient.go
🧬 Code graph analysis (7)
pkg/wshrpc/wshserver/wshserver.go (3)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (331-333)
pkg/wshrpc/wshclient/wshclient.go (1)
  • GetTempDirCommand (402-405)
pkg/wshrpc/wshrpctypes.go (1)
  • CommandGetTempDirData (627-629)
pkg/wshrpc/wshclient/wshclient.go (3)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (331-333)
pkg/wshutil/wshrpc.go (1)
  • WshRpc (47-61)
pkg/wshrpc/wshrpctypes.go (2)
  • CommandGetTempDirData (627-629)
  • RpcOpts (347-353)
frontend/types/gotypes.d.ts (1)
pkg/wshrpc/wshrpctypes.go (1)
  • CommandGetTempDirData (627-629)
pkg/wshrpc/wshrpctypes.go (2)
frontend/app/store/wshclientapi.ts (1)
  • GetTempDirCommand (331-333)
pkg/wshrpc/wshclient/wshclient.go (1)
  • GetTempDirCommand (402-405)
frontend/app/view/term/term-model.ts (2)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
frontend/app/store/global.ts (1)
  • getOverrideConfigAtom (834-834)
frontend/app/view/term/termutil.ts (3)
frontend/app/store/wshclientapi.ts (1)
  • RpcApi (637-637)
frontend/app/view/term/term-model.ts (2)
  • supportsImageInput (436-438)
  • handleImagePasteBlob (440-444)
frontend/app/view/term/termwrap.ts (2)
  • supportsImageInput (784-786)
  • handleImagePasteBlob (788-792)
frontend/app/view/term/termwrap.ts (1)
frontend/app/store/wshrpcutil.ts (1)
  • TabRpcClient (37-37)
🔇 Additional comments (10)
frontend/types/gotypes.d.ts (1)

282-286: New RPC type addition looks correct.

filename?: string aligns with Go’s json:"filename,omitempty". No issues.

pkg/wshrpc/wshclient/wshclient.go (1)

401-405: Client wrapper LGTM.

Consistent with other RPC wrappers; generic type and command string are correct.

pkg/wshrpc/wshrpctypes.go (1)

86-86: RPC plumbing is consistent end-to-end.

Command constant, interface, and data struct align with client/server and TS types.

Also applies to: 226-227, 627-630

frontend/app/view/term/term-model.ts (2)

401-434: Clipboard handling LGTM with graceful fallback.

Image-first via navigator.clipboard.read() and text fallback are correct; errors are handled.

Please verify Electron permission behavior on Linux/Windows where navigator.clipboard.read() may be restricted and confirm the text fallback path triggers as expected.


545-551: IME Escape reset and Shift+Enter behavior look right.

ESC resets composition state; Shift+Enter sends ESC+LF with default enabled.

Also applies to: 560-570

frontend/app/view/term/termutil.ts (1)

121-134: Helper flow LGTM.

Temp-file creation + paste callback with error logging is clean.

frontend/app/view/term/termwrap.ts (4)

359-372: IME state scaffolding LGTM.

Clear flags tracked for composition and first-send dedup.


444-506: Robust paste pipeline.

Prioritizes images via ClipboardEvent and falls back to Clipboard API/text. Dedup flagging via pasteActive is correct.


561-586: Composition listeners + blur reset LGTM.

Attaching to xterm textarea and cleaning up on dispose is correct.


784-802: Image helpers integration LGTM.

Reuses termutil helpers; pastes resulting path into terminal.

Fix critical path traversal vulnerability where attackers could use
../ sequences or absolute paths to access files outside the temp
directory. Now sanitizes input by extracting only the base filename
and rejecting traversal attempts.
@sawka
Copy link
Member

sawka commented Nov 7, 2025

overall this looks good, tested both the image pasting and the Shift+Enter code! i'm going to merge it as-is and continue testing a bit more... i can fix anything that comes up in a follow-up PR rather than waiting forever to get it merged.

@sawka sawka merged commit 8435958 into wavetermdev:main Nov 7, 2025
4 checks passed
@togo01
Copy link
Contributor Author

togo01 commented Nov 8, 2025

overall this looks good, tested both the image pasting and the Shift+Enter code! i'm going to merge it as-is and continue testing a bit more... i can fix anything that comes up in a follow-up PR rather than waiting forever to get it merged.

Thanks for the quick review and merge. I'm pleased to see this feature/fix incorporated.

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.

3 participants