- replace Ink implementation with opentui + React 19 (old version kept at cli/legacy-ink-src/ for field reference) - all traffic goes through Gateway host Bearer token; workspace bound via X-Astrion-Workspace-Id header - auto-spawn server only when no instance is alive (never kill/restart running ones); stale server without token is detected - astrion launcher (bin/astrion, symlink-aware, --version/--help fast-exit) - slash menu panels (session/model/mode/permission/path/context/approvals...) with shared selector component - backend: host workspace list/create dual-channel endpoints, host token generated at startup - i18n reads OS locale at startup (zh/en) Co-authored-by: Astrion powered by Kimi-K3 <astrion-agent@users.noreply.github.com>
40 lines
1.6 KiB
TypeScript
40 lines
1.6 KiB
TypeScript
// 显示宽度工具:中西文混排时按「显示宽度」对齐(CJK/全角=2,其余=1)。
|
||
// 面板的列对齐一律用 padEndWidth,不要用 String.padEnd(按字符数算,中文行会歪)。
|
||
|
||
/** 单字符显示宽度:CJK 统一表意、全角形式、韩文音节、日文假名等宽区段计 2 */
|
||
export function charWidth(cp: number): number {
|
||
if (
|
||
(cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo
|
||
(cp >= 0x2e80 && cp <= 0x303e) || // CJK Radicals .. CJK Symbols
|
||
(cp >= 0x3041 && cp <= 0x33ff) || // Hiragana .. CJK Compat
|
||
(cp >= 0x3400 && cp <= 0x4dbf) || // CJK Ext A
|
||
(cp >= 0x4e00 && cp <= 0x9fff) || // CJK Unified
|
||
(cp >= 0xa000 && cp <= 0xa4cf) || // Yi
|
||
(cp >= 0xac00 && cp <= 0xd7a3) || // Hangul Syllables
|
||
(cp >= 0xf900 && cp <= 0xfaff) || // CJK Compat Ideographs
|
||
(cp >= 0xfe30 && cp <= 0xfe6f) || // CJK Compat Forms
|
||
(cp >= 0xff01 && cp <= 0xff60) || // Fullwidth Forms
|
||
(cp >= 0xffe0 && cp <= 0xffe6) || // Fullwidth Signs
|
||
(cp >= 0x20000 && cp <= 0x2fffd) || // CJK Ext B..
|
||
(cp >= 0x30000 && cp <= 0x3fffd)
|
||
) {
|
||
return 2;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/** 字符串显示宽度 */
|
||
export function displayWidth(text: string): number {
|
||
let w = 0;
|
||
for (const ch of text) {
|
||
w += charWidth(ch.codePointAt(0)!);
|
||
}
|
||
return w;
|
||
}
|
||
|
||
/** 按显示宽度补齐(末尾补空格到 target 显示宽度;已超宽原样返回) */
|
||
export function padEndWidth(text: string, target: number, fill: string = ' '): string {
|
||
const diff = target - displayWidth(text);
|
||
return diff > 0 ? text + fill.repeat(diff) : text;
|
||
}
|