mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 17:00:59 +00:00
Merge branch 'fix/support-umlauts-and-unicode'
This commit is contained in:
commit
21f24061bb
3 changed files with 135 additions and 4 deletions
|
|
@ -115,7 +115,7 @@ editor.setAutocompleteProvider(provider);
|
|||
|
||||
**Key Bindings:**
|
||||
- `Enter` - Submit
|
||||
- `Shift+Enter` or `Ctrl+Enter` - New line
|
||||
- `Shift+Enter`, `Ctrl+Enter`, or `Alt+Enter` - New line (terminal-dependent, Alt+Enter most reliable)
|
||||
- `Tab` - Autocomplete
|
||||
- `Ctrl+K` - Delete line
|
||||
- `Ctrl+A` / `Ctrl+E` - Line start/end
|
||||
|
|
|
|||
|
|
@ -333,8 +333,8 @@ export class Editor implements Component {
|
|||
// Left
|
||||
this.moveCursor(0, -1);
|
||||
}
|
||||
// Regular characters (printable ASCII)
|
||||
else if (data.charCodeAt(0) >= 32 && data.charCodeAt(0) <= 126) {
|
||||
// Regular characters (printable characters and unicode, but not control characters)
|
||||
else if (data.charCodeAt(0) >= 32) {
|
||||
this.insertCharacter(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -472,7 +472,7 @@ export class Editor implements Component {
|
|||
// Filter out non-printable characters except newlines
|
||||
const filteredText = tabExpandedText
|
||||
.split("")
|
||||
.filter((char) => char === "\n" || (char >= " " && char <= "~"))
|
||||
.filter((char) => char === "\n" || char.charCodeAt(0) >= 32)
|
||||
.join("");
|
||||
|
||||
// Split into lines
|
||||
|
|
|
|||
131
packages/tui/test/editor.test.ts
Normal file
131
packages/tui/test/editor.test.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import assert from "node:assert";
|
||||
import { describe, it } from "node:test";
|
||||
import { Editor } from "../src/components/editor.js";
|
||||
|
||||
describe("Editor component", () => {
|
||||
describe("Unicode text editing behavior", () => {
|
||||
it("inserts mixed ASCII, umlauts, and emojis as literal text", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("H");
|
||||
editor.handleInput("e");
|
||||
editor.handleInput("l");
|
||||
editor.handleInput("l");
|
||||
editor.handleInput("o");
|
||||
editor.handleInput(" ");
|
||||
editor.handleInput("ä");
|
||||
editor.handleInput("ö");
|
||||
editor.handleInput("ü");
|
||||
editor.handleInput(" ");
|
||||
editor.handleInput("😀");
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "Hello äöü 😀");
|
||||
});
|
||||
|
||||
it("deletes single-code-unit unicode characters (umlauts) with Backspace", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("ä");
|
||||
editor.handleInput("ö");
|
||||
editor.handleInput("ü");
|
||||
|
||||
// Delete the last character (ü)
|
||||
editor.handleInput("\x7f"); // Backspace
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "äö");
|
||||
});
|
||||
|
||||
it("deletes multi-code-unit emojis with repeated Backspace", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("😀");
|
||||
editor.handleInput("👍");
|
||||
|
||||
// Delete the last emoji (👍) - requires 2 backspaces since emojis are 2 code units
|
||||
editor.handleInput("\x7f"); // Backspace
|
||||
editor.handleInput("\x7f"); // Backspace
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "😀");
|
||||
});
|
||||
|
||||
it("inserts characters at the correct position after cursor movement over umlauts", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("ä");
|
||||
editor.handleInput("ö");
|
||||
editor.handleInput("ü");
|
||||
|
||||
// Move cursor left twice
|
||||
editor.handleInput("\x1b[D"); // Left arrow
|
||||
editor.handleInput("\x1b[D"); // Left arrow
|
||||
|
||||
// Insert 'x' in the middle
|
||||
editor.handleInput("x");
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "äxöü");
|
||||
});
|
||||
|
||||
it("moves cursor in code units across multi-code-unit emojis before insertion", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("😀");
|
||||
editor.handleInput("👍");
|
||||
editor.handleInput("🎉");
|
||||
|
||||
// Move cursor left over last emoji (🎉)
|
||||
editor.handleInput("\x1b[D"); // Left arrow
|
||||
editor.handleInput("\x1b[D"); // Left arrow
|
||||
|
||||
// Move cursor left over second emoji (👍)
|
||||
editor.handleInput("\x1b[D");
|
||||
editor.handleInput("\x1b[D");
|
||||
|
||||
// Insert 'x' between first and second emoji
|
||||
editor.handleInput("x");
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "😀x👍🎉");
|
||||
});
|
||||
|
||||
it("preserves umlauts across line breaks", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("ä");
|
||||
editor.handleInput("ö");
|
||||
editor.handleInput("ü");
|
||||
editor.handleInput("\n"); // new line
|
||||
editor.handleInput("Ä");
|
||||
editor.handleInput("Ö");
|
||||
editor.handleInput("Ü");
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "äöü\nÄÖÜ");
|
||||
});
|
||||
|
||||
it("replaces the entire document with unicode text via setText (paste simulation)", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
// Simulate bracketed paste / programmatic replacement
|
||||
editor.setText("Hällö Wörld! 😀 äöüÄÖÜß");
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "Hällö Wörld! 😀 äöüÄÖÜß");
|
||||
});
|
||||
|
||||
it("moves cursor to document start on Ctrl+A and inserts at the beginning", () => {
|
||||
const editor = new Editor();
|
||||
|
||||
editor.handleInput("a");
|
||||
editor.handleInput("b");
|
||||
editor.handleInput("\x01"); // Ctrl+A (move to start)
|
||||
editor.handleInput("x"); // Insert at start
|
||||
|
||||
const text = editor.getText();
|
||||
assert.strictEqual(text, "xab");
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue