@paradow/ledge 0.29.0 → 0.30.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/dist/cli/{action-MAPPYLUM.js → action-IL5A2O4K.js} +4 -4
- package/dist/cli/{attach-OC64CLB2.js → attach-XFRWCGMD.js} +8 -8
- package/dist/cli/{bridge-TWX4V37E.js → bridge-KEDZGBEK.js} +1 -1
- package/dist/cli/{chunk-XZYQMKKO.js → chunk-2GSAJOLJ.js} +3 -3
- package/dist/cli/{chunk-WIUJNSAZ.js → chunk-4JQHF3HM.js} +5 -5
- package/dist/cli/{chunk-ETGOWBUL.js → chunk-D2EYVNJ6.js} +3 -3
- package/dist/cli/{chunk-73YNZ3BN.js → chunk-GOBUJGB6.js} +1 -1
- package/dist/cli/{chunk-TNYBWWRO.js → chunk-H563AKHN.js} +2 -2
- package/dist/cli/{chunk-WCBXS3EU.js → chunk-HH6LFLPR.js} +107 -15
- package/dist/cli/{chunk-RFFTWAAF.js → chunk-JBYVS2F4.js} +1 -1
- package/dist/cli/{chunk-353WRRGG.js → chunk-KESZUXUI.js} +3 -3
- package/dist/cli/{chunk-UWDE365N.js → chunk-LHWJBSLO.js} +47 -3
- package/dist/cli/{chunk-TREGH6WM.js → chunk-OOPOJK6Y.js} +206 -175
- package/dist/cli/chunk-P2X2M3TR.js +6 -0
- package/dist/cli/{chunk-MZVMW356.js → chunk-RGYQFRG5.js} +2 -2
- package/dist/cli/{chunk-7S7ZLGIY.js → chunk-U44NT4XJ.js} +35 -7
- package/dist/cli/{chunk-K3SDHHZF.js → chunk-XHC6EPZE.js} +1 -1
- package/dist/cli/{daemon-ZTZ7VMYA.js → daemon-A54MACKQ.js} +7 -7
- package/dist/cli/{decide-4BIYS3Y2.js → decide-2HKZF4VI.js} +8 -6
- package/dist/cli/{handover-IS7BJYGZ.js → handover-3PV3MDK3.js} +4 -4
- package/dist/cli/{hook-3VKVD4IP.js → hook-S5226MJF.js} +34 -13
- package/dist/cli/index.js +16 -16
- package/dist/cli/{launch-XAPN2M3Q.js → launch-NGWPGU6E.js} +8 -8
- package/dist/cli/{send-ZLF2ZXMJ.js → send-NYKZRELC.js} +4 -4
- package/dist/cli/{status-NONO6E2S.js → status-6USM7SJE.js} +2 -2
- package/dist/cli/{task-47MVWDCY.js → task-E5J6UT7S.js} +4 -4
- package/dist/cli/{team-EJ4YIXJH.js → team-AP7A5VMP.js} +6 -6
- package/dist/cli/{tui-CAP4O4YQ.js → tui-EK7VC7YP.js} +1273 -1176
- package/dist/cli/{version-EHBPJO7K.js → version-KORN5SQT.js} +1 -1
- package/dist/cli/{who-FHAXYCM2.js → who-RA4QBVUA.js} +3 -3
- package/package.json +1 -1
- package/dist/cli/chunk-JZEWCO6X.js +0 -6
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attachEnv
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4JQHF3HM.js";
|
|
4
4
|
import {
|
|
5
5
|
imageInfo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OOPOJK6Y.js";
|
|
7
7
|
import {
|
|
8
8
|
MAX_FRAME
|
|
9
9
|
} from "./chunk-OLLQBK6N.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-HH6LFLPR.js";
|
|
11
|
+
import "./chunk-LHWJBSLO.js";
|
|
12
|
+
import "./chunk-GOBUJGB6.js";
|
|
13
|
+
import "./chunk-XHC6EPZE.js";
|
|
14
|
+
import "./chunk-JBYVS2F4.js";
|
|
15
15
|
import {
|
|
16
16
|
ACTION_ERROR_CODES,
|
|
17
17
|
ATTACHMENTS_MAX,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
contextNear,
|
|
36
36
|
isArchived,
|
|
37
37
|
offers,
|
|
38
|
+
openDecision,
|
|
38
39
|
parseServerMessage,
|
|
39
40
|
pendingKey,
|
|
40
41
|
pickName,
|
|
@@ -43,7 +44,7 @@ import {
|
|
|
43
44
|
readDaemonToken,
|
|
44
45
|
taskClosed,
|
|
45
46
|
transmissions
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-U44NT4XJ.js";
|
|
47
48
|
import "./chunk-NLL2EQJ2.js";
|
|
48
49
|
import "./chunk-6IYPRUQG.js";
|
|
49
50
|
import {
|
|
@@ -1303,7 +1304,7 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
|
|
|
1303
1304
|
const { machineId: _m, ...rest } = outgoing;
|
|
1304
1305
|
outgoing = rest;
|
|
1305
1306
|
}
|
|
1306
|
-
if (msg.type === "team.join" || msg.type === "team.leave" || msg.type === "team.resume" || msg.type === "team.dissolve" || msg.type === "team.save" || msg.type === "task.cmd" && !msg.qid) machine = "local";
|
|
1307
|
+
if (msg.type === "team.join" || msg.type === "team.leave" || msg.type === "team.resume" || msg.type === "team.dissolve" || msg.type === "team.save" || msg.type === "task.cmd" && !msg.qid || msg.type === "action.run") machine = "local";
|
|
1307
1308
|
if (outgoing.type === "team.join" || outgoing.type === "team.leave") {
|
|
1308
1309
|
const o = splitId(outgoing.session);
|
|
1309
1310
|
if (o) {
|
|
@@ -1510,7 +1511,7 @@ function getImage(client, attachment, route = {}, preview = false, timeoutMs = T
|
|
|
1510
1511
|
}
|
|
1511
1512
|
|
|
1512
1513
|
// src/tui/App.tsx
|
|
1513
|
-
import { useCallback, useEffect as
|
|
1514
|
+
import { useCallback, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef13, useState as useState14 } from "react";
|
|
1514
1515
|
import { useApp, useInput as useInput7, useStdout, useWindowSize as useWindowSize2 } from "ink";
|
|
1515
1516
|
|
|
1516
1517
|
// src/tui/actions.ts
|
|
@@ -1935,7 +1936,8 @@ var fr = {
|
|
|
1935
1936
|
"team.dissolve": (v) => need(v, ["team"], (w) => `Dissoudre l\u2019\xE9quipe ${b(w.team)}`),
|
|
1936
1937
|
"team.save": (v) => need(v, ["team", "name"], (w) => v.replaces ? `Remplacer le mod\xE8le \xAB ${w.name} \xBB par ${b(w.team)}` : `Enregistrer ${b(w.team)} comme mod\xE8le \xAB ${w.name} \xBB`),
|
|
1937
1938
|
"task.cancel": (v) => need(v, ["task", "owner"], (w) => `Annuler la t\xE2che \xAB ${short(w.task)} \xBB de ${b(w.owner)}`),
|
|
1938
|
-
"task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Passer la t\xE2che \xAB ${short(w.task)} \xBB de ${b(w.owner)} \xE0 ${b(w.session)}`)
|
|
1939
|
+
"task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Passer la t\xE2che \xAB ${short(w.task)} \xBB de ${b(w.owner)} \xE0 ${b(w.session)}`),
|
|
1940
|
+
"task.answer": (v) => need(v, ["task", "session"], (w) => `R\xE9pondre \xE0 la d\xE9cision \xAB ${short(w.task)} \xBB de ${b(w.session)}`)
|
|
1939
1941
|
},
|
|
1940
1942
|
why: (code3, action, v) => {
|
|
1941
1943
|
const s = v.session ? b(v.session) : void 0, t = v.team ? b(v.team) : void 0, k = v.task ? `\xAB ${short(v.task)} \xBB` : void 0;
|
|
@@ -1985,6 +1987,8 @@ var fr = {
|
|
|
1985
1987
|
return k ? `la t\xE2che ${k} est d\xE9j\xE0 close.` : gone;
|
|
1986
1988
|
case "holder":
|
|
1987
1989
|
return s && k ? `${s} tient d\xE9j\xE0 la t\xE2che ${k}.` : gone;
|
|
1990
|
+
case "not_decision":
|
|
1991
|
+
return k ? `la t\xE2che ${k} n\u2019est pas une d\xE9cision qui t\u2019est pos\xE9e.` : gone;
|
|
1988
1992
|
case "changed":
|
|
1989
1993
|
return "la situation a chang\xE9 depuis ma r\xE9ponse : redemande-moi.";
|
|
1990
1994
|
case "failed":
|
|
@@ -2011,7 +2015,8 @@ var en = {
|
|
|
2011
2015
|
"team.dissolve": (v) => need(v, ["team"], (w) => `Dissolve the team ${b(w.team)}`),
|
|
2012
2016
|
"team.save": (v) => need(v, ["team", "name"], (w) => v.replaces ? `Replace the template \u201C${w.name}\u201D with ${b(w.team)}` : `Save ${b(w.team)} as the template \u201C${w.name}\u201D`),
|
|
2013
2017
|
"task.cancel": (v) => need(v, ["task", "owner"], (w) => `Cancel ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D`),
|
|
2014
|
-
"task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Pass ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D to ${b(w.session)}`)
|
|
2018
|
+
"task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Pass ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D to ${b(w.session)}`),
|
|
2019
|
+
"task.answer": (v) => need(v, ["task", "session"], (w) => `Answer ${b(w.session)}\u2019s decision \u201C${short(w.task)}\u201D`)
|
|
2015
2020
|
},
|
|
2016
2021
|
why: (code3, action, v) => {
|
|
2017
2022
|
const s = v.session ? b(v.session) : void 0, t = v.team ? b(v.team) : void 0, k = v.task ? `\u201C${short(v.task)}\u201D` : void 0;
|
|
@@ -2061,6 +2066,8 @@ var en = {
|
|
|
2061
2066
|
return k ? `the task ${k} is already closed.` : gone;
|
|
2062
2067
|
case "holder":
|
|
2063
2068
|
return s && k ? `${s} already holds the task ${k}.` : gone;
|
|
2069
|
+
case "not_decision":
|
|
2070
|
+
return k ? `the task ${k} is not a decision put to you.` : gone;
|
|
2064
2071
|
case "changed":
|
|
2065
2072
|
return "things have changed since my answer: ask me again.";
|
|
2066
2073
|
case "failed":
|
|
@@ -2095,7 +2102,8 @@ var fr2 = {
|
|
|
2095
2102
|
"team.dissolve": "Dissoudre",
|
|
2096
2103
|
"team.save": "Enregistrer",
|
|
2097
2104
|
"task.cancel": "Abandonner",
|
|
2098
|
-
"task.pass": "Passer le relais"
|
|
2105
|
+
"task.pass": "Passer le relais",
|
|
2106
|
+
"task.answer": "R\xE9pondre"
|
|
2099
2107
|
},
|
|
2100
2108
|
apply: "Appliquer",
|
|
2101
2109
|
deny: "Refuser",
|
|
@@ -2161,7 +2169,8 @@ var en2 = {
|
|
|
2161
2169
|
"team.dissolve": "Dissolve",
|
|
2162
2170
|
"team.save": "Save",
|
|
2163
2171
|
"task.cancel": "Drop",
|
|
2164
|
-
"task.pass": "Hand over"
|
|
2172
|
+
"task.pass": "Hand over",
|
|
2173
|
+
"task.answer": "Answer"
|
|
2165
2174
|
},
|
|
2166
2175
|
apply: "Apply",
|
|
2167
2176
|
deny: "Deny",
|
|
@@ -2270,6 +2279,9 @@ var fr3 = {
|
|
|
2270
2279
|
resume: "reprendre l\u2019\xE9quipe",
|
|
2271
2280
|
teamMode: "session ou \xE9quipe",
|
|
2272
2281
|
handover: "passer le relais",
|
|
2282
|
+
decide: "r\xE9pondre \xE0 la d\xE9cision",
|
|
2283
|
+
sendAnswer: "envoyer la r\xE9ponse",
|
|
2284
|
+
edit: "modifier",
|
|
2273
2285
|
sendOnly: "envoyer",
|
|
2274
2286
|
newLine: "nouvelle ligne",
|
|
2275
2287
|
keepAndLeave: "garder et sortir",
|
|
@@ -2336,6 +2348,10 @@ var fr3 = {
|
|
|
2336
2348
|
relievedBy: (name) => `relev\xE9e par ${name}`,
|
|
2337
2349
|
relieves: (name) => `rel\xE8ve de ${name}`,
|
|
2338
2350
|
ledgeTask: (owner, title2, state2) => `${owner} a ${state2 === "done" ? "fini" : state2 === "refused" ? "refus\xE9" : "abandonn\xE9"} \xAB ${title2} \xBB \xB7 t\xE2che de Ledge`,
|
|
2351
|
+
decision: (giver, title2) => `${giver} te demande : ${title2}`,
|
|
2352
|
+
decisionTitle: (giver, title2) => `R\xE9pondre \xE0 ${giver} \xB7 ${title2}`,
|
|
2353
|
+
decisionHint: "Ta r\xE9ponse, telle que la session la lira\u2026",
|
|
2354
|
+
confirmAnswer: (giver) => `Envoyer \xE0 ${giver} ? Une r\xE9ponse ne se reprend pas`,
|
|
2339
2355
|
joinWhich: (name) => `${name} rejoint quelle \xE9quipe ? (\u2191\u2193 \u21B5, esc)`,
|
|
2340
2356
|
noTeamHere: "Aucune \xE9quipe dans ce projet.",
|
|
2341
2357
|
notInTeam: "Cette session n\u2019est dans aucune \xE9quipe."
|
|
@@ -2555,6 +2571,7 @@ var fr3 = {
|
|
|
2555
2571
|
openFailed: (why) => `Image non ouverte : ${why}`
|
|
2556
2572
|
},
|
|
2557
2573
|
toast: {
|
|
2574
|
+
answerFailed: (why) => `R\xE9ponse non envoy\xE9e : ${why}`,
|
|
2558
2575
|
handover: (n) => `${n} \xE9crit son bilan : une nouvelle session prendra sa place`,
|
|
2559
2576
|
handoverRefused: (why) => `Rel\xE8ve refus\xE9e : ${why}`,
|
|
2560
2577
|
accept: (n) => `\u2713 ${n} : autoris\xE9`,
|
|
@@ -2657,6 +2674,9 @@ var en3 = {
|
|
|
2657
2674
|
resume: "resume the team",
|
|
2658
2675
|
teamMode: "session or team",
|
|
2659
2676
|
handover: "hand over",
|
|
2677
|
+
decide: "answer the decision",
|
|
2678
|
+
sendAnswer: "send the answer",
|
|
2679
|
+
edit: "edit",
|
|
2660
2680
|
sendOnly: "send",
|
|
2661
2681
|
newLine: "new line",
|
|
2662
2682
|
keepAndLeave: "keep and leave",
|
|
@@ -2723,6 +2743,10 @@ var en3 = {
|
|
|
2723
2743
|
relievedBy: (name) => `relieved by ${name}`,
|
|
2724
2744
|
relieves: (name) => `relieves ${name}`,
|
|
2725
2745
|
ledgeTask: (owner, title2, state2) => `${owner} ${state2 === "done" ? "finished" : state2 === "refused" ? "refused" : "dropped"} \u201C${title2}\u201D \xB7 Ledge\u2019s task`,
|
|
2746
|
+
decision: (giver, title2) => `${giver} asks you: ${title2}`,
|
|
2747
|
+
decisionTitle: (giver, title2) => `Answer ${giver} \xB7 ${title2}`,
|
|
2748
|
+
decisionHint: "Your answer, as the session will read it\u2026",
|
|
2749
|
+
confirmAnswer: (giver) => `Send to ${giver}? An answer cannot be taken back`,
|
|
2726
2750
|
joinWhich: (name) => `${name} joins which team? (\u2191\u2193 \u21B5, esc)`,
|
|
2727
2751
|
noTeamHere: "No team in this project.",
|
|
2728
2752
|
notInTeam: "This session is in no team."
|
|
@@ -2940,6 +2964,7 @@ var en3 = {
|
|
|
2940
2964
|
openFailed: (why) => `Image not opened: ${why}`
|
|
2941
2965
|
},
|
|
2942
2966
|
toast: {
|
|
2967
|
+
answerFailed: (why) => `Answer not sent: ${why}`,
|
|
2943
2968
|
handover: (n) => `${n} is writing its brief: a fresh session will take its place`,
|
|
2944
2969
|
handoverRefused: (why) => `Relief refused: ${why}`,
|
|
2945
2970
|
accept: (n) => `\u2713 ${n}: allowed`,
|
|
@@ -3078,6 +3103,7 @@ function exchanges(sessions, max, tasks = [], ledge = "Ledge") {
|
|
|
3078
3103
|
}
|
|
3079
3104
|
var nearContext = (sessions, near) => sessions.filter((s) => s.state !== "exited" && !s.archivedAt && (s.handover ? s.handover.state === "failed" : contextNear(s.context, near)));
|
|
3080
3105
|
var closedLedgeTasks = (tasks) => tasks.filter((x) => x.giver === LEDGE_GIVER && taskClosed(x) && !x.seen);
|
|
3106
|
+
var openDecisions = (tasks) => tasks.filter(openDecision);
|
|
3081
3107
|
|
|
3082
3108
|
// src/tui/model/time.ts
|
|
3083
3109
|
function ago(iso, now2, t) {
|
|
@@ -3126,8 +3152,8 @@ function notification(text5, mode, env) {
|
|
|
3126
3152
|
var title = (text5) => `\x1B]0;${text5.replace(/[\u0000-\u001f\u007f]/g, " ")}\x07`;
|
|
3127
3153
|
|
|
3128
3154
|
// src/tui/screens/Overview.tsx
|
|
3129
|
-
import { useMemo as useMemo2 } from "react";
|
|
3130
|
-
import { Box as
|
|
3155
|
+
import { useEffect as useEffect6, useMemo as useMemo2 } from "react";
|
|
3156
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
3131
3157
|
import { randomUUID } from "crypto";
|
|
3132
3158
|
import { useState as useState3 } from "react";
|
|
3133
3159
|
|
|
@@ -3146,49 +3172,48 @@ function botState(session) {
|
|
|
3146
3172
|
return FROM_SESSION[session.state];
|
|
3147
3173
|
}
|
|
3148
3174
|
|
|
3149
|
-
// src/
|
|
3150
|
-
var
|
|
3151
|
-
var
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3175
|
+
// ../ui/src/thread/turns.ts
|
|
3176
|
+
var OPEN_TURNS = 3;
|
|
3177
|
+
var byTime = (a, b2) => a.at < b2.at ? -1 : a.at > b2.at ? 1 : 0;
|
|
3178
|
+
function withoutEcho(items, reply) {
|
|
3179
|
+
if (!reply) return items;
|
|
3180
|
+
let last = -1;
|
|
3181
|
+
items.forEach((it, i) => {
|
|
3182
|
+
if (it.kind === "message") last = i;
|
|
3183
|
+
});
|
|
3184
|
+
const echo = items[last];
|
|
3185
|
+
return echo?.kind === "message" && echo.text.trim() === reply.text.trim() ? items.filter((_, i) => i !== last) : items;
|
|
3158
3186
|
}
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
now = Date.now();
|
|
3169
|
-
timer = setInterval(() => {
|
|
3170
|
-
now = Date.now();
|
|
3171
|
-
for (const l of listeners) l();
|
|
3172
|
-
}, 1e3);
|
|
3173
|
-
timer.unref?.();
|
|
3187
|
+
function turns(items) {
|
|
3188
|
+
const map3 = /* @__PURE__ */ new Map();
|
|
3189
|
+
for (const it of items) {
|
|
3190
|
+
if (it.helperId) continue;
|
|
3191
|
+
const t = map3.get(it.turn) ?? { n: it.turn, items: [] };
|
|
3192
|
+
t.items.push(it);
|
|
3193
|
+
if (it.kind === "prompt") t.prompt = it;
|
|
3194
|
+
if (it.kind === "reply") t.reply = it;
|
|
3195
|
+
map3.set(it.turn, t);
|
|
3174
3196
|
}
|
|
3175
|
-
return () => {
|
|
3176
|
-
listeners.delete(fn);
|
|
3177
|
-
if (listeners.size === 0 && timer) {
|
|
3178
|
-
clearInterval(timer);
|
|
3179
|
-
timer = null;
|
|
3180
|
-
}
|
|
3181
|
-
};
|
|
3197
|
+
return [...map3.values()].sort((a, b2) => a.n - b2.n).map((t) => ({ ...t, items: withoutEcho([...t.items].sort(byTime), t.reply) }));
|
|
3182
3198
|
}
|
|
3183
|
-
var useNow = () => useSyncExternalStore4(subscribe, () => now);
|
|
3184
3199
|
|
|
3185
|
-
// src/
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
const
|
|
3189
|
-
|
|
3190
|
-
|
|
3200
|
+
// ../ui/src/thread/tools.ts
|
|
3201
|
+
function rawTarget(item) {
|
|
3202
|
+
let s = item.label.trim();
|
|
3203
|
+
const prefix = `${item.tool} \xB7 `;
|
|
3204
|
+
if (s.startsWith(prefix)) s = s.slice(prefix.length);
|
|
3205
|
+
if (s === item.tool) return "";
|
|
3206
|
+
if (s.startsWith("{")) {
|
|
3207
|
+
try {
|
|
3208
|
+
const o = JSON.parse(s);
|
|
3209
|
+
const v = ["pattern", "query", "url", "file_path", "path", "command", "description", "prompt"].map((k) => o[k]).find((x) => typeof x === "string" && x);
|
|
3210
|
+
if (typeof v === "string") s = v;
|
|
3211
|
+
} catch {
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
return (s.split("\n")[0] ?? "").trim();
|
|
3191
3215
|
}
|
|
3216
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3192
3217
|
|
|
3193
3218
|
// src/tui/theme.ts
|
|
3194
3219
|
var THEMES = ["auto", "dark", "light"];
|
|
@@ -3253,9 +3278,6 @@ function setTone(next2) {
|
|
|
3253
3278
|
}
|
|
3254
3279
|
var projectColor = (color2) => color2 ? PROJECT_COLORS[color2] : C.dim;
|
|
3255
3280
|
|
|
3256
|
-
// src/tui/ui/Frame.tsx
|
|
3257
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
3258
|
-
|
|
3259
3281
|
// src/tui/model/cells.ts
|
|
3260
3282
|
import stringWidth from "string-width";
|
|
3261
3283
|
var graphemes = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
@@ -3332,753 +3354,63 @@ function row(prefix, body, meta, width, o = {}) {
|
|
|
3332
3354
|
});
|
|
3333
3355
|
}
|
|
3334
3356
|
|
|
3335
|
-
//
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
while (
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
return
|
|
3348
|
-
/* @__PURE__ */ jsxs(Text, { color: C.fg, backgroundColor: C.key, children: [
|
|
3349
|
-
" ",
|
|
3350
|
-
k,
|
|
3351
|
-
" "
|
|
3352
|
-
] }),
|
|
3353
|
-
/* @__PURE__ */ jsxs(Text, { color: C.dim, children: [
|
|
3354
|
-
" ",
|
|
3355
|
-
label
|
|
3356
|
-
] })
|
|
3357
|
-
] }) }, k)) });
|
|
3357
|
+
// ../../node_modules/.pnpm/ccount@2.0.1/node_modules/ccount/index.js
|
|
3358
|
+
function ccount(value, character) {
|
|
3359
|
+
const source2 = String(value);
|
|
3360
|
+
if (typeof character !== "string") {
|
|
3361
|
+
throw new TypeError("Expected character");
|
|
3362
|
+
}
|
|
3363
|
+
let count = 0;
|
|
3364
|
+
let index2 = source2.indexOf(character);
|
|
3365
|
+
while (index2 !== -1) {
|
|
3366
|
+
count++;
|
|
3367
|
+
index2 = source2.indexOf(character, index2 + character.length);
|
|
3368
|
+
}
|
|
3369
|
+
return count;
|
|
3358
3370
|
}
|
|
3359
3371
|
|
|
3360
|
-
//
|
|
3361
|
-
|
|
3362
|
-
var MIN = { columns: 60, rows: 16 };
|
|
3363
|
-
function Frame({ crumb, crumbShort, keys, kept, children }) {
|
|
3364
|
-
const { d, machines, t, g, lastToast, editing } = useTui();
|
|
3365
|
-
const { rows, columns } = useScreenSize();
|
|
3366
|
-
const live = listed(d.sessions);
|
|
3367
|
-
const pending = live.filter(isPending);
|
|
3368
|
-
const waiting = pending.length;
|
|
3369
|
-
const status = d.status === "open" ? null : d.status === "connecting" ? t.bar.connecting : t.bar.unreachable;
|
|
3370
|
-
if (columns < MIN.columns || rows < MIN.rows) {
|
|
3371
|
-
const why = [...pending[0] ? [t.small.waits(pending[0].name, pending[0].pending?.kind === "question")] : [], ...kept ? [t.small.kept] : []].join(" \xB7 ");
|
|
3372
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: rows, width: columns, alignItems: "center", justifyContent: "center", children: [
|
|
3373
|
-
/* @__PURE__ */ jsxs2(Text2, { color: C.coral, bold: true, children: [
|
|
3374
|
-
g.logo,
|
|
3375
|
-
" ledge"
|
|
3376
|
-
] }),
|
|
3377
|
-
/* @__PURE__ */ jsx4(Text2, { color: C.fg, children: t.small.tooSmall(columns, rows) }),
|
|
3378
|
-
/* @__PURE__ */ jsx4(Text2, { color: C.dim, children: t.small.atLeast(MIN.columns, MIN.rows) }),
|
|
3379
|
-
why ? /* @__PURE__ */ jsx4(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx4(Text2, { color: C.red, children: why }) }) : null
|
|
3380
|
-
] });
|
|
3381
|
-
}
|
|
3382
|
-
const left2 = cells(` ${g.logo} ledge ${t.bar.sessions(live.length)} \xB7 ${t.bar.machines(Math.max(1, machines.length))}`) + (waiting > 0 ? cells(` \xB7 ${t.bar.waiting(waiting)}`) : 0) + (status ? cells(` ${status}`) : 0);
|
|
3383
|
-
const room = columns - left2 - 2;
|
|
3384
|
-
const shown = cells(crumb) <= room ? crumb : crumbShort && cells(crumbShort) <= room ? crumbShort : "";
|
|
3385
|
-
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: rows, width: columns, children: [
|
|
3386
|
-
/* @__PURE__ */ jsxs2(Box2, { paddingX: 1, justifyContent: "space-between", height: 1, backgroundColor: C.bar, children: [
|
|
3387
|
-
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
3388
|
-
/* @__PURE__ */ jsxs2(Text2, { color: C.coral, children: [
|
|
3389
|
-
g.logo,
|
|
3390
|
-
" ledge"
|
|
3391
|
-
] }),
|
|
3392
|
-
/* @__PURE__ */ jsxs2(Text2, { color: C.dim, children: [
|
|
3393
|
-
" ",
|
|
3394
|
-
t.bar.sessions(live.length),
|
|
3395
|
-
" \xB7 ",
|
|
3396
|
-
t.bar.machines(Math.max(1, machines.length))
|
|
3397
|
-
] }),
|
|
3398
|
-
waiting > 0 ? /* @__PURE__ */ jsxs2(Text2, { color: C.red, children: [
|
|
3399
|
-
" \xB7 ",
|
|
3400
|
-
t.bar.waiting(waiting)
|
|
3401
|
-
] }) : null,
|
|
3402
|
-
status ? /* @__PURE__ */ jsxs2(Text2, { color: C.yellow, children: [
|
|
3403
|
-
" ",
|
|
3404
|
-
status
|
|
3405
|
-
] }) : null
|
|
3406
|
-
] }),
|
|
3407
|
-
shown ? /* @__PURE__ */ jsx4(Text2, { wrap: "truncate", color: C.dim, children: shown }) : null
|
|
3408
|
-
] }),
|
|
3409
|
-
/* @__PURE__ */ jsx4(Box2, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children }),
|
|
3410
|
-
lastToast ? /* @__PURE__ */ jsx4(Box2, { paddingX: 1, height: 1, children: /* @__PURE__ */ jsx4(Text2, { wrap: "truncate", color: lastToast.tone === "error" ? C.red : lastToast.tone === "ok" ? C.green : lastToast.tone === "quiet" ? C.dim : C.yellow, children: lastToast.text }) }) : null,
|
|
3411
|
-
/* @__PURE__ */ jsx4(KeyBar, { keys: fitKeys(keys, columns, editing ? null : ["?", t.keys.help]) })
|
|
3412
|
-
] });
|
|
3372
|
+
// ../../node_modules/.pnpm/devlop@1.1.0/node_modules/devlop/lib/default.js
|
|
3373
|
+
function ok() {
|
|
3413
3374
|
}
|
|
3414
3375
|
|
|
3415
|
-
//
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
return
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
] }),
|
|
3426
|
-
/* @__PURE__ */ jsxs3(Text3, { color: C.dim, backgroundColor: bg, children: [
|
|
3427
|
-
k,
|
|
3428
|
-
" "
|
|
3429
|
-
] })
|
|
3430
|
-
] });
|
|
3376
|
+
// ../../node_modules/.pnpm/micromark-util-character@2.1.1/node_modules/micromark-util-character/index.js
|
|
3377
|
+
var asciiAlpha = regexCheck(/[A-Za-z]/);
|
|
3378
|
+
var asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);
|
|
3379
|
+
var asciiAtext = regexCheck(/[#-'*+\--9=?A-Z^-~]/);
|
|
3380
|
+
function asciiControl(code3) {
|
|
3381
|
+
return (
|
|
3382
|
+
// Special whitespace codes (which have negative values), C0 and Control
|
|
3383
|
+
// character DEL
|
|
3384
|
+
code3 !== null && (code3 < 32 || code3 === 127)
|
|
3385
|
+
);
|
|
3431
3386
|
}
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
" \xB7 ",
|
|
3452
|
-
modelLabel(s, t.session.defaultModel)
|
|
3453
|
-
] }),
|
|
3454
|
-
s.context ? /* @__PURE__ */ jsxs3(Text3, { color: contextNear(s.context, threshold) ? C.yellow : C.dim, children: [
|
|
3455
|
-
" \xB7 ",
|
|
3456
|
-
t.overview.context(Math.round(s.context.used / s.context.window * 100))
|
|
3457
|
-
] }) : null,
|
|
3458
|
-
/* @__PURE__ */ jsxs3(Text3, { color: C.dim, children: [
|
|
3459
|
-
" \xB7 ",
|
|
3460
|
-
machineLabel(s),
|
|
3461
|
-
s.branch ? ` \xB7 ${g.branch} ${s.branch}` : ""
|
|
3462
|
-
] }),
|
|
3463
|
-
s.offline ? /* @__PURE__ */ jsxs3(Text3, { color: C.yellow, children: [
|
|
3464
|
-
" ",
|
|
3465
|
-
t.overview.offline
|
|
3466
|
-
] }) : s.source === "hook_only" ? /* @__PURE__ */ jsxs3(Text3, { color: C.faint, children: [
|
|
3467
|
-
" ",
|
|
3468
|
-
t.overview.readOnly
|
|
3469
|
-
] }) : null
|
|
3470
|
-
] }),
|
|
3471
|
-
s.handover || s.relieves ? /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
3472
|
-
s.handover ? /* @__PURE__ */ jsxs3(Text3, { color: s.handover.state === "failed" ? C.red : C.yellow, children: [
|
|
3473
|
-
" \u21C4 ",
|
|
3474
|
-
s.handover.state === "done" && s.handover.toName ? t.overview.relievedBy(s.handover.toName) : t.overview.relief[s.handover.state],
|
|
3475
|
-
s.handover.reason ? ` : ${s.handover.reason}` : ""
|
|
3476
|
-
] }) : null,
|
|
3477
|
-
s.relieves ? /* @__PURE__ */ jsxs3(Text3, { color: C.dim, children: [
|
|
3478
|
-
" \u21C4 ",
|
|
3479
|
-
t.overview.relieves(s.relieves.name)
|
|
3480
|
-
] }) : null
|
|
3481
|
-
] }) : null,
|
|
3482
|
-
helpers.length > 0 ? /* @__PURE__ */ jsxs3(Text3, { color: C.violet, wrap: "truncate", children: [
|
|
3483
|
-
" ",
|
|
3484
|
-
helpers.map((h) => `${g.helper} ${h.kind} \xB7 ${oneLine(h.task)}`).join(" ")
|
|
3485
|
-
] }) : null,
|
|
3486
|
-
p ? /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, children: [
|
|
3487
|
-
/* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
3488
|
-
/* @__PURE__ */ jsx5(Text3, { color: STATE_COLOR[state2], children: verb }),
|
|
3489
|
-
" ",
|
|
3490
|
-
/* @__PURE__ */ jsx5(Text3, { color: C.fg, bold: true, children: oneLine(p.text) }),
|
|
3491
|
-
p.tool ? /* @__PURE__ */ jsxs3(Text3, { color: C.faint, children: [
|
|
3492
|
-
" ",
|
|
3493
|
-
p.tool
|
|
3494
|
-
] }) : null
|
|
3495
|
-
] }),
|
|
3496
|
-
/* @__PURE__ */ jsx5(Box3, { marginTop: 1, columnGap: 2, flexWrap: "wrap", children: p.kind === "question" ? choices(p).slice(0, 9).map((c, i) => /* @__PURE__ */ jsx5(Button, { label: `${c.n} \xB7 ${c.label}`, k: String(c.n), primary: i === 0, off: !canAct }, c.n)) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
3497
|
-
/* @__PURE__ */ jsx5(Button, { label: t.overview.allow, k: "y", primary: true, off: !canAct }),
|
|
3498
|
-
/* @__PURE__ */ jsx5(Button, { label: t.overview.deny, k: "n", off: !canAct }),
|
|
3499
|
-
offers(s.tool, p.kind, "always") ? /* @__PURE__ */ jsx5(Button, { label: t.overview.always, k: "A", off: !canAct }) : null,
|
|
3500
|
-
note && offers(s.tool, p.kind, "note") ? /* @__PURE__ */ jsx5(Button, { label: t.overview.denyWithNote, k: "r", off: !canAct }) : null
|
|
3501
|
-
] }) })
|
|
3502
|
-
] }) : s.lastReply && state2 === "idle" ? /* @__PURE__ */ jsxs3(Text3, { color: C.dim, wrap: "truncate", children: [
|
|
3503
|
-
" ",
|
|
3504
|
-
oneLine(s.lastReply.text)
|
|
3505
|
-
] }) : null
|
|
3506
|
-
] });
|
|
3387
|
+
var asciiDigit = regexCheck(/\d/);
|
|
3388
|
+
var asciiHexDigit = regexCheck(/[\dA-Fa-f]/);
|
|
3389
|
+
var asciiPunctuation = regexCheck(/[!-/:-@[-`{-~]/);
|
|
3390
|
+
function markdownLineEnding(code3) {
|
|
3391
|
+
return code3 !== null && code3 < -2;
|
|
3392
|
+
}
|
|
3393
|
+
function markdownLineEndingOrSpace(code3) {
|
|
3394
|
+
return code3 !== null && (code3 < 0 || code3 === 32);
|
|
3395
|
+
}
|
|
3396
|
+
function markdownSpace(code3) {
|
|
3397
|
+
return code3 === -2 || code3 === -1 || code3 === 32;
|
|
3398
|
+
}
|
|
3399
|
+
var unicodePunctuation = regexCheck(new RegExp("\\p{P}|\\p{S}", "u"));
|
|
3400
|
+
var unicodeWhitespace = regexCheck(/\s/);
|
|
3401
|
+
function regexCheck(regex) {
|
|
3402
|
+
return check;
|
|
3403
|
+
function check(code3) {
|
|
3404
|
+
return code3 !== null && code3 > -1 && regex.test(String.fromCharCode(code3));
|
|
3405
|
+
}
|
|
3507
3406
|
}
|
|
3508
3407
|
|
|
3509
|
-
//
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
const { height } = useBoxMetrics(ref);
|
|
3516
|
-
let end2 = lines.length;
|
|
3517
|
-
while (end2 > 0 && !lines[end2 - 1].trim()) end2--;
|
|
3518
|
-
const shown = end2 === 0 ? [] : lines.slice(0, end2).slice(-Math.max(1, height));
|
|
3519
|
-
return /* @__PURE__ */ jsx6(Box4, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: shown.length === 0 ? /* @__PURE__ */ jsx6(Text4, { color: emptyColor, children: empty3 }) : shown.map((l, i) => /* @__PURE__ */ jsx6(Text4, { color: color2, wrap: "truncate", children: l || " " }, i)) });
|
|
3520
|
-
}
|
|
3521
|
-
|
|
3522
|
-
// src/tui/ui/TitledBox.tsx
|
|
3523
|
-
import { useRef as useRef5 } from "react";
|
|
3524
|
-
import { Box as Box5, Text as Text5, useBoxMetrics as useBoxMetrics2 } from "ink";
|
|
3525
|
-
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3526
|
-
function TitledBox({ title: title2, mark, color: color2 = C.line, titleColor = C.dim, titleBold, double, foot, footColor = C.faint, children, flexGrow, height, width, flexShrink, minHeight }) {
|
|
3527
|
-
const b2 = double ? { tl: "\u2554", h: "\u2550", tr: "\u2557", bl: "\u255A", br: "\u255D" } : { tl: "\u256D", h: "\u2500", tr: "\u256E", bl: "\u2570", br: "\u256F" };
|
|
3528
|
-
const ref = useRef5(null);
|
|
3529
|
-
const { width: w } = useBoxMetrics2(ref);
|
|
3530
|
-
const text5 = (l) => l.map((s) => s.text).join("");
|
|
3531
|
-
const tail = mark ? [{ text: ` ${mark.text.toUpperCase()}`, color: mark.color }] : [];
|
|
3532
|
-
const room = Math.max(0, w - 4 - (cells(text5(tail)) < w - 8 ? cells(text5(tail)) : 0));
|
|
3533
|
-
const head = [{ text: ` ${title2.toUpperCase()}`, color: titleColor }];
|
|
3534
|
-
const shown = cells(text5([...head, ...tail])) > w - 4 ? [...cut(head, room), ...cells(text5(tail)) < w - 8 ? tail : [], { text: " ", color: titleColor }] : [...head, ...tail, { text: " ", color: titleColor }];
|
|
3535
|
-
const rest = Math.max(0, w - 3 - cells(text5(shown)));
|
|
3536
|
-
const f = foot ? text5(cut([{ text: foot }], Math.max(0, w - 6))) : "";
|
|
3537
|
-
return /* @__PURE__ */ jsxs4(Box5, { ref, flexDirection: "column", flexGrow, flexShrink, height, width, minHeight, overflow: "hidden", children: [
|
|
3538
|
-
/* @__PURE__ */ jsxs4(Text5, { wrap: "truncate", children: [
|
|
3539
|
-
/* @__PURE__ */ jsxs4(Text5, { color: color2, children: [
|
|
3540
|
-
b2.tl,
|
|
3541
|
-
b2.h
|
|
3542
|
-
] }),
|
|
3543
|
-
w > 0 ? shown.map((s, i) => /* @__PURE__ */ jsx7(Text5, { color: s.color, bold: titleBold, children: s.text }, i)) : null,
|
|
3544
|
-
/* @__PURE__ */ jsxs4(Text5, { color: color2, children: [
|
|
3545
|
-
b2.h.repeat(rest),
|
|
3546
|
-
b2.tr
|
|
3547
|
-
] })
|
|
3548
|
-
] }),
|
|
3549
|
-
/* @__PURE__ */ jsx7(Box5, { flexDirection: "column", borderStyle: double ? "double" : "round", borderTop: false, borderBottom: !f, borderColor: color2, paddingX: 1, flexGrow: 1, overflow: "hidden", children }),
|
|
3550
|
-
f ? /* @__PURE__ */ jsxs4(Text5, { wrap: "truncate", children: [
|
|
3551
|
-
/* @__PURE__ */ jsxs4(Text5, { color: color2, children: [
|
|
3552
|
-
b2.bl,
|
|
3553
|
-
b2.h.repeat(Math.max(1, w - 5 - cells(f))),
|
|
3554
|
-
" "
|
|
3555
|
-
] }),
|
|
3556
|
-
/* @__PURE__ */ jsx7(Text5, { color: footColor, children: f }),
|
|
3557
|
-
/* @__PURE__ */ jsxs4(Text5, { color: color2, children: [
|
|
3558
|
-
" ",
|
|
3559
|
-
b2.h,
|
|
3560
|
-
b2.br
|
|
3561
|
-
] })
|
|
3562
|
-
] }) : null
|
|
3563
|
-
] });
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
// src/tui/screens/Overview.tsx
|
|
3567
|
-
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3568
|
-
var WIDE = 100;
|
|
3569
|
-
function windowed(rows, selectedKey, height) {
|
|
3570
|
-
if (rows.length <= height) return [...rows];
|
|
3571
|
-
const at = Math.max(0, rows.findIndex((r) => r.key === selectedKey));
|
|
3572
|
-
const start = Math.min(Math.max(0, at - Math.floor(height / 2)), rows.length - height);
|
|
3573
|
-
return rows.slice(start, start + height);
|
|
3574
|
-
}
|
|
3575
|
-
function Overview() {
|
|
3576
|
-
const tui = useTui();
|
|
3577
|
-
const { d, projects, t, g, selected, select, actions, go, attach } = tui;
|
|
3578
|
-
const { columns, rows: height } = useScreenSize();
|
|
3579
|
-
const wide = columns >= WIDE;
|
|
3580
|
-
const left2 = wide ? Math.min(64, Math.max(44, Math.floor(columns * 0.42))) : columns;
|
|
3581
|
-
const teams = useTeams(tui.client);
|
|
3582
|
-
const tasks = useTasks(tui.client);
|
|
3583
|
-
const view = useMemo2(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
|
|
3584
|
-
const sel = selected?.id ?? null;
|
|
3585
|
-
const closed = closedLedgeTasks(tasks);
|
|
3586
|
-
const threshold = useContextNear(tui.client);
|
|
3587
|
-
const near = useMemo2(() => nearContext(d.sessions, threshold), [d.sessions, threshold]);
|
|
3588
|
-
const [tasksShown, setTasksShown] = useState3(true);
|
|
3589
|
-
const [joining, setJoining] = useState3(null);
|
|
3590
|
-
const node2 = selected ? view.tree.find((n) => n.branches.some((b2) => b2.sessions.includes(selected)) || n.teams.some((x) => x.sessions.includes(selected))) : void 0;
|
|
3591
|
-
const seatTeam = selected?.team ? teams?.find((x) => x.id === selected.team && x.state !== "dissolved") : void 0;
|
|
3592
|
-
const choices2 = node2?.teams.map((x) => x.team).filter((x) => x.state !== "dissolved" && x.id !== selected?.team) ?? [];
|
|
3593
|
-
useScreenKeys((input, key) => {
|
|
3594
|
-
if (joining) {
|
|
3595
|
-
if (key.escape) {
|
|
3596
|
-
setJoining(null);
|
|
3597
|
-
return true;
|
|
3598
|
-
}
|
|
3599
|
-
if (input === "j" || key.downArrow) {
|
|
3600
|
-
setJoining({ index: (joining.index + 1) % Math.max(1, choices2.length) });
|
|
3601
|
-
return true;
|
|
3602
|
-
}
|
|
3603
|
-
if (input === "k" || key.upArrow) {
|
|
3604
|
-
setJoining({ index: (joining.index + choices2.length - 1) % Math.max(1, choices2.length) });
|
|
3605
|
-
return true;
|
|
3606
|
-
}
|
|
3607
|
-
if (key.return && selected && choices2[joining.index]) {
|
|
3608
|
-
const team = choices2[joining.index];
|
|
3609
|
-
const role = selected.role ?? team.seats.find((x) => x.sessionId === null)?.role;
|
|
3610
|
-
setJoining(null);
|
|
3611
|
-
if (!role) {
|
|
3612
|
-
tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
3613
|
-
return true;
|
|
3614
|
-
}
|
|
3615
|
-
tui.client.send({ type: "team.join", team: team.id, session: selected.id, role });
|
|
3616
|
-
tui.toast({ text: t.toast.joined(selected.name, team.name), tone: "ok" });
|
|
3617
|
-
return true;
|
|
3618
|
-
}
|
|
3619
|
-
return true;
|
|
3620
|
-
}
|
|
3621
|
-
if (input === "J" && selected) {
|
|
3622
|
-
if (choices2.length) setJoining({ index: 0 });
|
|
3623
|
-
else tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
3624
|
-
return true;
|
|
3625
|
-
}
|
|
3626
|
-
if (input === "R") {
|
|
3627
|
-
const resting = (seatTeam?.state === "resting" ? seatTeam : void 0) ?? node2?.teams.find((x) => x.team.state === "resting")?.team ?? view.tree.flatMap((n) => n.teams).find((x) => x.team.state === "resting")?.team;
|
|
3628
|
-
if (resting) {
|
|
3629
|
-
tui.client.send({ type: "team.resume", team: resting.id });
|
|
3630
|
-
tui.toast({ text: t.toast.resumed(resting.name), tone: "ok" });
|
|
3631
|
-
}
|
|
3632
|
-
return true;
|
|
3633
|
-
}
|
|
3634
|
-
if (input === "T") {
|
|
3635
|
-
setTasksShown((v) => !v);
|
|
3636
|
-
return true;
|
|
3637
|
-
}
|
|
3638
|
-
if (input === "H") {
|
|
3639
|
-
const s = (selected && selected.source === "launcher" && selected.state !== "exited" && selected.tool !== "omp" && (!selected.handover || selected.handover.state === "failed") ? selected : void 0) ?? near.find((x) => x.handover?.state !== "failed") ?? near[0];
|
|
3640
|
-
if (!s) return true;
|
|
3641
|
-
const rid = randomUUID();
|
|
3642
|
-
const off = tui.client.onMessage((m) => {
|
|
3643
|
-
if (m.type === "session.handover.result" && m.rid === rid) {
|
|
3644
|
-
off();
|
|
3645
|
-
tui.toast(m.ok ? { text: t.toast.handover(s.name), tone: "ok" } : { text: t.toast.handoverRefused(m.reason ?? "?"), tone: "info" });
|
|
3646
|
-
}
|
|
3647
|
-
});
|
|
3648
|
-
setTimeout(off, 5e3);
|
|
3649
|
-
tui.client.send({ type: "session.handover", rid, id: s.id });
|
|
3650
|
-
return true;
|
|
3651
|
-
}
|
|
3652
|
-
if (input === "x" && closed[0]) {
|
|
3653
|
-
tui.client.send({ type: "task.cmd", rid: randomUUID(), by: LEDGE_GIVER, op: "seen", task: closed[0].id });
|
|
3654
|
-
return true;
|
|
3655
|
-
}
|
|
3656
|
-
if (input === "j" || key.downArrow) {
|
|
3657
|
-
select(step(view.order, sel, 1));
|
|
3658
|
-
return true;
|
|
3659
|
-
}
|
|
3660
|
-
if (input === "k" || key.upArrow) {
|
|
3661
|
-
select(step(view.order, sel, -1));
|
|
3662
|
-
return true;
|
|
3663
|
-
}
|
|
3664
|
-
if (key.tab) {
|
|
3665
|
-
const next2 = nextWaiting(view.yours, sel);
|
|
3666
|
-
if (next2) select(next2);
|
|
3667
|
-
return true;
|
|
3668
|
-
}
|
|
3669
|
-
if (key.return) {
|
|
3670
|
-
if (selected) go("session");
|
|
3671
|
-
return true;
|
|
3672
|
-
}
|
|
3673
|
-
if (!selected) return false;
|
|
3674
|
-
if (input === "a") {
|
|
3675
|
-
attach(selected);
|
|
3676
|
-
return true;
|
|
3677
|
-
}
|
|
3678
|
-
if (input === "r" && selected.pending) {
|
|
3679
|
-
go("session");
|
|
3680
|
-
return true;
|
|
3681
|
-
}
|
|
3682
|
-
if (/^[1-9]$/.test(input) && selected.pending?.kind !== "question") return false;
|
|
3683
|
-
if (input === "y" || input === "n" || input === "A" || /^[1-9]$/.test(input)) return actions.key(selected, input);
|
|
3684
|
-
if (input === "i" && selected.state === "working") {
|
|
3685
|
-
actions.interrupt(selected);
|
|
3686
|
-
return true;
|
|
3687
|
-
}
|
|
3688
|
-
return false;
|
|
3689
|
-
});
|
|
3690
|
-
const tree2 = view.tree.flatMap((n) => {
|
|
3691
|
-
const teamed = n.teams.length > 0;
|
|
3692
|
-
const common = n.branches.length > 0;
|
|
3693
|
-
return [
|
|
3694
|
-
{ kind: "project", key: n.key, label: n.label, color: projectColor(n.color), count: n.count },
|
|
3695
|
-
...teamed && common ? [{ kind: "common", key: `${n.key}|common` }] : [],
|
|
3696
|
-
...n.branches.flatMap((b2) => [{ kind: "branch", key: `${n.key}|${b2.branch}`, branch: b2.branch, indent: teamed ? 4 : 2 }, ...b2.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: teamed ? 2 : 0 }))]),
|
|
3697
|
-
...n.teams.flatMap((x) => [{ kind: "team", key: x.key, team: x.team, done: x.done, given: x.given }, ...x.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: 2 }))])
|
|
3698
|
-
];
|
|
3699
|
-
});
|
|
3700
|
-
const yoursCount = view.yours.length + closed.length + near.length;
|
|
3701
|
-
const yoursHeight = Math.max(1, Math.min(yoursCount, 6));
|
|
3702
|
-
const joinHeight = joining && selected ? Math.max(1, choices2.length) + 2 : 0;
|
|
3703
|
-
const treeHeight = Math.max(3, height - 3 - (yoursHeight + 2) - joinHeight - 3);
|
|
3704
|
-
const inner = left2 - 4, nameW = 7;
|
|
3705
|
-
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
3706
|
-
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
3707
|
-
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
3708
|
-
const yoursBox = /* @__PURE__ */ jsx8(TitledBox, { title: t.overview.yours(yoursCount), color: yoursCount ? C.red : C.line, titleColor: yoursCount ? C.red : C.dim, height: yoursHeight + 2, children: yoursCount === 0 ? /* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.overview.nobodyWaits }) : [...view.yours.slice(0, yoursHeight).map((s) => {
|
|
3709
|
-
const st = botState(s);
|
|
3710
|
-
return /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
3711
|
-
/* @__PURE__ */ jsxs5(Text6, { color: STATE_COLOR[st], children: [
|
|
3712
|
-
g[st],
|
|
3713
|
-
" "
|
|
3714
|
-
] }),
|
|
3715
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
3716
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.dim, children: fit(s.repo ?? s.project, 9) }),
|
|
3717
|
-
/* @__PURE__ */ jsx8(Text6, { color: STATE_COLOR[st], children: fit(oneLine(s.pending?.text ?? ""), askW) }),
|
|
3718
|
-
/* @__PURE__ */ jsxs5(Text6, { color: C.faint, children: [
|
|
3719
|
-
" ",
|
|
3720
|
-
/* @__PURE__ */ jsx8(Ago, { iso: s.lastActivityAt })
|
|
3721
|
-
] })
|
|
3722
|
-
] }, s.id);
|
|
3723
|
-
}), ...closed.slice(0, Math.max(0, yoursHeight - view.yours.length)).map((x) => /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", children: [
|
|
3724
|
-
/* @__PURE__ */ jsxs5(Text6, { color: x.state === "done" ? C.green : C.yellow, children: [
|
|
3725
|
-
x.state === "done" ? g.ok : g.fail,
|
|
3726
|
-
" "
|
|
3727
|
-
] }),
|
|
3728
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, children: t.overview.ledgeTask(x.ownerName, x.title, x.state) })
|
|
3729
|
-
] }, x.id)), ...near.slice(0, Math.max(0, yoursHeight - view.yours.length - closed.length)).map((s) => /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
3730
|
-
/* @__PURE__ */ jsxs5(Text6, { color: C.yellow, children: [
|
|
3731
|
-
s.handover?.state === "failed" ? g.fail : "\u25D4",
|
|
3732
|
-
" "
|
|
3733
|
-
] }),
|
|
3734
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, children: s.handover?.state === "failed" ? t.overview.reliefFailed(s.name, s.handover.reason ?? "?") : t.overview.near(`${s.name}${s.role ? ` @${s.role}` : ""}`, Math.round((s.context?.used ?? 0) / (s.context?.window ?? 1) * 100)) })
|
|
3735
|
-
] }, `near:${s.id}`))] });
|
|
3736
|
-
const treeBox = /* @__PURE__ */ jsx8(TitledBox, { title: t.overview.projects, flexGrow: 1, children: tree2.length === 0 ? /* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.overview.noSession }) : windowed(tree2, sel, treeHeight).map((r) => r.kind === "project" ? /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", children: [
|
|
3737
|
-
/* @__PURE__ */ jsx8(Text6, { color: r.color, children: g.project }),
|
|
3738
|
-
" ",
|
|
3739
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, bold: true, children: r.label }),
|
|
3740
|
-
" ",
|
|
3741
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.overview.sessions(r.count) })
|
|
3742
|
-
] }, r.key) : r.kind === "common" ? /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", color: C.faint, children: [
|
|
3743
|
-
" ",
|
|
3744
|
-
g.common,
|
|
3745
|
-
" ",
|
|
3746
|
-
t.overview.common
|
|
3747
|
-
] }, r.key) : r.kind === "team" ? /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", children: [
|
|
3748
|
-
/* @__PURE__ */ jsx8(Text6, { children: " " }),
|
|
3749
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.coral, children: g.team }),
|
|
3750
|
-
" ",
|
|
3751
|
-
/* @__PURE__ */ jsx8(Text6, { color: r.team.state === "resting" ? C.dim : C.fg, bold: true, children: r.team.name }),
|
|
3752
|
-
" ",
|
|
3753
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.faint, children: r.team.state === "resting" ? t.overview.resting : t.overview.teamLine(r.done, r.given) })
|
|
3754
|
-
] }, r.key) : r.kind === "branch" ? /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", color: C.faint, children: [
|
|
3755
|
-
" ".repeat(r.indent),
|
|
3756
|
-
g.branch,
|
|
3757
|
-
" ",
|
|
3758
|
-
r.branch
|
|
3759
|
-
] }, r.key) : /* @__PURE__ */ jsx8(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent - (r.s.role ? r.s.role.length + 2 : 0)), stateW, indent: r.indent }, r.key)) });
|
|
3760
|
-
const keys = [
|
|
3761
|
-
["j k", t.keys.navigate],
|
|
3762
|
-
["\u21B5", t.keys.open],
|
|
3763
|
-
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
3764
|
-
["a", t.keys.attach],
|
|
3765
|
-
...seatTeam ? [["T", t.keys.tasks]] : [],
|
|
3766
|
-
...choices2.length ? [["J", t.keys.join]] : [],
|
|
3767
|
-
...view.tree.some((n) => n.teams.some((x) => x.team.state === "resting")) ? [["R", t.keys.resume]] : [],
|
|
3768
|
-
...near.length ? [["H", t.keys.handover]] : [],
|
|
3769
|
-
["/", t.keys.ask],
|
|
3770
|
-
["+", t.keys.launch],
|
|
3771
|
-
[",", t.keys.settings],
|
|
3772
|
-
["?", t.keys.help],
|
|
3773
|
-
["q", t.keys.quit]
|
|
3774
|
-
];
|
|
3775
|
-
const teamTasks = seatTeam ? tasks.filter((x) => x.team === seatTeam.id && (!taskClosed(x) || x.updatedAt.slice(0, 10) === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10))).slice(-4) : [];
|
|
3776
|
-
return /* @__PURE__ */ jsx8(Frame, { crumb: tui.machines.map((m) => `${m.label} ${m.ready ? g.machine : "\u25CB"}`).join(" "), keys, children: /* @__PURE__ */ jsxs5(Box6, { flexGrow: 1, children: [
|
|
3777
|
-
/* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", width: left2, flexShrink: 0, children: [
|
|
3778
|
-
yoursBox,
|
|
3779
|
-
joining && selected ? /* @__PURE__ */ jsx8(TitledBox, { title: t.overview.joinWhich(selected.name), color: C.coral, titleColor: C.coral, height: joinHeight, children: choices2.map((x, i) => /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", backgroundColor: i === joining.index ? C.sel : void 0, children: [
|
|
3780
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.coral, children: g.team }),
|
|
3781
|
-
" ",
|
|
3782
|
-
x.name
|
|
3783
|
-
] }, x.id)) }) : null,
|
|
3784
|
-
treeBox
|
|
3785
|
-
] }),
|
|
3786
|
-
wide ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", flexGrow: 1, children: [
|
|
3787
|
-
selected ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
3788
|
-
/* @__PURE__ */ jsx8(TitledBox, { title: `${selected.name} \xB7 ${oneLine(selected.topic ?? selected.title)}`, color: isPending(selected) ? C.red : C.line, titleColor: isPending(selected) ? C.red : C.dim, children: /* @__PURE__ */ jsx8(SessionCard, { s: selected, note: true }) }),
|
|
3789
|
-
seatTeam && tasksShown ? /* @__PURE__ */ jsx8(TitledBox, { title: t.overview.tasks(`${g.team} ${seatTeam.name}`), height: Math.max(3, teamTasks.length + 2), children: teamTasks.length === 0 ? /* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.overview.noTask }) : teamTasks.map((x) => /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", children: [
|
|
3790
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.faint, children: x.id }),
|
|
3791
|
-
" ",
|
|
3792
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, children: x.title }),
|
|
3793
|
-
" ",
|
|
3794
|
-
/* @__PURE__ */ jsxs5(Text6, { color: C.dim, children: [
|
|
3795
|
-
x.giverName,
|
|
3796
|
-
" ",
|
|
3797
|
-
g.arrow,
|
|
3798
|
-
" ",
|
|
3799
|
-
x.ownerName
|
|
3800
|
-
] }),
|
|
3801
|
-
" ",
|
|
3802
|
-
/* @__PURE__ */ jsx8(Text6, { color: x.state === "done" ? C.green : x.state === "taken" ? C.blue : C.faint, children: t.ask.taskState[x.state] })
|
|
3803
|
-
] }, x.id)) }) : null,
|
|
3804
|
-
/* @__PURE__ */ jsx8(TitledBox, { title: t.overview.terminal(paneName(selected)), flexGrow: 1, children: /* @__PURE__ */ jsx8(Tail, { lines: d.previews.get(selected.id) ?? [], color: C.dim, empty: t.overview.noPreview, emptyColor: C.faint }) })
|
|
3805
|
-
] }) : /* @__PURE__ */ jsx8(TitledBox, { title: t.tabs.session, flexGrow: 1, children: /* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.session.noSession }) }),
|
|
3806
|
-
/* @__PURE__ */ jsx8(TitledBox, { title: t.overview.exchanges, height: 5, children: (() => {
|
|
3807
|
-
const list3 = exchanges(d.sessions, 3, tasks, t.overview.ledge);
|
|
3808
|
-
return list3.length === 0 ? /* @__PURE__ */ jsx8(Text6, { color: C.faint, children: t.overview.noExchange }) : list3.map((x, i) => /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", children: [
|
|
3809
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.faint, children: clock(x.at) }),
|
|
3810
|
-
" ",
|
|
3811
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, children: x.from }),
|
|
3812
|
-
" ",
|
|
3813
|
-
/* @__PURE__ */ jsx8(Text6, { color: x.kind === "task" ? C.green : C.violet, children: g.arrow }),
|
|
3814
|
-
" ",
|
|
3815
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.fg, children: x.to }),
|
|
3816
|
-
" ",
|
|
3817
|
-
x.kind === "task" ? /* @__PURE__ */ jsxs5(Text6, { color: C.green, children: [
|
|
3818
|
-
g.task,
|
|
3819
|
-
" "
|
|
3820
|
-
] }) : null,
|
|
3821
|
-
/* @__PURE__ */ jsx8(Text6, { color: C.dim, children: oneLine(x.summary) })
|
|
3822
|
-
] }, i));
|
|
3823
|
-
})() })
|
|
3824
|
-
] }) : null
|
|
3825
|
-
] }) });
|
|
3826
|
-
}
|
|
3827
|
-
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0 }) {
|
|
3828
|
-
const { t, g } = useTui();
|
|
3829
|
-
const st = botState(s);
|
|
3830
|
-
const n = helperCount(s);
|
|
3831
|
-
return /* @__PURE__ */ jsxs5(Text6, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
3832
|
-
/* @__PURE__ */ jsx8(Text6, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
3833
|
-
/* @__PURE__ */ jsx8(Text6, { children: " ".repeat(2 + indent2) }),
|
|
3834
|
-
/* @__PURE__ */ jsxs5(Text6, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
3835
|
-
g[st],
|
|
3836
|
-
" "
|
|
3837
|
-
] }),
|
|
3838
|
-
/* @__PURE__ */ jsx8(Text6, { color: s.offline ? C.dim : C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
3839
|
-
s.role ? /* @__PURE__ */ jsxs5(Text6, { color: C.violet, children: [
|
|
3840
|
-
"@",
|
|
3841
|
-
s.role,
|
|
3842
|
-
" "
|
|
3843
|
-
] }) : null,
|
|
3844
|
-
/* @__PURE__ */ jsxs5(Text6, { color: C.dim, children: [
|
|
3845
|
-
fit(oneLine(s.topic ?? s.title), topicW),
|
|
3846
|
-
" "
|
|
3847
|
-
] }),
|
|
3848
|
-
/* @__PURE__ */ jsx8(Text6, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
3849
|
-
n > 0 ? /* @__PURE__ */ jsxs5(Text6, { color: C.violet, children: [
|
|
3850
|
-
" +",
|
|
3851
|
-
n
|
|
3852
|
-
] }) : null,
|
|
3853
|
-
s.machineName && s.machineId !== "local" ? /* @__PURE__ */ jsxs5(Text6, { color: C.faint, children: [
|
|
3854
|
-
" \xB7 ",
|
|
3855
|
-
machineLabel(s)
|
|
3856
|
-
] }) : null
|
|
3857
|
-
] });
|
|
3858
|
-
}
|
|
3859
|
-
|
|
3860
|
-
// src/tui/screens/Session.tsx
|
|
3861
|
-
import { useEffect as useEffect7, useMemo as useMemo3, useState as useState7 } from "react";
|
|
3862
|
-
import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
|
|
3863
|
-
|
|
3864
|
-
// src/tui/images.tsx
|
|
3865
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
3866
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
3867
|
-
import { join as join3 } from "path";
|
|
3868
|
-
import { useRef as useRef6, useState as useState4 } from "react";
|
|
3869
|
-
import { Text as Text7 } from "ink";
|
|
3870
|
-
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
3871
|
-
var counter2 = 0;
|
|
3872
|
-
var INLINE_BUDGET = MAX_FRAME - 64 * 1024;
|
|
3873
|
-
function useChips(put) {
|
|
3874
|
-
const { t, toast } = useTui();
|
|
3875
|
-
const [items, setItems] = useState4([]);
|
|
3876
|
-
const held = useRef6([]);
|
|
3877
|
-
const commit = (next2) => {
|
|
3878
|
-
held.current = next2;
|
|
3879
|
-
setItems(next2);
|
|
3880
|
-
};
|
|
3881
|
-
const patch = (key, over) => commit(held.current.map((c) => c.key === key ? { ...c, ...over } : c));
|
|
3882
|
-
const reasonOf = (e) => {
|
|
3883
|
-
const reason = e instanceof AttachmentFailure ? e.reason : null;
|
|
3884
|
-
return reason === "too_large" ? t.images.tooLarge : reason === "not_image" ? t.images.notImage : t.images.failed;
|
|
3885
|
-
};
|
|
3886
|
-
return {
|
|
3887
|
-
items,
|
|
3888
|
-
add({ image: image2, width, height, bytes }) {
|
|
3889
|
-
if (held.current.length >= ATTACHMENTS_MAX) {
|
|
3890
|
-
toast({ text: t.images.max, tone: "info" });
|
|
3891
|
-
return;
|
|
3892
|
-
}
|
|
3893
|
-
const key = counter2++;
|
|
3894
|
-
const inline2 = held.current.reduce((n, c) => n + (c.image?.data.length ?? 0), 0);
|
|
3895
|
-
if (bytes > ATTACHMENT_LOCAL_MAX || !put && inline2 + image2.data.length > INLINE_BUDGET) {
|
|
3896
|
-
commit([...held.current, { key, width, height, state: "refused", reason: t.images.tooLarge }]);
|
|
3897
|
-
return;
|
|
3898
|
-
}
|
|
3899
|
-
if (!put) {
|
|
3900
|
-
commit([...held.current, { key, width, height, state: "ready", image: image2 }]);
|
|
3901
|
-
return;
|
|
3902
|
-
}
|
|
3903
|
-
commit([...held.current, { key, width, height, state: "uploading" }]);
|
|
3904
|
-
put(image2).then((ref) => patch(key, { state: "ready", id: ref.id }), (e) => patch(key, { state: "refused", reason: reasonOf(e) }));
|
|
3905
|
-
},
|
|
3906
|
-
removeLast() {
|
|
3907
|
-
commit(held.current.slice(0, -1));
|
|
3908
|
-
},
|
|
3909
|
-
ready: items.every((c) => c.state === "ready"),
|
|
3910
|
-
ids: () => held.current.flatMap((c) => c.state === "ready" && c.id ? [c.id] : []),
|
|
3911
|
-
uploads: () => held.current.flatMap((c) => c.state === "ready" && c.image ? [c.image] : []),
|
|
3912
|
-
clear() {
|
|
3913
|
-
const taken = held.current;
|
|
3914
|
-
commit([]);
|
|
3915
|
-
return taken;
|
|
3916
|
-
},
|
|
3917
|
-
putBack(taken) {
|
|
3918
|
-
commit([...taken, ...held.current].slice(0, ATTACHMENTS_MAX));
|
|
3919
|
-
}
|
|
3920
|
-
};
|
|
3921
|
-
}
|
|
3922
|
-
function ChipsLine({ chips }) {
|
|
3923
|
-
const { t } = useTui();
|
|
3924
|
-
if (chips.length === 0) return null;
|
|
3925
|
-
return /* @__PURE__ */ jsx9(Text7, { wrap: "truncate", children: chips.map((c, i) => /* @__PURE__ */ jsxs6(Text7, { children: [
|
|
3926
|
-
i ? " " : "",
|
|
3927
|
-
/* @__PURE__ */ jsxs6(Text7, { color: c.state === "refused" ? C.red : C.dim, children: [
|
|
3928
|
-
"\u25A3 ",
|
|
3929
|
-
t.images.capture,
|
|
3930
|
-
" ",
|
|
3931
|
-
c.width,
|
|
3932
|
-
"\xD7",
|
|
3933
|
-
c.height,
|
|
3934
|
-
" ",
|
|
3935
|
-
c.state === "uploading" ? "\u2026" : "\u2715"
|
|
3936
|
-
] }),
|
|
3937
|
-
c.reason ? /* @__PURE__ */ jsxs6(Text7, { color: C.red, children: [
|
|
3938
|
-
" ",
|
|
3939
|
-
c.reason
|
|
3940
|
-
] }) : null
|
|
3941
|
-
] }, c.key)) });
|
|
3942
|
-
}
|
|
3943
|
-
var EXT = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" };
|
|
3944
|
-
function usePaste(chips) {
|
|
3945
|
-
const { readImage, toast, t } = useTui();
|
|
3946
|
-
const reading = useRef6(false);
|
|
3947
|
-
return () => {
|
|
3948
|
-
if (reading.current) return;
|
|
3949
|
-
reading.current = true;
|
|
3950
|
-
void readImage().then((got) => {
|
|
3951
|
-
if (got) chips.add(got);
|
|
3952
|
-
else toast({ text: t.images.none, tone: "info" });
|
|
3953
|
-
}, () => toast({ text: t.images.none, tone: "info" })).finally(() => {
|
|
3954
|
-
reading.current = false;
|
|
3955
|
-
});
|
|
3956
|
-
};
|
|
3957
|
-
}
|
|
3958
|
-
function useOpenImage() {
|
|
3959
|
-
const { client, openFile, toast, t } = useTui();
|
|
3960
|
-
const presses = useRef6(0);
|
|
3961
|
-
const last = useRef6("");
|
|
3962
|
-
return (items) => {
|
|
3963
|
-
const signature = items.map((x) => x.ref.id).join(",");
|
|
3964
|
-
if (signature !== last.current) {
|
|
3965
|
-
last.current = signature;
|
|
3966
|
-
presses.current = 0;
|
|
3967
|
-
}
|
|
3968
|
-
const item = items[items.length - 1 - presses.current++ % items.length];
|
|
3969
|
-
if (!item) return;
|
|
3970
|
-
const { ref, route } = item;
|
|
3971
|
-
void (async () => {
|
|
3972
|
-
const { mime, data } = await getImage(client, ref.id, route);
|
|
3973
|
-
const dir = join3(tmpdir2(), "ledge-images");
|
|
3974
|
-
await mkdir(dir, { recursive: true, mode: 448 });
|
|
3975
|
-
const file = join3(dir, `${ref.id}.${EXT[mime]}`);
|
|
3976
|
-
await writeFile(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
3977
|
-
await openFile(file);
|
|
3978
|
-
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
3979
|
-
};
|
|
3980
|
-
}
|
|
3981
|
-
|
|
3982
|
-
// ../ui/src/thread/turns.ts
|
|
3983
|
-
var OPEN_TURNS = 3;
|
|
3984
|
-
var byTime = (a, b2) => a.at < b2.at ? -1 : a.at > b2.at ? 1 : 0;
|
|
3985
|
-
function withoutEcho(items, reply) {
|
|
3986
|
-
if (!reply) return items;
|
|
3987
|
-
let last = -1;
|
|
3988
|
-
items.forEach((it, i) => {
|
|
3989
|
-
if (it.kind === "message") last = i;
|
|
3990
|
-
});
|
|
3991
|
-
const echo = items[last];
|
|
3992
|
-
return echo?.kind === "message" && echo.text.trim() === reply.text.trim() ? items.filter((_, i) => i !== last) : items;
|
|
3993
|
-
}
|
|
3994
|
-
function turns(items) {
|
|
3995
|
-
const map3 = /* @__PURE__ */ new Map();
|
|
3996
|
-
for (const it of items) {
|
|
3997
|
-
if (it.helperId) continue;
|
|
3998
|
-
const t = map3.get(it.turn) ?? { n: it.turn, items: [] };
|
|
3999
|
-
t.items.push(it);
|
|
4000
|
-
if (it.kind === "prompt") t.prompt = it;
|
|
4001
|
-
if (it.kind === "reply") t.reply = it;
|
|
4002
|
-
map3.set(it.turn, t);
|
|
4003
|
-
}
|
|
4004
|
-
return [...map3.values()].sort((a, b2) => a.n - b2.n).map((t) => ({ ...t, items: withoutEcho([...t.items].sort(byTime), t.reply) }));
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
// ../ui/src/thread/tools.ts
|
|
4008
|
-
function rawTarget(item) {
|
|
4009
|
-
let s = item.label.trim();
|
|
4010
|
-
const prefix = `${item.tool} \xB7 `;
|
|
4011
|
-
if (s.startsWith(prefix)) s = s.slice(prefix.length);
|
|
4012
|
-
if (s === item.tool) return "";
|
|
4013
|
-
if (s.startsWith("{")) {
|
|
4014
|
-
try {
|
|
4015
|
-
const o = JSON.parse(s);
|
|
4016
|
-
const v = ["pattern", "query", "url", "file_path", "path", "command", "description", "prompt"].map((k) => o[k]).find((x) => typeof x === "string" && x);
|
|
4017
|
-
if (typeof v === "string") s = v;
|
|
4018
|
-
} catch {
|
|
4019
|
-
}
|
|
4020
|
-
}
|
|
4021
|
-
return (s.split("\n")[0] ?? "").trim();
|
|
4022
|
-
}
|
|
4023
|
-
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4024
|
-
|
|
4025
|
-
// ../../node_modules/.pnpm/ccount@2.0.1/node_modules/ccount/index.js
|
|
4026
|
-
function ccount(value, character) {
|
|
4027
|
-
const source2 = String(value);
|
|
4028
|
-
if (typeof character !== "string") {
|
|
4029
|
-
throw new TypeError("Expected character");
|
|
4030
|
-
}
|
|
4031
|
-
let count = 0;
|
|
4032
|
-
let index2 = source2.indexOf(character);
|
|
4033
|
-
while (index2 !== -1) {
|
|
4034
|
-
count++;
|
|
4035
|
-
index2 = source2.indexOf(character, index2 + character.length);
|
|
4036
|
-
}
|
|
4037
|
-
return count;
|
|
4038
|
-
}
|
|
4039
|
-
|
|
4040
|
-
// ../../node_modules/.pnpm/devlop@1.1.0/node_modules/devlop/lib/default.js
|
|
4041
|
-
function ok() {
|
|
4042
|
-
}
|
|
4043
|
-
|
|
4044
|
-
// ../../node_modules/.pnpm/micromark-util-character@2.1.1/node_modules/micromark-util-character/index.js
|
|
4045
|
-
var asciiAlpha = regexCheck(/[A-Za-z]/);
|
|
4046
|
-
var asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);
|
|
4047
|
-
var asciiAtext = regexCheck(/[#-'*+\--9=?A-Z^-~]/);
|
|
4048
|
-
function asciiControl(code3) {
|
|
4049
|
-
return (
|
|
4050
|
-
// Special whitespace codes (which have negative values), C0 and Control
|
|
4051
|
-
// character DEL
|
|
4052
|
-
code3 !== null && (code3 < 32 || code3 === 127)
|
|
4053
|
-
);
|
|
4054
|
-
}
|
|
4055
|
-
var asciiDigit = regexCheck(/\d/);
|
|
4056
|
-
var asciiHexDigit = regexCheck(/[\dA-Fa-f]/);
|
|
4057
|
-
var asciiPunctuation = regexCheck(/[!-/:-@[-`{-~]/);
|
|
4058
|
-
function markdownLineEnding(code3) {
|
|
4059
|
-
return code3 !== null && code3 < -2;
|
|
4060
|
-
}
|
|
4061
|
-
function markdownLineEndingOrSpace(code3) {
|
|
4062
|
-
return code3 !== null && (code3 < 0 || code3 === 32);
|
|
4063
|
-
}
|
|
4064
|
-
function markdownSpace(code3) {
|
|
4065
|
-
return code3 === -2 || code3 === -1 || code3 === 32;
|
|
4066
|
-
}
|
|
4067
|
-
var unicodePunctuation = regexCheck(new RegExp("\\p{P}|\\p{S}", "u"));
|
|
4068
|
-
var unicodeWhitespace = regexCheck(/\s/);
|
|
4069
|
-
function regexCheck(regex) {
|
|
4070
|
-
return check;
|
|
4071
|
-
function check(code3) {
|
|
4072
|
-
return code3 !== null && code3 > -1 && regex.test(String.fromCharCode(code3));
|
|
4073
|
-
}
|
|
4074
|
-
}
|
|
4075
|
-
|
|
4076
|
-
// ../../node_modules/.pnpm/escape-string-regexp@5.0.0/node_modules/escape-string-regexp/index.js
|
|
4077
|
-
function escapeStringRegexp(string3) {
|
|
4078
|
-
if (typeof string3 !== "string") {
|
|
4079
|
-
throw new TypeError("Expected a string");
|
|
4080
|
-
}
|
|
4081
|
-
return string3.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
3408
|
+
// ../../node_modules/.pnpm/escape-string-regexp@5.0.0/node_modules/escape-string-regexp/index.js
|
|
3409
|
+
function escapeStringRegexp(string3) {
|
|
3410
|
+
if (typeof string3 !== "string") {
|
|
3411
|
+
throw new TypeError("Expected a string");
|
|
3412
|
+
}
|
|
3413
|
+
return string3.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
4082
3414
|
}
|
|
4083
3415
|
|
|
4084
3416
|
// ../../node_modules/.pnpm/unist-util-is@6.0.1/node_modules/unist-util-is/lib/index.js
|
|
@@ -15927,6 +15259,9 @@ function imageLine(ref, t, key) {
|
|
|
15927
15259
|
var DIFF_MAX = 30;
|
|
15928
15260
|
var CAP = { tool: 3, error: 3, message: 3, notice: 2, permission: 3, helper: 2 };
|
|
15929
15261
|
var LIVE_PAD = 9;
|
|
15262
|
+
function wrap2(text5, width) {
|
|
15263
|
+
return wrapSegs([{ text: text5.replace(/\r/g, "") }], Math.max(10, width)).map((l) => l.map((s) => s.text).join(""));
|
|
15264
|
+
}
|
|
15930
15265
|
function diffCount(diff) {
|
|
15931
15266
|
let add = 0, del2 = 0;
|
|
15932
15267
|
for (const l of (diff ?? "").split("\n")) {
|
|
@@ -16018,399 +15353,1160 @@ function foldedLine(turn, o) {
|
|
|
16018
15353
|
const parts = [turn.prompt ? oneLine(turn.prompt.text) : "", o.t.session.actions(tools.length), ...files.size ? [o.t.session.files(files.size)] : [], ...turn.reply ? [oneLine(turn.reply.text)] : []].filter(Boolean);
|
|
16019
15354
|
return [{ text: `${o.g.folded} ${clock(turn.prompt?.at ?? turn.items[0]?.at ?? (/* @__PURE__ */ new Date(0)).toISOString())} `, color: C.faint }, { text: parts.join(" \xB7 "), color: C.faint }];
|
|
16020
15355
|
}
|
|
16021
|
-
function threadView(items, o) {
|
|
16022
|
-
const all2 = turns(items);
|
|
16023
|
-
const lines = [], keys = [], turnOf = /* @__PURE__ */ new Map();
|
|
16024
|
-
const opts = { ...o, cap: (n) => o.unfolded ? Infinity : n };
|
|
16025
|
-
const push2 = (key, l) => {
|
|
16026
|
-
for (const x of l) {
|
|
16027
|
-
lines.push(x);
|
|
16028
|
-
keys.push(key);
|
|
15356
|
+
function threadView(items, o) {
|
|
15357
|
+
const all2 = turns(items);
|
|
15358
|
+
const lines = [], keys = [], turnOf = /* @__PURE__ */ new Map();
|
|
15359
|
+
const opts = { ...o, cap: (n) => o.unfolded ? Infinity : n };
|
|
15360
|
+
const push2 = (key, l) => {
|
|
15361
|
+
for (const x of l) {
|
|
15362
|
+
lines.push(x);
|
|
15363
|
+
keys.push(key);
|
|
15364
|
+
}
|
|
15365
|
+
};
|
|
15366
|
+
all2.forEach((turn, i) => {
|
|
15367
|
+
const recent = i >= all2.length - OPEN_TURNS;
|
|
15368
|
+
const first = turn.prompt?.id ?? turn.items[0]?.id ?? String(i);
|
|
15369
|
+
for (const it of turn.items) turnOf.set(it.id, first);
|
|
15370
|
+
if (!recent && !o.unfolded) {
|
|
15371
|
+
push2(first, [cut(foldedLine(turn, o), o.width)]);
|
|
15372
|
+
return;
|
|
15373
|
+
}
|
|
15374
|
+
if (lines.length) push2(first, [[{ text: "" }]]);
|
|
15375
|
+
for (const it of turn.items) push2(it.id, itemLines(it, opts));
|
|
15376
|
+
});
|
|
15377
|
+
return { lines, keys, turns: turnOf };
|
|
15378
|
+
}
|
|
15379
|
+
function anchorLine(v, anchor) {
|
|
15380
|
+
const at = v.keys.indexOf(anchor.key);
|
|
15381
|
+
if (at >= 0) return at + anchor.offset;
|
|
15382
|
+
const turn = v.turns.get(anchor.key);
|
|
15383
|
+
return turn === void 0 ? -1 : v.keys.indexOf(turn);
|
|
15384
|
+
}
|
|
15385
|
+
function threadImages(items, unfolded) {
|
|
15386
|
+
const all2 = turns(items);
|
|
15387
|
+
return (unfolded ? all2 : all2.slice(-OPEN_TURNS)).flatMap((turn) => turn.items.flatMap((it) => it.kind === "prompt" ? it.images ?? [] : []));
|
|
15388
|
+
}
|
|
15389
|
+
|
|
15390
|
+
// src/tui/model/labels.ts
|
|
15391
|
+
var TOOL_NAME = { claude: "Claude Code", codex: "Codex", cursor: "Cursor Agent", omp: "OMP" };
|
|
15392
|
+
var modelLabel = (s, fallback) => [s.model ?? (s.effort ? fallback : ""), s.effort ?? ""].filter(Boolean).join(" \xB7 ") || fallback;
|
|
15393
|
+
var machineLabel = (s) => s.machineName ?? splitId(s.id)?.machine ?? "Local";
|
|
15394
|
+
var paneName = (s) => s.tmuxSession ?? `ledge-${splitId(s.id)?.id ?? s.id}`;
|
|
15395
|
+
function fit(s, n) {
|
|
15396
|
+
const cps = [...s];
|
|
15397
|
+
if (n <= 0) return "";
|
|
15398
|
+
return cps.length > n ? `${cps.slice(0, Math.max(0, n - 1)).join("")}\u2026` : s + " ".repeat(n - cps.length);
|
|
15399
|
+
}
|
|
15400
|
+
|
|
15401
|
+
// src/tui/ui/now.ts
|
|
15402
|
+
import { useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
15403
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
15404
|
+
var now = Date.now();
|
|
15405
|
+
var timer = null;
|
|
15406
|
+
function subscribe(fn) {
|
|
15407
|
+
listeners.add(fn);
|
|
15408
|
+
if (!timer) {
|
|
15409
|
+
now = Date.now();
|
|
15410
|
+
timer = setInterval(() => {
|
|
15411
|
+
now = Date.now();
|
|
15412
|
+
for (const l of listeners) l();
|
|
15413
|
+
}, 1e3);
|
|
15414
|
+
timer.unref?.();
|
|
15415
|
+
}
|
|
15416
|
+
return () => {
|
|
15417
|
+
listeners.delete(fn);
|
|
15418
|
+
if (listeners.size === 0 && timer) {
|
|
15419
|
+
clearInterval(timer);
|
|
15420
|
+
timer = null;
|
|
15421
|
+
}
|
|
15422
|
+
};
|
|
15423
|
+
}
|
|
15424
|
+
var useNow = () => useSyncExternalStore4(subscribe, () => now);
|
|
15425
|
+
|
|
15426
|
+
// src/tui/ui/Ago.tsx
|
|
15427
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
15428
|
+
function Ago({ iso }) {
|
|
15429
|
+
const now2 = useNow();
|
|
15430
|
+
const { t } = useI18n();
|
|
15431
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: ago(iso, now2, t.time) });
|
|
15432
|
+
}
|
|
15433
|
+
|
|
15434
|
+
// src/tui/ui/Field.tsx
|
|
15435
|
+
import { useReducer, useRef as useRef6 } from "react";
|
|
15436
|
+
import { Box as Box3, usePaste, useInput } from "ink";
|
|
15437
|
+
|
|
15438
|
+
// src/tui/model/field.ts
|
|
15439
|
+
var graphemes2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
15440
|
+
var stops = (text5) => [...Array.from(graphemes2.segment(text5), (g) => g.index), text5.length];
|
|
15441
|
+
var newlines = (s) => s.replace(/\r\n?/g, "\n");
|
|
15442
|
+
var insert = (f, s) => {
|
|
15443
|
+
const add = newlines(s);
|
|
15444
|
+
return { text: f.text.slice(0, f.cursor) + add + f.text.slice(f.cursor), cursor: f.cursor + add.length };
|
|
15445
|
+
};
|
|
15446
|
+
var before = (f) => {
|
|
15447
|
+
const a = stops(f.text);
|
|
15448
|
+
let i = a.length - 1;
|
|
15449
|
+
while (i > 0 && a[i] >= f.cursor) i--;
|
|
15450
|
+
return a[i] < f.cursor ? a[i] : 0;
|
|
15451
|
+
};
|
|
15452
|
+
var after = (f) => stops(f.text).find((x) => x > f.cursor) ?? f.text.length;
|
|
15453
|
+
var left = (f) => ({ ...f, cursor: before(f) });
|
|
15454
|
+
var right = (f) => ({ ...f, cursor: after(f) });
|
|
15455
|
+
var back = (f) => {
|
|
15456
|
+
const to = before(f);
|
|
15457
|
+
return { text: f.text.slice(0, to) + f.text.slice(f.cursor), cursor: to };
|
|
15458
|
+
};
|
|
15459
|
+
var del = (f) => ({ text: f.text.slice(0, f.cursor) + f.text.slice(after(f)), cursor: f.cursor });
|
|
15460
|
+
function wordStart(f) {
|
|
15461
|
+
let i = f.cursor;
|
|
15462
|
+
while (i > 0 && /\s/.test(f.text[i - 1])) i--;
|
|
15463
|
+
while (i > 0 && !/\s/.test(f.text[i - 1])) i--;
|
|
15464
|
+
return i;
|
|
15465
|
+
}
|
|
15466
|
+
var wordLeft = (f) => ({ ...f, cursor: wordStart(f) });
|
|
15467
|
+
function wordRight(f) {
|
|
15468
|
+
let i = f.cursor;
|
|
15469
|
+
while (i < f.text.length && /\s/.test(f.text[i])) i++;
|
|
15470
|
+
while (i < f.text.length && !/\s/.test(f.text[i])) i++;
|
|
15471
|
+
return { ...f, cursor: i };
|
|
15472
|
+
}
|
|
15473
|
+
var killWord = (f) => {
|
|
15474
|
+
const to = wordStart(f);
|
|
15475
|
+
return { text: f.text.slice(0, to) + f.text.slice(f.cursor), cursor: to };
|
|
15476
|
+
};
|
|
15477
|
+
var home = (f) => ({ ...f, cursor: f.text.lastIndexOf("\n", f.cursor - 1) + 1 });
|
|
15478
|
+
var end = (f) => {
|
|
15479
|
+
const n = f.text.indexOf("\n", f.cursor);
|
|
15480
|
+
return { ...f, cursor: n < 0 ? f.text.length : n };
|
|
15481
|
+
};
|
|
15482
|
+
function layout(text5, width) {
|
|
15483
|
+
const w = Math.max(2, width - 1);
|
|
15484
|
+
const a = stops(text5), rows = [];
|
|
15485
|
+
let start = 0;
|
|
15486
|
+
for (let i = 0; i < a.length; i++) {
|
|
15487
|
+
const at = a[i];
|
|
15488
|
+
if (at < text5.length && text5[at] !== "\n") continue;
|
|
15489
|
+
if (start === i) rows.push({ a: at, b: at, last: true });
|
|
15490
|
+
for (let s = start; s < i; ) {
|
|
15491
|
+
let n = 0, j = s, space = -1;
|
|
15492
|
+
while (j < i) {
|
|
15493
|
+
const c = cells(text5.slice(a[j], a[j + 1]));
|
|
15494
|
+
if (n + c > w) break;
|
|
15495
|
+
if (text5[a[j]] === " ") space = j;
|
|
15496
|
+
n += c;
|
|
15497
|
+
j++;
|
|
15498
|
+
}
|
|
15499
|
+
if (j === s) j = s + 1;
|
|
15500
|
+
const e = j < i && space > s ? space + 1 : j;
|
|
15501
|
+
rows.push({ a: a[s], b: a[e], last: e >= i });
|
|
15502
|
+
s = e;
|
|
15503
|
+
}
|
|
15504
|
+
start = i + 1;
|
|
15505
|
+
}
|
|
15506
|
+
return rows;
|
|
15507
|
+
}
|
|
15508
|
+
var rowOf = (rows, cursor) => {
|
|
15509
|
+
const k = rows.findIndex((r) => cursor >= r.a && (cursor < r.b || cursor === r.b && r.last));
|
|
15510
|
+
return k < 0 ? rows.length - 1 : k;
|
|
15511
|
+
};
|
|
15512
|
+
function vertical(f, width, by) {
|
|
15513
|
+
const rows = layout(f.text, width), k = rowOf(rows, f.cursor), to = rows[k + by];
|
|
15514
|
+
if (!to) return null;
|
|
15515
|
+
const col = cells(f.text.slice(rows[k].a, f.cursor));
|
|
15516
|
+
let cursor = to.a;
|
|
15517
|
+
for (const x of stops(f.text)) {
|
|
15518
|
+
if (x <= to.a) continue;
|
|
15519
|
+
if (x > to.b || x === to.b && !to.last || cells(f.text.slice(to.a, x)) > col) break;
|
|
15520
|
+
cursor = x;
|
|
15521
|
+
}
|
|
15522
|
+
return { ...f, cursor };
|
|
15523
|
+
}
|
|
15524
|
+
function fieldLines(text5, cursor, width, o) {
|
|
15525
|
+
const indent2 = { text: " ".repeat(cells(o.prefix.text)) };
|
|
15526
|
+
const inv = (t) => ({ text: t, color: o.color, inverse: true });
|
|
15527
|
+
if (!text5) return [[o.prefix, ...cursor !== null ? [inv(" ")] : [], ...o.placeholder ? [o.placeholder] : []]];
|
|
15528
|
+
const rows = layout(text5, width - cells(o.prefix.text));
|
|
15529
|
+
return rows.map((r, k) => {
|
|
15530
|
+
const line = [k === 0 ? o.prefix : indent2];
|
|
15531
|
+
const at = cursor !== null && cursor >= r.a && cursor < r.b ? cursor : -1;
|
|
15532
|
+
if (at < 0) line.push({ text: text5.slice(r.a, r.b), color: o.color });
|
|
15533
|
+
else {
|
|
15534
|
+
const next2 = after({ text: text5, cursor: at });
|
|
15535
|
+
if (at > r.a) line.push({ text: text5.slice(r.a, at), color: o.color });
|
|
15536
|
+
line.push(inv(text5.slice(at, Math.min(next2, r.b))));
|
|
15537
|
+
if (next2 < r.b) line.push({ text: text5.slice(next2, r.b), color: o.color });
|
|
15538
|
+
}
|
|
15539
|
+
if (cursor === r.b && r.last) line.push(inv(" "));
|
|
15540
|
+
return line.filter((s) => s.text !== "" || s === line[0]);
|
|
15541
|
+
});
|
|
15542
|
+
}
|
|
15543
|
+
var scrollTop = (top, row2, total, max) => Math.max(0, Math.min(Math.max(0, total - max), row2 < top ? row2 : row2 >= top + max ? row2 - max + 1 : top));
|
|
15544
|
+
var lineAt = (f) => ({
|
|
15545
|
+
line: f.text.slice(0, f.cursor).split("\n").length,
|
|
15546
|
+
lines: f.text.split("\n").length
|
|
15547
|
+
});
|
|
15548
|
+
|
|
15549
|
+
// src/tui/ui/Lines.tsx
|
|
15550
|
+
import { memo, useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
15551
|
+
import { Box, Text, useBoxMetrics } from "ink";
|
|
15552
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
15553
|
+
function Live({ seg }) {
|
|
15554
|
+
const now2 = useNow();
|
|
15555
|
+
const { t } = useI18n();
|
|
15556
|
+
const l = seg.live;
|
|
15557
|
+
return /* @__PURE__ */ jsx3(Text, { color: seg.color, bold: seg.bold, children: "spin" in l ? spin(now2) : took(Math.max(0, now2 - l.since), t.time.decimal).padStart(l.pad) });
|
|
15558
|
+
}
|
|
15559
|
+
var linked = (text5, url) => `\x1B]8;;${url.replace(/[\x00-\x1f\x7f]/g, "")}\x07${text5}\x1B]8;;\x07`;
|
|
15560
|
+
var LineText = memo(function LineText2({ line }) {
|
|
15561
|
+
return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
|
|
15562
|
+
line.map((x, k) => x.live ? /* @__PURE__ */ jsx3(Live, { seg: x }, k) : /* @__PURE__ */ jsx3(Text, { color: x.color, backgroundColor: x.bg, bold: x.bold, italic: x.italic, underline: x.underline, strikethrough: x.strike, inverse: x.inverse, children: x.link && x.text ? linked(x.text, x.link) : x.text || (k === 0 ? " " : "") }, k)),
|
|
15563
|
+
line.length === 0 ? " " : null
|
|
15564
|
+
] });
|
|
15565
|
+
});
|
|
15566
|
+
function Lines({ lines, up = 0, onHeight }) {
|
|
15567
|
+
const ref = useRef4(null);
|
|
15568
|
+
const { height } = useBoxMetrics(ref);
|
|
15569
|
+
useEffect5(() => {
|
|
15570
|
+
if (height > 0) onHeight?.(height);
|
|
15571
|
+
}, [height, onHeight]);
|
|
15572
|
+
const h = Math.max(1, height);
|
|
15573
|
+
const back2 = Math.min(up, Math.max(0, lines.length - h));
|
|
15574
|
+
const shown = lines.slice(Math.max(0, lines.length - h - back2), lines.length - back2);
|
|
15575
|
+
return /* @__PURE__ */ jsx3(Box, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: shown.map((l, i) => /* @__PURE__ */ jsx3(Box, { flexShrink: 0, children: /* @__PURE__ */ jsx3(LineText, { line: l }) }, i)) });
|
|
15576
|
+
}
|
|
15577
|
+
|
|
15578
|
+
// src/tui/ui/TitledBox.tsx
|
|
15579
|
+
import { useRef as useRef5 } from "react";
|
|
15580
|
+
import { Box as Box2, Text as Text2, useBoxMetrics as useBoxMetrics2 } from "ink";
|
|
15581
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
15582
|
+
function TitledBox({ title: title2, mark, color: color2 = C.line, titleColor = C.dim, titleBold, double, foot, footColor = C.faint, children, flexGrow, height, width, flexShrink, minHeight }) {
|
|
15583
|
+
const b2 = double ? { tl: "\u2554", h: "\u2550", tr: "\u2557", bl: "\u255A", br: "\u255D" } : { tl: "\u256D", h: "\u2500", tr: "\u256E", bl: "\u2570", br: "\u256F" };
|
|
15584
|
+
const ref = useRef5(null);
|
|
15585
|
+
const { width: w } = useBoxMetrics2(ref);
|
|
15586
|
+
const text5 = (l) => l.map((s) => s.text).join("");
|
|
15587
|
+
const tail = mark ? [{ text: ` ${mark.text.toUpperCase()}`, color: mark.color }] : [];
|
|
15588
|
+
const room = Math.max(0, w - 4 - (cells(text5(tail)) < w - 8 ? cells(text5(tail)) : 0));
|
|
15589
|
+
const head = [{ text: ` ${title2.toUpperCase()}`, color: titleColor }];
|
|
15590
|
+
const shown = cells(text5([...head, ...tail])) > w - 4 ? [...cut(head, room), ...cells(text5(tail)) < w - 8 ? tail : [], { text: " ", color: titleColor }] : [...head, ...tail, { text: " ", color: titleColor }];
|
|
15591
|
+
const rest = Math.max(0, w - 3 - cells(text5(shown)));
|
|
15592
|
+
const f = foot ? text5(cut([{ text: foot }], Math.max(0, w - 6))) : "";
|
|
15593
|
+
return /* @__PURE__ */ jsxs2(Box2, { ref, flexDirection: "column", flexGrow, flexShrink, height, width, minHeight, overflow: "hidden", children: [
|
|
15594
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
15595
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15596
|
+
b2.tl,
|
|
15597
|
+
b2.h
|
|
15598
|
+
] }),
|
|
15599
|
+
w > 0 ? shown.map((s, i) => /* @__PURE__ */ jsx4(Text2, { color: s.color, bold: titleBold, children: s.text }, i)) : null,
|
|
15600
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15601
|
+
b2.h.repeat(rest),
|
|
15602
|
+
b2.tr
|
|
15603
|
+
] })
|
|
15604
|
+
] }),
|
|
15605
|
+
/* @__PURE__ */ jsx4(Box2, { flexDirection: "column", borderStyle: double ? "double" : "round", borderTop: false, borderBottom: !f, borderColor: color2, paddingX: 1, flexGrow: 1, overflow: "hidden", children }),
|
|
15606
|
+
f ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
15607
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15608
|
+
b2.bl,
|
|
15609
|
+
b2.h.repeat(Math.max(1, w - 5 - cells(f))),
|
|
15610
|
+
" "
|
|
15611
|
+
] }),
|
|
15612
|
+
/* @__PURE__ */ jsx4(Text2, { color: footColor, children: f }),
|
|
15613
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15614
|
+
" ",
|
|
15615
|
+
b2.h,
|
|
15616
|
+
b2.br
|
|
15617
|
+
] })
|
|
15618
|
+
] }) : null
|
|
15619
|
+
] });
|
|
15620
|
+
}
|
|
15621
|
+
|
|
15622
|
+
// src/tui/ui/Field.tsx
|
|
15623
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
15624
|
+
var fieldRows = (columns, rows) => rows < 24 ? 3 : columns >= 120 && rows >= 32 ? 8 : 6;
|
|
15625
|
+
var inHistory = (b2) => b2 !== null && b2.i < b2.items.length - 1;
|
|
15626
|
+
function Field({ value, onChange, onSubmit, onEscape, onBrowse, history = [], active, placeholder, title: title2, color: color2 = C.coral, titleColor }) {
|
|
15627
|
+
const { t, g } = useTui();
|
|
15628
|
+
const { columns, rows: height } = useScreenSize();
|
|
15629
|
+
const st = useRef6({ f: { text: value, cursor: value.length }, browse: null, pasted: 0 });
|
|
15630
|
+
const told = useRef6(value);
|
|
15631
|
+
if (value !== told.current) {
|
|
15632
|
+
told.current = value;
|
|
15633
|
+
st.current = { f: { text: value, cursor: value.length }, browse: null, pasted: 0 };
|
|
15634
|
+
}
|
|
15635
|
+
const [, render2] = useReducer((n) => n + 1, 0);
|
|
15636
|
+
const top = useRef6(0);
|
|
15637
|
+
const width = Math.max(10, columns - 4);
|
|
15638
|
+
const max = fieldRows(columns, height);
|
|
15639
|
+
const set = (f2, o = {}) => {
|
|
15640
|
+
const cur = st.current, changed = f2.text !== cur.f.text;
|
|
15641
|
+
let browse2 = cur.browse;
|
|
15642
|
+
if (browse2 && changed) {
|
|
15643
|
+
const items = [...browse2.items];
|
|
15644
|
+
items[browse2.i] = f2.text;
|
|
15645
|
+
browse2 = { ...browse2, items };
|
|
15646
|
+
}
|
|
15647
|
+
st.current = { f: f2, browse: browse2, pasted: o.paste ?? 0 };
|
|
15648
|
+
render2();
|
|
15649
|
+
if (changed && !inHistory(browse2)) {
|
|
15650
|
+
told.current = f2.text;
|
|
15651
|
+
onChange(f2.text);
|
|
16029
15652
|
}
|
|
16030
15653
|
};
|
|
16031
|
-
|
|
16032
|
-
const
|
|
16033
|
-
const
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
15654
|
+
const show = (b2, i) => {
|
|
15655
|
+
const next2 = b2 && i !== void 0 ? { ...b2, i } : b2;
|
|
15656
|
+
const text5 = next2 ? next2.items[next2.i] : st.current.f.text;
|
|
15657
|
+
st.current = { f: { text: text5, cursor: text5.length }, browse: next2, pasted: 0 };
|
|
15658
|
+
render2();
|
|
15659
|
+
onBrowse?.(inHistory(next2));
|
|
15660
|
+
};
|
|
15661
|
+
useInput((input, key) => {
|
|
15662
|
+
const { f: f2, browse: browse2 } = st.current;
|
|
15663
|
+
if (key.return && !key.meta && !key.shift) {
|
|
15664
|
+
const fromHistory = inHistory(browse2);
|
|
15665
|
+
if (onSubmit(f2.text, { fromHistory }) === false) return;
|
|
15666
|
+
if (fromHistory) show(browse2, browse2.items.length - 1);
|
|
15667
|
+
if (browse2) show(null);
|
|
16037
15668
|
return;
|
|
16038
15669
|
}
|
|
16039
|
-
if (
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
15670
|
+
if (key.return || input === "\n" || key.ctrl && input === "j") {
|
|
15671
|
+
set(insert(f2, "\n"));
|
|
15672
|
+
return;
|
|
15673
|
+
}
|
|
15674
|
+
if (key.escape) {
|
|
15675
|
+
if (inHistory(browse2)) {
|
|
15676
|
+
show(browse2, browse2.items.length - 1);
|
|
15677
|
+
show(null);
|
|
15678
|
+
return;
|
|
15679
|
+
}
|
|
15680
|
+
if (browse2) show(null);
|
|
15681
|
+
onEscape?.();
|
|
15682
|
+
return;
|
|
15683
|
+
}
|
|
15684
|
+
if (key.backspace) {
|
|
15685
|
+
set(key.meta ? killWord(f2) : back(f2));
|
|
15686
|
+
return;
|
|
15687
|
+
}
|
|
15688
|
+
if (key.delete) {
|
|
15689
|
+
set(del(f2));
|
|
15690
|
+
return;
|
|
15691
|
+
}
|
|
15692
|
+
if (key.ctrl && input === "u") {
|
|
15693
|
+
set({ text: "", cursor: 0 });
|
|
15694
|
+
return;
|
|
15695
|
+
}
|
|
15696
|
+
if (key.ctrl && input === "w") {
|
|
15697
|
+
set(killWord(f2));
|
|
15698
|
+
return;
|
|
15699
|
+
}
|
|
15700
|
+
if (key.ctrl && input === "a" || key.home) {
|
|
15701
|
+
set(home(f2));
|
|
15702
|
+
return;
|
|
15703
|
+
}
|
|
15704
|
+
if (key.ctrl && input === "e" || key.end) {
|
|
15705
|
+
set(end(f2));
|
|
15706
|
+
return;
|
|
15707
|
+
}
|
|
15708
|
+
if (key.leftArrow || key.meta && input === "b") {
|
|
15709
|
+
set(key.ctrl || key.meta ? wordLeft(f2) : left(f2));
|
|
15710
|
+
return;
|
|
15711
|
+
}
|
|
15712
|
+
if (key.rightArrow || key.meta && input === "f") {
|
|
15713
|
+
set(key.ctrl || key.meta ? wordRight(f2) : right(f2));
|
|
15714
|
+
return;
|
|
15715
|
+
}
|
|
15716
|
+
if (key.upArrow) {
|
|
15717
|
+
const up = vertical(f2, width - 2, -1);
|
|
15718
|
+
if (up) {
|
|
15719
|
+
set(up);
|
|
15720
|
+
return;
|
|
15721
|
+
}
|
|
15722
|
+
const b2 = browse2 ?? (history.length ? { items: [...history, f2.text], i: history.length } : null);
|
|
15723
|
+
if (b2 && b2.i > 0) show(b2, b2.i - 1);
|
|
15724
|
+
return;
|
|
15725
|
+
}
|
|
15726
|
+
if (key.downArrow) {
|
|
15727
|
+
const down = vertical(f2, width - 2, 1);
|
|
15728
|
+
if (down) {
|
|
15729
|
+
set(down);
|
|
15730
|
+
return;
|
|
15731
|
+
}
|
|
15732
|
+
if (inHistory(browse2)) show(browse2, browse2.i + 1);
|
|
15733
|
+
return;
|
|
15734
|
+
}
|
|
15735
|
+
if (key.ctrl || key.meta || key.tab || key.pageUp || key.pageDown) return;
|
|
15736
|
+
if (input) {
|
|
15737
|
+
const add = newlines(input);
|
|
15738
|
+
set(insert(f2, add), { paste: add.includes("\n") ? add.split("\n").length : 0 });
|
|
15739
|
+
}
|
|
15740
|
+
}, { isActive: active });
|
|
15741
|
+
usePaste((text5) => {
|
|
15742
|
+
const add = newlines(text5);
|
|
15743
|
+
set(insert(st.current.f, add), { paste: add.includes("\n") ? add.split("\n").length : 0 });
|
|
15744
|
+
}, { isActive: active });
|
|
15745
|
+
const { f, browse, pasted } = st.current;
|
|
15746
|
+
const prefix = { text: `${g.cursor} `, color: C.coral };
|
|
15747
|
+
const lines = fieldLines(f.text, active ? f.cursor : null, width, { prefix, color: C.fg, ...placeholder ? { placeholder: { text: f.text ? "" : ` ${placeholder}`, color: C.faint } } : {} });
|
|
15748
|
+
const row2 = rowOf(layout(f.text, width - 2), f.cursor);
|
|
15749
|
+
top.current = scrollTop(top.current, row2, lines.length, max);
|
|
15750
|
+
const shown = lines.slice(top.current, top.current + max);
|
|
15751
|
+
const below = lines.length - top.current - shown.length;
|
|
15752
|
+
const where = lineAt(f);
|
|
15753
|
+
const foot = inHistory(browse) ? t.field.history(browse.i + 1, browse.items.length - 1) : [
|
|
15754
|
+
top.current > 0 ? t.field.above(top.current) : "",
|
|
15755
|
+
below > 0 ? t.field.below(below) : "",
|
|
15756
|
+
pasted > 1 ? t.field.pasted(pasted) : active && where.lines > 1 ? t.field.line(where.line, where.lines) : ""
|
|
15757
|
+
].filter(Boolean).join(" \xB7 ");
|
|
15758
|
+
return /* @__PURE__ */ jsx5(TitledBox, { title: title2, color: active ? color2 : C.line, titleColor: titleColor ?? (active ? color2 : C.dim), foot: foot || void 0, flexShrink: 0, children: shown.map((l, i) => /* @__PURE__ */ jsx5(Box3, { flexShrink: 0, children: /* @__PURE__ */ jsx5(LineText, { line: l }) }, i)) });
|
|
16043
15759
|
}
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
15760
|
+
|
|
15761
|
+
// src/tui/ui/Frame.tsx
|
|
15762
|
+
import { Box as Box5, Text as Text4 } from "ink";
|
|
15763
|
+
|
|
15764
|
+
// src/tui/ui/KeyBar.tsx
|
|
15765
|
+
import { Box as Box4, Text as Text3 } from "ink";
|
|
15766
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
15767
|
+
var itemCells = ([k, label]) => cells(` ${k} `) + cells(` ${label}`);
|
|
15768
|
+
function fitKeys(keys, width, help) {
|
|
15769
|
+
const list3 = keys.filter((k) => k[0] !== help?.[0]);
|
|
15770
|
+
const fits = (l) => 1 + l.reduce((n, x, i) => n + itemCells(x) + (i ? 2 : 0), 0) <= width - 1;
|
|
15771
|
+
const tail = help ? [help] : [];
|
|
15772
|
+
while (list3.length && !fits([...list3, ...tail])) list3.pop();
|
|
15773
|
+
return [...list3, ...tail];
|
|
16049
15774
|
}
|
|
16050
|
-
function
|
|
16051
|
-
|
|
16052
|
-
|
|
15775
|
+
function KeyBar({ keys }) {
|
|
15776
|
+
return /* @__PURE__ */ jsx6(Box4, { paddingX: 1, columnGap: 2, height: 1, overflow: "hidden", children: keys.map(([k, label]) => /* @__PURE__ */ jsx6(Box4, { flexShrink: 0, children: /* @__PURE__ */ jsxs3(Text3, { wrap: "truncate", children: [
|
|
15777
|
+
/* @__PURE__ */ jsxs3(Text3, { color: C.fg, backgroundColor: C.key, children: [
|
|
15778
|
+
" ",
|
|
15779
|
+
k,
|
|
15780
|
+
" "
|
|
15781
|
+
] }),
|
|
15782
|
+
/* @__PURE__ */ jsxs3(Text3, { color: C.dim, children: [
|
|
15783
|
+
" ",
|
|
15784
|
+
label
|
|
15785
|
+
] })
|
|
15786
|
+
] }) }, k)) });
|
|
15787
|
+
}
|
|
15788
|
+
|
|
15789
|
+
// src/tui/ui/Frame.tsx
|
|
15790
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
15791
|
+
var MIN = { columns: 60, rows: 16 };
|
|
15792
|
+
function Frame({ crumb, crumbShort, keys, kept, children }) {
|
|
15793
|
+
const { d, machines, t, g, lastToast, editing, client } = useTui();
|
|
15794
|
+
const { rows, columns } = useScreenSize();
|
|
15795
|
+
const live = listed(d.sessions);
|
|
15796
|
+
const pending = live.filter(isPending);
|
|
15797
|
+
const waiting = pending.length + openDecisions(useTasks(client)).length;
|
|
15798
|
+
const status = d.status === "open" ? null : d.status === "connecting" ? t.bar.connecting : t.bar.unreachable;
|
|
15799
|
+
if (columns < MIN.columns || rows < MIN.rows) {
|
|
15800
|
+
const why = [...pending[0] ? [t.small.waits(pending[0].name, pending[0].pending?.kind === "question")] : [], ...kept ? [t.small.kept] : []].join(" \xB7 ");
|
|
15801
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", height: rows, width: columns, alignItems: "center", justifyContent: "center", children: [
|
|
15802
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.coral, bold: true, children: [
|
|
15803
|
+
g.logo,
|
|
15804
|
+
" ledge"
|
|
15805
|
+
] }),
|
|
15806
|
+
/* @__PURE__ */ jsx7(Text4, { color: C.fg, children: t.small.tooSmall(columns, rows) }),
|
|
15807
|
+
/* @__PURE__ */ jsx7(Text4, { color: C.dim, children: t.small.atLeast(MIN.columns, MIN.rows) }),
|
|
15808
|
+
why ? /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text4, { color: C.red, children: why }) }) : null
|
|
15809
|
+
] });
|
|
15810
|
+
}
|
|
15811
|
+
const left2 = cells(` ${g.logo} ledge ${t.bar.sessions(live.length)} \xB7 ${t.bar.machines(Math.max(1, machines.length))}`) + (waiting > 0 ? cells(` \xB7 ${t.bar.waiting(waiting)}`) : 0) + (status ? cells(` ${status}`) : 0);
|
|
15812
|
+
const room = columns - left2 - 2;
|
|
15813
|
+
const shown = cells(crumb) <= room ? crumb : crumbShort && cells(crumbShort) <= room ? crumbShort : "";
|
|
15814
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", height: rows, width: columns, children: [
|
|
15815
|
+
/* @__PURE__ */ jsxs4(Box5, { paddingX: 1, justifyContent: "space-between", height: 1, backgroundColor: C.bar, children: [
|
|
15816
|
+
/* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
|
|
15817
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.coral, children: [
|
|
15818
|
+
g.logo,
|
|
15819
|
+
" ledge"
|
|
15820
|
+
] }),
|
|
15821
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.dim, children: [
|
|
15822
|
+
" ",
|
|
15823
|
+
t.bar.sessions(live.length),
|
|
15824
|
+
" \xB7 ",
|
|
15825
|
+
t.bar.machines(Math.max(1, machines.length))
|
|
15826
|
+
] }),
|
|
15827
|
+
waiting > 0 ? /* @__PURE__ */ jsxs4(Text4, { color: C.red, children: [
|
|
15828
|
+
" \xB7 ",
|
|
15829
|
+
t.bar.waiting(waiting)
|
|
15830
|
+
] }) : null,
|
|
15831
|
+
status ? /* @__PURE__ */ jsxs4(Text4, { color: C.yellow, children: [
|
|
15832
|
+
" ",
|
|
15833
|
+
status
|
|
15834
|
+
] }) : null
|
|
15835
|
+
] }),
|
|
15836
|
+
shown ? /* @__PURE__ */ jsx7(Text4, { wrap: "truncate", color: C.dim, children: shown }) : null
|
|
15837
|
+
] }),
|
|
15838
|
+
/* @__PURE__ */ jsx7(Box5, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children }),
|
|
15839
|
+
lastToast ? /* @__PURE__ */ jsx7(Box5, { paddingX: 1, height: 1, children: /* @__PURE__ */ jsx7(Text4, { wrap: "truncate", color: lastToast.tone === "error" ? C.red : lastToast.tone === "ok" ? C.green : lastToast.tone === "quiet" ? C.dim : C.yellow, children: lastToast.text }) }) : null,
|
|
15840
|
+
/* @__PURE__ */ jsx7(KeyBar, { keys: fitKeys(keys, columns, editing ? null : ["?", t.keys.help]) })
|
|
15841
|
+
] });
|
|
15842
|
+
}
|
|
15843
|
+
|
|
15844
|
+
// src/tui/ui/SessionCard.tsx
|
|
15845
|
+
import { Box as Box6, Text as Text5 } from "ink";
|
|
15846
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
15847
|
+
function Button({ label, k, primary, danger, off }) {
|
|
15848
|
+
const bg = off ? void 0 : danger ? C.danger : primary ? C.primary : C.key;
|
|
15849
|
+
return /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
15850
|
+
/* @__PURE__ */ jsxs5(Text5, { color: off ? C.faint : C.fg, backgroundColor: bg, children: [
|
|
15851
|
+
" ",
|
|
15852
|
+
label,
|
|
15853
|
+
" "
|
|
15854
|
+
] }),
|
|
15855
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, backgroundColor: bg, children: [
|
|
15856
|
+
k,
|
|
15857
|
+
" "
|
|
15858
|
+
] })
|
|
15859
|
+
] });
|
|
15860
|
+
}
|
|
15861
|
+
function SessionCard({ s, note }) {
|
|
15862
|
+
const tui = useTui();
|
|
15863
|
+
const { t, g } = tui;
|
|
15864
|
+
const threshold = useContextNear(tui.client);
|
|
15865
|
+
const state2 = botState(s);
|
|
15866
|
+
const helpers = liveHelpers(s);
|
|
15867
|
+
const p = s.pending;
|
|
15868
|
+
const canAct = !s.offline && s.source !== "hook_only";
|
|
15869
|
+
const verb = !p ? "" : p.kind === "question" ? t.overview.asks : p.kind === "plan" ? t.overview.plan : p.kind === "diff" ? t.overview.diff : t.overview.wantsToRun;
|
|
15870
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
15871
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15872
|
+
/* @__PURE__ */ jsxs5(Text5, { color: STATE_COLOR[state2], children: [
|
|
15873
|
+
g[state2],
|
|
15874
|
+
" ",
|
|
15875
|
+
t.state[state2]
|
|
15876
|
+
] }),
|
|
15877
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15878
|
+
" ",
|
|
15879
|
+
TOOL_NAME[s.tool],
|
|
15880
|
+
" \xB7 ",
|
|
15881
|
+
modelLabel(s, t.session.defaultModel)
|
|
15882
|
+
] }),
|
|
15883
|
+
s.context ? /* @__PURE__ */ jsxs5(Text5, { color: contextNear(s.context, threshold) ? C.yellow : C.dim, children: [
|
|
15884
|
+
" \xB7 ",
|
|
15885
|
+
t.overview.context(Math.round(s.context.used / s.context.window * 100))
|
|
15886
|
+
] }) : null,
|
|
15887
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15888
|
+
" \xB7 ",
|
|
15889
|
+
machineLabel(s),
|
|
15890
|
+
s.branch ? ` \xB7 ${g.branch} ${s.branch}` : ""
|
|
15891
|
+
] }),
|
|
15892
|
+
s.offline ? /* @__PURE__ */ jsxs5(Text5, { color: C.yellow, children: [
|
|
15893
|
+
" ",
|
|
15894
|
+
t.overview.offline
|
|
15895
|
+
] }) : s.source === "hook_only" ? /* @__PURE__ */ jsxs5(Text5, { color: C.faint, children: [
|
|
15896
|
+
" ",
|
|
15897
|
+
t.overview.readOnly
|
|
15898
|
+
] }) : null
|
|
15899
|
+
] }),
|
|
15900
|
+
s.handover || s.relieves ? /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15901
|
+
s.handover ? /* @__PURE__ */ jsxs5(Text5, { color: s.handover.state === "failed" ? C.red : C.yellow, children: [
|
|
15902
|
+
" \u21C4 ",
|
|
15903
|
+
s.handover.state === "done" && s.handover.toName ? t.overview.relievedBy(s.handover.toName) : t.overview.relief[s.handover.state],
|
|
15904
|
+
s.handover.reason ? ` : ${s.handover.reason}` : ""
|
|
15905
|
+
] }) : null,
|
|
15906
|
+
s.relieves ? /* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15907
|
+
" \u21C4 ",
|
|
15908
|
+
t.overview.relieves(s.relieves.name)
|
|
15909
|
+
] }) : null
|
|
15910
|
+
] }) : null,
|
|
15911
|
+
helpers.length > 0 ? /* @__PURE__ */ jsxs5(Text5, { color: C.violet, wrap: "truncate", children: [
|
|
15912
|
+
" ",
|
|
15913
|
+
helpers.map((h) => `${g.helper} ${h.kind} \xB7 ${oneLine(h.task)}`).join(" ")
|
|
15914
|
+
] }) : null,
|
|
15915
|
+
p ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
15916
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15917
|
+
/* @__PURE__ */ jsx8(Text5, { color: STATE_COLOR[state2], children: verb }),
|
|
15918
|
+
" ",
|
|
15919
|
+
/* @__PURE__ */ jsx8(Text5, { color: C.fg, bold: true, children: oneLine(p.text) }),
|
|
15920
|
+
p.tool ? /* @__PURE__ */ jsxs5(Text5, { color: C.faint, children: [
|
|
15921
|
+
" ",
|
|
15922
|
+
p.tool
|
|
15923
|
+
] }) : null
|
|
15924
|
+
] }),
|
|
15925
|
+
/* @__PURE__ */ jsx8(Box6, { marginTop: 1, columnGap: 2, flexWrap: "wrap", children: p.kind === "question" ? choices(p).slice(0, 9).map((c, i) => /* @__PURE__ */ jsx8(Button, { label: `${c.n} \xB7 ${c.label}`, k: String(c.n), primary: i === 0, off: !canAct }, c.n)) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
15926
|
+
/* @__PURE__ */ jsx8(Button, { label: t.overview.allow, k: "y", primary: true, off: !canAct }),
|
|
15927
|
+
/* @__PURE__ */ jsx8(Button, { label: t.overview.deny, k: "n", off: !canAct }),
|
|
15928
|
+
offers(s.tool, p.kind, "always") ? /* @__PURE__ */ jsx8(Button, { label: t.overview.always, k: "A", off: !canAct }) : null,
|
|
15929
|
+
note && offers(s.tool, p.kind, "note") ? /* @__PURE__ */ jsx8(Button, { label: t.overview.denyWithNote, k: "r", off: !canAct }) : null
|
|
15930
|
+
] }) })
|
|
15931
|
+
] }) : s.lastReply && state2 === "idle" ? /* @__PURE__ */ jsxs5(Text5, { color: C.dim, wrap: "truncate", children: [
|
|
15932
|
+
" ",
|
|
15933
|
+
oneLine(s.lastReply.text)
|
|
15934
|
+
] }) : null
|
|
15935
|
+
] });
|
|
16053
15936
|
}
|
|
16054
15937
|
|
|
16055
|
-
// src/tui/ui/
|
|
16056
|
-
import {
|
|
16057
|
-
import { Box as Box7, Text as
|
|
16058
|
-
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
const key = machine === null || tool === null ? null : `${machine}|${tool}`;
|
|
16067
|
-
const [answer, setAnswer] = useState5(null);
|
|
16068
|
-
useEffect5(() => {
|
|
16069
|
-
if (key === null || machine === null || tool === null || known.has(key)) return;
|
|
16070
|
-
const requestId = `models-${++next}`;
|
|
16071
|
-
const off = client.onMessage((m) => {
|
|
16072
|
-
if (m.type !== "models.listing" || m.requestId !== requestId) return;
|
|
16073
|
-
const state2 = { status: "ready", models: m.models, ...m.error ? { error: m.error } : {} };
|
|
16074
|
-
if (!m.error) known.set(key, state2);
|
|
16075
|
-
setAnswer({ key, state: state2 });
|
|
16076
|
-
});
|
|
16077
|
-
client.send({ type: "models.list", requestId, tool, machineId: machine });
|
|
16078
|
-
return off;
|
|
16079
|
-
}, [client, key, machine, tool]);
|
|
16080
|
-
if (key === null) return IDLE;
|
|
16081
|
-
return known.get(key) ?? (answer && answer.key === key ? answer.state : LOADING);
|
|
15938
|
+
// src/tui/ui/Tail.tsx
|
|
15939
|
+
import { useRef as useRef7 } from "react";
|
|
15940
|
+
import { Box as Box7, Text as Text6, useBoxMetrics as useBoxMetrics3 } from "ink";
|
|
15941
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
15942
|
+
function Tail({ lines, color: color2, empty: empty3, emptyColor }) {
|
|
15943
|
+
const ref = useRef7(null);
|
|
15944
|
+
const { height } = useBoxMetrics3(ref);
|
|
15945
|
+
let end2 = lines.length;
|
|
15946
|
+
while (end2 > 0 && !lines[end2 - 1].trim()) end2--;
|
|
15947
|
+
const shown = end2 === 0 ? [] : lines.slice(0, end2).slice(-Math.max(1, height));
|
|
15948
|
+
return /* @__PURE__ */ jsx9(Box7, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: shown.length === 0 ? /* @__PURE__ */ jsx9(Text6, { color: emptyColor, children: empty3 }) : shown.map((l, i) => /* @__PURE__ */ jsx9(Text6, { color: color2, wrap: "truncate", children: l || " " }, i)) });
|
|
16082
15949
|
}
|
|
16083
15950
|
|
|
16084
|
-
// src/tui/
|
|
16085
|
-
import { jsx as jsx10, jsxs as
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
const
|
|
16090
|
-
const
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
|
|
15951
|
+
// src/tui/screens/Overview.tsx
|
|
15952
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
15953
|
+
var WIDE = 100;
|
|
15954
|
+
function windowed(rows, selectedKey, height) {
|
|
15955
|
+
if (rows.length <= height) return [...rows];
|
|
15956
|
+
const at = Math.max(0, rows.findIndex((r) => r.key === selectedKey));
|
|
15957
|
+
const start = Math.min(Math.max(0, at - Math.floor(height / 2)), rows.length - height);
|
|
15958
|
+
return rows.slice(start, start + height);
|
|
15959
|
+
}
|
|
15960
|
+
function Overview() {
|
|
15961
|
+
const tui = useTui();
|
|
15962
|
+
const { d, projects, t, g, selected, select, actions, go, attach } = tui;
|
|
15963
|
+
const { columns, rows: height } = useScreenSize();
|
|
15964
|
+
const wide = columns >= WIDE;
|
|
15965
|
+
const left2 = wide ? Math.min(64, Math.max(44, Math.floor(columns * 0.42))) : columns;
|
|
15966
|
+
const teams = useTeams(tui.client);
|
|
15967
|
+
const tasks = useTasks(tui.client);
|
|
15968
|
+
const view = useMemo2(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
|
|
15969
|
+
const sel = selected?.id ?? null;
|
|
15970
|
+
const closed = closedLedgeTasks(tasks);
|
|
15971
|
+
const decisions = useMemo2(() => openDecisions(tasks), [tasks]);
|
|
15972
|
+
const [deciding, setDeciding] = useState3(null);
|
|
15973
|
+
const decision = deciding ? decisions.find((x) => x.id === deciding.id) : void 0;
|
|
15974
|
+
useEffect6(() => {
|
|
15975
|
+
if (deciding && !decision) setDeciding(null);
|
|
15976
|
+
}, [deciding, decision]);
|
|
15977
|
+
useEditing(!!deciding && !deciding.confirm);
|
|
15978
|
+
const answer = (x, text5) => {
|
|
15979
|
+
const rid = randomUUID();
|
|
15980
|
+
const off = tui.client.onMessage((m) => {
|
|
15981
|
+
if (m.type !== "action.result" || m.rid !== rid) return;
|
|
15982
|
+
off();
|
|
15983
|
+
tui.toast(m.reason === void 0 ? { text: t.toast.reply(x.giverName), tone: "ok" } : { text: t.toast.answerFailed(m.reason), tone: "error" });
|
|
15984
|
+
});
|
|
15985
|
+
setTimeout(off, 1e4);
|
|
15986
|
+
tui.client.send({ type: "action.run", rid, action: "task.answer", args: { task: x.id, answer: text5 }, confirmed: true });
|
|
15987
|
+
};
|
|
15988
|
+
const threshold = useContextNear(tui.client);
|
|
15989
|
+
const near = useMemo2(() => nearContext(d.sessions, threshold), [d.sessions, threshold]);
|
|
15990
|
+
const [tasksShown, setTasksShown] = useState3(true);
|
|
15991
|
+
const [joining, setJoining] = useState3(null);
|
|
15992
|
+
const node2 = selected ? view.tree.find((n) => n.branches.some((b2) => b2.sessions.includes(selected)) || n.teams.some((x) => x.sessions.includes(selected))) : void 0;
|
|
15993
|
+
const seatTeam = selected?.team ? teams?.find((x) => x.id === selected.team && x.state !== "dissolved") : void 0;
|
|
15994
|
+
const choices2 = node2?.teams.map((x) => x.team).filter((x) => x.state !== "dissolved" && x.id !== selected?.team) ?? [];
|
|
15995
|
+
useScreenKeys((input, key) => {
|
|
15996
|
+
if (deciding) {
|
|
15997
|
+
if (!deciding.confirm) {
|
|
15998
|
+
if (key.escape) setDeciding(null);
|
|
15999
|
+
return true;
|
|
16000
|
+
}
|
|
16001
|
+
if (input === "y" && decision) {
|
|
16002
|
+
setDeciding(null);
|
|
16003
|
+
answer(decision, deciding.draft.trim());
|
|
16004
|
+
return true;
|
|
16005
|
+
}
|
|
16006
|
+
if (input === "n" || key.escape) {
|
|
16007
|
+
setDeciding({ ...deciding, confirm: false });
|
|
16008
|
+
return true;
|
|
16009
|
+
}
|
|
16010
|
+
return true;
|
|
16098
16011
|
}
|
|
16099
|
-
if (
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
return;
|
|
16012
|
+
if (input === "D") {
|
|
16013
|
+
if (decisions[0]) setDeciding({ id: decisions[0].id, draft: "", confirm: false });
|
|
16014
|
+
return true;
|
|
16103
16015
|
}
|
|
16104
|
-
if (
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
|
|
16016
|
+
if (joining) {
|
|
16017
|
+
if (key.escape) {
|
|
16018
|
+
setJoining(null);
|
|
16019
|
+
return true;
|
|
16020
|
+
}
|
|
16021
|
+
if (input === "j" || key.downArrow) {
|
|
16022
|
+
setJoining({ index: (joining.index + 1) % Math.max(1, choices2.length) });
|
|
16023
|
+
return true;
|
|
16024
|
+
}
|
|
16025
|
+
if (input === "k" || key.upArrow) {
|
|
16026
|
+
setJoining({ index: (joining.index + choices2.length - 1) % Math.max(1, choices2.length) });
|
|
16027
|
+
return true;
|
|
16028
|
+
}
|
|
16029
|
+
if (key.return && selected && choices2[joining.index]) {
|
|
16030
|
+
const team = choices2[joining.index];
|
|
16031
|
+
const role = selected.role ?? team.seats.find((x) => x.sessionId === null)?.role;
|
|
16032
|
+
setJoining(null);
|
|
16033
|
+
if (!role) {
|
|
16034
|
+
tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
16035
|
+
return true;
|
|
16036
|
+
}
|
|
16037
|
+
tui.client.send({ type: "team.join", team: team.id, session: selected.id, role });
|
|
16038
|
+
tui.toast({ text: t.toast.joined(selected.name, team.name), tone: "ok" });
|
|
16039
|
+
return true;
|
|
16040
|
+
}
|
|
16041
|
+
return true;
|
|
16108
16042
|
}
|
|
16109
|
-
if (
|
|
16110
|
-
|
|
16111
|
-
|
|
16112
|
-
return;
|
|
16043
|
+
if (input === "J" && selected) {
|
|
16044
|
+
if (choices2.length) setJoining({ index: 0 });
|
|
16045
|
+
else tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
16046
|
+
return true;
|
|
16113
16047
|
}
|
|
16114
|
-
if (
|
|
16048
|
+
if (input === "R") {
|
|
16049
|
+
const resting = (seatTeam?.state === "resting" ? seatTeam : void 0) ?? node2?.teams.find((x) => x.team.state === "resting")?.team ?? view.tree.flatMap((n) => n.teams).find((x) => x.team.state === "resting")?.team;
|
|
16050
|
+
if (resting) {
|
|
16051
|
+
tui.client.send({ type: "team.resume", team: resting.id });
|
|
16052
|
+
tui.toast({ text: t.toast.resumed(resting.name), tone: "ok" });
|
|
16053
|
+
}
|
|
16054
|
+
return true;
|
|
16055
|
+
}
|
|
16056
|
+
if (input === "T") {
|
|
16057
|
+
setTasksShown((v) => !v);
|
|
16058
|
+
return true;
|
|
16059
|
+
}
|
|
16060
|
+
if (input === "H") {
|
|
16061
|
+
const s = (selected && selected.source === "launcher" && selected.state !== "exited" && selected.tool !== "omp" && (!selected.handover || selected.handover.state === "failed") ? selected : void 0) ?? near.find((x) => x.handover?.state !== "failed") ?? near[0];
|
|
16062
|
+
if (!s) return true;
|
|
16063
|
+
const rid = randomUUID();
|
|
16064
|
+
const off = tui.client.onMessage((m) => {
|
|
16065
|
+
if (m.type === "session.handover.result" && m.rid === rid) {
|
|
16066
|
+
off();
|
|
16067
|
+
tui.toast(m.ok ? { text: t.toast.handover(s.name), tone: "ok" } : { text: t.toast.handoverRefused(m.reason ?? "?"), tone: "info" });
|
|
16068
|
+
}
|
|
16069
|
+
});
|
|
16070
|
+
setTimeout(off, 5e3);
|
|
16071
|
+
tui.client.send({ type: "session.handover", rid, id: s.id });
|
|
16072
|
+
return true;
|
|
16073
|
+
}
|
|
16074
|
+
if (input === "x" && closed[0]) {
|
|
16075
|
+
tui.client.send({ type: "task.cmd", rid: randomUUID(), by: LEDGE_GIVER, op: "seen", task: closed[0].id });
|
|
16076
|
+
return true;
|
|
16077
|
+
}
|
|
16078
|
+
if (input === "j" || key.downArrow) {
|
|
16079
|
+
select(step(view.order, sel, 1));
|
|
16080
|
+
return true;
|
|
16081
|
+
}
|
|
16082
|
+
if (input === "k" || key.upArrow) {
|
|
16083
|
+
select(step(view.order, sel, -1));
|
|
16084
|
+
return true;
|
|
16085
|
+
}
|
|
16086
|
+
if (key.tab) {
|
|
16087
|
+
const next2 = nextWaiting(view.yours, sel);
|
|
16088
|
+
if (next2) select(next2);
|
|
16089
|
+
return true;
|
|
16090
|
+
}
|
|
16091
|
+
if (key.return) {
|
|
16092
|
+
if (selected) go("session");
|
|
16093
|
+
return true;
|
|
16094
|
+
}
|
|
16095
|
+
if (!selected) return false;
|
|
16096
|
+
if (input === "a") {
|
|
16097
|
+
attach(selected);
|
|
16098
|
+
return true;
|
|
16099
|
+
}
|
|
16100
|
+
if (input === "r" && selected.pending) {
|
|
16101
|
+
go("session");
|
|
16102
|
+
return true;
|
|
16103
|
+
}
|
|
16104
|
+
if (/^[1-9]$/.test(input) && selected.pending?.kind !== "question") return false;
|
|
16105
|
+
if (input === "y" || input === "n" || input === "A" || /^[1-9]$/.test(input)) return actions.key(selected, input);
|
|
16106
|
+
if (input === "i" && selected.state === "working") {
|
|
16107
|
+
actions.interrupt(selected);
|
|
16108
|
+
return true;
|
|
16109
|
+
}
|
|
16110
|
+
return false;
|
|
16115
16111
|
});
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16112
|
+
const tree2 = view.tree.flatMap((n) => {
|
|
16113
|
+
const teamed = n.teams.length > 0;
|
|
16114
|
+
const common = n.branches.length > 0;
|
|
16115
|
+
return [
|
|
16116
|
+
{ kind: "project", key: n.key, label: n.label, color: projectColor(n.color), count: n.count },
|
|
16117
|
+
...teamed && common ? [{ kind: "common", key: `${n.key}|common` }] : [],
|
|
16118
|
+
...n.branches.flatMap((b2) => [{ kind: "branch", key: `${n.key}|${b2.branch}`, branch: b2.branch, indent: teamed ? 4 : 2 }, ...b2.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: teamed ? 2 : 0 }))]),
|
|
16119
|
+
...n.teams.flatMap((x) => [{ kind: "team", key: x.key, team: x.team, done: x.done, given: x.given }, ...x.sessions.map((s) => ({ kind: "session", key: s.id, s, indent: 2 }))])
|
|
16120
|
+
];
|
|
16121
|
+
});
|
|
16122
|
+
const yoursCount = view.yours.length + decisions.length + closed.length + near.length;
|
|
16123
|
+
const yoursHeight = Math.max(1, Math.min(yoursCount, 6));
|
|
16124
|
+
const joinHeight = joining && selected ? Math.max(1, choices2.length) + 2 : 0;
|
|
16125
|
+
const answerLines = deciding?.confirm ? wrap2(deciding.draft.trim(), Math.max(10, columns - 4)) : [];
|
|
16126
|
+
const decideHeight = !deciding ? 0 : deciding.confirm ? Math.min(answerLines.length, Math.max(1, height - 12)) + 2 : fieldRows(columns, height) + 2;
|
|
16127
|
+
const treeHeight = Math.max(3, height - 3 - (yoursHeight + 2) - joinHeight - decideHeight - 3);
|
|
16128
|
+
const inner = left2 - 4, nameW = 7;
|
|
16129
|
+
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
16130
|
+
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
16131
|
+
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
16132
|
+
const yoursBox = /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.yours(yoursCount), color: yoursCount ? C.red : C.line, titleColor: yoursCount ? C.red : C.dim, height: yoursHeight + 2, children: yoursCount === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.nobodyWaits }) : [...view.yours.slice(0, yoursHeight).map((s) => {
|
|
16133
|
+
const st = botState(s);
|
|
16134
|
+
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
16135
|
+
/* @__PURE__ */ jsxs6(Text7, { color: STATE_COLOR[st], children: [
|
|
16136
|
+
g[st],
|
|
16122
16137
|
" "
|
|
16123
16138
|
] }),
|
|
16124
|
-
/* @__PURE__ */ jsx10(
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16139
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
16140
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.dim, children: fit(s.repo ?? s.project, 9) }),
|
|
16141
|
+
/* @__PURE__ */ jsx10(Text7, { color: STATE_COLOR[st], children: fit(oneLine(s.pending?.text ?? ""), askW) }),
|
|
16142
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
16143
|
+
" ",
|
|
16144
|
+
/* @__PURE__ */ jsx10(Ago, { iso: s.lastActivityAt })
|
|
16145
|
+
] })
|
|
16146
|
+
] }, s.id);
|
|
16147
|
+
}), ...decisions.slice(0, Math.max(0, yoursHeight - view.yours.length)).map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16148
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.red, children: [
|
|
16149
|
+
g.question,
|
|
16150
|
+
" "
|
|
16151
|
+
] }),
|
|
16152
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: t.overview.decision(x.giverName, x.title) }),
|
|
16153
|
+
x.text ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16154
|
+
" \xB7 ",
|
|
16155
|
+
oneLine(x.text)
|
|
16156
|
+
] }) : null
|
|
16157
|
+
] }, `decision:${x.id}`)), ...closed.slice(0, Math.max(0, yoursHeight - view.yours.length - decisions.length)).map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16158
|
+
/* @__PURE__ */ jsxs6(Text7, { color: x.state === "done" ? C.green : C.yellow, children: [
|
|
16159
|
+
x.state === "done" ? g.ok : g.fail,
|
|
16160
|
+
" "
|
|
16161
|
+
] }),
|
|
16162
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: t.overview.ledgeTask(x.ownerName, x.title, x.state) })
|
|
16163
|
+
] }, x.id)), ...near.slice(0, Math.max(0, yoursHeight - view.yours.length - decisions.length - closed.length)).map((s) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
16164
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.yellow, children: [
|
|
16165
|
+
s.handover?.state === "failed" ? g.fail : "\u25D4",
|
|
16166
|
+
" "
|
|
16167
|
+
] }),
|
|
16168
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: s.handover?.state === "failed" ? t.overview.reliefFailed(s.name, s.handover.reason ?? "?") : t.overview.near(`${s.name}${s.role ? ` @${s.role}` : ""}`, Math.round((s.context?.used ?? 0) / (s.context?.window ?? 1) * 100)) })
|
|
16169
|
+
] }, `near:${s.id}`))] });
|
|
16170
|
+
const treeBox = /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.projects, flexGrow: 1, children: tree2.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noSession }) : windowed(tree2, sel, treeHeight).map((r) => r.kind === "project" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16171
|
+
/* @__PURE__ */ jsx10(Text7, { color: r.color, children: g.project }),
|
|
16172
|
+
" ",
|
|
16173
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: r.label }),
|
|
16174
|
+
" ",
|
|
16175
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.sessions(r.count) })
|
|
16176
|
+
] }, r.key) : r.kind === "common" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
|
|
16177
|
+
" ",
|
|
16178
|
+
g.common,
|
|
16179
|
+
" ",
|
|
16180
|
+
t.overview.common
|
|
16181
|
+
] }, r.key) : r.kind === "team" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16182
|
+
/* @__PURE__ */ jsx10(Text7, { children: " " }),
|
|
16183
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
|
|
16184
|
+
" ",
|
|
16185
|
+
/* @__PURE__ */ jsx10(Text7, { color: r.team.state === "resting" ? C.dim : C.fg, bold: true, children: r.team.name }),
|
|
16186
|
+
" ",
|
|
16187
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: r.team.state === "resting" ? t.overview.resting : t.overview.teamLine(r.done, r.given) })
|
|
16188
|
+
] }, r.key) : r.kind === "branch" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
|
|
16189
|
+
" ".repeat(r.indent),
|
|
16190
|
+
g.branch,
|
|
16191
|
+
" ",
|
|
16192
|
+
r.branch
|
|
16193
|
+
] }, r.key) : /* @__PURE__ */ jsx10(SessionRow, { s: r.s, on: r.s.id === sel, nameW, topicW: Math.max(6, topicW - r.indent - (r.s.role ? r.s.role.length + 2 : 0)), stateW, indent: r.indent }, r.key)) });
|
|
16194
|
+
const keys = deciding?.confirm ? [["y", t.keys.sendAnswer], ["n esc", t.keys.edit]] : deciding ? [["\u21B5", t.keys.review], ["alt+\u21B5", t.keys.newLine], ["esc", t.keys.cancel]] : [
|
|
16195
|
+
["j k", t.keys.navigate],
|
|
16196
|
+
["\u21B5", t.keys.open],
|
|
16197
|
+
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
16198
|
+
["a", t.keys.attach],
|
|
16199
|
+
...seatTeam ? [["T", t.keys.tasks]] : [],
|
|
16200
|
+
...choices2.length ? [["J", t.keys.join]] : [],
|
|
16201
|
+
...view.tree.some((n) => n.teams.some((x) => x.team.state === "resting")) ? [["R", t.keys.resume]] : [],
|
|
16202
|
+
...decisions.length ? [["D", t.keys.decide]] : [],
|
|
16203
|
+
...near.length ? [["H", t.keys.handover]] : [],
|
|
16204
|
+
["/", t.keys.ask],
|
|
16205
|
+
["+", t.keys.launch],
|
|
16206
|
+
[",", t.keys.settings],
|
|
16207
|
+
["?", t.keys.help],
|
|
16208
|
+
["q", t.keys.quit]
|
|
16209
|
+
];
|
|
16210
|
+
const teamTasks = seatTeam ? tasks.filter((x) => x.team === seatTeam.id && (!taskClosed(x) || x.updatedAt.slice(0, 10) === (/* @__PURE__ */ new Date()).toISOString().slice(0, 10))).slice(-4) : [];
|
|
16211
|
+
return /* @__PURE__ */ jsxs6(Frame, { crumb: tui.machines.map((m) => `${m.label} ${m.ready ? g.machine : "\u25CB"}`).join(" "), keys, children: [
|
|
16212
|
+
/* @__PURE__ */ jsxs6(Box8, { flexGrow: 1, children: [
|
|
16213
|
+
/* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", width: left2, flexShrink: 0, children: [
|
|
16214
|
+
yoursBox,
|
|
16215
|
+
joining && selected ? /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.joinWhich(selected.name), color: C.coral, titleColor: C.coral, height: joinHeight, children: choices2.map((x, i) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: i === joining.index ? C.sel : void 0, children: [
|
|
16216
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
|
|
16217
|
+
" ",
|
|
16218
|
+
x.name
|
|
16219
|
+
] }, x.id)) }) : null,
|
|
16220
|
+
treeBox
|
|
16221
|
+
] }),
|
|
16222
|
+
wide ? /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", flexGrow: 1, children: [
|
|
16223
|
+
selected ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
16224
|
+
/* @__PURE__ */ jsx10(TitledBox, { title: `${selected.name} \xB7 ${oneLine(selected.topic ?? selected.title)}`, color: isPending(selected) ? C.red : C.line, titleColor: isPending(selected) ? C.red : C.dim, children: /* @__PURE__ */ jsx10(SessionCard, { s: selected, note: true }) }),
|
|
16225
|
+
seatTeam && tasksShown ? /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.tasks(`${g.team} ${seatTeam.name}`), height: Math.max(3, teamTasks.length + 2), children: teamTasks.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noTask }) : teamTasks.map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16226
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: x.id }),
|
|
16227
|
+
" ",
|
|
16228
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.title }),
|
|
16229
|
+
" ",
|
|
16230
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16231
|
+
x.giverName,
|
|
16232
|
+
" ",
|
|
16233
|
+
g.arrow,
|
|
16234
|
+
" ",
|
|
16235
|
+
x.ownerName
|
|
16236
|
+
] }),
|
|
16237
|
+
" ",
|
|
16238
|
+
/* @__PURE__ */ jsx10(Text7, { color: x.state === "done" ? C.green : x.state === "taken" ? C.blue : C.faint, children: t.ask.taskState[x.state] })
|
|
16239
|
+
] }, x.id)) }) : null,
|
|
16240
|
+
/* @__PURE__ */ jsx10(TitledBox, { title: t.overview.terminal(paneName(selected)), flexGrow: 1, children: /* @__PURE__ */ jsx10(Tail, { lines: d.previews.get(selected.id) ?? [], color: C.dim, empty: t.overview.noPreview, emptyColor: C.faint }) })
|
|
16241
|
+
] }) : /* @__PURE__ */ jsx10(TitledBox, { title: t.tabs.session, flexGrow: 1, children: /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.session.noSession }) }),
|
|
16242
|
+
/* @__PURE__ */ jsx10(TitledBox, { title: t.overview.exchanges, height: 5, children: (() => {
|
|
16243
|
+
const list3 = exchanges(d.sessions, 3, tasks, t.overview.ledge);
|
|
16244
|
+
return list3.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noExchange }) : list3.map((x, i) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16245
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: clock(x.at) }),
|
|
16246
|
+
" ",
|
|
16247
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.from }),
|
|
16248
|
+
" ",
|
|
16249
|
+
/* @__PURE__ */ jsx10(Text7, { color: x.kind === "task" ? C.green : C.violet, children: g.arrow }),
|
|
16250
|
+
" ",
|
|
16251
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.to }),
|
|
16252
|
+
" ",
|
|
16253
|
+
x.kind === "task" ? /* @__PURE__ */ jsxs6(Text7, { color: C.green, children: [
|
|
16254
|
+
g.task,
|
|
16255
|
+
" "
|
|
16256
|
+
] }) : null,
|
|
16257
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.dim, children: oneLine(x.summary) })
|
|
16258
|
+
] }, i));
|
|
16259
|
+
})() })
|
|
16128
16260
|
] }) : null
|
|
16129
|
-
] },
|
|
16261
|
+
] }),
|
|
16262
|
+
deciding && decision ? deciding.confirm ? /* @__PURE__ */ jsx10(TitledBox, { title: t.overview.confirmAnswer(decision.giverName), color: C.red, titleColor: C.red, height: decideHeight, flexShrink: 0, children: answerLines.slice(0, decideHeight - 2).map((l, i) => /* @__PURE__ */ jsx10(Text7, { color: C.fg, wrap: "truncate", children: l || " " }, i)) }) : /* @__PURE__ */ jsx10(
|
|
16263
|
+
Field,
|
|
16264
|
+
{
|
|
16265
|
+
title: t.overview.decisionTitle(decision.giverName, decision.title),
|
|
16266
|
+
value: deciding.draft,
|
|
16267
|
+
onChange: (v) => setDeciding((x) => x ? { ...x, draft: v } : x),
|
|
16268
|
+
onSubmit: (v) => {
|
|
16269
|
+
if (!v.trim()) return false;
|
|
16270
|
+
setDeciding((x) => x ? { ...x, draft: v, confirm: true } : x);
|
|
16271
|
+
},
|
|
16272
|
+
onEscape: () => setDeciding(null),
|
|
16273
|
+
active: true,
|
|
16274
|
+
placeholder: t.overview.decisionHint
|
|
16275
|
+
}
|
|
16276
|
+
) : null
|
|
16130
16277
|
] });
|
|
16131
16278
|
}
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
}
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16279
|
+
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0 }) {
|
|
16280
|
+
const { t, g } = useTui();
|
|
16281
|
+
const st = botState(s);
|
|
16282
|
+
const n = helperCount(s);
|
|
16283
|
+
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
16284
|
+
/* @__PURE__ */ jsx10(Text7, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
16285
|
+
/* @__PURE__ */ jsx10(Text7, { children: " ".repeat(2 + indent2) }),
|
|
16286
|
+
/* @__PURE__ */ jsxs6(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
16287
|
+
g[st],
|
|
16288
|
+
" "
|
|
16289
|
+
] }),
|
|
16290
|
+
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
16291
|
+
s.role ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
16292
|
+
"@",
|
|
16293
|
+
s.role,
|
|
16294
|
+
" "
|
|
16295
|
+
] }) : null,
|
|
16296
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16297
|
+
fit(oneLine(s.topic ?? s.title), topicW),
|
|
16298
|
+
" "
|
|
16299
|
+
] }),
|
|
16300
|
+
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
16301
|
+
n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
16302
|
+
" +",
|
|
16303
|
+
n
|
|
16304
|
+
] }) : null,
|
|
16305
|
+
s.machineName && s.machineId !== "local" ? /* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
16306
|
+
" \xB7 ",
|
|
16307
|
+
machineLabel(s)
|
|
16308
|
+
] }) : null
|
|
16148
16309
|
] });
|
|
16149
|
-
});
|
|
16150
|
-
function Lines({ lines, up = 0, onHeight }) {
|
|
16151
|
-
const ref = useRef7(null);
|
|
16152
|
-
const { height } = useBoxMetrics3(ref);
|
|
16153
|
-
useEffect6(() => {
|
|
16154
|
-
if (height > 0) onHeight?.(height);
|
|
16155
|
-
}, [height, onHeight]);
|
|
16156
|
-
const h = Math.max(1, height);
|
|
16157
|
-
const back2 = Math.min(up, Math.max(0, lines.length - h));
|
|
16158
|
-
const shown = lines.slice(Math.max(0, lines.length - h - back2), lines.length - back2);
|
|
16159
|
-
return /* @__PURE__ */ jsx11(Box8, { ref, flexDirection: "column", flexGrow: 1, overflow: "hidden", children: shown.map((l, i) => /* @__PURE__ */ jsx11(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx11(LineText, { line: l }) }, i)) });
|
|
16160
16310
|
}
|
|
16161
16311
|
|
|
16162
|
-
// src/tui/
|
|
16163
|
-
import {
|
|
16164
|
-
import { Box as
|
|
16312
|
+
// src/tui/screens/Session.tsx
|
|
16313
|
+
import { useEffect as useEffect8, useMemo as useMemo3, useState as useState7 } from "react";
|
|
16314
|
+
import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
|
|
16165
16315
|
|
|
16166
|
-
// src/tui/
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
var
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
const
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16194
|
-
var wordLeft = (f) => ({ ...f, cursor: wordStart(f) });
|
|
16195
|
-
function wordRight(f) {
|
|
16196
|
-
let i = f.cursor;
|
|
16197
|
-
while (i < f.text.length && /\s/.test(f.text[i])) i++;
|
|
16198
|
-
while (i < f.text.length && !/\s/.test(f.text[i])) i++;
|
|
16199
|
-
return { ...f, cursor: i };
|
|
16200
|
-
}
|
|
16201
|
-
var killWord = (f) => {
|
|
16202
|
-
const to = wordStart(f);
|
|
16203
|
-
return { text: f.text.slice(0, to) + f.text.slice(f.cursor), cursor: to };
|
|
16204
|
-
};
|
|
16205
|
-
var home = (f) => ({ ...f, cursor: f.text.lastIndexOf("\n", f.cursor - 1) + 1 });
|
|
16206
|
-
var end = (f) => {
|
|
16207
|
-
const n = f.text.indexOf("\n", f.cursor);
|
|
16208
|
-
return { ...f, cursor: n < 0 ? f.text.length : n };
|
|
16209
|
-
};
|
|
16210
|
-
function layout(text5, width) {
|
|
16211
|
-
const w = Math.max(2, width - 1);
|
|
16212
|
-
const a = stops(text5), rows = [];
|
|
16213
|
-
let start = 0;
|
|
16214
|
-
for (let i = 0; i < a.length; i++) {
|
|
16215
|
-
const at = a[i];
|
|
16216
|
-
if (at < text5.length && text5[at] !== "\n") continue;
|
|
16217
|
-
if (start === i) rows.push({ a: at, b: at, last: true });
|
|
16218
|
-
for (let s = start; s < i; ) {
|
|
16219
|
-
let n = 0, j = s, space = -1;
|
|
16220
|
-
while (j < i) {
|
|
16221
|
-
const c = cells(text5.slice(a[j], a[j + 1]));
|
|
16222
|
-
if (n + c > w) break;
|
|
16223
|
-
if (text5[a[j]] === " ") space = j;
|
|
16224
|
-
n += c;
|
|
16225
|
-
j++;
|
|
16316
|
+
// src/tui/images.tsx
|
|
16317
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
16318
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
16319
|
+
import { join as join3 } from "path";
|
|
16320
|
+
import { useRef as useRef8, useState as useState4 } from "react";
|
|
16321
|
+
import { Text as Text8 } from "ink";
|
|
16322
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
16323
|
+
var counter2 = 0;
|
|
16324
|
+
var INLINE_BUDGET = MAX_FRAME - 64 * 1024;
|
|
16325
|
+
function useChips(put) {
|
|
16326
|
+
const { t, toast } = useTui();
|
|
16327
|
+
const [items, setItems] = useState4([]);
|
|
16328
|
+
const held = useRef8([]);
|
|
16329
|
+
const commit = (next2) => {
|
|
16330
|
+
held.current = next2;
|
|
16331
|
+
setItems(next2);
|
|
16332
|
+
};
|
|
16333
|
+
const patch = (key, over) => commit(held.current.map((c) => c.key === key ? { ...c, ...over } : c));
|
|
16334
|
+
const reasonOf = (e) => {
|
|
16335
|
+
const reason = e instanceof AttachmentFailure ? e.reason : null;
|
|
16336
|
+
return reason === "too_large" ? t.images.tooLarge : reason === "not_image" ? t.images.notImage : t.images.failed;
|
|
16337
|
+
};
|
|
16338
|
+
return {
|
|
16339
|
+
items,
|
|
16340
|
+
add({ image: image2, width, height, bytes }) {
|
|
16341
|
+
if (held.current.length >= ATTACHMENTS_MAX) {
|
|
16342
|
+
toast({ text: t.images.max, tone: "info" });
|
|
16343
|
+
return;
|
|
16226
16344
|
}
|
|
16227
|
-
|
|
16228
|
-
const
|
|
16229
|
-
|
|
16230
|
-
|
|
16345
|
+
const key = counter2++;
|
|
16346
|
+
const inline2 = held.current.reduce((n, c) => n + (c.image?.data.length ?? 0), 0);
|
|
16347
|
+
if (bytes > ATTACHMENT_LOCAL_MAX || !put && inline2 + image2.data.length > INLINE_BUDGET) {
|
|
16348
|
+
commit([...held.current, { key, width, height, state: "refused", reason: t.images.tooLarge }]);
|
|
16349
|
+
return;
|
|
16350
|
+
}
|
|
16351
|
+
if (!put) {
|
|
16352
|
+
commit([...held.current, { key, width, height, state: "ready", image: image2 }]);
|
|
16353
|
+
return;
|
|
16354
|
+
}
|
|
16355
|
+
commit([...held.current, { key, width, height, state: "uploading" }]);
|
|
16356
|
+
put(image2).then((ref) => patch(key, { state: "ready", id: ref.id }), (e) => patch(key, { state: "refused", reason: reasonOf(e) }));
|
|
16357
|
+
},
|
|
16358
|
+
removeLast() {
|
|
16359
|
+
commit(held.current.slice(0, -1));
|
|
16360
|
+
},
|
|
16361
|
+
ready: items.every((c) => c.state === "ready"),
|
|
16362
|
+
ids: () => held.current.flatMap((c) => c.state === "ready" && c.id ? [c.id] : []),
|
|
16363
|
+
uploads: () => held.current.flatMap((c) => c.state === "ready" && c.image ? [c.image] : []),
|
|
16364
|
+
clear() {
|
|
16365
|
+
const taken = held.current;
|
|
16366
|
+
commit([]);
|
|
16367
|
+
return taken;
|
|
16368
|
+
},
|
|
16369
|
+
putBack(taken) {
|
|
16370
|
+
commit([...taken, ...held.current].slice(0, ATTACHMENTS_MAX));
|
|
16231
16371
|
}
|
|
16232
|
-
|
|
16233
|
-
}
|
|
16234
|
-
return rows;
|
|
16372
|
+
};
|
|
16235
16373
|
}
|
|
16236
|
-
|
|
16237
|
-
const
|
|
16238
|
-
|
|
16239
|
-
|
|
16240
|
-
|
|
16241
|
-
|
|
16242
|
-
|
|
16243
|
-
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16374
|
+
function ChipsLine({ chips }) {
|
|
16375
|
+
const { t } = useTui();
|
|
16376
|
+
if (chips.length === 0) return null;
|
|
16377
|
+
return /* @__PURE__ */ jsx11(Text8, { wrap: "truncate", children: chips.map((c, i) => /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
16378
|
+
i ? " " : "",
|
|
16379
|
+
/* @__PURE__ */ jsxs7(Text8, { color: c.state === "refused" ? C.red : C.dim, children: [
|
|
16380
|
+
"\u25A3 ",
|
|
16381
|
+
t.images.capture,
|
|
16382
|
+
" ",
|
|
16383
|
+
c.width,
|
|
16384
|
+
"\xD7",
|
|
16385
|
+
c.height,
|
|
16386
|
+
" ",
|
|
16387
|
+
c.state === "uploading" ? "\u2026" : "\u2715"
|
|
16388
|
+
] }),
|
|
16389
|
+
c.reason ? /* @__PURE__ */ jsxs7(Text8, { color: C.red, children: [
|
|
16390
|
+
" ",
|
|
16391
|
+
c.reason
|
|
16392
|
+
] }) : null
|
|
16393
|
+
] }, c.key)) });
|
|
16394
|
+
}
|
|
16395
|
+
var EXT = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" };
|
|
16396
|
+
function usePaste2(chips) {
|
|
16397
|
+
const { readImage, toast, t } = useTui();
|
|
16398
|
+
const reading = useRef8(false);
|
|
16399
|
+
return () => {
|
|
16400
|
+
if (reading.current) return;
|
|
16401
|
+
reading.current = true;
|
|
16402
|
+
void readImage().then((got) => {
|
|
16403
|
+
if (got) chips.add(got);
|
|
16404
|
+
else toast({ text: t.images.none, tone: "info" });
|
|
16405
|
+
}, () => toast({ text: t.images.none, tone: "info" })).finally(() => {
|
|
16406
|
+
reading.current = false;
|
|
16407
|
+
});
|
|
16408
|
+
};
|
|
16251
16409
|
}
|
|
16252
|
-
function
|
|
16253
|
-
const
|
|
16254
|
-
const
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
16259
|
-
|
|
16260
|
-
|
|
16261
|
-
else {
|
|
16262
|
-
const next2 = after({ text: text5, cursor: at });
|
|
16263
|
-
if (at > r.a) line.push({ text: text5.slice(r.a, at), color: o.color });
|
|
16264
|
-
line.push(inv(text5.slice(at, Math.min(next2, r.b))));
|
|
16265
|
-
if (next2 < r.b) line.push({ text: text5.slice(next2, r.b), color: o.color });
|
|
16410
|
+
function useOpenImage() {
|
|
16411
|
+
const { client, openFile, toast, t } = useTui();
|
|
16412
|
+
const presses = useRef8(0);
|
|
16413
|
+
const last = useRef8("");
|
|
16414
|
+
return (items) => {
|
|
16415
|
+
const signature = items.map((x) => x.ref.id).join(",");
|
|
16416
|
+
if (signature !== last.current) {
|
|
16417
|
+
last.current = signature;
|
|
16418
|
+
presses.current = 0;
|
|
16266
16419
|
}
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16420
|
+
const item = items[items.length - 1 - presses.current++ % items.length];
|
|
16421
|
+
if (!item) return;
|
|
16422
|
+
const { ref, route } = item;
|
|
16423
|
+
void (async () => {
|
|
16424
|
+
const { mime, data } = await getImage(client, ref.id, route);
|
|
16425
|
+
const dir = join3(tmpdir2(), "ledge-images");
|
|
16426
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
16427
|
+
const file = join3(dir, `${ref.id}.${EXT[mime]}`);
|
|
16428
|
+
await writeFile(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
16429
|
+
await openFile(file);
|
|
16430
|
+
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
16431
|
+
};
|
|
16270
16432
|
}
|
|
16271
|
-
var scrollTop = (top, row2, total, max) => Math.max(0, Math.min(Math.max(0, total - max), row2 < top ? row2 : row2 >= top + max ? row2 - max + 1 : top));
|
|
16272
|
-
var lineAt = (f) => ({
|
|
16273
|
-
line: f.text.slice(0, f.cursor).split("\n").length,
|
|
16274
|
-
lines: f.text.split("\n").length
|
|
16275
|
-
});
|
|
16276
16433
|
|
|
16277
|
-
// src/tui/ui/
|
|
16278
|
-
import {
|
|
16279
|
-
|
|
16280
|
-
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16284
|
-
|
|
16285
|
-
|
|
16286
|
-
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
|
|
16296
|
-
|
|
16297
|
-
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16303
|
-
|
|
16304
|
-
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
const
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
|
|
16313
|
-
|
|
16314
|
-
|
|
16315
|
-
|
|
16434
|
+
// src/tui/ui/ModelPicker.tsx
|
|
16435
|
+
import { useState as useState6 } from "react";
|
|
16436
|
+
import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
|
|
16437
|
+
|
|
16438
|
+
// ../fleet/src/models.ts
|
|
16439
|
+
import { useEffect as useEffect7, useState as useState5 } from "react";
|
|
16440
|
+
var IDLE = { status: "idle", models: [] };
|
|
16441
|
+
var LOADING = { status: "loading", models: [] };
|
|
16442
|
+
var known = /* @__PURE__ */ new Map();
|
|
16443
|
+
var next = 0;
|
|
16444
|
+
function useModels(client, machine, tool) {
|
|
16445
|
+
const key = machine === null || tool === null ? null : `${machine}|${tool}`;
|
|
16446
|
+
const [answer, setAnswer] = useState5(null);
|
|
16447
|
+
useEffect7(() => {
|
|
16448
|
+
if (key === null || machine === null || tool === null || known.has(key)) return;
|
|
16449
|
+
const requestId = `models-${++next}`;
|
|
16450
|
+
const off = client.onMessage((m) => {
|
|
16451
|
+
if (m.type !== "models.listing" || m.requestId !== requestId) return;
|
|
16452
|
+
const state2 = { status: "ready", models: m.models, ...m.error ? { error: m.error } : {} };
|
|
16453
|
+
if (!m.error) known.set(key, state2);
|
|
16454
|
+
setAnswer({ key, state: state2 });
|
|
16455
|
+
});
|
|
16456
|
+
client.send({ type: "models.list", requestId, tool, machineId: machine });
|
|
16457
|
+
return off;
|
|
16458
|
+
}, [client, key, machine, tool]);
|
|
16459
|
+
if (key === null) return IDLE;
|
|
16460
|
+
return known.get(key) ?? (answer && answer.key === key ? answer.state : LOADING);
|
|
16461
|
+
}
|
|
16462
|
+
|
|
16463
|
+
// src/tui/ui/ModelPicker.tsx
|
|
16464
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
16465
|
+
function ModelPicker({ machine, tool, current: current2, onPick, onCancel }) {
|
|
16466
|
+
const { client, t, g } = useTui();
|
|
16467
|
+
useEditing(true);
|
|
16468
|
+
const models = useModels(client, machine, tool);
|
|
16469
|
+
const rows = [{ id: null, label: t.launch.defaultModel, efforts: [], defaultEffort: void 0 }, ...models.models.map((m) => ({ id: m.id, label: m.label, efforts: m.efforts ?? [], defaultEffort: m.defaultEffort }))];
|
|
16470
|
+
const [at, setAt] = useState6(() => Math.max(0, rows.findIndex((r) => r.id === current2.model)));
|
|
16471
|
+
const [effort, setEffort] = useState6(current2.effort);
|
|
16472
|
+
const row2 = rows[Math.min(at, rows.length - 1)];
|
|
16316
16473
|
useInput2((input, key) => {
|
|
16317
|
-
const { f: f2, browse: browse2 } = st.current;
|
|
16318
|
-
if (key.return && !key.meta && !key.shift) {
|
|
16319
|
-
const fromHistory = inHistory(browse2);
|
|
16320
|
-
if (onSubmit(f2.text, { fromHistory }) === false) return;
|
|
16321
|
-
if (fromHistory) show(browse2, browse2.items.length - 1);
|
|
16322
|
-
if (browse2) show(null);
|
|
16323
|
-
return;
|
|
16324
|
-
}
|
|
16325
|
-
if (key.return || input === "\n" || key.ctrl && input === "j") {
|
|
16326
|
-
set(insert(f2, "\n"));
|
|
16327
|
-
return;
|
|
16328
|
-
}
|
|
16329
16474
|
if (key.escape) {
|
|
16330
|
-
|
|
16331
|
-
show(browse2, browse2.items.length - 1);
|
|
16332
|
-
show(null);
|
|
16333
|
-
return;
|
|
16334
|
-
}
|
|
16335
|
-
if (browse2) show(null);
|
|
16336
|
-
onEscape?.();
|
|
16337
|
-
return;
|
|
16338
|
-
}
|
|
16339
|
-
if (key.backspace) {
|
|
16340
|
-
set(key.meta ? killWord(f2) : back(f2));
|
|
16341
|
-
return;
|
|
16342
|
-
}
|
|
16343
|
-
if (key.delete) {
|
|
16344
|
-
set(del(f2));
|
|
16345
|
-
return;
|
|
16346
|
-
}
|
|
16347
|
-
if (key.ctrl && input === "u") {
|
|
16348
|
-
set({ text: "", cursor: 0 });
|
|
16349
|
-
return;
|
|
16350
|
-
}
|
|
16351
|
-
if (key.ctrl && input === "w") {
|
|
16352
|
-
set(killWord(f2));
|
|
16353
|
-
return;
|
|
16354
|
-
}
|
|
16355
|
-
if (key.ctrl && input === "a" || key.home) {
|
|
16356
|
-
set(home(f2));
|
|
16357
|
-
return;
|
|
16358
|
-
}
|
|
16359
|
-
if (key.ctrl && input === "e" || key.end) {
|
|
16360
|
-
set(end(f2));
|
|
16361
|
-
return;
|
|
16362
|
-
}
|
|
16363
|
-
if (key.leftArrow || key.meta && input === "b") {
|
|
16364
|
-
set(key.ctrl || key.meta ? wordLeft(f2) : left(f2));
|
|
16475
|
+
onCancel();
|
|
16365
16476
|
return;
|
|
16366
16477
|
}
|
|
16367
|
-
if (key.
|
|
16368
|
-
|
|
16478
|
+
if (key.upArrow || input === "k") {
|
|
16479
|
+
setAt((i) => (i - 1 + rows.length) % rows.length);
|
|
16480
|
+
setEffort(null);
|
|
16369
16481
|
return;
|
|
16370
16482
|
}
|
|
16371
|
-
if (key.
|
|
16372
|
-
|
|
16373
|
-
|
|
16374
|
-
set(up);
|
|
16375
|
-
return;
|
|
16376
|
-
}
|
|
16377
|
-
const b2 = browse2 ?? (history.length ? { items: [...history, f2.text], i: history.length } : null);
|
|
16378
|
-
if (b2 && b2.i > 0) show(b2, b2.i - 1);
|
|
16483
|
+
if (key.downArrow || input === "j") {
|
|
16484
|
+
setAt((i) => (i + 1) % rows.length);
|
|
16485
|
+
setEffort(null);
|
|
16379
16486
|
return;
|
|
16380
16487
|
}
|
|
16381
|
-
if (key.
|
|
16382
|
-
const
|
|
16383
|
-
|
|
16384
|
-
set(down);
|
|
16385
|
-
return;
|
|
16386
|
-
}
|
|
16387
|
-
if (inHistory(browse2)) show(browse2, browse2.i + 1);
|
|
16488
|
+
if ((key.leftArrow || key.rightArrow) && row2.efforts.length) {
|
|
16489
|
+
const cur = Math.max(0, row2.efforts.indexOf(effort ?? row2.defaultEffort ?? row2.efforts[0]));
|
|
16490
|
+
setEffort(row2.efforts[(cur + (key.rightArrow ? 1 : -1) + row2.efforts.length) % row2.efforts.length]);
|
|
16388
16491
|
return;
|
|
16389
16492
|
}
|
|
16390
|
-
if (key.
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
}
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
|
|
16401
|
-
|
|
16402
|
-
|
|
16403
|
-
|
|
16404
|
-
|
|
16405
|
-
|
|
16406
|
-
|
|
16407
|
-
const where = lineAt(f);
|
|
16408
|
-
const foot = inHistory(browse) ? t.field.history(browse.i + 1, browse.items.length - 1) : [
|
|
16409
|
-
top.current > 0 ? t.field.above(top.current) : "",
|
|
16410
|
-
below > 0 ? t.field.below(below) : "",
|
|
16411
|
-
pasted > 1 ? t.field.pasted(pasted) : active && where.lines > 1 ? t.field.line(where.line, where.lines) : ""
|
|
16412
|
-
].filter(Boolean).join(" \xB7 ");
|
|
16413
|
-
return /* @__PURE__ */ jsx12(TitledBox, { title: title2, color: active ? color2 : C.line, titleColor: titleColor ?? (active ? color2 : C.dim), foot: foot || void 0, flexShrink: 0, children: shown.map((l, i) => /* @__PURE__ */ jsx12(Box9, { flexShrink: 0, children: /* @__PURE__ */ jsx12(LineText, { line: l }) }, i)) });
|
|
16493
|
+
if (key.return) onPick({ model: row2.id, effort: row2.efforts.length ? effort ?? null : null });
|
|
16494
|
+
});
|
|
16495
|
+
if (models.status !== "ready") return /* @__PURE__ */ jsx12(Text9, { color: C.faint, children: t.launch.loading });
|
|
16496
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
16497
|
+
models.error ? /* @__PURE__ */ jsx12(Text9, { color: C.yellow, children: models.error }) : null,
|
|
16498
|
+
rows.map((r, i) => /* @__PURE__ */ jsxs8(Text9, { wrap: "truncate", backgroundColor: i === at ? C.sel : void 0, children: [
|
|
16499
|
+
/* @__PURE__ */ jsxs8(Text9, { color: i === at ? C.coral : C.faint, children: [
|
|
16500
|
+
i === at ? g.cursor : " ",
|
|
16501
|
+
" "
|
|
16502
|
+
] }),
|
|
16503
|
+
/* @__PURE__ */ jsx12(Text9, { color: C.fg, children: r.label }),
|
|
16504
|
+
i === at && r.efforts.length ? /* @__PURE__ */ jsxs8(Text9, { color: C.dim, children: [
|
|
16505
|
+
" ",
|
|
16506
|
+
r.efforts.map((e) => e === (effort ?? r.defaultEffort) ? `${g.prev} ${e} ${g.next}` : e).join(" ")
|
|
16507
|
+
] }) : null
|
|
16508
|
+
] }, r.id ?? "-"))
|
|
16509
|
+
] });
|
|
16414
16510
|
}
|
|
16415
16511
|
|
|
16416
16512
|
// src/tui/screens/Session.tsx
|
|
@@ -16438,7 +16534,7 @@ function SessionView({ s }) {
|
|
|
16438
16534
|
const [anchor, setAnchor] = useState7(null);
|
|
16439
16535
|
useEditing(mode === "reply" || mode === "note");
|
|
16440
16536
|
const items = d.threads.get(s.id);
|
|
16441
|
-
|
|
16537
|
+
useEffect8(() => {
|
|
16442
16538
|
if (!items && !s.offline) client.send({ type: "session.thread", id: s.id });
|
|
16443
16539
|
}, [items === void 0, s.id, d.snapshotSeq]);
|
|
16444
16540
|
const width = Math.max(20, columns - 6);
|
|
@@ -16460,7 +16556,7 @@ function SessionView({ s }) {
|
|
|
16460
16556
|
};
|
|
16461
16557
|
const images = useMemo3(() => (items ? threadImages(items, unfolded) : []).map((ref) => ({ ref, route: { id: s.id } })), [items, unfolded, s.id]);
|
|
16462
16558
|
const chips = useChips((image2) => putImage(client, image2, { id: s.id }));
|
|
16463
|
-
const paste =
|
|
16559
|
+
const paste = usePaste2(chips);
|
|
16464
16560
|
const openImage2 = useOpenImage();
|
|
16465
16561
|
const machine = splitId(s.id)?.machine ?? "local";
|
|
16466
16562
|
const canSwitch = machines.find((m) => m.id === machine)?.daemon?.modelSwitch === true;
|
|
@@ -16673,7 +16769,7 @@ function PendingBox({ s, a }) {
|
|
|
16673
16769
|
|
|
16674
16770
|
// src/tui/screens/Ask.tsx
|
|
16675
16771
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16676
|
-
import { useEffect as
|
|
16772
|
+
import { useEffect as useEffect10, useMemo as useMemo4, useRef as useRef10, useState as useState9 } from "react";
|
|
16677
16773
|
import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
|
|
16678
16774
|
|
|
16679
16775
|
// ../ui/src/chat/actionRow.ts
|
|
@@ -16830,11 +16926,11 @@ function openCards(turns2) {
|
|
|
16830
16926
|
}
|
|
16831
16927
|
|
|
16832
16928
|
// src/tui/ui/throttle.ts
|
|
16833
|
-
import { useEffect as
|
|
16929
|
+
import { useEffect as useEffect9, useRef as useRef9, useState as useState8 } from "react";
|
|
16834
16930
|
function useThrottled(value, ms) {
|
|
16835
16931
|
const [shown, setShown] = useState8(value);
|
|
16836
16932
|
const last = useRef9(0);
|
|
16837
|
-
|
|
16933
|
+
useEffect9(() => {
|
|
16838
16934
|
const show = () => {
|
|
16839
16935
|
last.current = Date.now();
|
|
16840
16936
|
setShown(value);
|
|
@@ -17013,7 +17109,7 @@ function Ask() {
|
|
|
17013
17109
|
const [anchor, setAnchor] = useState9(null);
|
|
17014
17110
|
const [view, setView] = useState9(10);
|
|
17015
17111
|
const chips = useChips(null);
|
|
17016
|
-
const paste =
|
|
17112
|
+
const paste = usePaste2(chips);
|
|
17017
17113
|
const openImage2 = useOpenImage();
|
|
17018
17114
|
const [busy, setBusy] = useState9(null);
|
|
17019
17115
|
const [rowOf2, setRowOf] = useState9({});
|
|
@@ -17021,7 +17117,7 @@ function Ask() {
|
|
|
17021
17117
|
const [review, setReview] = useState9(null);
|
|
17022
17118
|
useEditing(focus === -1 && !confirm);
|
|
17023
17119
|
const sentAsk = useRef10(null);
|
|
17024
|
-
|
|
17120
|
+
useEffect10(() => {
|
|
17025
17121
|
const s = sentAsk.current;
|
|
17026
17122
|
const turn = s && d.chat.find((x) => x.qid === s.qid);
|
|
17027
17123
|
if (!s || !turn) return;
|
|
@@ -17037,10 +17133,10 @@ function Ask() {
|
|
|
17037
17133
|
height: (text5, indent2) => text5.split("\n").reduce((n, l) => n + Math.max(1, wrapSegs([{ text: l, color: C.fg }], Math.max(10, columns - 8 - indent2)).length), 0)
|
|
17038
17134
|
};
|
|
17039
17135
|
const planOf = (turn) => cardPlan(turn.actions ?? [], room);
|
|
17040
|
-
|
|
17136
|
+
useEffect10(() => {
|
|
17041
17137
|
if (focus >= cards.length) setFocus(cards.length ? cards.length - 1 : -1);
|
|
17042
17138
|
}, [cards.length, focus]);
|
|
17043
|
-
|
|
17139
|
+
useEffect10(() => {
|
|
17044
17140
|
if (!review) return;
|
|
17045
17141
|
const i = cards.findIndex((c) => c.qid === review);
|
|
17046
17142
|
if (i >= 0) {
|
|
@@ -17048,21 +17144,21 @@ function Ask() {
|
|
|
17048
17144
|
setReview(null);
|
|
17049
17145
|
}
|
|
17050
17146
|
}, [cards, review]);
|
|
17051
|
-
|
|
17147
|
+
useEffect10(() => {
|
|
17052
17148
|
if (confirming && !cards.some((c) => c.kind === "action" && c.qid === confirming.qid)) setConfirming(null);
|
|
17053
17149
|
}, [confirming, cards]);
|
|
17054
|
-
|
|
17150
|
+
useEffect10(() => {
|
|
17055
17151
|
const turn = confirming && d.chat.find((x) => x.qid === confirming.qid);
|
|
17056
17152
|
if (turn && !confirming.rows.every((i) => appliable(planOf(turn).shown[i]))) setConfirming(null);
|
|
17057
17153
|
});
|
|
17058
17154
|
const ignored = [...d.chat].filter((x) => x.ignoredAt).sort((a, b2) => b2.ignoredAt.localeCompare(a.ignoredAt))[0];
|
|
17059
|
-
|
|
17155
|
+
useEffect10(() => {
|
|
17060
17156
|
if (!busy?.rid) return;
|
|
17061
17157
|
return client.onMessage((m) => {
|
|
17062
17158
|
if (m.type === "chat.acted" && m.rid === busy.rid) setBusy(null);
|
|
17063
17159
|
});
|
|
17064
17160
|
}, [busy?.rid]);
|
|
17065
|
-
|
|
17161
|
+
useEffect10(() => {
|
|
17066
17162
|
if (!busy) return;
|
|
17067
17163
|
const turn = d.chat.find((x) => x.qid === busy.qid);
|
|
17068
17164
|
if (turn !== busy.turn && !busy.rid || (d.errors.at(-1)?.seq ?? 0) > busy.errorSeq) {
|
|
@@ -17278,7 +17374,7 @@ function Ask() {
|
|
|
17278
17374
|
}
|
|
17279
17375
|
if (input === "s" && cards[focus].kind === "team") {
|
|
17280
17376
|
const team = d.chat.find((x) => x.qid === cards[focus].qid)?.team;
|
|
17281
|
-
if (team && !team.template && !team.savedPath) client.send({ type: "team.save", rid: randomUUID2(), qid: cards[focus].qid, name: team.name, seats: team.seats.map((x) => ({ role: x.role, tool: x.tool, ...x.model ? { model: x.model } : {}, ...x.effort ? { effort: x.effort } : {} })) });
|
|
17377
|
+
if (team && !team.template && !team.savedPath) client.send({ type: "team.save", rid: randomUUID2(), qid: cards[focus].qid, name: team.name, seats: team.seats.map((x) => ({ role: x.role, tool: x.tool, ...x.model ? { model: x.model } : {}, ...x.effort ? { effort: x.effort } : {}, ...x.name ? { name: x.name } : {} })) });
|
|
17282
17378
|
return true;
|
|
17283
17379
|
}
|
|
17284
17380
|
if (key.escape) {
|
|
@@ -17395,6 +17491,7 @@ function CardBox({ card, turn, focused, busy }) {
|
|
|
17395
17491
|
] }, x.role)),
|
|
17396
17492
|
auto.length ? /* @__PURE__ */ jsx15(Text12, { color: C.yellow, wrap: "truncate", children: t.ask.autoWarning(auto.join(", ")) }) : null,
|
|
17397
17493
|
team.error ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.error }) : null,
|
|
17494
|
+
team.saveError && !team.savedPath ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.saveError }) : null,
|
|
17398
17495
|
/* @__PURE__ */ jsxs11(Box12, { columnGap: 2, children: [
|
|
17399
17496
|
/* @__PURE__ */ jsx15(Button, { label: busy ? `${g.live} ${t.ask.launchTeam}` : t.ask.launchTeam, k: "\u21B5", primary: focused }),
|
|
17400
17497
|
!team.template ? team.savedPath ? /* @__PURE__ */ jsx15(Text12, { color: C.faint, children: t.ask.saved }) : /* @__PURE__ */ jsx15(Button, { label: t.ask.save, k: "s" }) : null,
|
|
@@ -17481,7 +17578,7 @@ function CardBox({ card, turn, focused, busy }) {
|
|
|
17481
17578
|
}
|
|
17482
17579
|
|
|
17483
17580
|
// src/tui/screens/Launch.tsx
|
|
17484
|
-
import { useEffect as
|
|
17581
|
+
import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef11, useState as useState10 } from "react";
|
|
17485
17582
|
import { Box as Box13, Text as Text14, useInput as useInput6 } from "ink";
|
|
17486
17583
|
|
|
17487
17584
|
// ../fleet/src/launch.ts
|
|
@@ -17633,7 +17730,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17633
17730
|
const [repoAt, setRepoAt] = useState10(0);
|
|
17634
17731
|
const [launching, setLaunching] = useState10(null);
|
|
17635
17732
|
useEditing(true);
|
|
17636
|
-
|
|
17733
|
+
useEffect11(() => {
|
|
17637
17734
|
if (form.machine === null) {
|
|
17638
17735
|
const up = machines.find((m) => m.ready);
|
|
17639
17736
|
if (up) setForm((f) => withMachine(f, list3, up.id));
|
|
@@ -17656,7 +17753,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17656
17753
|
const readHere = sameMachine && target !== null && target === prefill.cwd;
|
|
17657
17754
|
const asked2 = sameMachine && !readHere && target !== null ? `${withFolder.machine}\0${target}` : null;
|
|
17658
17755
|
const [seen, setSeen] = useState10(null);
|
|
17659
|
-
|
|
17756
|
+
useEffect11(() => {
|
|
17660
17757
|
if (!asked2) return;
|
|
17661
17758
|
const [machineId, cwd] = asked2.split("\0");
|
|
17662
17759
|
const requestId = `tui-role-${randomUUID3()}`;
|
|
@@ -17683,7 +17780,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17683
17780
|
const [name, setName] = useState10(null);
|
|
17684
17781
|
const shownName = name ?? drawn.name;
|
|
17685
17782
|
const missing = missingOf(withFolder, machines);
|
|
17686
|
-
|
|
17783
|
+
useEffect11(() => {
|
|
17687
17784
|
if (!launching) return;
|
|
17688
17785
|
if ((d.lastLaunched?.seq ?? 0) > launching.seq) {
|
|
17689
17786
|
select(d.lastLaunched.id);
|
|
@@ -17894,7 +17991,7 @@ function TeamLaunch({ onSession, prefill }) {
|
|
|
17894
17991
|
const [name, setName] = useState10("");
|
|
17895
17992
|
const [launching, setLaunching] = useState10(false);
|
|
17896
17993
|
useEditing(true);
|
|
17897
|
-
|
|
17994
|
+
useEffect11(() => {
|
|
17898
17995
|
if (!cwd) {
|
|
17899
17996
|
setDefs({ roles: [], templates: [] });
|
|
17900
17997
|
return;
|
|
@@ -18180,7 +18277,7 @@ function Help() {
|
|
|
18180
18277
|
const k = t.keys;
|
|
18181
18278
|
const groups = [
|
|
18182
18279
|
[t.help.global, [["1\u20266", t.help.screens], ["/", k.ask], ["+", k.launch], [",", k.settings], ["?", k.help], ["esc", k.back], ["q", k.quit]]],
|
|
18183
|
-
[t.help.overview, [["j k \u2191 \u2193", k.navigate], ["tab", k.next], ["\u21B5", k.open], ["y n A", k.answer], ["1\u20269", k.answer], ["r", t.overview.denyWithNote], ["a", k.attach], ["i", k.interrupt]]],
|
|
18280
|
+
[t.help.overview, [["j k \u2191 \u2193", k.navigate], ["tab", k.next], ["\u21B5", k.open], ["y n A", k.answer], ["1\u20269", k.answer], ["r", t.overview.denyWithNote], ["D", k.decide], ["a", k.attach], ["i", k.interrupt]]],
|
|
18184
18281
|
[t.help.session, [["y n A 1\u20269", k.answer], ["r \u21B5", k.write], ["j k PgUp PgDn", k.scroll], [t.keys.space, k.unfold], ["d", k.diffs], ["t", k.terminal], ["a", k.attach], ["m", k.model], ["i", k.interrupt]]],
|
|
18185
18282
|
[t.help.ask, [["\u21B5", k.send], ["tab", k.cycle], ["PgUp PgDn", k.scroll], ["j k G", k.scroll], ["ctrl+n", k.newChat], ["ctrl+c", k.stopAnswer]]],
|
|
18186
18283
|
[t.help.field, [["\u21B5", k.sendOnly], ["alt+\u21B5 ctrl+j", k.newLine], ["\u2191 \u2193", k.lineHistory], ["ctrl \u2190 \u2192", k.word], ["ctrl+w", t.help.wordBefore], ["ctrl+u", k.clear], ["esc", k.keepAndLeave]]],
|
|
@@ -18218,7 +18315,7 @@ function Help() {
|
|
|
18218
18315
|
}
|
|
18219
18316
|
|
|
18220
18317
|
// src/tui/screens/Teams.tsx
|
|
18221
|
-
import { useEffect as
|
|
18318
|
+
import { useEffect as useEffect12, useMemo as useMemo7, useState as useState13 } from "react";
|
|
18222
18319
|
import { Box as Box16, Text as Text17 } from "ink";
|
|
18223
18320
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
18224
18321
|
|
|
@@ -18298,7 +18395,7 @@ function Teams() {
|
|
|
18298
18395
|
const [problems, setProblems] = useState13(false);
|
|
18299
18396
|
const [silent, setSilent] = useState13(false);
|
|
18300
18397
|
const [reads, setReads] = useState13(0);
|
|
18301
|
-
|
|
18398
|
+
useEffect12(() => {
|
|
18302
18399
|
setDefs(null);
|
|
18303
18400
|
setSilent(false);
|
|
18304
18401
|
const requestId = `tui-catalog-${randomUUID4()}`;
|
|
@@ -18579,7 +18676,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
18579
18676
|
const kept = useRef13({ drafts: /* @__PURE__ */ new Map(), sent: /* @__PURE__ */ new Map(), asked: [] }).current;
|
|
18580
18677
|
const armed = useRef13(0);
|
|
18581
18678
|
const catalog = useRef13({});
|
|
18582
|
-
|
|
18679
|
+
useEffect13(() => {
|
|
18583
18680
|
if (!lastToast) return;
|
|
18584
18681
|
const timer2 = setTimeout(() => setToast(null), lastToast.ms ?? TOAST_MS);
|
|
18585
18682
|
return () => clearTimeout(timer2);
|
|
@@ -18588,22 +18685,22 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
18588
18685
|
const setEditing = useCallback((on) => setEditingState(on), []);
|
|
18589
18686
|
const view = useMemo8(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
|
|
18590
18687
|
const order2 = useRef13([]);
|
|
18591
|
-
|
|
18688
|
+
useEffect13(() => {
|
|
18592
18689
|
setSelectedId((cur) => cur === null && view.yours[0] ? view.yours[0].id : keepSelection(view.order, cur, order2.current));
|
|
18593
18690
|
order2.current = view.order;
|
|
18594
18691
|
}, [view]);
|
|
18595
18692
|
const selected = (selectedId ? d.byId.get(selectedId) : void 0) ?? null;
|
|
18596
18693
|
const seen = useRef13(null);
|
|
18597
18694
|
const titled = useRef13("");
|
|
18598
|
-
|
|
18695
|
+
useEffect13(() => {
|
|
18599
18696
|
if (seen.current) for (const text5 of asked(seen.current, d.sessions, t.notify)) write(notification(text5, notify, env));
|
|
18600
18697
|
seen.current = d.byId;
|
|
18601
|
-
const next2 = title(t.notify.title(listed(d.sessions).filter(isPending).length));
|
|
18698
|
+
const next2 = title(t.notify.title(listed(d.sessions).filter(isPending).length + openDecisions(tasks).length));
|
|
18602
18699
|
if (next2 !== titled.current) {
|
|
18603
18700
|
titled.current = next2;
|
|
18604
18701
|
write(next2);
|
|
18605
18702
|
}
|
|
18606
|
-
}, [d.byId]);
|
|
18703
|
+
}, [d.byId, tasks]);
|
|
18607
18704
|
const actions = useActions({ client, sessions: d.sessions, errors: d.errors, t, toast });
|
|
18608
18705
|
const go = useCallback((next2) => {
|
|
18609
18706
|
setScreen((cur) => {
|