kfc-code-cli 0.0.1-alpha.17 → 0.0.1-alpha.19
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/agents/default/agent.yaml +1 -0
- package/dist/main.mjs +905 -245
- package/package.json +2 -2
package/dist/main.mjs
CHANGED
|
@@ -5214,6 +5214,7 @@ var AgentTool = class {
|
|
|
5214
5214
|
name = "Agent";
|
|
5215
5215
|
description;
|
|
5216
5216
|
inputSchema = AgentToolInputSchema;
|
|
5217
|
+
isConcurrencySafe = (_input) => true;
|
|
5217
5218
|
constructor(subagentHost, parentAgentId, backgroundManager, typeRegistry) {
|
|
5218
5219
|
this.subagentHost = subagentHost;
|
|
5219
5220
|
this.parentAgentId = parentAgentId;
|
|
@@ -23912,7 +23913,7 @@ const IMAGE_MIME_BY_SUFFIX = Object.freeze({
|
|
|
23912
23913
|
".avif": "image/avif",
|
|
23913
23914
|
".svgz": "image/svg+xml"
|
|
23914
23915
|
});
|
|
23915
|
-
const VIDEO_MIME_BY_SUFFIX = Object.freeze({
|
|
23916
|
+
const VIDEO_MIME_BY_SUFFIX$1 = Object.freeze({
|
|
23916
23917
|
".mp4": "video/mp4",
|
|
23917
23918
|
".mpg": "video/mpeg",
|
|
23918
23919
|
".mpeg": "video/mpeg",
|
|
@@ -24183,9 +24184,9 @@ function detectFileType(path, header) {
|
|
|
24183
24184
|
kind: "image",
|
|
24184
24185
|
mimeType: IMAGE_MIME_BY_SUFFIX[suffix]
|
|
24185
24186
|
};
|
|
24186
|
-
else if (suffix in VIDEO_MIME_BY_SUFFIX) mediaHint = {
|
|
24187
|
+
else if (suffix in VIDEO_MIME_BY_SUFFIX$1) mediaHint = {
|
|
24187
24188
|
kind: "video",
|
|
24188
|
-
mimeType: VIDEO_MIME_BY_SUFFIX[suffix]
|
|
24189
|
+
mimeType: VIDEO_MIME_BY_SUFFIX$1[suffix]
|
|
24189
24190
|
};
|
|
24190
24191
|
if (header !== void 0) {
|
|
24191
24192
|
const buf = toBuffer(header);
|
|
@@ -95567,17 +95568,32 @@ const TuiThemeSchema = z.enum([
|
|
|
95567
95568
|
"light",
|
|
95568
95569
|
"auto"
|
|
95569
95570
|
]);
|
|
95571
|
+
const NotificationConditionSchema = z.enum(["unfocused", "always"]);
|
|
95572
|
+
const NotificationsConfigSchema = z.object({
|
|
95573
|
+
enabled: z.boolean(),
|
|
95574
|
+
condition: NotificationConditionSchema
|
|
95575
|
+
});
|
|
95570
95576
|
const TuiConfigFileSchema = z.object({
|
|
95571
95577
|
theme: TuiThemeSchema.optional(),
|
|
95572
|
-
editor: z.object({ command: z.string().optional() }).optional()
|
|
95578
|
+
editor: z.object({ command: z.string().optional() }).optional(),
|
|
95579
|
+
notifications: z.object({
|
|
95580
|
+
enabled: z.boolean().optional(),
|
|
95581
|
+
notification_condition: NotificationConditionSchema.optional()
|
|
95582
|
+
}).optional()
|
|
95573
95583
|
});
|
|
95574
95584
|
const TuiConfigSchema = z.object({
|
|
95575
95585
|
theme: TuiThemeSchema,
|
|
95576
|
-
editorCommand: z.string().nullable()
|
|
95586
|
+
editorCommand: z.string().nullable(),
|
|
95587
|
+
notifications: NotificationsConfigSchema
|
|
95577
95588
|
});
|
|
95589
|
+
const DEFAULT_NOTIFICATIONS_CONFIG = {
|
|
95590
|
+
enabled: true,
|
|
95591
|
+
condition: "unfocused"
|
|
95592
|
+
};
|
|
95578
95593
|
const DEFAULT_TUI_CONFIG = TuiConfigSchema.parse({
|
|
95579
95594
|
theme: "auto",
|
|
95580
|
-
editorCommand: null
|
|
95595
|
+
editorCommand: null,
|
|
95596
|
+
notifications: DEFAULT_NOTIFICATIONS_CONFIG
|
|
95581
95597
|
});
|
|
95582
95598
|
/**
|
|
95583
95599
|
* Thrown by `loadTuiConfig` when the on-disk TOML cannot be parsed.
|
|
@@ -95620,7 +95636,11 @@ function normalizeTuiConfig(config) {
|
|
|
95620
95636
|
const command = config.editor?.command?.trim();
|
|
95621
95637
|
return TuiConfigSchema.parse({
|
|
95622
95638
|
theme: config.theme ?? DEFAULT_TUI_CONFIG.theme,
|
|
95623
|
-
editorCommand: command === void 0 || command.length === 0 ? null : command
|
|
95639
|
+
editorCommand: command === void 0 || command.length === 0 ? null : command,
|
|
95640
|
+
notifications: {
|
|
95641
|
+
enabled: config.notifications?.enabled ?? DEFAULT_NOTIFICATIONS_CONFIG.enabled,
|
|
95642
|
+
condition: config.notifications?.notification_condition ?? DEFAULT_NOTIFICATIONS_CONFIG.condition
|
|
95643
|
+
}
|
|
95624
95644
|
});
|
|
95625
95645
|
}
|
|
95626
95646
|
function renderTuiConfig(config) {
|
|
@@ -95632,6 +95652,10 @@ theme = "${config.theme}" # "auto" | "dark" | "light"
|
|
|
95632
95652
|
|
|
95633
95653
|
[editor]
|
|
95634
95654
|
command = "${escapeTomlBasicString(config.editorCommand ?? "")}" # Empty uses $VISUAL / $EDITOR
|
|
95655
|
+
|
|
95656
|
+
[notifications]
|
|
95657
|
+
enabled = ${String(config.notifications.enabled)} # true | false
|
|
95658
|
+
notification_condition = "${config.notifications.condition}" # "unfocused" | "always"
|
|
95635
95659
|
`;
|
|
95636
95660
|
}
|
|
95637
95661
|
function escapeTomlBasicString(value) {
|
|
@@ -95803,45 +95827,11 @@ function parseSlashInput(input) {
|
|
|
95803
95827
|
};
|
|
95804
95828
|
}
|
|
95805
95829
|
//#endregion
|
|
95806
|
-
//#region src/tui/components/media/image-thumbnail.ts
|
|
95807
|
-
/**
|
|
95808
|
-
* Transcript-side rendering of a pasted image.
|
|
95809
|
-
*
|
|
95810
|
-
* On terminals that speak the Kitty graphics protocol or iTerm2 inline
|
|
95811
|
-
* image protocol (detected by pi-tui's `getCapabilities()`), we show
|
|
95812
|
-
* the actual image. Everywhere else we fall back to a one-line text
|
|
95813
|
-
* marker matching the placeholder the user sees in the input box —
|
|
95814
|
-
* this keeps the transcript readable on Terminal.app / Linux default
|
|
95815
|
-
* terminals / `script` recordings without extra chrome.
|
|
95816
|
-
*
|
|
95817
|
-
* Height is capped at ~12 rows so a single screenshot can't monopolize
|
|
95818
|
-
* the viewport; pi-tui handles proportional scaling internally.
|
|
95819
|
-
*/
|
|
95820
|
-
const MAX_IMAGE_ROWS = 12;
|
|
95821
|
-
var ImageThumbnail = class extends Container {
|
|
95822
|
-
constructor(attachment, colors) {
|
|
95823
|
-
super();
|
|
95824
|
-
const caps = getCapabilities();
|
|
95825
|
-
if (!(caps.images === "kitty" || caps.images === "iterm2")) {
|
|
95826
|
-
this.addChild(new Text(chalk.hex(colors.accent)(` ${attachment.placeholder}`), 0, 0));
|
|
95827
|
-
return;
|
|
95828
|
-
}
|
|
95829
|
-
const image = new Image(Buffer.from(attachment.bytes).toString("base64"), attachment.mime, { fallbackColor: (s) => chalk.hex(colors.textDim)(s) }, {
|
|
95830
|
-
maxHeightCells: MAX_IMAGE_ROWS,
|
|
95831
|
-
filename: attachment.placeholder
|
|
95832
|
-
}, {
|
|
95833
|
-
widthPx: attachment.width,
|
|
95834
|
-
heightPx: attachment.height
|
|
95835
|
-
});
|
|
95836
|
-
this.addChild(image);
|
|
95837
|
-
}
|
|
95838
|
-
};
|
|
95839
|
-
//#endregion
|
|
95840
95830
|
//#region src/tui/symbols.ts
|
|
95841
95831
|
const STATUS_BULLET = "⏺︎ ";
|
|
95842
95832
|
//#endregion
|
|
95843
95833
|
//#region src/tui/components/messages/assistant-message.ts
|
|
95844
|
-
const INDENT$
|
|
95834
|
+
const INDENT$2 = " ";
|
|
95845
95835
|
var AssistantMessageComponent = class {
|
|
95846
95836
|
contentContainer;
|
|
95847
95837
|
markdownTheme;
|
|
@@ -95868,18 +95858,36 @@ var AssistantMessageComponent = class {
|
|
|
95868
95858
|
}
|
|
95869
95859
|
render(width) {
|
|
95870
95860
|
if (this.lastText.trim().length === 0) return [];
|
|
95871
|
-
const prefix = this.showBullet ? STATUS_BULLET : INDENT$
|
|
95861
|
+
const prefix = this.showBullet ? STATUS_BULLET : INDENT$2;
|
|
95872
95862
|
const contentWidth = Math.max(1, width - visibleWidth(prefix));
|
|
95873
95863
|
const contentLines = this.contentContainer.render(contentWidth);
|
|
95874
95864
|
const lines = [""];
|
|
95875
95865
|
for (let i = 0; i < contentLines.length; i++) {
|
|
95876
|
-
const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(STATUS_BULLET) : INDENT$
|
|
95866
|
+
const p = i === 0 && this.showBullet ? chalk.hex(this.bulletColor)(STATUS_BULLET) : INDENT$2;
|
|
95877
95867
|
lines.push(p + contentLines[i]);
|
|
95878
95868
|
}
|
|
95879
95869
|
return lines;
|
|
95880
95870
|
}
|
|
95881
95871
|
};
|
|
95882
95872
|
//#endregion
|
|
95873
|
+
//#region src/tui/components/messages/background-agent-status.ts
|
|
95874
|
+
const INDENT$1 = " ";
|
|
95875
|
+
const FAIL_MARK = "✗ ";
|
|
95876
|
+
var BackgroundAgentStatusComponent = class {
|
|
95877
|
+
constructor(data, colors) {
|
|
95878
|
+
this.data = data;
|
|
95879
|
+
this.colors = colors;
|
|
95880
|
+
}
|
|
95881
|
+
invalidate() {}
|
|
95882
|
+
render(width) {
|
|
95883
|
+
const tone = this.data.phase === "started" ? this.colors.primary : this.data.phase === "completed" ? this.colors.success : this.colors.error;
|
|
95884
|
+
const bullet = this.data.phase === "failed" ? chalk.hex(tone)(FAIL_MARK) : chalk.hex(tone)(STATUS_BULLET);
|
|
95885
|
+
const textComponent = new Text(chalk.hex(tone)(this.data.headline) + (this.data.detail !== void 0 && this.data.detail.length > 0 ? chalk.hex(this.colors.textDim)(` (${this.data.detail})`) : ""), 0, 0);
|
|
95886
|
+
const contentWidth = Math.max(1, width - 2);
|
|
95887
|
+
return ["", ...textComponent.render(contentWidth).map((line, index) => (index === 0 ? bullet : INDENT$1) + line)];
|
|
95888
|
+
}
|
|
95889
|
+
};
|
|
95890
|
+
//#endregion
|
|
95883
95891
|
//#region src/tui/components/messages/skill-activation.ts
|
|
95884
95892
|
/**
|
|
95885
95893
|
* Skill activation card.
|
|
@@ -97322,15 +97330,77 @@ function formatActivityLine(verb, toolName, args) {
|
|
|
97322
97330
|
return keyArg ? `${verb} ${toolName} (${keyArg})` : `${verb} ${toolName}`;
|
|
97323
97331
|
}
|
|
97324
97332
|
//#endregion
|
|
97325
|
-
//#region src/tui/components/
|
|
97333
|
+
//#region src/tui/components/media/image-thumbnail.ts
|
|
97326
97334
|
/**
|
|
97327
|
-
*
|
|
97335
|
+
* Transcript-side rendering of a pasted image.
|
|
97336
|
+
*
|
|
97337
|
+
* On terminals that speak the Kitty graphics protocol or iTerm2 inline
|
|
97338
|
+
* image protocol (detected by pi-tui's `getCapabilities()`), we show
|
|
97339
|
+
* the actual image. Everywhere else we fall back to a one-line text
|
|
97340
|
+
* marker matching the placeholder the user sees in the input box —
|
|
97341
|
+
* this keeps the transcript readable on Terminal.app / Linux default
|
|
97342
|
+
* terminals / `script` recordings without extra chrome.
|
|
97343
|
+
*
|
|
97344
|
+
* Height is capped at ~12 rows so a single screenshot can't monopolize
|
|
97345
|
+
* the viewport; pi-tui handles proportional scaling internally.
|
|
97328
97346
|
*/
|
|
97329
|
-
|
|
97330
|
-
|
|
97347
|
+
const MAX_IMAGE_ROWS = 12;
|
|
97348
|
+
const MAX_IMAGE_WIDTH = 40;
|
|
97349
|
+
var ImageThumbnail = class extends Container {
|
|
97350
|
+
constructor(attachment, colors) {
|
|
97331
97351
|
super();
|
|
97332
|
-
|
|
97333
|
-
|
|
97352
|
+
const caps = getCapabilities();
|
|
97353
|
+
if (!(caps.images === "kitty" || caps.images === "iterm2")) {
|
|
97354
|
+
this.addChild(new Text(chalk.hex(colors.accent)(attachment.placeholder), 0, 0));
|
|
97355
|
+
return;
|
|
97356
|
+
}
|
|
97357
|
+
const image = new Image(Buffer.from(attachment.bytes).toString("base64"), attachment.mime, { fallbackColor: (s) => chalk.hex(colors.textDim)(s) }, {
|
|
97358
|
+
maxHeightCells: MAX_IMAGE_ROWS,
|
|
97359
|
+
maxWidthCells: MAX_IMAGE_WIDTH,
|
|
97360
|
+
filename: attachment.placeholder
|
|
97361
|
+
}, {
|
|
97362
|
+
widthPx: attachment.width,
|
|
97363
|
+
heightPx: attachment.height
|
|
97364
|
+
});
|
|
97365
|
+
this.addChild(image);
|
|
97366
|
+
}
|
|
97367
|
+
};
|
|
97368
|
+
//#endregion
|
|
97369
|
+
//#region src/tui/components/messages/user-message.ts
|
|
97370
|
+
const BULLET = "✨ ";
|
|
97371
|
+
var UserMessageComponent = class {
|
|
97372
|
+
text;
|
|
97373
|
+
color;
|
|
97374
|
+
textComponent;
|
|
97375
|
+
spacerComponent;
|
|
97376
|
+
imageThumbnails;
|
|
97377
|
+
constructor(text, colors, images) {
|
|
97378
|
+
this.text = text;
|
|
97379
|
+
this.color = colors.roleUser;
|
|
97380
|
+
this.textComponent = new Text(chalk.hex(colors.roleUser).bold(text), 0, 0);
|
|
97381
|
+
this.spacerComponent = new Spacer(1);
|
|
97382
|
+
this.imageThumbnails = images?.map((img) => new ImageThumbnail(img, colors)) ?? [];
|
|
97383
|
+
}
|
|
97384
|
+
invalidate() {
|
|
97385
|
+
this.textComponent.invalidate();
|
|
97386
|
+
for (const img of this.imageThumbnails) img.invalidate?.();
|
|
97387
|
+
}
|
|
97388
|
+
render(width) {
|
|
97389
|
+
const bullet = chalk.hex(this.color).bold(BULLET);
|
|
97390
|
+
const bulletWidth = visibleWidth(bullet);
|
|
97391
|
+
const contentWidth = Math.max(1, width - bulletWidth);
|
|
97392
|
+
const lines = [];
|
|
97393
|
+
for (const line of this.spacerComponent.render(width)) lines.push(line);
|
|
97394
|
+
const textLines = this.textComponent.render(contentWidth);
|
|
97395
|
+
for (let i = 0; i < textLines.length; i++) {
|
|
97396
|
+
const prefix = i === 0 ? bullet : " ".repeat(bulletWidth);
|
|
97397
|
+
lines.push(prefix + textLines[i]);
|
|
97398
|
+
}
|
|
97399
|
+
for (const thumbnail of this.imageThumbnails) {
|
|
97400
|
+
const imageLines = thumbnail.render(contentWidth);
|
|
97401
|
+
for (const line of imageLines) lines.push(" ".repeat(bulletWidth) + line);
|
|
97402
|
+
}
|
|
97403
|
+
return lines;
|
|
97334
97404
|
}
|
|
97335
97405
|
};
|
|
97336
97406
|
//#endregion
|
|
@@ -97387,6 +97457,82 @@ var WelcomeComponent = class {
|
|
|
97387
97457
|
}
|
|
97388
97458
|
};
|
|
97389
97459
|
//#endregion
|
|
97460
|
+
//#region src/tui/utils/terminal-notification.ts
|
|
97461
|
+
const MAX_MESSAGE_LENGTH = 240;
|
|
97462
|
+
const ESC$1 = "\x1B";
|
|
97463
|
+
const BEL = "\x07";
|
|
97464
|
+
const ST = "\\";
|
|
97465
|
+
function notifyTerminalOnce(state, key, notification) {
|
|
97466
|
+
const { enabled, condition } = state.appState.notifications;
|
|
97467
|
+
if (!enabled) return;
|
|
97468
|
+
if (state.terminalNotificationKeys.has(key)) return;
|
|
97469
|
+
state.terminalNotificationKeys.add(key);
|
|
97470
|
+
if (condition === "unfocused" && state.terminalFocused) return;
|
|
97471
|
+
emitTerminalNotification(state.terminal, notification, {
|
|
97472
|
+
supportsOsc9: state.osc9Supported,
|
|
97473
|
+
insideTmux: state.insideTmux
|
|
97474
|
+
});
|
|
97475
|
+
}
|
|
97476
|
+
function emitTerminalNotification(terminal, notification, options = {}) {
|
|
97477
|
+
const sequences = buildTerminalNotificationSequences(notification, {
|
|
97478
|
+
supportsOsc9: options.supportsOsc9 ?? supportsOsc9Notification(),
|
|
97479
|
+
insideTmux: options.insideTmux ?? isInsideTmux()
|
|
97480
|
+
});
|
|
97481
|
+
for (const sequence of sequences) terminal.write(sequence);
|
|
97482
|
+
}
|
|
97483
|
+
function formatNotification(notification) {
|
|
97484
|
+
const title = sanitizeNotificationText(notification.title);
|
|
97485
|
+
const body = sanitizeNotificationText(notification.body ?? "");
|
|
97486
|
+
return (title.length > 0 && body.length > 0 ? `${title}: ${body}` : title.length > 0 ? title : body).slice(0, MAX_MESSAGE_LENGTH);
|
|
97487
|
+
}
|
|
97488
|
+
/**
|
|
97489
|
+
* Build the wire bytes for a terminal notification.
|
|
97490
|
+
*
|
|
97491
|
+
* - `supportsOsc9 === true`: emit a single OSC 9 sequence — the modern
|
|
97492
|
+
* desktop-notification path used by iTerm2, WezTerm, Kitty, Ghostty
|
|
97493
|
+
* and Warp.
|
|
97494
|
+
* - `supportsOsc9 === false`: fall back to a bare BEL so the user still
|
|
97495
|
+
* gets the system bell on terminals that don't recognize OSC 9.
|
|
97496
|
+
*
|
|
97497
|
+
* When `insideTmux === true` and we're emitting OSC 9, wrap the sequence
|
|
97498
|
+
* in a tmux DCS passthrough (`ESC P tmux ; <payload> ESC \`) and double
|
|
97499
|
+
* any `ESC` bytes inside the payload — otherwise tmux swallows the OSC.
|
|
97500
|
+
* BEL is single-byte and passes through tmux unchanged, so no wrap is
|
|
97501
|
+
* needed in the fallback path.
|
|
97502
|
+
*/
|
|
97503
|
+
function buildTerminalNotificationSequences(notification, options) {
|
|
97504
|
+
const message = formatNotification(notification);
|
|
97505
|
+
if (message.length === 0) return [];
|
|
97506
|
+
if (!options.supportsOsc9) return [BEL];
|
|
97507
|
+
const osc9 = `${ESC$1}]9;${message}${BEL}`;
|
|
97508
|
+
if (options.insideTmux) return [`${ESC$1}Ptmux;${osc9.replaceAll(ESC$1, `${ESC$1}${ESC$1}`)}${ESC$1}${ST}`];
|
|
97509
|
+
return [osc9];
|
|
97510
|
+
}
|
|
97511
|
+
/**
|
|
97512
|
+
* Best-effort detection of OSC 9 desktop-notification support, driven
|
|
97513
|
+
* entirely off well-known environment variables. The allow-list mirrors
|
|
97514
|
+
* Codex's terminal-detection crate — it is intentionally short and
|
|
97515
|
+
* conservative because BEL is safe everywhere, while shipping OSC 9 to
|
|
97516
|
+
* a terminal that doesn't grok it would print escape garbage on screen.
|
|
97517
|
+
*/
|
|
97518
|
+
function supportsOsc9Notification(env = process.env) {
|
|
97519
|
+
const termProgram = env.TERM_PROGRAM ?? "";
|
|
97520
|
+
if (termProgram === "iTerm.app" || termProgram === "WezTerm" || termProgram === "ghostty" || termProgram === "WarpTerminal") return true;
|
|
97521
|
+
const term = env.TERM ?? "";
|
|
97522
|
+
if (term === "xterm-kitty" || term === "xterm-ghostty") return true;
|
|
97523
|
+
return false;
|
|
97524
|
+
}
|
|
97525
|
+
function isInsideTmux(env = process.env) {
|
|
97526
|
+
return (env.TMUX ?? "").length > 0;
|
|
97527
|
+
}
|
|
97528
|
+
function sanitizeNotificationText(value) {
|
|
97529
|
+
return Array.from(value).map((ch) => isControlCharacter(ch) ? " " : ch).join("").replaceAll(/\s+/g, " ").trim();
|
|
97530
|
+
}
|
|
97531
|
+
function isControlCharacter(ch) {
|
|
97532
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
97533
|
+
return code >= 0 && code <= 31 || code >= 127 && code <= 159;
|
|
97534
|
+
}
|
|
97535
|
+
//#endregion
|
|
97390
97536
|
//#region src/tui/handlers/helpers.ts
|
|
97391
97537
|
const TOAST_TTL_MS = 5e3;
|
|
97392
97538
|
let transcriptIdCounter = 0;
|
|
@@ -97448,6 +97594,7 @@ function flushTurnBuffers(ectx, nextMode = "idle") {
|
|
|
97448
97594
|
*/
|
|
97449
97595
|
function finalizeTurn(ectx, sendQueued) {
|
|
97450
97596
|
if (!ectx.state.appState.isStreaming) return;
|
|
97597
|
+
const completedTurnKey = ectx.state.currentTurnId ?? `local:${String(ectx.state.appState.streamingStartTime)}`;
|
|
97451
97598
|
flushTurnBuffers(ectx, "idle");
|
|
97452
97599
|
ectx.state.activeToolCalls.clear();
|
|
97453
97600
|
ectx.state.currentTurnId = void 0;
|
|
@@ -97459,7 +97606,9 @@ function finalizeTurn(ectx, sendQueued) {
|
|
|
97459
97606
|
streamingPhase: "idle"
|
|
97460
97607
|
});
|
|
97461
97608
|
ectx.resetLivePane();
|
|
97462
|
-
setTimeout(() =>
|
|
97609
|
+
if (next !== void 0) setTimeout(() => {
|
|
97610
|
+
sendQueued(next);
|
|
97611
|
+
}, 0);
|
|
97463
97612
|
return;
|
|
97464
97613
|
}
|
|
97465
97614
|
ectx.setAppState({
|
|
@@ -97467,6 +97616,10 @@ function finalizeTurn(ectx, sendQueued) {
|
|
|
97467
97616
|
streamingPhase: "idle"
|
|
97468
97617
|
});
|
|
97469
97618
|
ectx.resetLivePane();
|
|
97619
|
+
notifyTerminalOnce(ectx.state, `turn-complete:${completedTurnKey}`, {
|
|
97620
|
+
title: "Kimi Code task complete",
|
|
97621
|
+
body: ectx.state.appState.sessionTitle ?? void 0
|
|
97622
|
+
});
|
|
97470
97623
|
}
|
|
97471
97624
|
/** Push a toast into the live list and schedule auto-dismiss. */
|
|
97472
97625
|
function pushToast(ectx, notification) {
|
|
@@ -97507,17 +97660,8 @@ function pushToast(ectx, notification) {
|
|
|
97507
97660
|
function createTranscriptComponent(state, entry) {
|
|
97508
97661
|
switch (entry.kind) {
|
|
97509
97662
|
case "user": {
|
|
97510
|
-
const
|
|
97511
|
-
|
|
97512
|
-
const wrap = new Container();
|
|
97513
|
-
wrap.addChild(msg);
|
|
97514
|
-
for (const id of entry.imageAttachmentIds) {
|
|
97515
|
-
const attachment = state.imageStore.get(id);
|
|
97516
|
-
if (attachment !== void 0) wrap.addChild(new ImageThumbnail(attachment, state.colors));
|
|
97517
|
-
}
|
|
97518
|
-
return wrap;
|
|
97519
|
-
}
|
|
97520
|
-
return msg;
|
|
97663
|
+
const images = entry.imageAttachmentIds?.map((id) => state.imageStore.get(id)).filter((a) => a?.kind === "image");
|
|
97664
|
+
return new UserMessageComponent(entry.content, state.colors, images);
|
|
97521
97665
|
}
|
|
97522
97666
|
case "skill_activation": return new SkillActivationComponent(entry.skillName ?? entry.content, entry.skillArgs, state.colors);
|
|
97523
97667
|
case "assistant": return createAssistantEntry(state, entry.content);
|
|
@@ -97528,8 +97672,11 @@ function createTranscriptComponent(state, entry) {
|
|
|
97528
97672
|
if (state.toolOutputExpanded) tc.setExpanded(true);
|
|
97529
97673
|
return tc;
|
|
97530
97674
|
}
|
|
97675
|
+
if (entry.backgroundAgentStatus !== void 0) return new BackgroundAgentStatusComponent(entry.backgroundAgentStatus, state.colors);
|
|
97676
|
+
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, state.colors, entry.color);
|
|
97677
|
+
case "status":
|
|
97678
|
+
if (entry.backgroundAgentStatus !== void 0) return new BackgroundAgentStatusComponent(entry.backgroundAgentStatus, state.colors);
|
|
97531
97679
|
return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, state.colors, entry.color);
|
|
97532
|
-
case "status": return entry.renderMode === "notice" ? createNoticeEntry(entry.content, entry.detail, state.colors) : createStatusEntry(entry.content, state.colors, entry.color);
|
|
97533
97680
|
default: return null;
|
|
97534
97681
|
}
|
|
97535
97682
|
}
|
|
@@ -97612,42 +97759,54 @@ function emitMuted(state, message) {
|
|
|
97612
97759
|
}
|
|
97613
97760
|
//#endregion
|
|
97614
97761
|
//#region src/tui/input/image-placeholder.ts
|
|
97615
|
-
const PLACEHOLDER_REGEX = /\[image #(\d+)
|
|
97616
|
-
function
|
|
97762
|
+
const PLACEHOLDER_REGEX = /\[(image|video) #(\d+) (?:(\(\d+×\d+\))|([^\]]+))\]/g;
|
|
97763
|
+
function extractMediaAttachments(text, store) {
|
|
97617
97764
|
const parts = [];
|
|
97618
97765
|
const cleanedSegments = [];
|
|
97619
|
-
const
|
|
97766
|
+
const imageAttachmentIds = [];
|
|
97620
97767
|
let cursor = 0;
|
|
97621
|
-
let
|
|
97768
|
+
let hasMedia = false;
|
|
97622
97769
|
PLACEHOLDER_REGEX.lastIndex = 0;
|
|
97623
97770
|
let match;
|
|
97624
97771
|
while ((match = PLACEHOLDER_REGEX.exec(text)) !== null) {
|
|
97625
|
-
const [literal, idStr] = match;
|
|
97772
|
+
const [literal, kind, idStr] = match;
|
|
97773
|
+
if (kind !== "image" && kind !== "video") continue;
|
|
97626
97774
|
if (idStr === void 0) continue;
|
|
97627
97775
|
const id = Number.parseInt(idStr, 10);
|
|
97628
97776
|
const attachment = store.get(id);
|
|
97629
97777
|
if (attachment === void 0) continue;
|
|
97778
|
+
if (attachment.kind !== kind) continue;
|
|
97630
97779
|
const before = text.slice(cursor, match.index);
|
|
97631
97780
|
pushText(parts, before);
|
|
97632
97781
|
cleanedSegments.push(before);
|
|
97633
|
-
|
|
97634
|
-
|
|
97635
|
-
|
|
97782
|
+
if (attachment.kind === "video") {
|
|
97783
|
+
pushText(parts, pathTextForVideo(attachment));
|
|
97784
|
+
cleanedSegments.push(pathTextForVideo(attachment));
|
|
97785
|
+
} else {
|
|
97786
|
+
parts.push(buildImagePart(attachment));
|
|
97787
|
+
imageAttachmentIds.push(id);
|
|
97788
|
+
}
|
|
97789
|
+
hasMedia = true;
|
|
97636
97790
|
cursor = match.index + literal.length;
|
|
97637
97791
|
}
|
|
97638
97792
|
const tail = text.slice(cursor);
|
|
97639
97793
|
pushText(parts, tail);
|
|
97640
97794
|
cleanedSegments.push(tail);
|
|
97641
97795
|
return {
|
|
97642
|
-
parts:
|
|
97643
|
-
|
|
97644
|
-
cleanedText: cleanedSegments.join("").
|
|
97645
|
-
|
|
97796
|
+
parts: hasMedia ? parts : [],
|
|
97797
|
+
hasMedia,
|
|
97798
|
+
cleanedText: cleanedSegments.join("").trim(),
|
|
97799
|
+
imageAttachmentIds
|
|
97646
97800
|
};
|
|
97647
97801
|
}
|
|
97648
97802
|
function pushText(parts, segment) {
|
|
97649
97803
|
if (segment.length === 0) return;
|
|
97650
97804
|
if (segment.trim().length === 0) return;
|
|
97805
|
+
const last = parts.at(-1);
|
|
97806
|
+
if (last?.type === "text") {
|
|
97807
|
+
last.text += segment;
|
|
97808
|
+
return;
|
|
97809
|
+
}
|
|
97651
97810
|
parts.push({
|
|
97652
97811
|
type: "text",
|
|
97653
97812
|
text: segment
|
|
@@ -97660,6 +97819,9 @@ function buildImagePart(att) {
|
|
|
97660
97819
|
image_url: { url: `data:${att.mime};base64,${base64}` }
|
|
97661
97820
|
};
|
|
97662
97821
|
}
|
|
97822
|
+
function pathTextForVideo(att) {
|
|
97823
|
+
return att.sourcePath;
|
|
97824
|
+
}
|
|
97663
97825
|
//#endregion
|
|
97664
97826
|
//#region src/tui/theme/colors.ts
|
|
97665
97827
|
/**
|
|
@@ -97893,7 +98055,7 @@ function createMarkdownTheme(colors) {
|
|
|
97893
98055
|
linkUrl: (text) => muted(text),
|
|
97894
98056
|
code: (text) => chalk.hex(colors.primary)(text),
|
|
97895
98057
|
codeBlock: (text) => text,
|
|
97896
|
-
codeBlockBorder: (text) =>
|
|
98058
|
+
codeBlockBorder: (text) => muted(text),
|
|
97897
98059
|
quote: (text) => dim(text),
|
|
97898
98060
|
quoteBorder: (text) => dim(text),
|
|
97899
98061
|
hr: (text) => border(text),
|
|
@@ -97956,7 +98118,8 @@ function mutateBottomSlot(state, slot, mutate) {
|
|
|
97956
98118
|
const width = state.terminal?.columns ?? 80;
|
|
97957
98119
|
const previousHeight = renderHeight(slot, width);
|
|
97958
98120
|
mutate();
|
|
97959
|
-
if (previousHeight !== renderHeight(slot, width)) state.ui.requestRender(true);
|
|
98121
|
+
if (previousHeight !== renderHeight(slot, width)) if (state.bottomLayoutState.hasFilledViewport) state.ui.requestRender(true);
|
|
98122
|
+
else state.ui.requestRender();
|
|
97960
98123
|
else state.ui.requestRender();
|
|
97961
98124
|
}
|
|
97962
98125
|
function renderHeight(component, width) {
|
|
@@ -98017,6 +98180,7 @@ function clearTranscriptAndRedraw(state) {
|
|
|
98017
98180
|
state.pendingToolComponents.clear();
|
|
98018
98181
|
state.streamingComponent = void 0;
|
|
98019
98182
|
state.streamingTranscriptEntry = void 0;
|
|
98183
|
+
state.bottomLayoutState.hasFilledViewport = false;
|
|
98020
98184
|
state.todoPanel.clear();
|
|
98021
98185
|
state.todoPanelContainer.clear();
|
|
98022
98186
|
state.imageStore.clear();
|
|
@@ -98050,6 +98214,7 @@ function applyTheme(state, theme, hooks, resolved) {
|
|
|
98050
98214
|
}
|
|
98051
98215
|
//#endregion
|
|
98052
98216
|
//#region src/tui/handlers/subagent.ts
|
|
98217
|
+
const MAX_BACKGROUND_FIELD_LENGTH = 240;
|
|
98053
98218
|
function handleSubagentSourceEvent(ectx, payload) {
|
|
98054
98219
|
const tc = ectx.state.pendingToolComponents.get(payload.parent_tool_call_id);
|
|
98055
98220
|
if (tc === void 0) return;
|
|
@@ -98089,7 +98254,10 @@ function handleSubagentSourceEvent(ectx, payload) {
|
|
|
98089
98254
|
}
|
|
98090
98255
|
function handleSubagentSpawned(ectx, data) {
|
|
98091
98256
|
if (data.run_in_background) {
|
|
98257
|
+
const meta = buildBackgroundAgentMetadata(ectx, data);
|
|
98258
|
+
ectx.state.backgroundAgentMetadata.set(data.agent_id, meta);
|
|
98092
98259
|
ectx.state.backgroundAgents.add(data.agent_id);
|
|
98260
|
+
appendBackgroundAgentEntry(ectx, "started", meta);
|
|
98093
98261
|
syncBackgroundAgentBadge(ectx);
|
|
98094
98262
|
return;
|
|
98095
98263
|
}
|
|
@@ -98109,7 +98277,13 @@ function handleSubagentSpawned(ectx, data) {
|
|
|
98109
98277
|
});
|
|
98110
98278
|
}
|
|
98111
98279
|
function handleSubagentCompleted(ectx, data) {
|
|
98280
|
+
const backgroundMeta = ectx.state.backgroundAgentMetadata.get(data.agent_id);
|
|
98112
98281
|
if (ectx.state.backgroundAgents.delete(data.agent_id)) syncBackgroundAgentBadge(ectx);
|
|
98282
|
+
if (backgroundMeta !== void 0) {
|
|
98283
|
+
ectx.state.backgroundAgentMetadata.delete(data.agent_id);
|
|
98284
|
+
appendBackgroundAgentEntry(ectx, "completed", backgroundMeta, { resultSummary: data.result_summary });
|
|
98285
|
+
return;
|
|
98286
|
+
}
|
|
98113
98287
|
const tc = ectx.state.pendingToolComponents.get(data.parent_tool_call_id);
|
|
98114
98288
|
if (tc === void 0) return;
|
|
98115
98289
|
tc.onSubagentCompleted({
|
|
@@ -98118,15 +98292,66 @@ function handleSubagentCompleted(ectx, data) {
|
|
|
98118
98292
|
});
|
|
98119
98293
|
}
|
|
98120
98294
|
function handleSubagentFailed(ectx, data) {
|
|
98295
|
+
const backgroundMeta = ectx.state.backgroundAgentMetadata.get(data.agent_id);
|
|
98121
98296
|
if (ectx.state.backgroundAgents.delete(data.agent_id)) syncBackgroundAgentBadge(ectx);
|
|
98297
|
+
if (backgroundMeta !== void 0) {
|
|
98298
|
+
ectx.state.backgroundAgentMetadata.delete(data.agent_id);
|
|
98299
|
+
appendBackgroundAgentEntry(ectx, "failed", backgroundMeta, { error: data.error });
|
|
98300
|
+
return;
|
|
98301
|
+
}
|
|
98122
98302
|
const tc = ectx.state.pendingToolComponents.get(data.parent_tool_call_id);
|
|
98123
98303
|
if (tc === void 0) return;
|
|
98124
98304
|
tc.onSubagentFailed({ error: data.error });
|
|
98125
98305
|
}
|
|
98306
|
+
function buildBackgroundAgentMetadata(ectx, data) {
|
|
98307
|
+
const parent = ectx.state.activeToolCalls.get(data.parent_tool_call_id);
|
|
98308
|
+
return {
|
|
98309
|
+
agentId: data.agent_id,
|
|
98310
|
+
parentToolCallId: data.parent_tool_call_id,
|
|
98311
|
+
agentName: data.agent_name,
|
|
98312
|
+
description: getAgentDescription$1(parent)
|
|
98313
|
+
};
|
|
98314
|
+
}
|
|
98315
|
+
function getAgentDescription$1(toolCall) {
|
|
98316
|
+
if (toolCall === void 0) return void 0;
|
|
98317
|
+
const description = toolCall.args["description"];
|
|
98318
|
+
return typeof description === "string" ? description : void 0;
|
|
98319
|
+
}
|
|
98320
|
+
function appendBackgroundAgentEntry(ectx, phase, meta, extras = void 0) {
|
|
98321
|
+
const status = formatBackgroundAgentTranscript(phase, meta, extras);
|
|
98322
|
+
const entry = {
|
|
98323
|
+
id: nextTranscriptId(),
|
|
98324
|
+
kind: "status",
|
|
98325
|
+
turnId: ectx.state.currentTurnId,
|
|
98326
|
+
renderMode: "plain",
|
|
98327
|
+
content: status.headline,
|
|
98328
|
+
detail: status.detail,
|
|
98329
|
+
backgroundAgentStatus: status
|
|
98330
|
+
};
|
|
98331
|
+
ectx.addTranscriptEntry(entry);
|
|
98332
|
+
}
|
|
98126
98333
|
function syncBackgroundAgentBadge(ectx) {
|
|
98127
98334
|
ectx.state.footer.setBackgroundAgentCount(ectx.state.backgroundAgents.size);
|
|
98128
98335
|
ectx.state.ui.requestRender();
|
|
98129
98336
|
}
|
|
98337
|
+
function normalizeBackgroundField(value) {
|
|
98338
|
+
if (value === void 0) return void 0;
|
|
98339
|
+
const collapsed = value.trim().replaceAll(/\s+/g, " ");
|
|
98340
|
+
if (collapsed.length === 0) return void 0;
|
|
98341
|
+
if (collapsed.length <= MAX_BACKGROUND_FIELD_LENGTH) return collapsed;
|
|
98342
|
+
return `${collapsed.slice(0, MAX_BACKGROUND_FIELD_LENGTH - 3)}...`;
|
|
98343
|
+
}
|
|
98344
|
+
function formatBackgroundAgentTranscript(phase, meta, extras = void 0) {
|
|
98345
|
+
const subject = normalizeBackgroundField(meta.agentName) !== void 0 ? `${normalizeBackgroundField(meta.agentName)} agent` : "agent";
|
|
98346
|
+
const headline = phase === "started" ? `${subject} started in background` : phase === "completed" ? `${subject} completed in background` : `${subject} failed in background`;
|
|
98347
|
+
const tail = phase === "failed" ? normalizeBackgroundField(extras?.error) : void 0;
|
|
98348
|
+
const detailParts = [normalizeBackgroundField(meta.description), tail].filter((part) => part !== void 0);
|
|
98349
|
+
return {
|
|
98350
|
+
phase,
|
|
98351
|
+
headline,
|
|
98352
|
+
detail: detailParts.length > 0 ? detailParts.join(" · ") : void 0
|
|
98353
|
+
};
|
|
98354
|
+
}
|
|
98130
98355
|
//#endregion
|
|
98131
98356
|
//#region src/utils/git/git-ls-files.ts
|
|
98132
98357
|
/**
|
|
@@ -99328,10 +99553,14 @@ var ImageAttachmentStore = class {
|
|
|
99328
99553
|
nextId = 1;
|
|
99329
99554
|
byId = /* @__PURE__ */ new Map();
|
|
99330
99555
|
add(bytes, mime, width, height) {
|
|
99556
|
+
return this.addImage(bytes, mime, width, height);
|
|
99557
|
+
}
|
|
99558
|
+
addImage(bytes, mime, width, height) {
|
|
99331
99559
|
const id = this.nextId;
|
|
99332
99560
|
this.nextId += 1;
|
|
99333
99561
|
const attachment = {
|
|
99334
99562
|
id,
|
|
99563
|
+
kind: "image",
|
|
99335
99564
|
bytes,
|
|
99336
99565
|
mime,
|
|
99337
99566
|
width,
|
|
@@ -99341,6 +99570,23 @@ var ImageAttachmentStore = class {
|
|
|
99341
99570
|
this.byId.set(id, attachment);
|
|
99342
99571
|
return attachment;
|
|
99343
99572
|
}
|
|
99573
|
+
addVideo(mime, sourcePath, filename) {
|
|
99574
|
+
const id = this.nextId;
|
|
99575
|
+
this.nextId += 1;
|
|
99576
|
+
const normalizedFilename = basenameLike(filename !== void 0 && filename !== "" ? filename : sourcePath);
|
|
99577
|
+
const label = sanitizeVideoLabel(normalizedFilename.length > 0 ? normalizedFilename : mime);
|
|
99578
|
+
const attachment = {
|
|
99579
|
+
id,
|
|
99580
|
+
kind: "video",
|
|
99581
|
+
mime,
|
|
99582
|
+
filename: normalizedFilename,
|
|
99583
|
+
sourcePath,
|
|
99584
|
+
label,
|
|
99585
|
+
placeholder: formatVideoPlaceholder(id, label)
|
|
99586
|
+
};
|
|
99587
|
+
this.byId.set(id, attachment);
|
|
99588
|
+
return attachment;
|
|
99589
|
+
}
|
|
99344
99590
|
get(id) {
|
|
99345
99591
|
return this.byId.get(id);
|
|
99346
99592
|
}
|
|
@@ -99355,6 +99601,21 @@ var ImageAttachmentStore = class {
|
|
|
99355
99601
|
function formatPlaceholder(id, width, height) {
|
|
99356
99602
|
return `[image #${String(id)} (${String(width)}×${String(height)})]`;
|
|
99357
99603
|
}
|
|
99604
|
+
function formatVideoPlaceholder(id, label) {
|
|
99605
|
+
return `[video #${String(id)} ${sanitizeVideoLabel(label)}]`;
|
|
99606
|
+
}
|
|
99607
|
+
function sanitizeVideoLabel(raw) {
|
|
99608
|
+
let label = "";
|
|
99609
|
+
for (const char of raw) {
|
|
99610
|
+
const code = char.codePointAt(0);
|
|
99611
|
+
label += code < 32 || code === 127 || char === "[" || char === "]" ? "_" : char;
|
|
99612
|
+
}
|
|
99613
|
+
label = label.trim();
|
|
99614
|
+
return label.length > 0 ? label : "video";
|
|
99615
|
+
}
|
|
99616
|
+
function basenameLike(raw) {
|
|
99617
|
+
return raw.split(/[\\/]/).filter((part) => part.length > 0).at(-1) ?? raw;
|
|
99618
|
+
}
|
|
99358
99619
|
//#endregion
|
|
99359
99620
|
//#region src/tui/reverse-rpc/base-controller.ts
|
|
99360
99621
|
var ReverseRpcController = class {
|
|
@@ -99461,6 +99722,7 @@ function createTUIState(options) {
|
|
|
99461
99722
|
colors,
|
|
99462
99723
|
styles,
|
|
99463
99724
|
markdownTheme,
|
|
99725
|
+
bottomLayoutState: { hasFilledViewport: false },
|
|
99464
99726
|
resolvedTheme,
|
|
99465
99727
|
appState: { ...initialAppState },
|
|
99466
99728
|
startupState: "pending",
|
|
@@ -99468,6 +99730,10 @@ function createTUIState(options) {
|
|
|
99468
99730
|
livePane: { ...INITIAL_LIVE_PANE },
|
|
99469
99731
|
transcriptEntries: [],
|
|
99470
99732
|
toasts: [],
|
|
99733
|
+
terminalNotificationKeys: /* @__PURE__ */ new Set(),
|
|
99734
|
+
terminalFocused: true,
|
|
99735
|
+
osc9Supported: supportsOsc9Notification(),
|
|
99736
|
+
insideTmux: isInsideTmux(),
|
|
99471
99737
|
loadingAnimation: void 0,
|
|
99472
99738
|
phaseSpinner: void 0,
|
|
99473
99739
|
activeThinkingComponent: void 0,
|
|
@@ -99476,11 +99742,13 @@ function createTUIState(options) {
|
|
|
99476
99742
|
activeCompactionBlock: void 0,
|
|
99477
99743
|
toolOutputExpanded: false,
|
|
99478
99744
|
lastActivityMode: void 0,
|
|
99745
|
+
terminalProgressActive: false,
|
|
99479
99746
|
lastHistoryContent: void 0,
|
|
99480
99747
|
pendingToolComponents: /* @__PURE__ */ new Map(),
|
|
99481
99748
|
pendingAgentGroup: null,
|
|
99482
99749
|
pendingReadGroup: null,
|
|
99483
99750
|
backgroundAgents: /* @__PURE__ */ new Set(),
|
|
99751
|
+
backgroundAgentMetadata: /* @__PURE__ */ new Map(),
|
|
99484
99752
|
sessions: [],
|
|
99485
99753
|
loadingSessions: false,
|
|
99486
99754
|
showingSessionPicker: false,
|
|
@@ -99629,11 +99897,13 @@ function nextQueueId(state) {
|
|
|
99629
99897
|
state.queueIdCounter += 1;
|
|
99630
99898
|
return `q-${String(state.queueIdCounter)}`;
|
|
99631
99899
|
}
|
|
99632
|
-
function enqueueMessage(state, text) {
|
|
99900
|
+
function enqueueMessage(state, text, options) {
|
|
99633
99901
|
state.queuedMessages.push({
|
|
99634
99902
|
id: nextQueueId(state),
|
|
99635
99903
|
kind: "message",
|
|
99636
|
-
text
|
|
99904
|
+
text,
|
|
99905
|
+
...options?.parts !== void 0 ? { parts: options.parts } : {},
|
|
99906
|
+
...options?.imageAttachmentIds !== void 0 && options.imageAttachmentIds.length > 0 ? { imageAttachmentIds: options.imageAttachmentIds } : {}
|
|
99637
99907
|
});
|
|
99638
99908
|
}
|
|
99639
99909
|
function queuedMessageText(item) {
|
|
@@ -99776,7 +100046,7 @@ function sendSkillActivation(state, addEntry, skillName, skillArgs, fullPrompt)
|
|
|
99776
100046
|
/** Send from user input: enqueue if busy, otherwise send immediately. */
|
|
99777
100047
|
function sendMessage(state, addEntry, input, options) {
|
|
99778
100048
|
if (state.appState.isStreaming || state.appState.isCompacting) {
|
|
99779
|
-
enqueueMessage(state, input);
|
|
100049
|
+
enqueueMessage(state, input, options);
|
|
99780
100050
|
return;
|
|
99781
100051
|
}
|
|
99782
100052
|
sendMessageInternal(state, addEntry, input, options);
|
|
@@ -99836,11 +100106,11 @@ function handleUserInput(state, text, hooks, onSlash) {
|
|
|
99836
100106
|
sendNormalUserInput(state, text, hooks);
|
|
99837
100107
|
}
|
|
99838
100108
|
function sendNormalUserInput(state, text, hooks) {
|
|
99839
|
-
const extraction =
|
|
100109
|
+
const extraction = extractMediaAttachments(text, state.imageStore);
|
|
99840
100110
|
const addEntry = (e) => addTranscriptEntry(state, e);
|
|
99841
|
-
if (extraction.
|
|
100111
|
+
if (extraction.hasMedia) sendMessage(state, addEntry, text, {
|
|
99842
100112
|
parts: extraction.parts,
|
|
99843
|
-
imageAttachmentIds: extraction.
|
|
100113
|
+
imageAttachmentIds: extraction.imageAttachmentIds
|
|
99844
100114
|
});
|
|
99845
100115
|
else sendMessage(state, addEntry, text);
|
|
99846
100116
|
hooks.refreshQueuePane();
|
|
@@ -100090,6 +100360,7 @@ async function hydrateTranscriptFromReplay(state, hooks, sessionId) {
|
|
|
100090
100360
|
const projection = projectReplayRecords(replay.records);
|
|
100091
100361
|
hydrateProjectedEntries(state, projection.entries);
|
|
100092
100362
|
state.backgroundAgents = new Set(projection.backgroundAgents);
|
|
100363
|
+
state.backgroundAgentMetadata = new Map(projection.backgroundAgentMetadata);
|
|
100093
100364
|
state.footer.setBackgroundAgentCount(state.backgroundAgents.size);
|
|
100094
100365
|
state.ui.requestRender();
|
|
100095
100366
|
if (replay.warnings !== void 0 && replay.warnings.length > 0) emitError(state, `Replay completed with ${String(replay.warnings.length)} warning(s).`);
|
|
@@ -100110,7 +100381,8 @@ function projectReplayRecords(records) {
|
|
|
100110
100381
|
thinking: [],
|
|
100111
100382
|
text: []
|
|
100112
100383
|
},
|
|
100113
|
-
backgroundAgents: /* @__PURE__ */ new Set()
|
|
100384
|
+
backgroundAgents: /* @__PURE__ */ new Set(),
|
|
100385
|
+
backgroundAgentMetadata: /* @__PURE__ */ new Map()
|
|
100114
100386
|
};
|
|
100115
100387
|
for (const envelope of records) {
|
|
100116
100388
|
if (envelope.source?.kind === "subagent") {
|
|
@@ -100122,7 +100394,8 @@ function projectReplayRecords(records) {
|
|
|
100122
100394
|
flushAssistant(state);
|
|
100123
100395
|
return {
|
|
100124
100396
|
entries: state.entries,
|
|
100125
|
-
backgroundAgents: state.backgroundAgents
|
|
100397
|
+
backgroundAgents: state.backgroundAgents,
|
|
100398
|
+
backgroundAgentMetadata: state.backgroundAgentMetadata
|
|
100126
100399
|
};
|
|
100127
100400
|
}
|
|
100128
100401
|
function projectMainRecord(state, rawRecord) {
|
|
@@ -100170,15 +100443,54 @@ function projectMainRecord(state, rawRecord) {
|
|
|
100170
100443
|
if (!isObject(data)) return;
|
|
100171
100444
|
if (data["run_in_background"] !== true) return;
|
|
100172
100445
|
const agentId = stringValue(data["agent_id"]);
|
|
100446
|
+
const parentToolCallId = stringValue(data["parent_tool_call_id"]);
|
|
100173
100447
|
if (agentId !== void 0) state.backgroundAgents.add(agentId);
|
|
100448
|
+
if (agentId === void 0 || parentToolCallId === void 0) return;
|
|
100449
|
+
removeReplayToolCallEntry(state.entries, parentToolCallId);
|
|
100450
|
+
const meta = {
|
|
100451
|
+
agentId,
|
|
100452
|
+
parentToolCallId,
|
|
100453
|
+
agentName: stringValue(data["agent_name"]),
|
|
100454
|
+
description: getAgentDescription(state.toolCalls.get(parentToolCallId))
|
|
100455
|
+
};
|
|
100456
|
+
state.backgroundAgentMetadata.set(agentId, meta);
|
|
100457
|
+
const status = formatBackgroundAgentTranscript("started", meta);
|
|
100458
|
+
state.entries.push(entry("status", status.headline, "plain", {
|
|
100459
|
+
detail: status.detail,
|
|
100460
|
+
backgroundAgentStatus: status
|
|
100461
|
+
}));
|
|
100462
|
+
return;
|
|
100463
|
+
}
|
|
100464
|
+
case "subagent_completed": {
|
|
100465
|
+
const data = record.data;
|
|
100466
|
+
if (!isObject(data)) return;
|
|
100467
|
+
const agentId = stringValue(data["agent_id"]);
|
|
100468
|
+
if (agentId !== void 0) state.backgroundAgents.delete(agentId);
|
|
100469
|
+
if (agentId === void 0) return;
|
|
100470
|
+
const meta = state.backgroundAgentMetadata.get(agentId);
|
|
100471
|
+
if (meta === void 0) return;
|
|
100472
|
+
state.backgroundAgentMetadata.delete(agentId);
|
|
100473
|
+
const status = formatBackgroundAgentTranscript("completed", meta, { resultSummary: stringValue(data["result_summary"]) });
|
|
100474
|
+
state.entries.push(entry("status", status.headline, "plain", {
|
|
100475
|
+
detail: status.detail,
|
|
100476
|
+
backgroundAgentStatus: status
|
|
100477
|
+
}));
|
|
100174
100478
|
return;
|
|
100175
100479
|
}
|
|
100176
|
-
case "subagent_completed":
|
|
100177
100480
|
case "subagent_failed": {
|
|
100178
100481
|
const data = record.data;
|
|
100179
100482
|
if (!isObject(data)) return;
|
|
100180
100483
|
const agentId = stringValue(data["agent_id"]);
|
|
100181
100484
|
if (agentId !== void 0) state.backgroundAgents.delete(agentId);
|
|
100485
|
+
if (agentId === void 0) return;
|
|
100486
|
+
const meta = state.backgroundAgentMetadata.get(agentId);
|
|
100487
|
+
if (meta === void 0) return;
|
|
100488
|
+
state.backgroundAgentMetadata.delete(agentId);
|
|
100489
|
+
const status = formatBackgroundAgentTranscript("failed", meta, { error: stringValue(data["error"]) });
|
|
100490
|
+
state.entries.push(entry("status", status.headline, "plain", {
|
|
100491
|
+
detail: status.detail,
|
|
100492
|
+
backgroundAgentStatus: status
|
|
100493
|
+
}));
|
|
100182
100494
|
return;
|
|
100183
100495
|
}
|
|
100184
100496
|
default: return;
|
|
@@ -100294,6 +100606,11 @@ function toolCallFromRecord(record) {
|
|
|
100294
100606
|
description: stringValue(data["activity_description"])
|
|
100295
100607
|
};
|
|
100296
100608
|
}
|
|
100609
|
+
function getAgentDescription(toolCall) {
|
|
100610
|
+
if (toolCall === void 0) return void 0;
|
|
100611
|
+
const description = toolCall.args["description"];
|
|
100612
|
+
return typeof description === "string" ? description : void 0;
|
|
100613
|
+
}
|
|
100297
100614
|
function toolResultFromRecord(record) {
|
|
100298
100615
|
const id = stringValue(record.tool_call_id);
|
|
100299
100616
|
if (id === void 0) return void 0;
|
|
@@ -100318,9 +100635,16 @@ function entry(kind, content, renderMode, extras) {
|
|
|
100318
100635
|
renderMode,
|
|
100319
100636
|
content,
|
|
100320
100637
|
turnId: extras?.turnId,
|
|
100321
|
-
|
|
100638
|
+
detail: extras?.detail,
|
|
100639
|
+
color: extras?.color,
|
|
100640
|
+
toolCallData: extras?.toolCallData,
|
|
100641
|
+
backgroundAgentStatus: extras?.backgroundAgentStatus
|
|
100322
100642
|
};
|
|
100323
100643
|
}
|
|
100644
|
+
function removeReplayToolCallEntry(entries, toolCallId) {
|
|
100645
|
+
const idx = entries.findIndex((entry) => entry.kind === "tool_call" && entry.toolCallData?.id === toolCallId);
|
|
100646
|
+
if (idx >= 0) entries.splice(idx, 1);
|
|
100647
|
+
}
|
|
100324
100648
|
function userContentToText(content) {
|
|
100325
100649
|
if (typeof content === "string") return content;
|
|
100326
100650
|
if (!Array.isArray(content)) return "";
|
|
@@ -100568,6 +100892,9 @@ function releaseSessionSideEffects(state) {
|
|
|
100568
100892
|
state.queueIdCounter = 0;
|
|
100569
100893
|
state.activeToolCalls.clear();
|
|
100570
100894
|
state.streamingToolCallArguments?.clear();
|
|
100895
|
+
state.backgroundAgents.clear();
|
|
100896
|
+
state.backgroundAgentMetadata.clear();
|
|
100897
|
+
state.footer.setBackgroundAgentCount(0);
|
|
100571
100898
|
state.currentTurnId = void 0;
|
|
100572
100899
|
state.assistantDraft = "";
|
|
100573
100900
|
state.assistantStreamActive = false;
|
|
@@ -101173,7 +101500,8 @@ async function applyThemeChoice(state, theme, hooks) {
|
|
|
101173
101500
|
try {
|
|
101174
101501
|
await saveTuiConfig({
|
|
101175
101502
|
theme,
|
|
101176
|
-
editorCommand: state.appState.editorCommand
|
|
101503
|
+
editorCommand: state.appState.editorCommand,
|
|
101504
|
+
notifications: state.appState.notifications
|
|
101177
101505
|
});
|
|
101178
101506
|
} catch (error) {
|
|
101179
101507
|
emitStatus(state, `Failed to save theme: ${error instanceof Error ? error.message : String(error)}`, state.colors.error);
|
|
@@ -101739,6 +102067,10 @@ function handleCompactionEnd(ectx, data, sendQueued) {
|
|
|
101739
102067
|
//#region src/tui/handlers/notification.ts
|
|
101740
102068
|
function handleNotification(ectx, data) {
|
|
101741
102069
|
pushToast(ectx, data);
|
|
102070
|
+
notifyTerminalOnce(ectx.state, `wire:${data.id}`, {
|
|
102071
|
+
title: data.title,
|
|
102072
|
+
body: data.body
|
|
102073
|
+
});
|
|
101742
102074
|
}
|
|
101743
102075
|
//#endregion
|
|
101744
102076
|
//#region src/tui/handlers/dispatch.ts
|
|
@@ -101874,7 +102206,8 @@ async function applyEditorChoice(state, value, hooks) {
|
|
|
101874
102206
|
try {
|
|
101875
102207
|
await saveTuiConfig({
|
|
101876
102208
|
theme: state.appState.theme,
|
|
101877
|
-
editorCommand
|
|
102209
|
+
editorCommand,
|
|
102210
|
+
notifications: state.appState.notifications
|
|
101878
102211
|
});
|
|
101879
102212
|
} catch (error) {
|
|
101880
102213
|
emitStatus(state, `Failed to save editor: ${error instanceof Error ? error.message : String(error)}`, state.colors.error);
|
|
@@ -102507,7 +102840,8 @@ var MoonLoader = class extends Text {
|
|
|
102507
102840
|
* progress across renders.
|
|
102508
102841
|
*/
|
|
102509
102842
|
function updateActivityPane(state) {
|
|
102510
|
-
const effectiveMode = state.showingSessionPicker ? "hidden" : state.livePane.pendingApproval !== null ? "hidden" : state.appState.isCompacting ? "hidden" : state.livePane.pendingQuestion !== null ? "hidden" : state.livePane.mode === "idle" && state.appState.streamingPhase
|
|
102843
|
+
const effectiveMode = state.showingSessionPicker ? "hidden" : state.livePane.pendingApproval !== null ? "hidden" : state.appState.isCompacting ? "hidden" : state.livePane.pendingQuestion !== null ? "hidden" : state.livePane.mode === "idle" && isVisibleStreamingPhase(state.appState.streamingPhase) ? state.appState.streamingPhase : state.livePane.mode;
|
|
102844
|
+
syncTerminalProgress(state, shouldShowTerminalProgress(state, effectiveMode));
|
|
102511
102845
|
if (effectiveMode === state.lastActivityMode && (effectiveMode === "waiting" || effectiveMode === "tool")) return;
|
|
102512
102846
|
state.lastActivityMode = effectiveMode;
|
|
102513
102847
|
mutateBottomSlot(state, state.activityContainer, () => {
|
|
@@ -102550,6 +102884,18 @@ function updateActivityPane(state) {
|
|
|
102550
102884
|
}
|
|
102551
102885
|
});
|
|
102552
102886
|
}
|
|
102887
|
+
function shouldShowTerminalProgress(state, effectiveMode) {
|
|
102888
|
+
if (state.appState.isCompacting) return true;
|
|
102889
|
+
return effectiveMode === "waiting" || effectiveMode === "thinking" || effectiveMode === "composing" || effectiveMode === "tool";
|
|
102890
|
+
}
|
|
102891
|
+
function isVisibleStreamingPhase(phase) {
|
|
102892
|
+
return phase === "thinking" || phase === "composing";
|
|
102893
|
+
}
|
|
102894
|
+
function syncTerminalProgress(state, active) {
|
|
102895
|
+
if (state.terminalProgressActive === active) return;
|
|
102896
|
+
state.terminal.setProgress(active);
|
|
102897
|
+
state.terminalProgressActive = active;
|
|
102898
|
+
}
|
|
102553
102899
|
function stopLoader(state) {
|
|
102554
102900
|
if (state.loadingAnimation) {
|
|
102555
102901
|
state.loadingAnimation.stop();
|
|
@@ -103140,6 +103486,10 @@ function buildNumericHint(count) {
|
|
|
103140
103486
|
//#region src/tui/reverse-rpc/approval/ui.ts
|
|
103141
103487
|
function showApprovalPanel(state, payload) {
|
|
103142
103488
|
state.livePane.pendingApproval = { data: payload };
|
|
103489
|
+
notifyTerminalOnce(state, `approval:${payload.id}`, {
|
|
103490
|
+
title: "Kimi Code approval required",
|
|
103491
|
+
body: payload.tool_name
|
|
103492
|
+
});
|
|
103143
103493
|
updateActivityPane(state);
|
|
103144
103494
|
mountPanel(state, new ApprovalPanelComponent({ data: payload }, (response) => {
|
|
103145
103495
|
state.approvalController.respond(adaptPanelResponse(response));
|
|
@@ -103721,6 +104071,10 @@ var QuestionDialogComponent = class extends Container {
|
|
|
103721
104071
|
//#region src/tui/reverse-rpc/question/ui.ts
|
|
103722
104072
|
function showQuestionDialog(state, payload) {
|
|
103723
104073
|
state.livePane.pendingQuestion = { data: payload };
|
|
104074
|
+
notifyTerminalOnce(state, `question:${payload.id}`, {
|
|
104075
|
+
title: "Kimi Code needs your answer",
|
|
104076
|
+
body: payload.questions[0]?.question
|
|
104077
|
+
});
|
|
103724
104078
|
updateActivityPane(state);
|
|
103725
104079
|
mountPanel(state, new QuestionDialogComponent({ data: payload }, (answers) => {
|
|
103726
104080
|
state.questionController.respond(answers);
|
|
@@ -103745,6 +104099,136 @@ function registerReverseRPCHandlers(state, client) {
|
|
|
103745
104099
|
return [client.onRequest("approval.request", createApprovalRequestHandler(state)), client.onRequest("question.ask", createQuestionAskHandler(state))];
|
|
103746
104100
|
}
|
|
103747
104101
|
//#endregion
|
|
104102
|
+
//#region src/utils/image/image-mime.ts
|
|
104103
|
+
function parseImageMeta(bytes) {
|
|
104104
|
+
if (isPng(bytes)) return parsePng(bytes);
|
|
104105
|
+
if (isJpeg(bytes)) return parseJpeg(bytes);
|
|
104106
|
+
if (isGif(bytes)) return parseGif(bytes);
|
|
104107
|
+
if (isWebp(bytes)) return parseWebp(bytes);
|
|
104108
|
+
return null;
|
|
104109
|
+
}
|
|
104110
|
+
function isPng(b) {
|
|
104111
|
+
return b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71 && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10;
|
|
104112
|
+
}
|
|
104113
|
+
function parsePng(b) {
|
|
104114
|
+
if (b.length < 24) return null;
|
|
104115
|
+
const width = readUInt32BE(b, 16);
|
|
104116
|
+
const height = readUInt32BE(b, 20);
|
|
104117
|
+
if (width <= 0 || height <= 0) return null;
|
|
104118
|
+
return {
|
|
104119
|
+
mime: "image/png",
|
|
104120
|
+
width,
|
|
104121
|
+
height
|
|
104122
|
+
};
|
|
104123
|
+
}
|
|
104124
|
+
function isJpeg(b) {
|
|
104125
|
+
return b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255;
|
|
104126
|
+
}
|
|
104127
|
+
function parseJpeg(b) {
|
|
104128
|
+
let i = 2;
|
|
104129
|
+
while (i < b.length) {
|
|
104130
|
+
if (b[i] !== 255) {
|
|
104131
|
+
i += 1;
|
|
104132
|
+
continue;
|
|
104133
|
+
}
|
|
104134
|
+
while (i < b.length && b[i] === 255) i += 1;
|
|
104135
|
+
if (i >= b.length) return null;
|
|
104136
|
+
const marker = b[i];
|
|
104137
|
+
i += 1;
|
|
104138
|
+
if (marker === 216 || marker === 217) continue;
|
|
104139
|
+
if (i + 1 >= b.length) return null;
|
|
104140
|
+
const segLen = readUInt16BE(b, i);
|
|
104141
|
+
if (isSofMarker(marker)) {
|
|
104142
|
+
if (i + 7 >= b.length) return null;
|
|
104143
|
+
const height = readUInt16BE(b, i + 3);
|
|
104144
|
+
const width = readUInt16BE(b, i + 5);
|
|
104145
|
+
if (width <= 0 || height <= 0) return null;
|
|
104146
|
+
return {
|
|
104147
|
+
mime: "image/jpeg",
|
|
104148
|
+
width,
|
|
104149
|
+
height
|
|
104150
|
+
};
|
|
104151
|
+
}
|
|
104152
|
+
i += segLen;
|
|
104153
|
+
}
|
|
104154
|
+
return null;
|
|
104155
|
+
}
|
|
104156
|
+
function isSofMarker(marker) {
|
|
104157
|
+
if (marker < 192 || marker > 207) return false;
|
|
104158
|
+
return marker !== 196 && marker !== 200 && marker !== 204;
|
|
104159
|
+
}
|
|
104160
|
+
function isGif(b) {
|
|
104161
|
+
return b.length >= 6 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56 && (b[4] === 55 || b[4] === 57) && b[5] === 97;
|
|
104162
|
+
}
|
|
104163
|
+
function parseGif(b) {
|
|
104164
|
+
if (b.length < 10) return null;
|
|
104165
|
+
const width = readUInt16LE(b, 6);
|
|
104166
|
+
const height = readUInt16LE(b, 8);
|
|
104167
|
+
if (width <= 0 || height <= 0) return null;
|
|
104168
|
+
return {
|
|
104169
|
+
mime: "image/gif",
|
|
104170
|
+
width,
|
|
104171
|
+
height
|
|
104172
|
+
};
|
|
104173
|
+
}
|
|
104174
|
+
function isWebp(b) {
|
|
104175
|
+
return b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80;
|
|
104176
|
+
}
|
|
104177
|
+
function parseWebp(b) {
|
|
104178
|
+
if (b.length < 30) return null;
|
|
104179
|
+
const chunk = String.fromCharCode(b[12], b[13], b[14], b[15]);
|
|
104180
|
+
if (chunk === "VP8 ") {
|
|
104181
|
+
const widthRaw = readUInt16LE(b, 26);
|
|
104182
|
+
const heightRaw = readUInt16LE(b, 28);
|
|
104183
|
+
const width = widthRaw & 16383;
|
|
104184
|
+
const height = heightRaw & 16383;
|
|
104185
|
+
if (width <= 0 || height <= 0) return null;
|
|
104186
|
+
return {
|
|
104187
|
+
mime: "image/webp",
|
|
104188
|
+
width,
|
|
104189
|
+
height
|
|
104190
|
+
};
|
|
104191
|
+
}
|
|
104192
|
+
if (chunk === "VP8L") {
|
|
104193
|
+
if (b[20] !== 47) return null;
|
|
104194
|
+
const b1 = b[21];
|
|
104195
|
+
const b2 = b[22];
|
|
104196
|
+
const b3 = b[23];
|
|
104197
|
+
const b4 = b[24];
|
|
104198
|
+
const width = 1 + ((b2 & 63) << 8 | b1);
|
|
104199
|
+
const height = 1 + ((b4 & 15) << 10 | b3 << 2 | (b2 & 192) >> 6);
|
|
104200
|
+
if (width <= 0 || height <= 0) return null;
|
|
104201
|
+
return {
|
|
104202
|
+
mime: "image/webp",
|
|
104203
|
+
width,
|
|
104204
|
+
height
|
|
104205
|
+
};
|
|
104206
|
+
}
|
|
104207
|
+
if (chunk === "VP8X") {
|
|
104208
|
+
const width = 1 + readUInt24LE(b, 24);
|
|
104209
|
+
const height = 1 + readUInt24LE(b, 27);
|
|
104210
|
+
if (width <= 0 || height <= 0) return null;
|
|
104211
|
+
return {
|
|
104212
|
+
mime: "image/webp",
|
|
104213
|
+
width,
|
|
104214
|
+
height
|
|
104215
|
+
};
|
|
104216
|
+
}
|
|
104217
|
+
return null;
|
|
104218
|
+
}
|
|
104219
|
+
function readUInt16BE(b, off) {
|
|
104220
|
+
return b[off] << 8 | b[off + 1];
|
|
104221
|
+
}
|
|
104222
|
+
function readUInt16LE(b, off) {
|
|
104223
|
+
return b[off] | b[off + 1] << 8;
|
|
104224
|
+
}
|
|
104225
|
+
function readUInt24LE(b, off) {
|
|
104226
|
+
return b[off] | b[off + 1] << 8 | b[off + 2] << 16;
|
|
104227
|
+
}
|
|
104228
|
+
function readUInt32BE(b, off) {
|
|
104229
|
+
return b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3] >>> 0;
|
|
104230
|
+
}
|
|
104231
|
+
//#endregion
|
|
103748
104232
|
//#region src/utils/clipboard/clipboard-native.ts
|
|
103749
104233
|
/**
|
|
103750
104234
|
* Optional native clipboard binding.
|
|
@@ -103766,32 +104250,89 @@ if (process.env["TERMUX_VERSION"] === void 0 && hasDisplay) try {
|
|
|
103766
104250
|
//#endregion
|
|
103767
104251
|
//#region src/utils/clipboard/clipboard-image.ts
|
|
103768
104252
|
/**
|
|
103769
|
-
* Read
|
|
104253
|
+
* Read media from the system clipboard with graceful platform
|
|
103770
104254
|
* fallbacks. Ported from `libs/pi-mono/packages/coding-agent/src/utils/
|
|
103771
|
-
* clipboard-image.ts` and trimmed
|
|
103772
|
-
* BMP
|
|
104255
|
+
* clipboard-image.ts` and trimmed -- we don't carry the Photon WASM
|
|
104256
|
+
* BMP-to-PNG converter because kimi-core's LLM pipeline only accepts
|
|
103773
104257
|
* PNG/JPEG/GIF/WebP, and the clipboard sources we query already emit
|
|
103774
104258
|
* those formats on the supported platforms.
|
|
103775
104259
|
*
|
|
103776
104260
|
* Lookup order:
|
|
103777
|
-
* macOS
|
|
103778
|
-
*
|
|
103779
|
-
* Linux
|
|
103780
|
-
*
|
|
104261
|
+
* macOS file clipboard -> osascript/AppKit file URLs
|
|
104262
|
+
* macOS / Windows -> native `@mariozechner/clipboard`
|
|
104263
|
+
* Linux Wayland -> wl-paste
|
|
104264
|
+
* Linux X11 -> xclip
|
|
104265
|
+
* WSL (image not on Linux cb) -> PowerShell fallback via wslpath
|
|
103781
104266
|
*
|
|
103782
|
-
* Returns `null` when no
|
|
104267
|
+
* Returns `null` when no supported media is available, the format isn't
|
|
103783
104268
|
* supported, or every fallback fails.
|
|
103784
104269
|
*/
|
|
104270
|
+
var ClipboardMediaError = class extends Error {
|
|
104271
|
+
constructor(message) {
|
|
104272
|
+
super(message);
|
|
104273
|
+
this.name = "ClipboardMediaError";
|
|
104274
|
+
}
|
|
104275
|
+
};
|
|
103785
104276
|
const SUPPORTED_IMAGE_MIME_TYPES = [
|
|
103786
104277
|
"image/png",
|
|
103787
104278
|
"image/jpeg",
|
|
103788
104279
|
"image/webp",
|
|
103789
104280
|
"image/gif"
|
|
103790
104281
|
];
|
|
104282
|
+
const MAX_VIDEO_BYTES = 100 * 1024 * 1024;
|
|
104283
|
+
const VIDEO_MIME_BY_SUFFIX = Object.freeze({
|
|
104284
|
+
".mp4": "video/mp4",
|
|
104285
|
+
".mpg": "video/mpeg",
|
|
104286
|
+
".mpeg": "video/mpeg",
|
|
104287
|
+
".mkv": "video/x-matroska",
|
|
104288
|
+
".avi": "video/x-msvideo",
|
|
104289
|
+
".mov": "video/quicktime",
|
|
104290
|
+
".ogv": "video/ogg",
|
|
104291
|
+
".wmv": "video/x-ms-wmv",
|
|
104292
|
+
".webm": "video/webm",
|
|
104293
|
+
".m4v": "video/x-m4v",
|
|
104294
|
+
".flv": "video/x-flv",
|
|
104295
|
+
".3gp": "video/3gpp",
|
|
104296
|
+
".3g2": "video/3gpp2"
|
|
104297
|
+
});
|
|
103791
104298
|
const DEFAULT_LIST_TIMEOUT_MS = 1e3;
|
|
103792
104299
|
const DEFAULT_READ_TIMEOUT_MS = 3e3;
|
|
103793
104300
|
const DEFAULT_POWERSHELL_TIMEOUT_MS = 5e3;
|
|
103794
104301
|
const DEFAULT_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
104302
|
+
const MACOS_FILE_PATH_SCRIPT = String.raw`
|
|
104303
|
+
ObjC.import('AppKit');
|
|
104304
|
+
ObjC.import('Foundation');
|
|
104305
|
+
|
|
104306
|
+
const out = [];
|
|
104307
|
+
const pb = $.NSPasteboard.generalPasteboard;
|
|
104308
|
+
if (String(pb) !== '[id nil]') {
|
|
104309
|
+
try {
|
|
104310
|
+
const options = $.NSMutableDictionary.dictionary;
|
|
104311
|
+
options.setObjectForKey($.NSNumber.numberWithBool(true), $.NSPasteboardURLReadingFileURLsOnlyKey);
|
|
104312
|
+
const urls = pb.readObjectsForClassesOptions([$.NSURL], options);
|
|
104313
|
+
const count = urls ? urls.count : 0;
|
|
104314
|
+
for (let i = 0; i < count; i++) {
|
|
104315
|
+
const value = urls.objectAtIndex(i).path;
|
|
104316
|
+
const path = value ? ObjC.unwrap(value) : '';
|
|
104317
|
+
if (path) out.push(path);
|
|
104318
|
+
}
|
|
104319
|
+
} catch (error) {}
|
|
104320
|
+
|
|
104321
|
+
if (out.length === 0) {
|
|
104322
|
+
try {
|
|
104323
|
+
const files = ObjC.deepUnwrap(pb.propertyListForType('NSFilenamesPboardType'));
|
|
104324
|
+
if (Array.isArray(files)) {
|
|
104325
|
+
for (const path of files) {
|
|
104326
|
+
if (path) out.push(String(path));
|
|
104327
|
+
}
|
|
104328
|
+
} else if (files) {
|
|
104329
|
+
out.push(String(files));
|
|
104330
|
+
}
|
|
104331
|
+
} catch (error) {}
|
|
104332
|
+
}
|
|
104333
|
+
}
|
|
104334
|
+
out.join('\n');
|
|
104335
|
+
`.trim();
|
|
103795
104336
|
function isWaylandSession(env) {
|
|
103796
104337
|
return Boolean(env["WAYLAND_DISPLAY"]) || env["XDG_SESSION_TYPE"] === "wayland";
|
|
103797
104338
|
}
|
|
@@ -103821,6 +104362,90 @@ function selectPreferredImageMimeType(candidates) {
|
|
|
103821
104362
|
}
|
|
103822
104363
|
return normalized.find((t) => t.base.startsWith("image/"))?.raw ?? null;
|
|
103823
104364
|
}
|
|
104365
|
+
function videoMimeFromPath(path) {
|
|
104366
|
+
const dot = path.lastIndexOf(".");
|
|
104367
|
+
if (dot < 0) return null;
|
|
104368
|
+
return VIDEO_MIME_BY_SUFFIX[path.slice(dot).toLowerCase()] ?? null;
|
|
104369
|
+
}
|
|
104370
|
+
function parseClipboardPaths(text) {
|
|
104371
|
+
return splitClipboardPathLines(text).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((line) => {
|
|
104372
|
+
if (line.startsWith("file://")) try {
|
|
104373
|
+
return fileURLToPath(line);
|
|
104374
|
+
} catch {
|
|
104375
|
+
return "";
|
|
104376
|
+
}
|
|
104377
|
+
return line;
|
|
104378
|
+
}).filter((line) => line.length > 0 && isAbsolute(line));
|
|
104379
|
+
}
|
|
104380
|
+
function splitClipboardPathLines(text) {
|
|
104381
|
+
const lines = [];
|
|
104382
|
+
let start = 0;
|
|
104383
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
104384
|
+
const char = text[i];
|
|
104385
|
+
if (char === "\r" || char === "\n" || text.codePointAt(i) === 0) {
|
|
104386
|
+
lines.push(text.slice(start, i));
|
|
104387
|
+
start = i + 1;
|
|
104388
|
+
}
|
|
104389
|
+
}
|
|
104390
|
+
lines.push(text.slice(start));
|
|
104391
|
+
return lines;
|
|
104392
|
+
}
|
|
104393
|
+
function readImagePath(path) {
|
|
104394
|
+
let stat;
|
|
104395
|
+
try {
|
|
104396
|
+
stat = statSync(path);
|
|
104397
|
+
} catch {
|
|
104398
|
+
return null;
|
|
104399
|
+
}
|
|
104400
|
+
if (!stat.isFile()) return null;
|
|
104401
|
+
let bytes;
|
|
104402
|
+
try {
|
|
104403
|
+
bytes = readFileSync(path);
|
|
104404
|
+
} catch {
|
|
104405
|
+
return null;
|
|
104406
|
+
}
|
|
104407
|
+
if (bytes.length === 0) return null;
|
|
104408
|
+
const meta = parseImageMeta(bytes);
|
|
104409
|
+
if (meta === null) return null;
|
|
104410
|
+
return {
|
|
104411
|
+
kind: "image",
|
|
104412
|
+
bytes: new Uint8Array(bytes),
|
|
104413
|
+
mimeType: meta.mime
|
|
104414
|
+
};
|
|
104415
|
+
}
|
|
104416
|
+
function readVideoPath(path) {
|
|
104417
|
+
const mimeType = videoMimeFromPath(path);
|
|
104418
|
+
if (mimeType === null) return null;
|
|
104419
|
+
let stat;
|
|
104420
|
+
try {
|
|
104421
|
+
stat = statSync(path);
|
|
104422
|
+
} catch {
|
|
104423
|
+
return null;
|
|
104424
|
+
}
|
|
104425
|
+
if (!stat.isFile()) return null;
|
|
104426
|
+
if (stat.size > MAX_VIDEO_BYTES) throw new ClipboardMediaError(`Video is ${(stat.size / 1024 / 1024).toFixed(1)} MB; maximum supported size is 100 MB.`);
|
|
104427
|
+
return {
|
|
104428
|
+
kind: "video",
|
|
104429
|
+
mimeType,
|
|
104430
|
+
filename: basename(path),
|
|
104431
|
+
sourcePath: path
|
|
104432
|
+
};
|
|
104433
|
+
}
|
|
104434
|
+
function readMediaPath(path) {
|
|
104435
|
+
const video = readVideoPath(path);
|
|
104436
|
+
if (video !== null) return video;
|
|
104437
|
+
return readImagePath(path);
|
|
104438
|
+
}
|
|
104439
|
+
function readMediaFromPaths(paths) {
|
|
104440
|
+
for (const path of paths) {
|
|
104441
|
+
const media = readMediaPath(path);
|
|
104442
|
+
if (media !== null) return media;
|
|
104443
|
+
}
|
|
104444
|
+
return null;
|
|
104445
|
+
}
|
|
104446
|
+
function readMediaFromText(text) {
|
|
104447
|
+
return readMediaFromPaths(parseClipboardPaths(text));
|
|
104448
|
+
}
|
|
103824
104449
|
function runCommand(command, args, options) {
|
|
103825
104450
|
const result = spawnSync(command, args, {
|
|
103826
104451
|
timeout: options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS,
|
|
@@ -103836,10 +104461,25 @@ function runCommand(command, args, options) {
|
|
|
103836
104461
|
stdout: Buffer.isBuffer(result.stdout) ? result.stdout : Buffer.from(result.stdout ?? "")
|
|
103837
104462
|
};
|
|
103838
104463
|
}
|
|
104464
|
+
function parseTargetList(output) {
|
|
104465
|
+
return output.toString("utf-8").split(/\r?\n/).map((t) => t.trim()).filter((t) => t.length > 0);
|
|
104466
|
+
}
|
|
104467
|
+
function readClipboardFileMediaViaWlPaste() {
|
|
104468
|
+
const list = runCommand("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
104469
|
+
if (!list.ok) return null;
|
|
104470
|
+
const uriType = parseTargetList(list.stdout).find((t) => baseMimeType(t) === "text/uri-list");
|
|
104471
|
+
if (uriType === void 0) return null;
|
|
104472
|
+
const uris = runCommand("wl-paste", [
|
|
104473
|
+
"--type",
|
|
104474
|
+
uriType,
|
|
104475
|
+
"--no-newline"
|
|
104476
|
+
]);
|
|
104477
|
+
return uris.ok ? readMediaFromText(uris.stdout.toString("utf-8")) : null;
|
|
104478
|
+
}
|
|
103839
104479
|
function readClipboardImageViaWlPaste() {
|
|
103840
104480
|
const list = runCommand("wl-paste", ["--list-types"], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
103841
104481
|
if (!list.ok) return null;
|
|
103842
|
-
const selected = selectPreferredImageMimeType(list.stdout
|
|
104482
|
+
const selected = selectPreferredImageMimeType(parseTargetList(list.stdout));
|
|
103843
104483
|
if (selected === null) return null;
|
|
103844
104484
|
const data = runCommand("wl-paste", [
|
|
103845
104485
|
"--type",
|
|
@@ -103848,10 +104488,31 @@ function readClipboardImageViaWlPaste() {
|
|
|
103848
104488
|
]);
|
|
103849
104489
|
if (!data.ok || data.stdout.length === 0) return null;
|
|
103850
104490
|
return {
|
|
104491
|
+
kind: "image",
|
|
103851
104492
|
bytes: data.stdout,
|
|
103852
104493
|
mimeType: baseMimeType(selected)
|
|
103853
104494
|
};
|
|
103854
104495
|
}
|
|
104496
|
+
function readClipboardFileMediaViaXclip() {
|
|
104497
|
+
const targets = runCommand("xclip", [
|
|
104498
|
+
"-selection",
|
|
104499
|
+
"clipboard",
|
|
104500
|
+
"-t",
|
|
104501
|
+
"TARGETS",
|
|
104502
|
+
"-o"
|
|
104503
|
+
], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
104504
|
+
if (!targets.ok) return null;
|
|
104505
|
+
const uriType = parseTargetList(targets.stdout).find((t) => baseMimeType(t) === "text/uri-list");
|
|
104506
|
+
if (uriType === void 0) return null;
|
|
104507
|
+
const uris = runCommand("xclip", [
|
|
104508
|
+
"-selection",
|
|
104509
|
+
"clipboard",
|
|
104510
|
+
"-t",
|
|
104511
|
+
uriType,
|
|
104512
|
+
"-o"
|
|
104513
|
+
]);
|
|
104514
|
+
return uris.ok ? readMediaFromText(uris.stdout.toString("utf-8")) : null;
|
|
104515
|
+
}
|
|
103855
104516
|
function readClipboardImageViaXclip() {
|
|
103856
104517
|
const targets = runCommand("xclip", [
|
|
103857
104518
|
"-selection",
|
|
@@ -103860,7 +104521,7 @@ function readClipboardImageViaXclip() {
|
|
|
103860
104521
|
"TARGETS",
|
|
103861
104522
|
"-o"
|
|
103862
104523
|
], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
103863
|
-
const candidates = targets.ok ? targets.stdout
|
|
104524
|
+
const candidates = targets.ok ? parseTargetList(targets.stdout) : [];
|
|
103864
104525
|
const preferred = candidates.length > 0 ? selectPreferredImageMimeType(candidates) : null;
|
|
103865
104526
|
const tryTypes = preferred !== null ? [preferred, ...SUPPORTED_IMAGE_MIME_TYPES] : [...SUPPORTED_IMAGE_MIME_TYPES];
|
|
103866
104527
|
for (const mime of tryTypes) {
|
|
@@ -103872,6 +104533,7 @@ function readClipboardImageViaXclip() {
|
|
|
103872
104533
|
"-o"
|
|
103873
104534
|
]);
|
|
103874
104535
|
if (data.ok && data.stdout.length > 0) return {
|
|
104536
|
+
kind: "image",
|
|
103875
104537
|
bytes: data.stdout,
|
|
103876
104538
|
mimeType: baseMimeType(mime)
|
|
103877
104539
|
};
|
|
@@ -103913,6 +104575,7 @@ function readClipboardImageViaPowerShell() {
|
|
|
103913
104575
|
const bytes = readFileSync(tmpFile);
|
|
103914
104576
|
if (bytes.length === 0) return null;
|
|
103915
104577
|
return {
|
|
104578
|
+
kind: "image",
|
|
103916
104579
|
bytes: new Uint8Array(bytes),
|
|
103917
104580
|
mimeType: "image/png"
|
|
103918
104581
|
};
|
|
@@ -103924,12 +104587,66 @@ function readClipboardImageViaPowerShell() {
|
|
|
103924
104587
|
} catch {}
|
|
103925
104588
|
}
|
|
103926
104589
|
}
|
|
103927
|
-
|
|
103928
|
-
|
|
104590
|
+
function readClipboardFilePathsViaMacOs(run) {
|
|
104591
|
+
const result = run("osascript", [
|
|
104592
|
+
"-l",
|
|
104593
|
+
"JavaScript",
|
|
104594
|
+
"-e",
|
|
104595
|
+
MACOS_FILE_PATH_SCRIPT
|
|
104596
|
+
], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
|
|
104597
|
+
if (!result.ok || result.stdout.length === 0) return [];
|
|
104598
|
+
return parseClipboardPaths(result.stdout.toString("utf-8"));
|
|
104599
|
+
}
|
|
104600
|
+
function isFileLikeNativeFormat(format) {
|
|
104601
|
+
const f = format.toLowerCase();
|
|
104602
|
+
const base = baseMimeType(format);
|
|
104603
|
+
return f.includes("file-url") || f.includes("file url") || f.includes("nsfilenames") || f.includes("com.apple.finder") || base === "text/uri-list" || base === "public.url";
|
|
104604
|
+
}
|
|
104605
|
+
function safeAvailableFormats(clip) {
|
|
104606
|
+
if (clip?.availableFormats === void 0) return [];
|
|
104607
|
+
try {
|
|
104608
|
+
return clip.availableFormats();
|
|
104609
|
+
} catch {
|
|
104610
|
+
return [];
|
|
104611
|
+
}
|
|
104612
|
+
}
|
|
104613
|
+
async function readClipboardFileMediaViaNativeText(clip) {
|
|
104614
|
+
if (clip === null) return {
|
|
104615
|
+
media: null,
|
|
104616
|
+
lookedFileLike: false
|
|
104617
|
+
};
|
|
104618
|
+
const lookedFileLike = safeAvailableFormats(clip).some(isFileLikeNativeFormat);
|
|
104619
|
+
if (!lookedFileLike || clip.getText === void 0) return {
|
|
104620
|
+
media: null,
|
|
104621
|
+
lookedFileLike
|
|
104622
|
+
};
|
|
104623
|
+
try {
|
|
104624
|
+
return {
|
|
104625
|
+
media: readMediaFromText(await clip.getText()),
|
|
104626
|
+
lookedFileLike
|
|
104627
|
+
};
|
|
104628
|
+
} catch (error) {
|
|
104629
|
+
if (error instanceof ClipboardMediaError) throw error;
|
|
104630
|
+
return {
|
|
104631
|
+
media: null,
|
|
104632
|
+
lookedFileLike
|
|
104633
|
+
};
|
|
104634
|
+
}
|
|
104635
|
+
}
|
|
104636
|
+
async function readClipboardImageViaNative(clip = clipboard) {
|
|
104637
|
+
if (clip === null) return null;
|
|
104638
|
+
let hasImage = false;
|
|
103929
104639
|
try {
|
|
103930
|
-
|
|
104640
|
+
hasImage = clip.hasImage();
|
|
104641
|
+
} catch {
|
|
104642
|
+
return null;
|
|
104643
|
+
}
|
|
104644
|
+
if (!hasImage) return null;
|
|
104645
|
+
try {
|
|
104646
|
+
const data = await clip.getImageBinary();
|
|
103931
104647
|
if (data.length === 0) return null;
|
|
103932
104648
|
return {
|
|
104649
|
+
kind: "image",
|
|
103933
104650
|
bytes: Uint8Array.from(data),
|
|
103934
104651
|
mimeType: "image/png"
|
|
103935
104652
|
};
|
|
@@ -103937,151 +104654,41 @@ async function readClipboardImageViaNative() {
|
|
|
103937
104654
|
return null;
|
|
103938
104655
|
}
|
|
103939
104656
|
}
|
|
103940
|
-
async function
|
|
104657
|
+
async function readClipboardMedia(options) {
|
|
103941
104658
|
const env = options?.env ?? process.env;
|
|
103942
104659
|
const platform = options?.platform ?? process.platform;
|
|
104660
|
+
const clip = options?.clipboard ?? clipboard;
|
|
104661
|
+
const run = options?.runCommand ?? runCommand;
|
|
103943
104662
|
if (env["TERMUX_VERSION"] !== void 0) return null;
|
|
103944
104663
|
let image = null;
|
|
103945
104664
|
if (platform === "linux") {
|
|
103946
104665
|
const wayland = isWaylandSession(env);
|
|
103947
104666
|
const wsl = isWSL(env);
|
|
103948
|
-
if (wayland || wsl)
|
|
104667
|
+
if (wayland || wsl) {
|
|
104668
|
+
const fileMedia = readClipboardFileMediaViaWlPaste() ?? readClipboardFileMediaViaXclip();
|
|
104669
|
+
if (fileMedia !== null) return fileMedia;
|
|
104670
|
+
image = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();
|
|
104671
|
+
}
|
|
103949
104672
|
if (image === null && wsl) image = readClipboardImageViaPowerShell();
|
|
103950
|
-
if (image === null && !wayland)
|
|
103951
|
-
|
|
103952
|
-
|
|
103953
|
-
|
|
103954
|
-
|
|
103955
|
-
}
|
|
103956
|
-
//#endregion
|
|
103957
|
-
//#region src/utils/image/image-mime.ts
|
|
103958
|
-
function parseImageMeta(bytes) {
|
|
103959
|
-
if (isPng(bytes)) return parsePng(bytes);
|
|
103960
|
-
if (isJpeg(bytes)) return parseJpeg(bytes);
|
|
103961
|
-
if (isGif(bytes)) return parseGif(bytes);
|
|
103962
|
-
if (isWebp(bytes)) return parseWebp(bytes);
|
|
103963
|
-
return null;
|
|
103964
|
-
}
|
|
103965
|
-
function isPng(b) {
|
|
103966
|
-
return b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71 && b[4] === 13 && b[5] === 10 && b[6] === 26 && b[7] === 10;
|
|
103967
|
-
}
|
|
103968
|
-
function parsePng(b) {
|
|
103969
|
-
if (b.length < 24) return null;
|
|
103970
|
-
const width = readUInt32BE(b, 16);
|
|
103971
|
-
const height = readUInt32BE(b, 20);
|
|
103972
|
-
if (width <= 0 || height <= 0) return null;
|
|
103973
|
-
return {
|
|
103974
|
-
mime: "image/png",
|
|
103975
|
-
width,
|
|
103976
|
-
height
|
|
103977
|
-
};
|
|
103978
|
-
}
|
|
103979
|
-
function isJpeg(b) {
|
|
103980
|
-
return b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255;
|
|
103981
|
-
}
|
|
103982
|
-
function parseJpeg(b) {
|
|
103983
|
-
let i = 2;
|
|
103984
|
-
while (i < b.length) {
|
|
103985
|
-
if (b[i] !== 255) {
|
|
103986
|
-
i += 1;
|
|
103987
|
-
continue;
|
|
104673
|
+
if (image === null && !wayland) {
|
|
104674
|
+
const nativeFileMedia = await readClipboardFileMediaViaNativeText(clip);
|
|
104675
|
+
if (nativeFileMedia.media !== null) return nativeFileMedia.media;
|
|
104676
|
+
if (nativeFileMedia.lookedFileLike) return null;
|
|
104677
|
+
image = await readClipboardImageViaNative(clip);
|
|
103988
104678
|
}
|
|
103989
|
-
|
|
103990
|
-
if (
|
|
103991
|
-
|
|
103992
|
-
|
|
103993
|
-
if (marker === 216 || marker === 217) continue;
|
|
103994
|
-
if (i + 1 >= b.length) return null;
|
|
103995
|
-
const segLen = readUInt16BE(b, i);
|
|
103996
|
-
if (isSofMarker(marker)) {
|
|
103997
|
-
if (i + 7 >= b.length) return null;
|
|
103998
|
-
const height = readUInt16BE(b, i + 3);
|
|
103999
|
-
const width = readUInt16BE(b, i + 5);
|
|
104000
|
-
if (width <= 0 || height <= 0) return null;
|
|
104001
|
-
return {
|
|
104002
|
-
mime: "image/jpeg",
|
|
104003
|
-
width,
|
|
104004
|
-
height
|
|
104005
|
-
};
|
|
104679
|
+
} else {
|
|
104680
|
+
if (platform === "darwin") {
|
|
104681
|
+
const fileMedia = readMediaFromPaths(readClipboardFilePathsViaMacOs(run));
|
|
104682
|
+
if (fileMedia !== null) return fileMedia;
|
|
104006
104683
|
}
|
|
104007
|
-
|
|
104008
|
-
|
|
104009
|
-
|
|
104010
|
-
|
|
104011
|
-
function isSofMarker(marker) {
|
|
104012
|
-
if (marker < 192 || marker > 207) return false;
|
|
104013
|
-
return marker !== 196 && marker !== 200 && marker !== 204;
|
|
104014
|
-
}
|
|
104015
|
-
function isGif(b) {
|
|
104016
|
-
return b.length >= 6 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56 && (b[4] === 55 || b[4] === 57) && b[5] === 97;
|
|
104017
|
-
}
|
|
104018
|
-
function parseGif(b) {
|
|
104019
|
-
if (b.length < 10) return null;
|
|
104020
|
-
const width = readUInt16LE(b, 6);
|
|
104021
|
-
const height = readUInt16LE(b, 8);
|
|
104022
|
-
if (width <= 0 || height <= 0) return null;
|
|
104023
|
-
return {
|
|
104024
|
-
mime: "image/gif",
|
|
104025
|
-
width,
|
|
104026
|
-
height
|
|
104027
|
-
};
|
|
104028
|
-
}
|
|
104029
|
-
function isWebp(b) {
|
|
104030
|
-
return b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80;
|
|
104031
|
-
}
|
|
104032
|
-
function parseWebp(b) {
|
|
104033
|
-
if (b.length < 30) return null;
|
|
104034
|
-
const chunk = String.fromCharCode(b[12], b[13], b[14], b[15]);
|
|
104035
|
-
if (chunk === "VP8 ") {
|
|
104036
|
-
const widthRaw = readUInt16LE(b, 26);
|
|
104037
|
-
const heightRaw = readUInt16LE(b, 28);
|
|
104038
|
-
const width = widthRaw & 16383;
|
|
104039
|
-
const height = heightRaw & 16383;
|
|
104040
|
-
if (width <= 0 || height <= 0) return null;
|
|
104041
|
-
return {
|
|
104042
|
-
mime: "image/webp",
|
|
104043
|
-
width,
|
|
104044
|
-
height
|
|
104045
|
-
};
|
|
104046
|
-
}
|
|
104047
|
-
if (chunk === "VP8L") {
|
|
104048
|
-
if (b[20] !== 47) return null;
|
|
104049
|
-
const b1 = b[21];
|
|
104050
|
-
const b2 = b[22];
|
|
104051
|
-
const b3 = b[23];
|
|
104052
|
-
const b4 = b[24];
|
|
104053
|
-
const width = 1 + ((b2 & 63) << 8 | b1);
|
|
104054
|
-
const height = 1 + ((b4 & 15) << 10 | b3 << 2 | (b2 & 192) >> 6);
|
|
104055
|
-
if (width <= 0 || height <= 0) return null;
|
|
104056
|
-
return {
|
|
104057
|
-
mime: "image/webp",
|
|
104058
|
-
width,
|
|
104059
|
-
height
|
|
104060
|
-
};
|
|
104061
|
-
}
|
|
104062
|
-
if (chunk === "VP8X") {
|
|
104063
|
-
const width = 1 + readUInt24LE(b, 24);
|
|
104064
|
-
const height = 1 + readUInt24LE(b, 27);
|
|
104065
|
-
if (width <= 0 || height <= 0) return null;
|
|
104066
|
-
return {
|
|
104067
|
-
mime: "image/webp",
|
|
104068
|
-
width,
|
|
104069
|
-
height
|
|
104070
|
-
};
|
|
104684
|
+
const nativeFileMedia = await readClipboardFileMediaViaNativeText(clip);
|
|
104685
|
+
if (nativeFileMedia.media !== null) return nativeFileMedia.media;
|
|
104686
|
+
if (platform === "darwin" && nativeFileMedia.lookedFileLike) return null;
|
|
104687
|
+
image = await readClipboardImageViaNative(clip);
|
|
104071
104688
|
}
|
|
104072
|
-
return null;
|
|
104073
|
-
|
|
104074
|
-
|
|
104075
|
-
return b[off] << 8 | b[off + 1];
|
|
104076
|
-
}
|
|
104077
|
-
function readUInt16LE(b, off) {
|
|
104078
|
-
return b[off] | b[off + 1] << 8;
|
|
104079
|
-
}
|
|
104080
|
-
function readUInt24LE(b, off) {
|
|
104081
|
-
return b[off] | b[off + 1] << 8 | b[off + 2] << 16;
|
|
104082
|
-
}
|
|
104083
|
-
function readUInt32BE(b, off) {
|
|
104084
|
-
return b[off] * 16777216 + (b[off + 1] << 16) + (b[off + 2] << 8) + b[off + 3] >>> 0;
|
|
104689
|
+
if (image === null) return null;
|
|
104690
|
+
if (!isSupportedImageMimeType(image.mimeType)) return null;
|
|
104691
|
+
return image;
|
|
104085
104692
|
}
|
|
104086
104693
|
//#endregion
|
|
104087
104694
|
//#region src/tui/components/editor/file-mention-provider.ts
|
|
@@ -104278,15 +104885,18 @@ function toItem(path) {
|
|
|
104278
104885
|
//#endregion
|
|
104279
104886
|
//#region src/tui/components/layout/bottom-pinned-layout.ts
|
|
104280
104887
|
var BottomPinnedLayout = class {
|
|
104281
|
-
constructor(terminal, body, bottom) {
|
|
104888
|
+
constructor(terminal, body, bottom, state) {
|
|
104282
104889
|
this.terminal = terminal;
|
|
104283
104890
|
this.body = body;
|
|
104284
104891
|
this.bottom = bottom;
|
|
104892
|
+
this.state = state;
|
|
104285
104893
|
}
|
|
104286
104894
|
render(width) {
|
|
104287
104895
|
const bodyLines = this.body.render(width);
|
|
104288
104896
|
const bottomLines = this.bottom.flatMap((component) => component.render(width));
|
|
104289
|
-
|
|
104897
|
+
const totalHeight = bodyLines.length + bottomLines.length;
|
|
104898
|
+
if (!this.state.hasFilledViewport && totalHeight < this.terminal.rows) return [...bodyLines, ...bottomLines];
|
|
104899
|
+
this.state.hasFilledViewport = true;
|
|
104290
104900
|
const spacerHeight = Math.max(0, this.terminal.rows - bodyLines.length - bottomLines.length);
|
|
104291
104901
|
return [
|
|
104292
104902
|
...bodyLines,
|
|
@@ -104387,11 +104997,26 @@ function setupEditor(state, cb) {
|
|
|
104387
104997
|
editor.onPasteImage = async () => handleClipboardImagePaste(state);
|
|
104388
104998
|
}
|
|
104389
104999
|
async function handleClipboardImagePaste(state) {
|
|
104390
|
-
|
|
104391
|
-
|
|
104392
|
-
|
|
105000
|
+
let media;
|
|
105001
|
+
try {
|
|
105002
|
+
media = await readClipboardMedia();
|
|
105003
|
+
} catch (error) {
|
|
105004
|
+
if (error instanceof ClipboardMediaError) {
|
|
105005
|
+
emitError(state, error.message);
|
|
105006
|
+
return true;
|
|
105007
|
+
}
|
|
105008
|
+
return false;
|
|
105009
|
+
}
|
|
105010
|
+
if (media === null) return false;
|
|
105011
|
+
if (media.kind === "video") {
|
|
105012
|
+
const attachment = state.imageStore.addVideo(media.mimeType, media.sourcePath, media.filename);
|
|
105013
|
+
state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
|
|
105014
|
+
state.ui.requestRender();
|
|
105015
|
+
return true;
|
|
105016
|
+
}
|
|
105017
|
+
const meta = parseImageMeta(media.bytes);
|
|
104393
105018
|
if (meta === null) return false;
|
|
104394
|
-
const attachment = state.imageStore.
|
|
105019
|
+
const attachment = state.imageStore.addImage(media.bytes, meta.mime, meta.width, meta.height);
|
|
104395
105020
|
state.editor.insertTextAtCursor?.(`${attachment.placeholder} `);
|
|
104396
105021
|
state.ui.requestRender();
|
|
104397
105022
|
return true;
|
|
@@ -104410,7 +105035,7 @@ function buildLayout(state) {
|
|
|
104410
105035
|
state.queueContainer,
|
|
104411
105036
|
state.editorContainer,
|
|
104412
105037
|
state.footer
|
|
104413
|
-
]));
|
|
105038
|
+
], state.bottomLayoutState));
|
|
104414
105039
|
}
|
|
104415
105040
|
/**
|
|
104416
105041
|
* Register the file-mention + slash-command autocomplete provider on
|
|
@@ -104497,12 +105122,40 @@ async function loadPersistedInputHistory(state) {
|
|
|
104497
105122
|
} catch {}
|
|
104498
105123
|
}
|
|
104499
105124
|
//#endregion
|
|
105125
|
+
//#region src/tui/utils/terminal-focus.ts
|
|
105126
|
+
const ESC = "\x1B";
|
|
105127
|
+
const TERMINAL_FOCUS_IN = `${ESC}[I`;
|
|
105128
|
+
const TERMINAL_FOCUS_OUT = `${ESC}[O`;
|
|
105129
|
+
const ENABLE_TERMINAL_FOCUS_REPORTING = `${ESC}[?1004h`;
|
|
105130
|
+
const DISABLE_TERMINAL_FOCUS_REPORTING = `${ESC}[?1004l`;
|
|
105131
|
+
function installTerminalFocusTracking(state) {
|
|
105132
|
+
state.terminalFocused = true;
|
|
105133
|
+
const disposeInputListener = state.ui.addInputListener((data) => handleTerminalFocusInput(state, data));
|
|
105134
|
+
state.terminal.write(ENABLE_TERMINAL_FOCUS_REPORTING);
|
|
105135
|
+
return () => {
|
|
105136
|
+
disposeInputListener();
|
|
105137
|
+
state.terminal.write(DISABLE_TERMINAL_FOCUS_REPORTING);
|
|
105138
|
+
state.terminalFocused = true;
|
|
105139
|
+
};
|
|
105140
|
+
}
|
|
105141
|
+
function handleTerminalFocusInput(state, data) {
|
|
105142
|
+
if (data === TERMINAL_FOCUS_IN) {
|
|
105143
|
+
state.terminalFocused = true;
|
|
105144
|
+
return { consume: true };
|
|
105145
|
+
}
|
|
105146
|
+
if (data === TERMINAL_FOCUS_OUT) {
|
|
105147
|
+
state.terminalFocused = false;
|
|
105148
|
+
return { consume: true };
|
|
105149
|
+
}
|
|
105150
|
+
}
|
|
105151
|
+
//#endregion
|
|
104500
105152
|
//#region src/tui/kimi-tui.ts
|
|
104501
105153
|
var KimiTUI = class {
|
|
104502
105154
|
state;
|
|
104503
105155
|
stateHooks;
|
|
104504
105156
|
streamCallbacks;
|
|
104505
105157
|
reverseRpcDisposers = [];
|
|
105158
|
+
terminalFocusTrackingDispose;
|
|
104506
105159
|
onExit;
|
|
104507
105160
|
constructor(client, initialState, options) {
|
|
104508
105161
|
this.state = createTUIState({
|
|
@@ -104558,6 +105211,7 @@ var KimiTUI = class {
|
|
|
104558
105211
|
this.state.editorContainer.addChild(this.state.editor);
|
|
104559
105212
|
this.state.ui.setFocus(this.state.editor);
|
|
104560
105213
|
this.state.ui.start();
|
|
105214
|
+
this.terminalFocusTrackingDispose = installTerminalFocusTracking(this.state);
|
|
104561
105215
|
attachFooterFeed(this.state, buildFooterFeedHandlers(this.state));
|
|
104562
105216
|
if (this.state.startupNotice !== void 0) {
|
|
104563
105217
|
emitMuted(this.state, this.state.startupNotice);
|
|
@@ -104661,6 +105315,8 @@ var KimiTUI = class {
|
|
|
104661
105315
|
this.reverseRpcDisposers.length = 0;
|
|
104662
105316
|
this.state.approvalController.cancelAll("shutting down");
|
|
104663
105317
|
this.state.questionController.cancelAll("shutting down");
|
|
105318
|
+
this.terminalFocusTrackingDispose?.();
|
|
105319
|
+
this.terminalFocusTrackingDispose = void 0;
|
|
104664
105320
|
this.state.ui.stop();
|
|
104665
105321
|
if (this.onExit) await this.onExit();
|
|
104666
105322
|
}
|
|
@@ -104677,7 +105333,10 @@ var KimiTUI = class {
|
|
|
104677
105333
|
sendSkillActivation(this.state, (e) => this.addEntry(e), item.skillName, item.skillArgs, item.fullPrompt);
|
|
104678
105334
|
return;
|
|
104679
105335
|
}
|
|
104680
|
-
sendMessageInternal(this.state, (e) => this.addEntry(e), item.text
|
|
105336
|
+
sendMessageInternal(this.state, (e) => this.addEntry(e), item.text, {
|
|
105337
|
+
...item.parts !== void 0 ? { parts: item.parts } : {},
|
|
105338
|
+
...item.imageAttachmentIds !== void 0 ? { imageAttachmentIds: item.imageAttachmentIds } : {}
|
|
105339
|
+
});
|
|
104681
105340
|
};
|
|
104682
105341
|
subscribeToWire(this.state, (event) => {
|
|
104683
105342
|
dispatchEvent(event, ectx, sendQueued);
|
|
@@ -104805,6 +105464,7 @@ async function runShell(opts, version) {
|
|
|
104805
105464
|
theme: tuiConfig.theme,
|
|
104806
105465
|
version,
|
|
104807
105466
|
editorCommand: tuiConfig.editorCommand,
|
|
105467
|
+
notifications: tuiConfig.notifications,
|
|
104808
105468
|
availableModels: ctx.availableModels,
|
|
104809
105469
|
sessionTitle: null
|
|
104810
105470
|
};
|