mirror of https://github.com/garrytan/gstack.git
Merge PR #1297: Korean/CJK IME input and rendering in Sidebar Terminal
This commit is contained in:
commit
9f17e9ba41
|
|
@ -362,8 +362,26 @@ function buildServer() {
|
|||
// Binary input. Lazy-spawn claude on the first byte.
|
||||
if (!session.spawned) {
|
||||
session.spawned = true;
|
||||
// UTF-8 boundary detection to prevent splitting multi-byte characters (issue #1272).
|
||||
// Buffer incomplete UTF-8 sequences until the next chunk completes them.
|
||||
let leftover = Buffer.alloc(0);
|
||||
const proc = spawnClaude(session.cols, session.rows, (chunk) => {
|
||||
try { ws.sendBinary(chunk); } catch {}
|
||||
const combined = Buffer.concat([leftover, Buffer.from(chunk)]);
|
||||
// Find the last index where a UTF-8 codepoint ends. Look back at most 3 bytes.
|
||||
let safeEnd = combined.length;
|
||||
for (let i = combined.length - 1; i >= Math.max(0, combined.length - 3); i--) {
|
||||
const b = combined[i];
|
||||
if ((b & 0x80) === 0) { safeEnd = i + 1; break; } // ASCII
|
||||
if ((b & 0xC0) === 0x80) continue; // continuation byte
|
||||
const expected = (b & 0xE0) === 0xC0 ? 2 : (b & 0xF0) === 0xE0 ? 3 : 4;
|
||||
safeEnd = (combined.length - i >= expected) ? combined.length : i;
|
||||
break;
|
||||
}
|
||||
const flush = combined.slice(0, safeEnd);
|
||||
leftover = combined.slice(safeEnd);
|
||||
if (flush.length) {
|
||||
try { ws.sendBinary(flush); } catch {}
|
||||
}
|
||||
});
|
||||
if (!proc) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@
|
|||
function ensureXterm() {
|
||||
if (term) return;
|
||||
term = new Terminal({
|
||||
fontFamily: '"JetBrains Mono", "SF Mono", Menlo, monospace',
|
||||
fontFamily: '"JetBrains Mono", "SF Mono", Menlo, "Noto Sans Mono CJK KR", "Malgun Gothic", monospace',
|
||||
fontSize: 13,
|
||||
theme: { background: '#0a0a0a', foreground: '#e5e5e5' },
|
||||
cursorBlink: true,
|
||||
|
|
@ -196,7 +196,25 @@
|
|||
});
|
||||
ro.observe(els.mount);
|
||||
|
||||
// IME composition handling for Korean/CJK input (issue #1272).
|
||||
// Suppress partial jamo during composition; only send the final
|
||||
// composed string on compositionend. Without this, Korean IME
|
||||
// sends fragmented input or doubles characters.
|
||||
let composing = false;
|
||||
const ta = term.textarea;
|
||||
if (ta) {
|
||||
ta.addEventListener('compositionstart', () => { composing = true; });
|
||||
ta.addEventListener('compositionend', (e) => {
|
||||
composing = false;
|
||||
if (e.data && ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(new TextEncoder().encode(e.data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
term.onData((data) => {
|
||||
if (composing) return; // suppress partial input events during IME composition
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(new TextEncoder().encode(data));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
/* Typography */
|
||||
--font-system: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
|
||||
--font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
|
||||
--font-mono: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Cascadia Code', 'Noto Sans Mono CJK KR', 'Malgun Gothic', monospace;
|
||||
|
||||
/* Radius */
|
||||
--radius-sm: 4px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue