@visual-z/xeno 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +143 -33
- package/docs/install.md +18 -6
- package/docs/providers.md +147 -0
- package/package.json +8 -2
- package/src/apps/cli.ts +42 -14
- package/src/apps/commands.ts +32 -4
- package/src/apps/completions.ts +9 -6
- package/src/apps/export-cli.ts +24 -0
- package/src/apps/extension-ui.ts +330 -0
- package/src/apps/input.ts +16 -1
- package/src/apps/model-catalog.ts +42 -0
- package/src/apps/project-trust.ts +49 -0
- package/src/apps/render.ts +1 -0
- package/src/apps/resource-browser.ts +75 -0
- package/src/apps/resources.ts +25 -0
- package/src/apps/session-browser.ts +113 -0
- package/src/apps/session-picker.ts +55 -0
- package/src/apps/shell.ts +1177 -378
- package/src/apps/startup.ts +400 -268
- package/src/core/action-defaults.ts +306 -0
- package/src/core/attachment-input.ts +17 -0
- package/src/core/catalog-merge.ts +19 -0
- package/src/core/event-validation.ts +19 -2
- package/src/core/events.ts +45 -2
- package/src/core/export.ts +6 -2
- package/src/core/extension-flags.ts +14 -0
- package/src/core/images.ts +46 -0
- package/src/core/json-event.ts +2 -0
- package/src/core/keymap.ts +43 -119
- package/src/core/model-replay.ts +22 -0
- package/src/core/model-scope.ts +53 -0
- package/src/core/packages.ts +47 -0
- package/src/core/project.ts +21 -3
- package/src/core/prompts.ts +33 -5
- package/src/core/queue.ts +18 -0
- package/src/core/resource-selection.ts +36 -0
- package/src/{runtime → core}/resume.ts +15 -22
- package/src/core/session-archive.ts +4 -0
- package/src/core/session-browser.ts +82 -0
- package/src/core/session-entry.ts +56 -0
- package/src/core/session-position.ts +26 -0
- package/src/core/settings.ts +169 -154
- package/src/core/theme.ts +15 -6
- package/src/core/thinking.ts +26 -0
- package/src/core/tokens.ts +2 -0
- package/src/core/transcript.ts +15 -5
- package/src/runtime/auth.ts +116 -11
- package/src/runtime/catalog.ts +54 -151
- package/src/runtime/clipboard.ts +114 -0
- package/src/runtime/extension-loader.ts +37 -0
- package/src/runtime/extension-ui.ts +55 -0
- package/src/runtime/extensions.ts +273 -0
- package/src/runtime/images.ts +76 -0
- package/src/runtime/loop.ts +213 -83
- package/src/runtime/model-config.ts +232 -0
- package/src/runtime/model-data.LICENSE +27 -0
- package/src/runtime/model-data.ts +21 -0
- package/src/runtime/model-replay.ts +10 -0
- package/src/runtime/model-scope.ts +20 -0
- package/src/runtime/model-sources.ts +151 -0
- package/src/runtime/model.ts +74 -11
- package/src/runtime/models-fetch.ts +6 -104
- package/src/runtime/oauth.ts +4 -3
- package/src/runtime/openai-responses.ts +12 -4
- package/src/runtime/openai.ts +13 -4
- package/src/runtime/package-manager.ts +125 -0
- package/src/runtime/pi-provider.ts +235 -0
- package/src/runtime/progress.ts +26 -0
- package/src/runtime/provider-access.ts +21 -0
- package/src/runtime/provider-protocols.ts +29 -0
- package/src/runtime/provider-request.ts +73 -0
- package/src/runtime/provider.ts +26 -6
- package/src/runtime/rich-text.ts +36 -0
- package/src/runtime/tools.ts +41 -5
- package/src/store/command.ts +7 -1
- package/src/store/context-files.ts +1 -1
- package/src/store/log.ts +65 -0
- package/src/store/models.ts +28 -9
- package/src/store/observations.ts +4 -2
- package/src/store/packages.ts +98 -0
- package/src/store/recovery.ts +8 -0
- package/src/store/resource-index.ts +170 -0
- package/src/store/resources.ts +75 -142
- package/src/store/session-archive.ts +162 -0
- package/src/store/session-browser.ts +68 -0
- package/src/store/session-position.ts +27 -0
- package/src/store/sessions.ts +87 -66
- package/src/store/trust.ts +13 -0
- package/src/tui/chat-viewport.ts +47 -0
- package/src/tui/editor.ts +62 -15
- package/src/tui/host.ts +50 -9
- package/src/tui/input.ts +8 -2
- package/src/tui/select.ts +24 -6
- package/src/tui/settings.ts +4 -0
- package/src/tui/system-theme.ts +700 -0
- package/src/tui/transcript.ts +63 -11
- package/src/tui/tree.ts +161 -95
- package/src/vendor/pi-rich/LICENSE.upstream +21 -0
- package/src/vendor/pi-rich/UPSTREAM.json +17 -0
- package/src/vendor/pi-rich/clipboard-command.ts +44 -0
- package/src/vendor/pi-rich/exif-orientation.ts +182 -0
- package/src/vendor/pi-rich/html.ts +51 -0
- package/src/vendor/pi-rich/image-convert.ts +49 -0
- package/src/vendor/pi-rich/image-resize-core.ts +164 -0
- package/src/vendor/pi-rich/image-resize-worker.ts +42 -0
- package/src/vendor/pi-rich/image-resize.ts +123 -0
- package/src/vendor/pi-rich/mime.ts +116 -0
- package/src/vendor/pi-rich/photon.ts +139 -0
- package/src/vendor/pi-rich/syntax-highlight.ts +214 -0
- package/src/vendor/pi-tui/UPSTREAM.json +50 -0
- package/src/vendor/pi-tui/VENDOR.md +7 -5
- package/src/vendor/pi-tui/autocomplete.ts +35 -10
- package/src/vendor/pi-tui/colors.ts +367 -0
- package/src/vendor/pi-tui/components/box.ts +8 -9
- package/src/vendor/pi-tui/components/editor.ts +4 -2
- package/src/vendor/pi-tui/components/markdown.ts +8 -2
- package/src/vendor/pi-tui/index.ts +31 -1
- package/src/vendor/pi-tui/native/UPSTREAM.json +23 -0
- package/src/vendor/pi-tui/native/clipboard.h +208 -0
- package/src/vendor/pi-tui/native/darwin/README.md +22 -0
- package/src/vendor/pi-tui/native/darwin/build.sh +69 -0
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +132 -0
- package/src/vendor/pi-tui/native/linux/README.md +23 -0
- package/src/vendor/pi-tui/native/linux/build.sh +65 -0
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +70 -0
- package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +274 -0
- package/src/vendor/pi-tui/native/napi.h +91 -0
- package/src/vendor/pi-tui/native/win32/README.md +31 -0
- package/src/vendor/pi-tui/native/win32/build.mjs +232 -0
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/src/vendor/pi-tui/native/win32/src/win32-platform.c +241 -0
- package/src/vendor/pi-tui/native-platform.ts +2 -0
- package/src/vendor/pi-tui/oklab.ts +233 -0
- package/src/vendor/pi-tui/terminal-colors.ts +26 -8
- package/src/vendor/pi-tui/terminal-image.ts +43 -8
- package/src/vendor/pi-tui/terminal.ts +16 -9
- package/src/vendor/pi-tui/tui-alt-screen.ts +26 -18
- package/src/vendor/pi-tui/tui.ts +112 -75
- package/src/vendor/pi-tui/utils.ts +89 -49
- package/src/vendor/pi-tui/wheel-scroll.ts +82 -0
|
@@ -180,8 +180,10 @@ function finalizeTruncatedResult(
|
|
|
180
180
|
* check to avoid running the RGI_Emoji regex unnecessarily.
|
|
181
181
|
*/
|
|
182
182
|
function graphemeWidth(segment: string): number {
|
|
183
|
-
if (segment ===
|
|
184
|
-
|
|
183
|
+
if (segment.length === 1) {
|
|
184
|
+
const code = segment.charCodeAt(0);
|
|
185
|
+
if (code >= 0x20 && code <= 0x7e) return 1;
|
|
186
|
+
if (code === 0x09) return 3;
|
|
185
187
|
}
|
|
186
188
|
|
|
187
189
|
// Some marks occupy cells even without a base character.
|
|
@@ -250,9 +252,11 @@ export function visibleWidth(str: string): number {
|
|
|
250
252
|
return 0;
|
|
251
253
|
}
|
|
252
254
|
|
|
253
|
-
// Fast path:
|
|
254
|
-
|
|
255
|
-
|
|
255
|
+
// Fast path: printable ASCII, tabs, and ANSI escape sequences. Styled lines take this path, so
|
|
256
|
+
// re-rendering after a theme change does not run grapheme segmentation on every line.
|
|
257
|
+
const asciiWidth = asciiVisibleWidth(str);
|
|
258
|
+
if (asciiWidth !== -1) {
|
|
259
|
+
return asciiWidth;
|
|
256
260
|
}
|
|
257
261
|
|
|
258
262
|
// Check cache
|
|
@@ -266,22 +270,25 @@ export function visibleWidth(str: string): number {
|
|
|
266
270
|
if (str.includes("\t")) {
|
|
267
271
|
clean = clean.replace(/\t/g, " ");
|
|
268
272
|
}
|
|
269
|
-
|
|
273
|
+
let escapeIndex = clean.indexOf("\x1b");
|
|
274
|
+
if (escapeIndex !== -1) {
|
|
270
275
|
// Strip supported ANSI/OSC/APC escape sequences in one pass.
|
|
271
276
|
// This covers CSI styling/cursor codes, OSC hyperlinks and prompt markers,
|
|
272
277
|
// and APC sequences like CURSOR_MARKER.
|
|
273
278
|
let stripped = "";
|
|
274
|
-
let
|
|
275
|
-
while (
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
279
|
+
let copyFrom = 0;
|
|
280
|
+
while (escapeIndex !== -1) {
|
|
281
|
+
const length = ansiCodeLength(clean, escapeIndex);
|
|
282
|
+
if (length > 0) {
|
|
283
|
+
stripped += clean.slice(copyFrom, escapeIndex);
|
|
284
|
+
escapeIndex += length;
|
|
285
|
+
copyFrom = escapeIndex;
|
|
286
|
+
} else {
|
|
287
|
+
escapeIndex++;
|
|
280
288
|
}
|
|
281
|
-
|
|
282
|
-
i++;
|
|
289
|
+
escapeIndex = clean.indexOf("\x1b", escapeIndex);
|
|
283
290
|
}
|
|
284
|
-
clean = stripped;
|
|
291
|
+
clean = stripped + clean.slice(copyFrom);
|
|
285
292
|
}
|
|
286
293
|
|
|
287
294
|
// Calculate width
|
|
@@ -412,43 +419,66 @@ export function normalizeTerminalOutput(str: string): string {
|
|
|
412
419
|
* Extract ANSI escape sequences from a string at the given position.
|
|
413
420
|
*/
|
|
414
421
|
export function extractAnsiCode(str: string, pos: number): { code: string; length: number } | null {
|
|
415
|
-
|
|
422
|
+
const length = ansiCodeLength(str, pos);
|
|
423
|
+
return length > 0 ? { code: str.substring(pos, pos + length), length } : null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Width of a string made of printable ASCII, tabs, and ANSI escape sequences, or -1 if it contains
|
|
428
|
+
* anything else. Matches `visibleWidth` for those strings without allocating.
|
|
429
|
+
*/
|
|
430
|
+
function asciiVisibleWidth(str: string): number {
|
|
431
|
+
let width = 0;
|
|
432
|
+
let i = 0;
|
|
433
|
+
while (i < str.length) {
|
|
434
|
+
const code = str.charCodeAt(i);
|
|
435
|
+
if (code >= 0x20 && code <= 0x7e) {
|
|
436
|
+
width++;
|
|
437
|
+
i++;
|
|
438
|
+
} else if (code === 0x09) {
|
|
439
|
+
width += 3;
|
|
440
|
+
i++;
|
|
441
|
+
} else if (code === 0x1b) {
|
|
442
|
+
const length = ansiCodeLength(str, i);
|
|
443
|
+
if (length === 0) return -1;
|
|
444
|
+
i += length;
|
|
445
|
+
} else {
|
|
446
|
+
return -1;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return width;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/** Length of the ANSI/OSC/APC escape sequence starting at `pos`, or 0 if there is none. */
|
|
453
|
+
function ansiCodeLength(str: string, pos: number): number {
|
|
454
|
+
if (pos >= str.length || str.charCodeAt(pos) !== 0x1b) return 0;
|
|
416
455
|
|
|
417
456
|
const next = str[pos + 1];
|
|
418
457
|
|
|
419
458
|
// CSI sequence: ESC [ ... m/G/K/H/J
|
|
420
459
|
if (next === "[") {
|
|
421
|
-
let j = pos + 2;
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
460
|
+
for (let j = pos + 2; j < str.length; j++) {
|
|
461
|
+
const c = str.charCodeAt(j);
|
|
462
|
+
// m, G, K, H, J
|
|
463
|
+
if (c === 0x6d || c === 0x47 || c === 0x4b || c === 0x48 || c === 0x4a) return j + 1 - pos;
|
|
464
|
+
}
|
|
465
|
+
return 0;
|
|
425
466
|
}
|
|
426
467
|
|
|
427
468
|
// OSC sequence: ESC ] ... BEL or ESC ] ... ST (ESC \)
|
|
428
469
|
// Used for hyperlinks (OSC 8), window titles, etc.
|
|
429
|
-
if (next === "]") {
|
|
430
|
-
let j = pos + 2;
|
|
431
|
-
while (j < str.length) {
|
|
432
|
-
if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos };
|
|
433
|
-
if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos };
|
|
434
|
-
j++;
|
|
435
|
-
}
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
470
|
// APC sequence: ESC _ ... BEL or ESC _ ... ST (ESC \)
|
|
440
471
|
// Used for cursor marker and application-specific commands
|
|
441
|
-
if (next === "_") {
|
|
442
|
-
let j = pos + 2;
|
|
443
|
-
|
|
444
|
-
if (
|
|
445
|
-
if (
|
|
446
|
-
j++;
|
|
472
|
+
if (next === "]" || next === "_") {
|
|
473
|
+
for (let j = pos + 2; j < str.length; j++) {
|
|
474
|
+
const c = str.charCodeAt(j);
|
|
475
|
+
if (c === 0x07) return j + 1 - pos;
|
|
476
|
+
if (c === 0x1b && str[j + 1] === "\\") return j + 2 - pos;
|
|
447
477
|
}
|
|
448
|
-
return
|
|
478
|
+
return 0;
|
|
449
479
|
}
|
|
450
480
|
|
|
451
|
-
return
|
|
481
|
+
return 0;
|
|
452
482
|
}
|
|
453
483
|
|
|
454
484
|
type Osc8Terminator = "\x07" | "\x1b\\";
|
|
@@ -739,15 +769,16 @@ class AnsiCodeTracker {
|
|
|
739
769
|
}
|
|
740
770
|
|
|
741
771
|
function updateTrackerFromText(text: string, tracker: AnsiCodeTracker): void {
|
|
742
|
-
let i =
|
|
743
|
-
while (i
|
|
744
|
-
const
|
|
745
|
-
if (
|
|
746
|
-
tracker.process(
|
|
747
|
-
i +=
|
|
772
|
+
let i = text.indexOf("\x1b");
|
|
773
|
+
while (i !== -1) {
|
|
774
|
+
const length = ansiCodeLength(text, i);
|
|
775
|
+
if (length > 0) {
|
|
776
|
+
tracker.process(text.substring(i, i + length));
|
|
777
|
+
i += length;
|
|
748
778
|
} else {
|
|
749
779
|
i++;
|
|
750
780
|
}
|
|
781
|
+
i = text.indexOf("\x1b", i);
|
|
751
782
|
}
|
|
752
783
|
}
|
|
753
784
|
|
|
@@ -758,6 +789,10 @@ export function getActiveBackgroundAnsi(text: string): string {
|
|
|
758
789
|
return tracker.getActiveBackgroundCode();
|
|
759
790
|
}
|
|
760
791
|
|
|
792
|
+
function* graphemeSegments(text: string): Generator<string> {
|
|
793
|
+
for (const { segment } of graphemeSegmenter.segment(text)) yield segment;
|
|
794
|
+
}
|
|
795
|
+
|
|
761
796
|
/**
|
|
762
797
|
* Split text into words while keeping ANSI codes attached.
|
|
763
798
|
*/
|
|
@@ -786,14 +821,19 @@ function splitIntoTokensWithAnsi(text: string): string[] {
|
|
|
786
821
|
continue;
|
|
787
822
|
}
|
|
788
823
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
824
|
+
// Visible text runs up to the next escape sequence.
|
|
825
|
+
let end = text.indexOf("\x1b", i + 1);
|
|
826
|
+
while (end !== -1 && ansiCodeLength(text, end) === 0) {
|
|
827
|
+
end = text.indexOf("\x1b", end + 1);
|
|
792
828
|
}
|
|
829
|
+
if (end === -1) end = text.length;
|
|
793
830
|
|
|
794
|
-
|
|
831
|
+
const chunk = text.slice(i, end);
|
|
832
|
+
// Printable ASCII characters are single graphemes, so skip the segmenter for them.
|
|
833
|
+
const ascii = isPrintableAscii(chunk);
|
|
834
|
+
for (const segment of ascii ? chunk : graphemeSegments(chunk)) {
|
|
795
835
|
const segmentIsSpace = segment === " ";
|
|
796
|
-
if (!segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
836
|
+
if (!ascii && !segmentIsSpace && cjkBreakRegex.test(segment)) {
|
|
797
837
|
flushCurrent();
|
|
798
838
|
const token = pendingAnsi + segment;
|
|
799
839
|
pendingAnsi = "";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Lines moved per mouse-wheel event, or `"auto"` to accelerate fast wheel spins. */
|
|
2
|
+
export type WheelScrollLines = number | "auto";
|
|
3
|
+
|
|
4
|
+
// Several events closer than this belong to one physical notch (Ghostty emits them ~4 ms apart)
|
|
5
|
+
// or come from a high-resolution source. They move one line each and do not accelerate.
|
|
6
|
+
const BURST_GAP_MS = 5;
|
|
7
|
+
// A pause longer than this ends a scroll gesture.
|
|
8
|
+
const GESTURE_GAP_MS = 200;
|
|
9
|
+
// Average event gap that maps to one line per event. Faster events scale up proportionally.
|
|
10
|
+
const REFERENCE_GAP_MS = 100;
|
|
11
|
+
const MAX_AUTO_LINES = 6;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Local macOS terminals receive wheel and trackpad deltas that the OS has already accelerated,
|
|
15
|
+
* and they emit one event per line. Other platforms, and SSH sessions where the client platform
|
|
16
|
+
* is unknown, usually send one event per wheel notch.
|
|
17
|
+
*/
|
|
18
|
+
function terminalAcceleratesWheel(): boolean {
|
|
19
|
+
const env = process.env;
|
|
20
|
+
return (
|
|
21
|
+
process.platform === "darwin" &&
|
|
22
|
+
env.SSH_CONNECTION === undefined &&
|
|
23
|
+
env.SSH_CLIENT === undefined &&
|
|
24
|
+
env.SSH_TTY === undefined
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Converts wheel events into line counts.
|
|
30
|
+
*
|
|
31
|
+
* In `"auto"` mode on terminals that do not accelerate wheel input, the count follows event
|
|
32
|
+
* velocity: an isolated notch moves one line, while a fast spin moves up to six lines per event.
|
|
33
|
+
* For example, notches 100 ms apart move 1 line each, 50 ms apart move 2, and 20 ms apart move 5.
|
|
34
|
+
*/
|
|
35
|
+
export class WheelScrollAccelerator {
|
|
36
|
+
private lines: WheelScrollLines;
|
|
37
|
+
private readonly accelerate: boolean;
|
|
38
|
+
private lastTime = Number.NEGATIVE_INFINITY;
|
|
39
|
+
private lastDirection = 0;
|
|
40
|
+
private averageGap: number | undefined;
|
|
41
|
+
private carry = 0;
|
|
42
|
+
|
|
43
|
+
constructor(lines: WheelScrollLines = "auto", accelerate = !terminalAcceleratesWheel()) {
|
|
44
|
+
this.lines = lines;
|
|
45
|
+
this.accelerate = accelerate;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setLines(lines: WheelScrollLines): void {
|
|
49
|
+
this.lines = lines;
|
|
50
|
+
this.reset();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Return the positive line count for a wheel event in `direction` at time `now` (milliseconds). */
|
|
54
|
+
next(direction: -1 | 1, now: number): number {
|
|
55
|
+
if (this.lines !== "auto") return Number.isFinite(this.lines) ? Math.max(1, Math.floor(this.lines)) : 1;
|
|
56
|
+
if (!this.accelerate) return 1;
|
|
57
|
+
|
|
58
|
+
const gap = now - this.lastTime;
|
|
59
|
+
const sameGesture = direction === this.lastDirection && gap <= GESTURE_GAP_MS;
|
|
60
|
+
this.lastTime = now;
|
|
61
|
+
this.lastDirection = direction;
|
|
62
|
+
if (!sameGesture) {
|
|
63
|
+
this.averageGap = undefined;
|
|
64
|
+
this.carry = 0;
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
if (gap < BURST_GAP_MS) return 1;
|
|
68
|
+
|
|
69
|
+
this.averageGap = this.averageGap === undefined ? gap : (this.averageGap + gap) / 2;
|
|
70
|
+
const lines = Math.min(MAX_AUTO_LINES, Math.max(1, REFERENCE_GAP_MS / this.averageGap)) + this.carry;
|
|
71
|
+
const whole = Math.floor(lines);
|
|
72
|
+
this.carry = lines - whole;
|
|
73
|
+
return whole;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private reset(): void {
|
|
77
|
+
this.lastTime = Number.NEGATIVE_INFINITY;
|
|
78
|
+
this.lastDirection = 0;
|
|
79
|
+
this.averageGap = undefined;
|
|
80
|
+
this.carry = 0;
|
|
81
|
+
}
|
|
82
|
+
}
|