@paradow/ledge 0.29.0 → 0.31.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-CFYJM2A2.js} +4 -4
- package/dist/cli/{attach-OC64CLB2.js → attach-23PTEIAA.js} +9 -8
- package/dist/cli/{bridge-TWX4V37E.js → bridge-UJKWEKA5.js} +1 -1
- package/dist/cli/{chunk-RFFTWAAF.js → chunk-6YBIZZQG.js} +1 -1
- package/dist/cli/{chunk-TREGH6WM.js → chunk-GMG3ROBU.js} +504 -302
- package/dist/cli/{chunk-MZVMW356.js → chunk-HEYHVOD6.js} +2 -2
- package/dist/cli/{chunk-XZYQMKKO.js → chunk-HNC5Q5PV.js} +3 -3
- package/dist/cli/{chunk-WCBXS3EU.js → chunk-HUY4CFMX.js} +107 -15
- package/dist/cli/{chunk-73YNZ3BN.js → chunk-MIPE2FWL.js} +2 -1
- package/dist/cli/{chunk-7S7ZLGIY.js → chunk-MYIBYAZC.js} +36 -8
- package/dist/cli/{chunk-WIUJNSAZ.js → chunk-NXNIRIGI.js} +5 -5
- package/dist/cli/{chunk-353WRRGG.js → chunk-QXH2L4UH.js} +3 -3
- package/dist/cli/{chunk-UWDE365N.js → chunk-TCJG3EHB.js} +47 -3
- package/dist/cli/{chunk-K3SDHHZF.js → chunk-TLFKLMBN.js} +1 -1
- package/dist/cli/{chunk-TNYBWWRO.js → chunk-WF27BASF.js} +2 -2
- package/dist/cli/chunk-WY5WSK2D.js +6 -0
- package/dist/cli/{chunk-ETGOWBUL.js → chunk-YI4V7YVW.js} +3 -3
- package/dist/cli/{daemon-ZTZ7VMYA.js → daemon-WW2P7RDV.js} +7 -7
- package/dist/cli/{decide-4BIYS3Y2.js → decide-HLRD37PW.js} +8 -6
- package/dist/cli/{handover-IS7BJYGZ.js → handover-56M4LIOP.js} +4 -4
- package/dist/cli/{hook-3VKVD4IP.js → hook-UJVKYSQ5.js} +34 -13
- package/dist/cli/index.js +16 -16
- package/dist/cli/{launch-XAPN2M3Q.js → launch-GIPQDPAC.js} +9 -8
- package/dist/cli/{send-ZLF2ZXMJ.js → send-HWZHE65N.js} +4 -4
- package/dist/cli/{status-NONO6E2S.js → status-V6ITP7VT.js} +2 -2
- package/dist/cli/{task-47MVWDCY.js → task-N53TM34B.js} +4 -4
- package/dist/cli/{team-EJ4YIXJH.js → team-CZFZ24P6.js} +6 -6
- package/dist/cli/{tui-CAP4O4YQ.js → tui-K32C34XI.js} +1274 -1176
- package/dist/cli/{version-EHBPJO7K.js → version-QIFJVKAQ.js} +1 -1
- package/dist/cli/{who-FHAXYCM2.js → who-NIOLHHB3.js} +3 -3
- package/dist/ledge-guide.md +268 -0
- 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-NXNIRIGI.js";
|
|
4
4
|
import {
|
|
5
5
|
imageInfo
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GMG3ROBU.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-HUY4CFMX.js";
|
|
11
|
+
import "./chunk-TCJG3EHB.js";
|
|
12
|
+
import "./chunk-MIPE2FWL.js";
|
|
13
|
+
import "./chunk-TLFKLMBN.js";
|
|
14
|
+
import "./chunk-6YBIZZQG.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,8 @@ import {
|
|
|
43
44
|
readDaemonToken,
|
|
44
45
|
taskClosed,
|
|
45
46
|
transmissions
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-MYIBYAZC.js";
|
|
48
|
+
import "./chunk-WY5WSK2D.js";
|
|
47
49
|
import "./chunk-NLL2EQJ2.js";
|
|
48
50
|
import "./chunk-6IYPRUQG.js";
|
|
49
51
|
import {
|
|
@@ -1303,7 +1305,7 @@ function createFleetClient(config, makeClient = createDaemonClient, opts = {}) {
|
|
|
1303
1305
|
const { machineId: _m, ...rest } = outgoing;
|
|
1304
1306
|
outgoing = rest;
|
|
1305
1307
|
}
|
|
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";
|
|
1308
|
+
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
1309
|
if (outgoing.type === "team.join" || outgoing.type === "team.leave") {
|
|
1308
1310
|
const o = splitId(outgoing.session);
|
|
1309
1311
|
if (o) {
|
|
@@ -1510,7 +1512,7 @@ function getImage(client, attachment, route = {}, preview = false, timeoutMs = T
|
|
|
1510
1512
|
}
|
|
1511
1513
|
|
|
1512
1514
|
// src/tui/App.tsx
|
|
1513
|
-
import { useCallback, useEffect as
|
|
1515
|
+
import { useCallback, useEffect as useEffect13, useMemo as useMemo8, useRef as useRef13, useState as useState14 } from "react";
|
|
1514
1516
|
import { useApp, useInput as useInput7, useStdout, useWindowSize as useWindowSize2 } from "ink";
|
|
1515
1517
|
|
|
1516
1518
|
// src/tui/actions.ts
|
|
@@ -1935,7 +1937,8 @@ var fr = {
|
|
|
1935
1937
|
"team.dissolve": (v) => need(v, ["team"], (w) => `Dissoudre l\u2019\xE9quipe ${b(w.team)}`),
|
|
1936
1938
|
"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
1939
|
"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)}`)
|
|
1940
|
+
"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)}`),
|
|
1941
|
+
"task.answer": (v) => need(v, ["task", "session"], (w) => `R\xE9pondre \xE0 la d\xE9cision \xAB ${short(w.task)} \xBB de ${b(w.session)}`)
|
|
1939
1942
|
},
|
|
1940
1943
|
why: (code3, action, v) => {
|
|
1941
1944
|
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 +1988,8 @@ var fr = {
|
|
|
1985
1988
|
return k ? `la t\xE2che ${k} est d\xE9j\xE0 close.` : gone;
|
|
1986
1989
|
case "holder":
|
|
1987
1990
|
return s && k ? `${s} tient d\xE9j\xE0 la t\xE2che ${k}.` : gone;
|
|
1991
|
+
case "not_decision":
|
|
1992
|
+
return k ? `la t\xE2che ${k} n\u2019est pas une d\xE9cision qui t\u2019est pos\xE9e.` : gone;
|
|
1988
1993
|
case "changed":
|
|
1989
1994
|
return "la situation a chang\xE9 depuis ma r\xE9ponse : redemande-moi.";
|
|
1990
1995
|
case "failed":
|
|
@@ -2011,7 +2016,8 @@ var en = {
|
|
|
2011
2016
|
"team.dissolve": (v) => need(v, ["team"], (w) => `Dissolve the team ${b(w.team)}`),
|
|
2012
2017
|
"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
2018
|
"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)}`)
|
|
2019
|
+
"task.pass": (v) => need(v, ["task", "owner", "session"], (w) => `Pass ${b(w.owner)}\u2019s task \u201C${short(w.task)}\u201D to ${b(w.session)}`),
|
|
2020
|
+
"task.answer": (v) => need(v, ["task", "session"], (w) => `Answer ${b(w.session)}\u2019s decision \u201C${short(w.task)}\u201D`)
|
|
2015
2021
|
},
|
|
2016
2022
|
why: (code3, action, v) => {
|
|
2017
2023
|
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 +2067,8 @@ var en = {
|
|
|
2061
2067
|
return k ? `the task ${k} is already closed.` : gone;
|
|
2062
2068
|
case "holder":
|
|
2063
2069
|
return s && k ? `${s} already holds the task ${k}.` : gone;
|
|
2070
|
+
case "not_decision":
|
|
2071
|
+
return k ? `the task ${k} is not a decision put to you.` : gone;
|
|
2064
2072
|
case "changed":
|
|
2065
2073
|
return "things have changed since my answer: ask me again.";
|
|
2066
2074
|
case "failed":
|
|
@@ -2095,7 +2103,8 @@ var fr2 = {
|
|
|
2095
2103
|
"team.dissolve": "Dissoudre",
|
|
2096
2104
|
"team.save": "Enregistrer",
|
|
2097
2105
|
"task.cancel": "Abandonner",
|
|
2098
|
-
"task.pass": "Passer le relais"
|
|
2106
|
+
"task.pass": "Passer le relais",
|
|
2107
|
+
"task.answer": "R\xE9pondre"
|
|
2099
2108
|
},
|
|
2100
2109
|
apply: "Appliquer",
|
|
2101
2110
|
deny: "Refuser",
|
|
@@ -2161,7 +2170,8 @@ var en2 = {
|
|
|
2161
2170
|
"team.dissolve": "Dissolve",
|
|
2162
2171
|
"team.save": "Save",
|
|
2163
2172
|
"task.cancel": "Drop",
|
|
2164
|
-
"task.pass": "Hand over"
|
|
2173
|
+
"task.pass": "Hand over",
|
|
2174
|
+
"task.answer": "Answer"
|
|
2165
2175
|
},
|
|
2166
2176
|
apply: "Apply",
|
|
2167
2177
|
deny: "Deny",
|
|
@@ -2270,6 +2280,9 @@ var fr3 = {
|
|
|
2270
2280
|
resume: "reprendre l\u2019\xE9quipe",
|
|
2271
2281
|
teamMode: "session ou \xE9quipe",
|
|
2272
2282
|
handover: "passer le relais",
|
|
2283
|
+
decide: "r\xE9pondre \xE0 la d\xE9cision",
|
|
2284
|
+
sendAnswer: "envoyer la r\xE9ponse",
|
|
2285
|
+
edit: "modifier",
|
|
2273
2286
|
sendOnly: "envoyer",
|
|
2274
2287
|
newLine: "nouvelle ligne",
|
|
2275
2288
|
keepAndLeave: "garder et sortir",
|
|
@@ -2336,6 +2349,10 @@ var fr3 = {
|
|
|
2336
2349
|
relievedBy: (name) => `relev\xE9e par ${name}`,
|
|
2337
2350
|
relieves: (name) => `rel\xE8ve de ${name}`,
|
|
2338
2351
|
ledgeTask: (owner, title2, state2) => `${owner} a ${state2 === "done" ? "fini" : state2 === "refused" ? "refus\xE9" : "abandonn\xE9"} \xAB ${title2} \xBB \xB7 t\xE2che de Ledge`,
|
|
2352
|
+
decision: (giver, title2) => `${giver} te demande : ${title2}`,
|
|
2353
|
+
decisionTitle: (giver, title2) => `R\xE9pondre \xE0 ${giver} \xB7 ${title2}`,
|
|
2354
|
+
decisionHint: "Ta r\xE9ponse, telle que la session la lira\u2026",
|
|
2355
|
+
confirmAnswer: (giver) => `Envoyer \xE0 ${giver} ? Une r\xE9ponse ne se reprend pas`,
|
|
2339
2356
|
joinWhich: (name) => `${name} rejoint quelle \xE9quipe ? (\u2191\u2193 \u21B5, esc)`,
|
|
2340
2357
|
noTeamHere: "Aucune \xE9quipe dans ce projet.",
|
|
2341
2358
|
notInTeam: "Cette session n\u2019est dans aucune \xE9quipe."
|
|
@@ -2555,6 +2572,7 @@ var fr3 = {
|
|
|
2555
2572
|
openFailed: (why) => `Image non ouverte : ${why}`
|
|
2556
2573
|
},
|
|
2557
2574
|
toast: {
|
|
2575
|
+
answerFailed: (why) => `R\xE9ponse non envoy\xE9e : ${why}`,
|
|
2558
2576
|
handover: (n) => `${n} \xE9crit son bilan : une nouvelle session prendra sa place`,
|
|
2559
2577
|
handoverRefused: (why) => `Rel\xE8ve refus\xE9e : ${why}`,
|
|
2560
2578
|
accept: (n) => `\u2713 ${n} : autoris\xE9`,
|
|
@@ -2657,6 +2675,9 @@ var en3 = {
|
|
|
2657
2675
|
resume: "resume the team",
|
|
2658
2676
|
teamMode: "session or team",
|
|
2659
2677
|
handover: "hand over",
|
|
2678
|
+
decide: "answer the decision",
|
|
2679
|
+
sendAnswer: "send the answer",
|
|
2680
|
+
edit: "edit",
|
|
2660
2681
|
sendOnly: "send",
|
|
2661
2682
|
newLine: "new line",
|
|
2662
2683
|
keepAndLeave: "keep and leave",
|
|
@@ -2723,6 +2744,10 @@ var en3 = {
|
|
|
2723
2744
|
relievedBy: (name) => `relieved by ${name}`,
|
|
2724
2745
|
relieves: (name) => `relieves ${name}`,
|
|
2725
2746
|
ledgeTask: (owner, title2, state2) => `${owner} ${state2 === "done" ? "finished" : state2 === "refused" ? "refused" : "dropped"} \u201C${title2}\u201D \xB7 Ledge\u2019s task`,
|
|
2747
|
+
decision: (giver, title2) => `${giver} asks you: ${title2}`,
|
|
2748
|
+
decisionTitle: (giver, title2) => `Answer ${giver} \xB7 ${title2}`,
|
|
2749
|
+
decisionHint: "Your answer, as the session will read it\u2026",
|
|
2750
|
+
confirmAnswer: (giver) => `Send to ${giver}? An answer cannot be taken back`,
|
|
2726
2751
|
joinWhich: (name) => `${name} joins which team? (\u2191\u2193 \u21B5, esc)`,
|
|
2727
2752
|
noTeamHere: "No team in this project.",
|
|
2728
2753
|
notInTeam: "This session is in no team."
|
|
@@ -2940,6 +2965,7 @@ var en3 = {
|
|
|
2940
2965
|
openFailed: (why) => `Image not opened: ${why}`
|
|
2941
2966
|
},
|
|
2942
2967
|
toast: {
|
|
2968
|
+
answerFailed: (why) => `Answer not sent: ${why}`,
|
|
2943
2969
|
handover: (n) => `${n} is writing its brief: a fresh session will take its place`,
|
|
2944
2970
|
handoverRefused: (why) => `Relief refused: ${why}`,
|
|
2945
2971
|
accept: (n) => `\u2713 ${n}: allowed`,
|
|
@@ -3078,6 +3104,7 @@ function exchanges(sessions, max, tasks = [], ledge = "Ledge") {
|
|
|
3078
3104
|
}
|
|
3079
3105
|
var nearContext = (sessions, near) => sessions.filter((s) => s.state !== "exited" && !s.archivedAt && (s.handover ? s.handover.state === "failed" : contextNear(s.context, near)));
|
|
3080
3106
|
var closedLedgeTasks = (tasks) => tasks.filter((x) => x.giver === LEDGE_GIVER && taskClosed(x) && !x.seen);
|
|
3107
|
+
var openDecisions = (tasks) => tasks.filter(openDecision);
|
|
3081
3108
|
|
|
3082
3109
|
// src/tui/model/time.ts
|
|
3083
3110
|
function ago(iso, now2, t) {
|
|
@@ -3126,8 +3153,8 @@ function notification(text5, mode, env) {
|
|
|
3126
3153
|
var title = (text5) => `\x1B]0;${text5.replace(/[\u0000-\u001f\u007f]/g, " ")}\x07`;
|
|
3127
3154
|
|
|
3128
3155
|
// src/tui/screens/Overview.tsx
|
|
3129
|
-
import { useMemo as useMemo2 } from "react";
|
|
3130
|
-
import { Box as
|
|
3156
|
+
import { useEffect as useEffect6, useMemo as useMemo2 } from "react";
|
|
3157
|
+
import { Box as Box8, Text as Text7 } from "ink";
|
|
3131
3158
|
import { randomUUID } from "crypto";
|
|
3132
3159
|
import { useState as useState3 } from "react";
|
|
3133
3160
|
|
|
@@ -3146,49 +3173,48 @@ function botState(session) {
|
|
|
3146
3173
|
return FROM_SESSION[session.state];
|
|
3147
3174
|
}
|
|
3148
3175
|
|
|
3149
|
-
// src/
|
|
3150
|
-
var
|
|
3151
|
-
var
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3176
|
+
// ../ui/src/thread/turns.ts
|
|
3177
|
+
var OPEN_TURNS = 3;
|
|
3178
|
+
var byTime = (a, b2) => a.at < b2.at ? -1 : a.at > b2.at ? 1 : 0;
|
|
3179
|
+
function withoutEcho(items, reply) {
|
|
3180
|
+
if (!reply) return items;
|
|
3181
|
+
let last = -1;
|
|
3182
|
+
items.forEach((it, i) => {
|
|
3183
|
+
if (it.kind === "message") last = i;
|
|
3184
|
+
});
|
|
3185
|
+
const echo = items[last];
|
|
3186
|
+
return echo?.kind === "message" && echo.text.trim() === reply.text.trim() ? items.filter((_, i) => i !== last) : items;
|
|
3158
3187
|
}
|
|
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?.();
|
|
3188
|
+
function turns(items) {
|
|
3189
|
+
const map3 = /* @__PURE__ */ new Map();
|
|
3190
|
+
for (const it of items) {
|
|
3191
|
+
if (it.helperId) continue;
|
|
3192
|
+
const t = map3.get(it.turn) ?? { n: it.turn, items: [] };
|
|
3193
|
+
t.items.push(it);
|
|
3194
|
+
if (it.kind === "prompt") t.prompt = it;
|
|
3195
|
+
if (it.kind === "reply") t.reply = it;
|
|
3196
|
+
map3.set(it.turn, t);
|
|
3174
3197
|
}
|
|
3175
|
-
return () => {
|
|
3176
|
-
listeners.delete(fn);
|
|
3177
|
-
if (listeners.size === 0 && timer) {
|
|
3178
|
-
clearInterval(timer);
|
|
3179
|
-
timer = null;
|
|
3180
|
-
}
|
|
3181
|
-
};
|
|
3198
|
+
return [...map3.values()].sort((a, b2) => a.n - b2.n).map((t) => ({ ...t, items: withoutEcho([...t.items].sort(byTime), t.reply) }));
|
|
3182
3199
|
}
|
|
3183
|
-
var useNow = () => useSyncExternalStore4(subscribe, () => now);
|
|
3184
3200
|
|
|
3185
|
-
// src/
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
const
|
|
3189
|
-
|
|
3190
|
-
|
|
3201
|
+
// ../ui/src/thread/tools.ts
|
|
3202
|
+
function rawTarget(item) {
|
|
3203
|
+
let s = item.label.trim();
|
|
3204
|
+
const prefix = `${item.tool} \xB7 `;
|
|
3205
|
+
if (s.startsWith(prefix)) s = s.slice(prefix.length);
|
|
3206
|
+
if (s === item.tool) return "";
|
|
3207
|
+
if (s.startsWith("{")) {
|
|
3208
|
+
try {
|
|
3209
|
+
const o = JSON.parse(s);
|
|
3210
|
+
const v = ["pattern", "query", "url", "file_path", "path", "command", "description", "prompt"].map((k) => o[k]).find((x) => typeof x === "string" && x);
|
|
3211
|
+
if (typeof v === "string") s = v;
|
|
3212
|
+
} catch {
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
return (s.split("\n")[0] ?? "").trim();
|
|
3191
3216
|
}
|
|
3217
|
+
var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3192
3218
|
|
|
3193
3219
|
// src/tui/theme.ts
|
|
3194
3220
|
var THEMES = ["auto", "dark", "light"];
|
|
@@ -3253,9 +3279,6 @@ function setTone(next2) {
|
|
|
3253
3279
|
}
|
|
3254
3280
|
var projectColor = (color2) => color2 ? PROJECT_COLORS[color2] : C.dim;
|
|
3255
3281
|
|
|
3256
|
-
// src/tui/ui/Frame.tsx
|
|
3257
|
-
import { Box as Box2, Text as Text2 } from "ink";
|
|
3258
|
-
|
|
3259
3282
|
// src/tui/model/cells.ts
|
|
3260
3283
|
import stringWidth from "string-width";
|
|
3261
3284
|
var graphemes = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
@@ -3332,753 +3355,63 @@ function row(prefix, body, meta, width, o = {}) {
|
|
|
3332
3355
|
});
|
|
3333
3356
|
}
|
|
3334
3357
|
|
|
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)) });
|
|
3358
|
+
// ../../node_modules/.pnpm/ccount@2.0.1/node_modules/ccount/index.js
|
|
3359
|
+
function ccount(value, character) {
|
|
3360
|
+
const source2 = String(value);
|
|
3361
|
+
if (typeof character !== "string") {
|
|
3362
|
+
throw new TypeError("Expected character");
|
|
3363
|
+
}
|
|
3364
|
+
let count = 0;
|
|
3365
|
+
let index2 = source2.indexOf(character);
|
|
3366
|
+
while (index2 !== -1) {
|
|
3367
|
+
count++;
|
|
3368
|
+
index2 = source2.indexOf(character, index2 + character.length);
|
|
3369
|
+
}
|
|
3370
|
+
return count;
|
|
3358
3371
|
}
|
|
3359
3372
|
|
|
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
|
-
] });
|
|
3373
|
+
// ../../node_modules/.pnpm/devlop@1.1.0/node_modules/devlop/lib/default.js
|
|
3374
|
+
function ok() {
|
|
3413
3375
|
}
|
|
3414
3376
|
|
|
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
|
-
] });
|
|
3377
|
+
// ../../node_modules/.pnpm/micromark-util-character@2.1.1/node_modules/micromark-util-character/index.js
|
|
3378
|
+
var asciiAlpha = regexCheck(/[A-Za-z]/);
|
|
3379
|
+
var asciiAlphanumeric = regexCheck(/[\dA-Za-z]/);
|
|
3380
|
+
var asciiAtext = regexCheck(/[#-'*+\--9=?A-Z^-~]/);
|
|
3381
|
+
function asciiControl(code3) {
|
|
3382
|
+
return (
|
|
3383
|
+
// Special whitespace codes (which have negative values), C0 and Control
|
|
3384
|
+
// character DEL
|
|
3385
|
+
code3 !== null && (code3 < 32 || code3 === 127)
|
|
3386
|
+
);
|
|
3431
3387
|
}
|
|
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
|
-
] });
|
|
3388
|
+
var asciiDigit = regexCheck(/\d/);
|
|
3389
|
+
var asciiHexDigit = regexCheck(/[\dA-Fa-f]/);
|
|
3390
|
+
var asciiPunctuation = regexCheck(/[!-/:-@[-`{-~]/);
|
|
3391
|
+
function markdownLineEnding(code3) {
|
|
3392
|
+
return code3 !== null && code3 < -2;
|
|
3393
|
+
}
|
|
3394
|
+
function markdownLineEndingOrSpace(code3) {
|
|
3395
|
+
return code3 !== null && (code3 < 0 || code3 === 32);
|
|
3396
|
+
}
|
|
3397
|
+
function markdownSpace(code3) {
|
|
3398
|
+
return code3 === -2 || code3 === -1 || code3 === 32;
|
|
3399
|
+
}
|
|
3400
|
+
var unicodePunctuation = regexCheck(new RegExp("\\p{P}|\\p{S}", "u"));
|
|
3401
|
+
var unicodeWhitespace = regexCheck(/\s/);
|
|
3402
|
+
function regexCheck(regex) {
|
|
3403
|
+
return check;
|
|
3404
|
+
function check(code3) {
|
|
3405
|
+
return code3 !== null && code3 > -1 && regex.test(String.fromCharCode(code3));
|
|
3406
|
+
}
|
|
3507
3407
|
}
|
|
3508
3408
|
|
|
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");
|
|
3409
|
+
// ../../node_modules/.pnpm/escape-string-regexp@5.0.0/node_modules/escape-string-regexp/index.js
|
|
3410
|
+
function escapeStringRegexp(string3) {
|
|
3411
|
+
if (typeof string3 !== "string") {
|
|
3412
|
+
throw new TypeError("Expected a string");
|
|
3413
|
+
}
|
|
3414
|
+
return string3.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
|
4082
3415
|
}
|
|
4083
3416
|
|
|
4084
3417
|
// ../../node_modules/.pnpm/unist-util-is@6.0.1/node_modules/unist-util-is/lib/index.js
|
|
@@ -15927,6 +15260,9 @@ function imageLine(ref, t, key) {
|
|
|
15927
15260
|
var DIFF_MAX = 30;
|
|
15928
15261
|
var CAP = { tool: 3, error: 3, message: 3, notice: 2, permission: 3, helper: 2 };
|
|
15929
15262
|
var LIVE_PAD = 9;
|
|
15263
|
+
function wrap2(text5, width) {
|
|
15264
|
+
return wrapSegs([{ text: text5.replace(/\r/g, "") }], Math.max(10, width)).map((l) => l.map((s) => s.text).join(""));
|
|
15265
|
+
}
|
|
15930
15266
|
function diffCount(diff) {
|
|
15931
15267
|
let add = 0, del2 = 0;
|
|
15932
15268
|
for (const l of (diff ?? "").split("\n")) {
|
|
@@ -16018,399 +15354,1160 @@ function foldedLine(turn, o) {
|
|
|
16018
15354
|
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
15355
|
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
15356
|
}
|
|
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);
|
|
15357
|
+
function threadView(items, o) {
|
|
15358
|
+
const all2 = turns(items);
|
|
15359
|
+
const lines = [], keys = [], turnOf = /* @__PURE__ */ new Map();
|
|
15360
|
+
const opts = { ...o, cap: (n) => o.unfolded ? Infinity : n };
|
|
15361
|
+
const push2 = (key, l) => {
|
|
15362
|
+
for (const x of l) {
|
|
15363
|
+
lines.push(x);
|
|
15364
|
+
keys.push(key);
|
|
15365
|
+
}
|
|
15366
|
+
};
|
|
15367
|
+
all2.forEach((turn, i) => {
|
|
15368
|
+
const recent = i >= all2.length - OPEN_TURNS;
|
|
15369
|
+
const first = turn.prompt?.id ?? turn.items[0]?.id ?? String(i);
|
|
15370
|
+
for (const it of turn.items) turnOf.set(it.id, first);
|
|
15371
|
+
if (!recent && !o.unfolded) {
|
|
15372
|
+
push2(first, [cut(foldedLine(turn, o), o.width)]);
|
|
15373
|
+
return;
|
|
15374
|
+
}
|
|
15375
|
+
if (lines.length) push2(first, [[{ text: "" }]]);
|
|
15376
|
+
for (const it of turn.items) push2(it.id, itemLines(it, opts));
|
|
15377
|
+
});
|
|
15378
|
+
return { lines, keys, turns: turnOf };
|
|
15379
|
+
}
|
|
15380
|
+
function anchorLine(v, anchor) {
|
|
15381
|
+
const at = v.keys.indexOf(anchor.key);
|
|
15382
|
+
if (at >= 0) return at + anchor.offset;
|
|
15383
|
+
const turn = v.turns.get(anchor.key);
|
|
15384
|
+
return turn === void 0 ? -1 : v.keys.indexOf(turn);
|
|
15385
|
+
}
|
|
15386
|
+
function threadImages(items, unfolded) {
|
|
15387
|
+
const all2 = turns(items);
|
|
15388
|
+
return (unfolded ? all2 : all2.slice(-OPEN_TURNS)).flatMap((turn) => turn.items.flatMap((it) => it.kind === "prompt" ? it.images ?? [] : []));
|
|
15389
|
+
}
|
|
15390
|
+
|
|
15391
|
+
// src/tui/model/labels.ts
|
|
15392
|
+
var TOOL_NAME = { claude: "Claude Code", codex: "Codex", cursor: "Cursor Agent", omp: "OMP" };
|
|
15393
|
+
var modelLabel = (s, fallback) => [s.model ?? (s.effort ? fallback : ""), s.effort ?? ""].filter(Boolean).join(" \xB7 ") || fallback;
|
|
15394
|
+
var machineLabel = (s) => s.machineName ?? splitId(s.id)?.machine ?? "Local";
|
|
15395
|
+
var paneName = (s) => s.tmuxSession ?? `ledge-${splitId(s.id)?.id ?? s.id}`;
|
|
15396
|
+
function fit(s, n) {
|
|
15397
|
+
const cps = [...s];
|
|
15398
|
+
if (n <= 0) return "";
|
|
15399
|
+
return cps.length > n ? `${cps.slice(0, Math.max(0, n - 1)).join("")}\u2026` : s + " ".repeat(n - cps.length);
|
|
15400
|
+
}
|
|
15401
|
+
|
|
15402
|
+
// src/tui/ui/now.ts
|
|
15403
|
+
import { useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
15404
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
15405
|
+
var now = Date.now();
|
|
15406
|
+
var timer = null;
|
|
15407
|
+
function subscribe(fn) {
|
|
15408
|
+
listeners.add(fn);
|
|
15409
|
+
if (!timer) {
|
|
15410
|
+
now = Date.now();
|
|
15411
|
+
timer = setInterval(() => {
|
|
15412
|
+
now = Date.now();
|
|
15413
|
+
for (const l of listeners) l();
|
|
15414
|
+
}, 1e3);
|
|
15415
|
+
timer.unref?.();
|
|
15416
|
+
}
|
|
15417
|
+
return () => {
|
|
15418
|
+
listeners.delete(fn);
|
|
15419
|
+
if (listeners.size === 0 && timer) {
|
|
15420
|
+
clearInterval(timer);
|
|
15421
|
+
timer = null;
|
|
15422
|
+
}
|
|
15423
|
+
};
|
|
15424
|
+
}
|
|
15425
|
+
var useNow = () => useSyncExternalStore4(subscribe, () => now);
|
|
15426
|
+
|
|
15427
|
+
// src/tui/ui/Ago.tsx
|
|
15428
|
+
import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
|
|
15429
|
+
function Ago({ iso }) {
|
|
15430
|
+
const now2 = useNow();
|
|
15431
|
+
const { t } = useI18n();
|
|
15432
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: ago(iso, now2, t.time) });
|
|
15433
|
+
}
|
|
15434
|
+
|
|
15435
|
+
// src/tui/ui/Field.tsx
|
|
15436
|
+
import { useReducer, useRef as useRef6 } from "react";
|
|
15437
|
+
import { Box as Box3, usePaste, useInput } from "ink";
|
|
15438
|
+
|
|
15439
|
+
// src/tui/model/field.ts
|
|
15440
|
+
var graphemes2 = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
15441
|
+
var stops = (text5) => [...Array.from(graphemes2.segment(text5), (g) => g.index), text5.length];
|
|
15442
|
+
var newlines = (s) => s.replace(/\r\n?/g, "\n");
|
|
15443
|
+
var insert = (f, s) => {
|
|
15444
|
+
const add = newlines(s);
|
|
15445
|
+
return { text: f.text.slice(0, f.cursor) + add + f.text.slice(f.cursor), cursor: f.cursor + add.length };
|
|
15446
|
+
};
|
|
15447
|
+
var before = (f) => {
|
|
15448
|
+
const a = stops(f.text);
|
|
15449
|
+
let i = a.length - 1;
|
|
15450
|
+
while (i > 0 && a[i] >= f.cursor) i--;
|
|
15451
|
+
return a[i] < f.cursor ? a[i] : 0;
|
|
15452
|
+
};
|
|
15453
|
+
var after = (f) => stops(f.text).find((x) => x > f.cursor) ?? f.text.length;
|
|
15454
|
+
var left = (f) => ({ ...f, cursor: before(f) });
|
|
15455
|
+
var right = (f) => ({ ...f, cursor: after(f) });
|
|
15456
|
+
var back = (f) => {
|
|
15457
|
+
const to = before(f);
|
|
15458
|
+
return { text: f.text.slice(0, to) + f.text.slice(f.cursor), cursor: to };
|
|
15459
|
+
};
|
|
15460
|
+
var del = (f) => ({ text: f.text.slice(0, f.cursor) + f.text.slice(after(f)), cursor: f.cursor });
|
|
15461
|
+
function wordStart(f) {
|
|
15462
|
+
let i = f.cursor;
|
|
15463
|
+
while (i > 0 && /\s/.test(f.text[i - 1])) i--;
|
|
15464
|
+
while (i > 0 && !/\s/.test(f.text[i - 1])) i--;
|
|
15465
|
+
return i;
|
|
15466
|
+
}
|
|
15467
|
+
var wordLeft = (f) => ({ ...f, cursor: wordStart(f) });
|
|
15468
|
+
function wordRight(f) {
|
|
15469
|
+
let i = f.cursor;
|
|
15470
|
+
while (i < f.text.length && /\s/.test(f.text[i])) i++;
|
|
15471
|
+
while (i < f.text.length && !/\s/.test(f.text[i])) i++;
|
|
15472
|
+
return { ...f, cursor: i };
|
|
15473
|
+
}
|
|
15474
|
+
var killWord = (f) => {
|
|
15475
|
+
const to = wordStart(f);
|
|
15476
|
+
return { text: f.text.slice(0, to) + f.text.slice(f.cursor), cursor: to };
|
|
15477
|
+
};
|
|
15478
|
+
var home = (f) => ({ ...f, cursor: f.text.lastIndexOf("\n", f.cursor - 1) + 1 });
|
|
15479
|
+
var end = (f) => {
|
|
15480
|
+
const n = f.text.indexOf("\n", f.cursor);
|
|
15481
|
+
return { ...f, cursor: n < 0 ? f.text.length : n };
|
|
15482
|
+
};
|
|
15483
|
+
function layout(text5, width) {
|
|
15484
|
+
const w = Math.max(2, width - 1);
|
|
15485
|
+
const a = stops(text5), rows = [];
|
|
15486
|
+
let start = 0;
|
|
15487
|
+
for (let i = 0; i < a.length; i++) {
|
|
15488
|
+
const at = a[i];
|
|
15489
|
+
if (at < text5.length && text5[at] !== "\n") continue;
|
|
15490
|
+
if (start === i) rows.push({ a: at, b: at, last: true });
|
|
15491
|
+
for (let s = start; s < i; ) {
|
|
15492
|
+
let n = 0, j = s, space = -1;
|
|
15493
|
+
while (j < i) {
|
|
15494
|
+
const c = cells(text5.slice(a[j], a[j + 1]));
|
|
15495
|
+
if (n + c > w) break;
|
|
15496
|
+
if (text5[a[j]] === " ") space = j;
|
|
15497
|
+
n += c;
|
|
15498
|
+
j++;
|
|
15499
|
+
}
|
|
15500
|
+
if (j === s) j = s + 1;
|
|
15501
|
+
const e = j < i && space > s ? space + 1 : j;
|
|
15502
|
+
rows.push({ a: a[s], b: a[e], last: e >= i });
|
|
15503
|
+
s = e;
|
|
15504
|
+
}
|
|
15505
|
+
start = i + 1;
|
|
15506
|
+
}
|
|
15507
|
+
return rows;
|
|
15508
|
+
}
|
|
15509
|
+
var rowOf = (rows, cursor) => {
|
|
15510
|
+
const k = rows.findIndex((r) => cursor >= r.a && (cursor < r.b || cursor === r.b && r.last));
|
|
15511
|
+
return k < 0 ? rows.length - 1 : k;
|
|
15512
|
+
};
|
|
15513
|
+
function vertical(f, width, by) {
|
|
15514
|
+
const rows = layout(f.text, width), k = rowOf(rows, f.cursor), to = rows[k + by];
|
|
15515
|
+
if (!to) return null;
|
|
15516
|
+
const col = cells(f.text.slice(rows[k].a, f.cursor));
|
|
15517
|
+
let cursor = to.a;
|
|
15518
|
+
for (const x of stops(f.text)) {
|
|
15519
|
+
if (x <= to.a) continue;
|
|
15520
|
+
if (x > to.b || x === to.b && !to.last || cells(f.text.slice(to.a, x)) > col) break;
|
|
15521
|
+
cursor = x;
|
|
15522
|
+
}
|
|
15523
|
+
return { ...f, cursor };
|
|
15524
|
+
}
|
|
15525
|
+
function fieldLines(text5, cursor, width, o) {
|
|
15526
|
+
const indent2 = { text: " ".repeat(cells(o.prefix.text)) };
|
|
15527
|
+
const inv = (t) => ({ text: t, color: o.color, inverse: true });
|
|
15528
|
+
if (!text5) return [[o.prefix, ...cursor !== null ? [inv(" ")] : [], ...o.placeholder ? [o.placeholder] : []]];
|
|
15529
|
+
const rows = layout(text5, width - cells(o.prefix.text));
|
|
15530
|
+
return rows.map((r, k) => {
|
|
15531
|
+
const line = [k === 0 ? o.prefix : indent2];
|
|
15532
|
+
const at = cursor !== null && cursor >= r.a && cursor < r.b ? cursor : -1;
|
|
15533
|
+
if (at < 0) line.push({ text: text5.slice(r.a, r.b), color: o.color });
|
|
15534
|
+
else {
|
|
15535
|
+
const next2 = after({ text: text5, cursor: at });
|
|
15536
|
+
if (at > r.a) line.push({ text: text5.slice(r.a, at), color: o.color });
|
|
15537
|
+
line.push(inv(text5.slice(at, Math.min(next2, r.b))));
|
|
15538
|
+
if (next2 < r.b) line.push({ text: text5.slice(next2, r.b), color: o.color });
|
|
15539
|
+
}
|
|
15540
|
+
if (cursor === r.b && r.last) line.push(inv(" "));
|
|
15541
|
+
return line.filter((s) => s.text !== "" || s === line[0]);
|
|
15542
|
+
});
|
|
15543
|
+
}
|
|
15544
|
+
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));
|
|
15545
|
+
var lineAt = (f) => ({
|
|
15546
|
+
line: f.text.slice(0, f.cursor).split("\n").length,
|
|
15547
|
+
lines: f.text.split("\n").length
|
|
15548
|
+
});
|
|
15549
|
+
|
|
15550
|
+
// src/tui/ui/Lines.tsx
|
|
15551
|
+
import { memo, useEffect as useEffect5, useRef as useRef4 } from "react";
|
|
15552
|
+
import { Box, Text, useBoxMetrics } from "ink";
|
|
15553
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
15554
|
+
function Live({ seg }) {
|
|
15555
|
+
const now2 = useNow();
|
|
15556
|
+
const { t } = useI18n();
|
|
15557
|
+
const l = seg.live;
|
|
15558
|
+
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) });
|
|
15559
|
+
}
|
|
15560
|
+
var linked = (text5, url) => `\x1B]8;;${url.replace(/[\x00-\x1f\x7f]/g, "")}\x07${text5}\x1B]8;;\x07`;
|
|
15561
|
+
var LineText = memo(function LineText2({ line }) {
|
|
15562
|
+
return /* @__PURE__ */ jsxs(Text, { wrap: "truncate", children: [
|
|
15563
|
+
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)),
|
|
15564
|
+
line.length === 0 ? " " : null
|
|
15565
|
+
] });
|
|
15566
|
+
});
|
|
15567
|
+
function Lines({ lines, up = 0, onHeight }) {
|
|
15568
|
+
const ref = useRef4(null);
|
|
15569
|
+
const { height } = useBoxMetrics(ref);
|
|
15570
|
+
useEffect5(() => {
|
|
15571
|
+
if (height > 0) onHeight?.(height);
|
|
15572
|
+
}, [height, onHeight]);
|
|
15573
|
+
const h = Math.max(1, height);
|
|
15574
|
+
const back2 = Math.min(up, Math.max(0, lines.length - h));
|
|
15575
|
+
const shown = lines.slice(Math.max(0, lines.length - h - back2), lines.length - back2);
|
|
15576
|
+
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)) });
|
|
15577
|
+
}
|
|
15578
|
+
|
|
15579
|
+
// src/tui/ui/TitledBox.tsx
|
|
15580
|
+
import { useRef as useRef5 } from "react";
|
|
15581
|
+
import { Box as Box2, Text as Text2, useBoxMetrics as useBoxMetrics2 } from "ink";
|
|
15582
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
15583
|
+
function TitledBox({ title: title2, mark, color: color2 = C.line, titleColor = C.dim, titleBold, double, foot, footColor = C.faint, children, flexGrow, height, width, flexShrink, minHeight }) {
|
|
15584
|
+
const b2 = double ? { tl: "\u2554", h: "\u2550", tr: "\u2557", bl: "\u255A", br: "\u255D" } : { tl: "\u256D", h: "\u2500", tr: "\u256E", bl: "\u2570", br: "\u256F" };
|
|
15585
|
+
const ref = useRef5(null);
|
|
15586
|
+
const { width: w } = useBoxMetrics2(ref);
|
|
15587
|
+
const text5 = (l) => l.map((s) => s.text).join("");
|
|
15588
|
+
const tail = mark ? [{ text: ` ${mark.text.toUpperCase()}`, color: mark.color }] : [];
|
|
15589
|
+
const room = Math.max(0, w - 4 - (cells(text5(tail)) < w - 8 ? cells(text5(tail)) : 0));
|
|
15590
|
+
const head = [{ text: ` ${title2.toUpperCase()}`, color: titleColor }];
|
|
15591
|
+
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 }];
|
|
15592
|
+
const rest = Math.max(0, w - 3 - cells(text5(shown)));
|
|
15593
|
+
const f = foot ? text5(cut([{ text: foot }], Math.max(0, w - 6))) : "";
|
|
15594
|
+
return /* @__PURE__ */ jsxs2(Box2, { ref, flexDirection: "column", flexGrow, flexShrink, height, width, minHeight, overflow: "hidden", children: [
|
|
15595
|
+
/* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
15596
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15597
|
+
b2.tl,
|
|
15598
|
+
b2.h
|
|
15599
|
+
] }),
|
|
15600
|
+
w > 0 ? shown.map((s, i) => /* @__PURE__ */ jsx4(Text2, { color: s.color, bold: titleBold, children: s.text }, i)) : null,
|
|
15601
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15602
|
+
b2.h.repeat(rest),
|
|
15603
|
+
b2.tr
|
|
15604
|
+
] })
|
|
15605
|
+
] }),
|
|
15606
|
+
/* @__PURE__ */ jsx4(Box2, { flexDirection: "column", borderStyle: double ? "double" : "round", borderTop: false, borderBottom: !f, borderColor: color2, paddingX: 1, flexGrow: 1, overflow: "hidden", children }),
|
|
15607
|
+
f ? /* @__PURE__ */ jsxs2(Text2, { wrap: "truncate", children: [
|
|
15608
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15609
|
+
b2.bl,
|
|
15610
|
+
b2.h.repeat(Math.max(1, w - 5 - cells(f))),
|
|
15611
|
+
" "
|
|
15612
|
+
] }),
|
|
15613
|
+
/* @__PURE__ */ jsx4(Text2, { color: footColor, children: f }),
|
|
15614
|
+
/* @__PURE__ */ jsxs2(Text2, { color: color2, children: [
|
|
15615
|
+
" ",
|
|
15616
|
+
b2.h,
|
|
15617
|
+
b2.br
|
|
15618
|
+
] })
|
|
15619
|
+
] }) : null
|
|
15620
|
+
] });
|
|
15621
|
+
}
|
|
15622
|
+
|
|
15623
|
+
// src/tui/ui/Field.tsx
|
|
15624
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
15625
|
+
var fieldRows = (columns, rows) => rows < 24 ? 3 : columns >= 120 && rows >= 32 ? 8 : 6;
|
|
15626
|
+
var inHistory = (b2) => b2 !== null && b2.i < b2.items.length - 1;
|
|
15627
|
+
function Field({ value, onChange, onSubmit, onEscape, onBrowse, history = [], active, placeholder, title: title2, color: color2 = C.coral, titleColor }) {
|
|
15628
|
+
const { t, g } = useTui();
|
|
15629
|
+
const { columns, rows: height } = useScreenSize();
|
|
15630
|
+
const st = useRef6({ f: { text: value, cursor: value.length }, browse: null, pasted: 0 });
|
|
15631
|
+
const told = useRef6(value);
|
|
15632
|
+
if (value !== told.current) {
|
|
15633
|
+
told.current = value;
|
|
15634
|
+
st.current = { f: { text: value, cursor: value.length }, browse: null, pasted: 0 };
|
|
15635
|
+
}
|
|
15636
|
+
const [, render2] = useReducer((n) => n + 1, 0);
|
|
15637
|
+
const top = useRef6(0);
|
|
15638
|
+
const width = Math.max(10, columns - 4);
|
|
15639
|
+
const max = fieldRows(columns, height);
|
|
15640
|
+
const set = (f2, o = {}) => {
|
|
15641
|
+
const cur = st.current, changed = f2.text !== cur.f.text;
|
|
15642
|
+
let browse2 = cur.browse;
|
|
15643
|
+
if (browse2 && changed) {
|
|
15644
|
+
const items = [...browse2.items];
|
|
15645
|
+
items[browse2.i] = f2.text;
|
|
15646
|
+
browse2 = { ...browse2, items };
|
|
15647
|
+
}
|
|
15648
|
+
st.current = { f: f2, browse: browse2, pasted: o.paste ?? 0 };
|
|
15649
|
+
render2();
|
|
15650
|
+
if (changed && !inHistory(browse2)) {
|
|
15651
|
+
told.current = f2.text;
|
|
15652
|
+
onChange(f2.text);
|
|
16029
15653
|
}
|
|
16030
15654
|
};
|
|
16031
|
-
|
|
16032
|
-
const
|
|
16033
|
-
const
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
15655
|
+
const show = (b2, i) => {
|
|
15656
|
+
const next2 = b2 && i !== void 0 ? { ...b2, i } : b2;
|
|
15657
|
+
const text5 = next2 ? next2.items[next2.i] : st.current.f.text;
|
|
15658
|
+
st.current = { f: { text: text5, cursor: text5.length }, browse: next2, pasted: 0 };
|
|
15659
|
+
render2();
|
|
15660
|
+
onBrowse?.(inHistory(next2));
|
|
15661
|
+
};
|
|
15662
|
+
useInput((input, key) => {
|
|
15663
|
+
const { f: f2, browse: browse2 } = st.current;
|
|
15664
|
+
if (key.return && !key.meta && !key.shift) {
|
|
15665
|
+
const fromHistory = inHistory(browse2);
|
|
15666
|
+
if (onSubmit(f2.text, { fromHistory }) === false) return;
|
|
15667
|
+
if (fromHistory) show(browse2, browse2.items.length - 1);
|
|
15668
|
+
if (browse2) show(null);
|
|
16037
15669
|
return;
|
|
16038
15670
|
}
|
|
16039
|
-
if (
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
15671
|
+
if (key.return || input === "\n" || key.ctrl && input === "j") {
|
|
15672
|
+
set(insert(f2, "\n"));
|
|
15673
|
+
return;
|
|
15674
|
+
}
|
|
15675
|
+
if (key.escape) {
|
|
15676
|
+
if (inHistory(browse2)) {
|
|
15677
|
+
show(browse2, browse2.items.length - 1);
|
|
15678
|
+
show(null);
|
|
15679
|
+
return;
|
|
15680
|
+
}
|
|
15681
|
+
if (browse2) show(null);
|
|
15682
|
+
onEscape?.();
|
|
15683
|
+
return;
|
|
15684
|
+
}
|
|
15685
|
+
if (key.backspace) {
|
|
15686
|
+
set(key.meta ? killWord(f2) : back(f2));
|
|
15687
|
+
return;
|
|
15688
|
+
}
|
|
15689
|
+
if (key.delete) {
|
|
15690
|
+
set(del(f2));
|
|
15691
|
+
return;
|
|
15692
|
+
}
|
|
15693
|
+
if (key.ctrl && input === "u") {
|
|
15694
|
+
set({ text: "", cursor: 0 });
|
|
15695
|
+
return;
|
|
15696
|
+
}
|
|
15697
|
+
if (key.ctrl && input === "w") {
|
|
15698
|
+
set(killWord(f2));
|
|
15699
|
+
return;
|
|
15700
|
+
}
|
|
15701
|
+
if (key.ctrl && input === "a" || key.home) {
|
|
15702
|
+
set(home(f2));
|
|
15703
|
+
return;
|
|
15704
|
+
}
|
|
15705
|
+
if (key.ctrl && input === "e" || key.end) {
|
|
15706
|
+
set(end(f2));
|
|
15707
|
+
return;
|
|
15708
|
+
}
|
|
15709
|
+
if (key.leftArrow || key.meta && input === "b") {
|
|
15710
|
+
set(key.ctrl || key.meta ? wordLeft(f2) : left(f2));
|
|
15711
|
+
return;
|
|
15712
|
+
}
|
|
15713
|
+
if (key.rightArrow || key.meta && input === "f") {
|
|
15714
|
+
set(key.ctrl || key.meta ? wordRight(f2) : right(f2));
|
|
15715
|
+
return;
|
|
15716
|
+
}
|
|
15717
|
+
if (key.upArrow) {
|
|
15718
|
+
const up = vertical(f2, width - 2, -1);
|
|
15719
|
+
if (up) {
|
|
15720
|
+
set(up);
|
|
15721
|
+
return;
|
|
15722
|
+
}
|
|
15723
|
+
const b2 = browse2 ?? (history.length ? { items: [...history, f2.text], i: history.length } : null);
|
|
15724
|
+
if (b2 && b2.i > 0) show(b2, b2.i - 1);
|
|
15725
|
+
return;
|
|
15726
|
+
}
|
|
15727
|
+
if (key.downArrow) {
|
|
15728
|
+
const down = vertical(f2, width - 2, 1);
|
|
15729
|
+
if (down) {
|
|
15730
|
+
set(down);
|
|
15731
|
+
return;
|
|
15732
|
+
}
|
|
15733
|
+
if (inHistory(browse2)) show(browse2, browse2.i + 1);
|
|
15734
|
+
return;
|
|
15735
|
+
}
|
|
15736
|
+
if (key.ctrl || key.meta || key.tab || key.pageUp || key.pageDown) return;
|
|
15737
|
+
if (input) {
|
|
15738
|
+
const add = newlines(input);
|
|
15739
|
+
set(insert(f2, add), { paste: add.includes("\n") ? add.split("\n").length : 0 });
|
|
15740
|
+
}
|
|
15741
|
+
}, { isActive: active });
|
|
15742
|
+
usePaste((text5) => {
|
|
15743
|
+
const add = newlines(text5);
|
|
15744
|
+
set(insert(st.current.f, add), { paste: add.includes("\n") ? add.split("\n").length : 0 });
|
|
15745
|
+
}, { isActive: active });
|
|
15746
|
+
const { f, browse, pasted } = st.current;
|
|
15747
|
+
const prefix = { text: `${g.cursor} `, color: C.coral };
|
|
15748
|
+
const lines = fieldLines(f.text, active ? f.cursor : null, width, { prefix, color: C.fg, ...placeholder ? { placeholder: { text: f.text ? "" : ` ${placeholder}`, color: C.faint } } : {} });
|
|
15749
|
+
const row2 = rowOf(layout(f.text, width - 2), f.cursor);
|
|
15750
|
+
top.current = scrollTop(top.current, row2, lines.length, max);
|
|
15751
|
+
const shown = lines.slice(top.current, top.current + max);
|
|
15752
|
+
const below = lines.length - top.current - shown.length;
|
|
15753
|
+
const where = lineAt(f);
|
|
15754
|
+
const foot = inHistory(browse) ? t.field.history(browse.i + 1, browse.items.length - 1) : [
|
|
15755
|
+
top.current > 0 ? t.field.above(top.current) : "",
|
|
15756
|
+
below > 0 ? t.field.below(below) : "",
|
|
15757
|
+
pasted > 1 ? t.field.pasted(pasted) : active && where.lines > 1 ? t.field.line(where.line, where.lines) : ""
|
|
15758
|
+
].filter(Boolean).join(" \xB7 ");
|
|
15759
|
+
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
15760
|
}
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
15761
|
+
|
|
15762
|
+
// src/tui/ui/Frame.tsx
|
|
15763
|
+
import { Box as Box5, Text as Text4 } from "ink";
|
|
15764
|
+
|
|
15765
|
+
// src/tui/ui/KeyBar.tsx
|
|
15766
|
+
import { Box as Box4, Text as Text3 } from "ink";
|
|
15767
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
15768
|
+
var itemCells = ([k, label]) => cells(` ${k} `) + cells(` ${label}`);
|
|
15769
|
+
function fitKeys(keys, width, help) {
|
|
15770
|
+
const list3 = keys.filter((k) => k[0] !== help?.[0]);
|
|
15771
|
+
const fits = (l) => 1 + l.reduce((n, x, i) => n + itemCells(x) + (i ? 2 : 0), 0) <= width - 1;
|
|
15772
|
+
const tail = help ? [help] : [];
|
|
15773
|
+
while (list3.length && !fits([...list3, ...tail])) list3.pop();
|
|
15774
|
+
return [...list3, ...tail];
|
|
16049
15775
|
}
|
|
16050
|
-
function
|
|
16051
|
-
|
|
16052
|
-
|
|
15776
|
+
function KeyBar({ keys }) {
|
|
15777
|
+
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: [
|
|
15778
|
+
/* @__PURE__ */ jsxs3(Text3, { color: C.fg, backgroundColor: C.key, children: [
|
|
15779
|
+
" ",
|
|
15780
|
+
k,
|
|
15781
|
+
" "
|
|
15782
|
+
] }),
|
|
15783
|
+
/* @__PURE__ */ jsxs3(Text3, { color: C.dim, children: [
|
|
15784
|
+
" ",
|
|
15785
|
+
label
|
|
15786
|
+
] })
|
|
15787
|
+
] }) }, k)) });
|
|
15788
|
+
}
|
|
15789
|
+
|
|
15790
|
+
// src/tui/ui/Frame.tsx
|
|
15791
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
15792
|
+
var MIN = { columns: 60, rows: 16 };
|
|
15793
|
+
function Frame({ crumb, crumbShort, keys, kept, children }) {
|
|
15794
|
+
const { d, machines, t, g, lastToast, editing, client } = useTui();
|
|
15795
|
+
const { rows, columns } = useScreenSize();
|
|
15796
|
+
const live = listed(d.sessions);
|
|
15797
|
+
const pending = live.filter(isPending);
|
|
15798
|
+
const waiting = pending.length + openDecisions(useTasks(client)).length;
|
|
15799
|
+
const status = d.status === "open" ? null : d.status === "connecting" ? t.bar.connecting : t.bar.unreachable;
|
|
15800
|
+
if (columns < MIN.columns || rows < MIN.rows) {
|
|
15801
|
+
const why = [...pending[0] ? [t.small.waits(pending[0].name, pending[0].pending?.kind === "question")] : [], ...kept ? [t.small.kept] : []].join(" \xB7 ");
|
|
15802
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", height: rows, width: columns, alignItems: "center", justifyContent: "center", children: [
|
|
15803
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.coral, bold: true, children: [
|
|
15804
|
+
g.logo,
|
|
15805
|
+
" ledge"
|
|
15806
|
+
] }),
|
|
15807
|
+
/* @__PURE__ */ jsx7(Text4, { color: C.fg, children: t.small.tooSmall(columns, rows) }),
|
|
15808
|
+
/* @__PURE__ */ jsx7(Text4, { color: C.dim, children: t.small.atLeast(MIN.columns, MIN.rows) }),
|
|
15809
|
+
why ? /* @__PURE__ */ jsx7(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text4, { color: C.red, children: why }) }) : null
|
|
15810
|
+
] });
|
|
15811
|
+
}
|
|
15812
|
+
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);
|
|
15813
|
+
const room = columns - left2 - 2;
|
|
15814
|
+
const shown = cells(crumb) <= room ? crumb : crumbShort && cells(crumbShort) <= room ? crumbShort : "";
|
|
15815
|
+
return /* @__PURE__ */ jsxs4(Box5, { flexDirection: "column", height: rows, width: columns, children: [
|
|
15816
|
+
/* @__PURE__ */ jsxs4(Box5, { paddingX: 1, justifyContent: "space-between", height: 1, backgroundColor: C.bar, children: [
|
|
15817
|
+
/* @__PURE__ */ jsxs4(Text4, { wrap: "truncate", children: [
|
|
15818
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.coral, children: [
|
|
15819
|
+
g.logo,
|
|
15820
|
+
" ledge"
|
|
15821
|
+
] }),
|
|
15822
|
+
/* @__PURE__ */ jsxs4(Text4, { color: C.dim, children: [
|
|
15823
|
+
" ",
|
|
15824
|
+
t.bar.sessions(live.length),
|
|
15825
|
+
" \xB7 ",
|
|
15826
|
+
t.bar.machines(Math.max(1, machines.length))
|
|
15827
|
+
] }),
|
|
15828
|
+
waiting > 0 ? /* @__PURE__ */ jsxs4(Text4, { color: C.red, children: [
|
|
15829
|
+
" \xB7 ",
|
|
15830
|
+
t.bar.waiting(waiting)
|
|
15831
|
+
] }) : null,
|
|
15832
|
+
status ? /* @__PURE__ */ jsxs4(Text4, { color: C.yellow, children: [
|
|
15833
|
+
" ",
|
|
15834
|
+
status
|
|
15835
|
+
] }) : null
|
|
15836
|
+
] }),
|
|
15837
|
+
shown ? /* @__PURE__ */ jsx7(Text4, { wrap: "truncate", color: C.dim, children: shown }) : null
|
|
15838
|
+
] }),
|
|
15839
|
+
/* @__PURE__ */ jsx7(Box5, { flexGrow: 1, flexDirection: "column", overflow: "hidden", children }),
|
|
15840
|
+
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,
|
|
15841
|
+
/* @__PURE__ */ jsx7(KeyBar, { keys: fitKeys(keys, columns, editing ? null : ["?", t.keys.help]) })
|
|
15842
|
+
] });
|
|
15843
|
+
}
|
|
15844
|
+
|
|
15845
|
+
// src/tui/ui/SessionCard.tsx
|
|
15846
|
+
import { Box as Box6, Text as Text5 } from "ink";
|
|
15847
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
15848
|
+
function Button({ label, k, primary, danger, off }) {
|
|
15849
|
+
const bg = off ? void 0 : danger ? C.danger : primary ? C.primary : C.key;
|
|
15850
|
+
return /* @__PURE__ */ jsxs5(Text5, { children: [
|
|
15851
|
+
/* @__PURE__ */ jsxs5(Text5, { color: off ? C.faint : C.fg, backgroundColor: bg, children: [
|
|
15852
|
+
" ",
|
|
15853
|
+
label,
|
|
15854
|
+
" "
|
|
15855
|
+
] }),
|
|
15856
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, backgroundColor: bg, children: [
|
|
15857
|
+
k,
|
|
15858
|
+
" "
|
|
15859
|
+
] })
|
|
15860
|
+
] });
|
|
15861
|
+
}
|
|
15862
|
+
function SessionCard({ s, note }) {
|
|
15863
|
+
const tui = useTui();
|
|
15864
|
+
const { t, g } = tui;
|
|
15865
|
+
const threshold = useContextNear(tui.client);
|
|
15866
|
+
const state2 = botState(s);
|
|
15867
|
+
const helpers = liveHelpers(s);
|
|
15868
|
+
const p = s.pending;
|
|
15869
|
+
const canAct = !s.offline && s.source !== "hook_only";
|
|
15870
|
+
const verb = !p ? "" : p.kind === "question" ? t.overview.asks : p.kind === "plan" ? t.overview.plan : p.kind === "diff" ? t.overview.diff : t.overview.wantsToRun;
|
|
15871
|
+
return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
|
|
15872
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15873
|
+
/* @__PURE__ */ jsxs5(Text5, { color: STATE_COLOR[state2], children: [
|
|
15874
|
+
g[state2],
|
|
15875
|
+
" ",
|
|
15876
|
+
t.state[state2]
|
|
15877
|
+
] }),
|
|
15878
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15879
|
+
" ",
|
|
15880
|
+
TOOL_NAME[s.tool],
|
|
15881
|
+
" \xB7 ",
|
|
15882
|
+
modelLabel(s, t.session.defaultModel)
|
|
15883
|
+
] }),
|
|
15884
|
+
s.context ? /* @__PURE__ */ jsxs5(Text5, { color: contextNear(s.context, threshold) ? C.yellow : C.dim, children: [
|
|
15885
|
+
" \xB7 ",
|
|
15886
|
+
t.overview.context(Math.round(s.context.used / s.context.window * 100))
|
|
15887
|
+
] }) : null,
|
|
15888
|
+
/* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15889
|
+
" \xB7 ",
|
|
15890
|
+
machineLabel(s),
|
|
15891
|
+
s.branch ? ` \xB7 ${g.branch} ${s.branch}` : ""
|
|
15892
|
+
] }),
|
|
15893
|
+
s.offline ? /* @__PURE__ */ jsxs5(Text5, { color: C.yellow, children: [
|
|
15894
|
+
" ",
|
|
15895
|
+
t.overview.offline
|
|
15896
|
+
] }) : s.source === "hook_only" ? /* @__PURE__ */ jsxs5(Text5, { color: C.faint, children: [
|
|
15897
|
+
" ",
|
|
15898
|
+
t.overview.readOnly
|
|
15899
|
+
] }) : null
|
|
15900
|
+
] }),
|
|
15901
|
+
s.handover || s.relieves ? /* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15902
|
+
s.handover ? /* @__PURE__ */ jsxs5(Text5, { color: s.handover.state === "failed" ? C.red : C.yellow, children: [
|
|
15903
|
+
" \u21C4 ",
|
|
15904
|
+
s.handover.state === "done" && s.handover.toName ? t.overview.relievedBy(s.handover.toName) : t.overview.relief[s.handover.state],
|
|
15905
|
+
s.handover.reason ? ` : ${s.handover.reason}` : ""
|
|
15906
|
+
] }) : null,
|
|
15907
|
+
s.relieves ? /* @__PURE__ */ jsxs5(Text5, { color: C.dim, children: [
|
|
15908
|
+
" \u21C4 ",
|
|
15909
|
+
t.overview.relieves(s.relieves.name)
|
|
15910
|
+
] }) : null
|
|
15911
|
+
] }) : null,
|
|
15912
|
+
helpers.length > 0 ? /* @__PURE__ */ jsxs5(Text5, { color: C.violet, wrap: "truncate", children: [
|
|
15913
|
+
" ",
|
|
15914
|
+
helpers.map((h) => `${g.helper} ${h.kind} \xB7 ${oneLine(h.task)}`).join(" ")
|
|
15915
|
+
] }) : null,
|
|
15916
|
+
p ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
15917
|
+
/* @__PURE__ */ jsxs5(Text5, { wrap: "truncate", children: [
|
|
15918
|
+
/* @__PURE__ */ jsx8(Text5, { color: STATE_COLOR[state2], children: verb }),
|
|
15919
|
+
" ",
|
|
15920
|
+
/* @__PURE__ */ jsx8(Text5, { color: C.fg, bold: true, children: oneLine(p.text) }),
|
|
15921
|
+
p.tool ? /* @__PURE__ */ jsxs5(Text5, { color: C.faint, children: [
|
|
15922
|
+
" ",
|
|
15923
|
+
p.tool
|
|
15924
|
+
] }) : null
|
|
15925
|
+
] }),
|
|
15926
|
+
/* @__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: [
|
|
15927
|
+
/* @__PURE__ */ jsx8(Button, { label: t.overview.allow, k: "y", primary: true, off: !canAct }),
|
|
15928
|
+
/* @__PURE__ */ jsx8(Button, { label: t.overview.deny, k: "n", off: !canAct }),
|
|
15929
|
+
offers(s.tool, p.kind, "always") ? /* @__PURE__ */ jsx8(Button, { label: t.overview.always, k: "A", off: !canAct }) : null,
|
|
15930
|
+
note && offers(s.tool, p.kind, "note") ? /* @__PURE__ */ jsx8(Button, { label: t.overview.denyWithNote, k: "r", off: !canAct }) : null
|
|
15931
|
+
] }) })
|
|
15932
|
+
] }) : s.lastReply && state2 === "idle" ? /* @__PURE__ */ jsxs5(Text5, { color: C.dim, wrap: "truncate", children: [
|
|
15933
|
+
" ",
|
|
15934
|
+
oneLine(s.lastReply.text)
|
|
15935
|
+
] }) : null
|
|
15936
|
+
] });
|
|
16053
15937
|
}
|
|
16054
15938
|
|
|
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);
|
|
15939
|
+
// src/tui/ui/Tail.tsx
|
|
15940
|
+
import { useRef as useRef7 } from "react";
|
|
15941
|
+
import { Box as Box7, Text as Text6, useBoxMetrics as useBoxMetrics3 } from "ink";
|
|
15942
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
15943
|
+
function Tail({ lines, color: color2, empty: empty3, emptyColor }) {
|
|
15944
|
+
const ref = useRef7(null);
|
|
15945
|
+
const { height } = useBoxMetrics3(ref);
|
|
15946
|
+
let end2 = lines.length;
|
|
15947
|
+
while (end2 > 0 && !lines[end2 - 1].trim()) end2--;
|
|
15948
|
+
const shown = end2 === 0 ? [] : lines.slice(0, end2).slice(-Math.max(1, height));
|
|
15949
|
+
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
15950
|
}
|
|
16083
15951
|
|
|
16084
|
-
// src/tui/
|
|
16085
|
-
import { jsx as jsx10, jsxs as
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
const
|
|
16090
|
-
const
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
|
|
15952
|
+
// src/tui/screens/Overview.tsx
|
|
15953
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
15954
|
+
var WIDE = 100;
|
|
15955
|
+
function windowed(rows, selectedKey, height) {
|
|
15956
|
+
if (rows.length <= height) return [...rows];
|
|
15957
|
+
const at = Math.max(0, rows.findIndex((r) => r.key === selectedKey));
|
|
15958
|
+
const start = Math.min(Math.max(0, at - Math.floor(height / 2)), rows.length - height);
|
|
15959
|
+
return rows.slice(start, start + height);
|
|
15960
|
+
}
|
|
15961
|
+
function Overview() {
|
|
15962
|
+
const tui = useTui();
|
|
15963
|
+
const { d, projects, t, g, selected, select, actions, go, attach } = tui;
|
|
15964
|
+
const { columns, rows: height } = useScreenSize();
|
|
15965
|
+
const wide = columns >= WIDE;
|
|
15966
|
+
const left2 = wide ? Math.min(64, Math.max(44, Math.floor(columns * 0.42))) : columns;
|
|
15967
|
+
const teams = useTeams(tui.client);
|
|
15968
|
+
const tasks = useTasks(tui.client);
|
|
15969
|
+
const view = useMemo2(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
|
|
15970
|
+
const sel = selected?.id ?? null;
|
|
15971
|
+
const closed = closedLedgeTasks(tasks);
|
|
15972
|
+
const decisions = useMemo2(() => openDecisions(tasks), [tasks]);
|
|
15973
|
+
const [deciding, setDeciding] = useState3(null);
|
|
15974
|
+
const decision = deciding ? decisions.find((x) => x.id === deciding.id) : void 0;
|
|
15975
|
+
useEffect6(() => {
|
|
15976
|
+
if (deciding && !decision) setDeciding(null);
|
|
15977
|
+
}, [deciding, decision]);
|
|
15978
|
+
useEditing(!!deciding && !deciding.confirm);
|
|
15979
|
+
const answer = (x, text5) => {
|
|
15980
|
+
const rid = randomUUID();
|
|
15981
|
+
const off = tui.client.onMessage((m) => {
|
|
15982
|
+
if (m.type !== "action.result" || m.rid !== rid) return;
|
|
15983
|
+
off();
|
|
15984
|
+
tui.toast(m.reason === void 0 ? { text: t.toast.reply(x.giverName), tone: "ok" } : { text: t.toast.answerFailed(m.reason), tone: "error" });
|
|
15985
|
+
});
|
|
15986
|
+
setTimeout(off, 1e4);
|
|
15987
|
+
tui.client.send({ type: "action.run", rid, action: "task.answer", args: { task: x.id, answer: text5 }, confirmed: true });
|
|
15988
|
+
};
|
|
15989
|
+
const threshold = useContextNear(tui.client);
|
|
15990
|
+
const near = useMemo2(() => nearContext(d.sessions, threshold), [d.sessions, threshold]);
|
|
15991
|
+
const [tasksShown, setTasksShown] = useState3(true);
|
|
15992
|
+
const [joining, setJoining] = useState3(null);
|
|
15993
|
+
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;
|
|
15994
|
+
const seatTeam = selected?.team ? teams?.find((x) => x.id === selected.team && x.state !== "dissolved") : void 0;
|
|
15995
|
+
const choices2 = node2?.teams.map((x) => x.team).filter((x) => x.state !== "dissolved" && x.id !== selected?.team) ?? [];
|
|
15996
|
+
useScreenKeys((input, key) => {
|
|
15997
|
+
if (deciding) {
|
|
15998
|
+
if (!deciding.confirm) {
|
|
15999
|
+
if (key.escape) setDeciding(null);
|
|
16000
|
+
return true;
|
|
16001
|
+
}
|
|
16002
|
+
if (input === "y" && decision) {
|
|
16003
|
+
setDeciding(null);
|
|
16004
|
+
answer(decision, deciding.draft.trim());
|
|
16005
|
+
return true;
|
|
16006
|
+
}
|
|
16007
|
+
if (input === "n" || key.escape) {
|
|
16008
|
+
setDeciding({ ...deciding, confirm: false });
|
|
16009
|
+
return true;
|
|
16010
|
+
}
|
|
16011
|
+
return true;
|
|
16098
16012
|
}
|
|
16099
|
-
if (
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
return;
|
|
16013
|
+
if (input === "D") {
|
|
16014
|
+
if (decisions[0]) setDeciding({ id: decisions[0].id, draft: "", confirm: false });
|
|
16015
|
+
return true;
|
|
16103
16016
|
}
|
|
16104
|
-
if (
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
|
|
16017
|
+
if (joining) {
|
|
16018
|
+
if (key.escape) {
|
|
16019
|
+
setJoining(null);
|
|
16020
|
+
return true;
|
|
16021
|
+
}
|
|
16022
|
+
if (input === "j" || key.downArrow) {
|
|
16023
|
+
setJoining({ index: (joining.index + 1) % Math.max(1, choices2.length) });
|
|
16024
|
+
return true;
|
|
16025
|
+
}
|
|
16026
|
+
if (input === "k" || key.upArrow) {
|
|
16027
|
+
setJoining({ index: (joining.index + choices2.length - 1) % Math.max(1, choices2.length) });
|
|
16028
|
+
return true;
|
|
16029
|
+
}
|
|
16030
|
+
if (key.return && selected && choices2[joining.index]) {
|
|
16031
|
+
const team = choices2[joining.index];
|
|
16032
|
+
const role = selected.role ?? team.seats.find((x) => x.sessionId === null)?.role;
|
|
16033
|
+
setJoining(null);
|
|
16034
|
+
if (!role) {
|
|
16035
|
+
tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
16036
|
+
return true;
|
|
16037
|
+
}
|
|
16038
|
+
tui.client.send({ type: "team.join", team: team.id, session: selected.id, role });
|
|
16039
|
+
tui.toast({ text: t.toast.joined(selected.name, team.name), tone: "ok" });
|
|
16040
|
+
return true;
|
|
16041
|
+
}
|
|
16042
|
+
return true;
|
|
16108
16043
|
}
|
|
16109
|
-
if (
|
|
16110
|
-
|
|
16111
|
-
|
|
16112
|
-
return;
|
|
16044
|
+
if (input === "J" && selected) {
|
|
16045
|
+
if (choices2.length) setJoining({ index: 0 });
|
|
16046
|
+
else tui.toast({ text: t.overview.noTeamHere, tone: "info" });
|
|
16047
|
+
return true;
|
|
16113
16048
|
}
|
|
16114
|
-
if (
|
|
16049
|
+
if (input === "R") {
|
|
16050
|
+
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;
|
|
16051
|
+
if (resting) {
|
|
16052
|
+
tui.client.send({ type: "team.resume", team: resting.id });
|
|
16053
|
+
tui.toast({ text: t.toast.resumed(resting.name), tone: "ok" });
|
|
16054
|
+
}
|
|
16055
|
+
return true;
|
|
16056
|
+
}
|
|
16057
|
+
if (input === "T") {
|
|
16058
|
+
setTasksShown((v) => !v);
|
|
16059
|
+
return true;
|
|
16060
|
+
}
|
|
16061
|
+
if (input === "H") {
|
|
16062
|
+
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];
|
|
16063
|
+
if (!s) return true;
|
|
16064
|
+
const rid = randomUUID();
|
|
16065
|
+
const off = tui.client.onMessage((m) => {
|
|
16066
|
+
if (m.type === "session.handover.result" && m.rid === rid) {
|
|
16067
|
+
off();
|
|
16068
|
+
tui.toast(m.ok ? { text: t.toast.handover(s.name), tone: "ok" } : { text: t.toast.handoverRefused(m.reason ?? "?"), tone: "info" });
|
|
16069
|
+
}
|
|
16070
|
+
});
|
|
16071
|
+
setTimeout(off, 5e3);
|
|
16072
|
+
tui.client.send({ type: "session.handover", rid, id: s.id });
|
|
16073
|
+
return true;
|
|
16074
|
+
}
|
|
16075
|
+
if (input === "x" && closed[0]) {
|
|
16076
|
+
tui.client.send({ type: "task.cmd", rid: randomUUID(), by: LEDGE_GIVER, op: "seen", task: closed[0].id });
|
|
16077
|
+
return true;
|
|
16078
|
+
}
|
|
16079
|
+
if (input === "j" || key.downArrow) {
|
|
16080
|
+
select(step(view.order, sel, 1));
|
|
16081
|
+
return true;
|
|
16082
|
+
}
|
|
16083
|
+
if (input === "k" || key.upArrow) {
|
|
16084
|
+
select(step(view.order, sel, -1));
|
|
16085
|
+
return true;
|
|
16086
|
+
}
|
|
16087
|
+
if (key.tab) {
|
|
16088
|
+
const next2 = nextWaiting(view.yours, sel);
|
|
16089
|
+
if (next2) select(next2);
|
|
16090
|
+
return true;
|
|
16091
|
+
}
|
|
16092
|
+
if (key.return) {
|
|
16093
|
+
if (selected) go("session");
|
|
16094
|
+
return true;
|
|
16095
|
+
}
|
|
16096
|
+
if (!selected) return false;
|
|
16097
|
+
if (input === "a") {
|
|
16098
|
+
attach(selected);
|
|
16099
|
+
return true;
|
|
16100
|
+
}
|
|
16101
|
+
if (input === "r" && selected.pending) {
|
|
16102
|
+
go("session");
|
|
16103
|
+
return true;
|
|
16104
|
+
}
|
|
16105
|
+
if (/^[1-9]$/.test(input) && selected.pending?.kind !== "question") return false;
|
|
16106
|
+
if (input === "y" || input === "n" || input === "A" || /^[1-9]$/.test(input)) return actions.key(selected, input);
|
|
16107
|
+
if (input === "i" && selected.state === "working") {
|
|
16108
|
+
actions.interrupt(selected);
|
|
16109
|
+
return true;
|
|
16110
|
+
}
|
|
16111
|
+
return false;
|
|
16115
16112
|
});
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
|
|
16113
|
+
const tree2 = view.tree.flatMap((n) => {
|
|
16114
|
+
const teamed = n.teams.length > 0;
|
|
16115
|
+
const common = n.branches.length > 0;
|
|
16116
|
+
return [
|
|
16117
|
+
{ kind: "project", key: n.key, label: n.label, color: projectColor(n.color), count: n.count },
|
|
16118
|
+
...teamed && common ? [{ kind: "common", key: `${n.key}|common` }] : [],
|
|
16119
|
+
...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 }))]),
|
|
16120
|
+
...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 }))])
|
|
16121
|
+
];
|
|
16122
|
+
});
|
|
16123
|
+
const yoursCount = view.yours.length + decisions.length + closed.length + near.length;
|
|
16124
|
+
const yoursHeight = Math.max(1, Math.min(yoursCount, 6));
|
|
16125
|
+
const joinHeight = joining && selected ? Math.max(1, choices2.length) + 2 : 0;
|
|
16126
|
+
const answerLines = deciding?.confirm ? wrap2(deciding.draft.trim(), Math.max(10, columns - 4)) : [];
|
|
16127
|
+
const decideHeight = !deciding ? 0 : deciding.confirm ? Math.min(answerLines.length, Math.max(1, height - 12)) + 2 : fieldRows(columns, height) + 2;
|
|
16128
|
+
const treeHeight = Math.max(3, height - 3 - (yoursHeight + 2) - joinHeight - decideHeight - 3);
|
|
16129
|
+
const inner = left2 - 4, nameW = 7;
|
|
16130
|
+
const stateW = Math.max(...Object.values(t.state).map((x) => x.length)) + 1;
|
|
16131
|
+
const topicW = Math.max(6, inner - 6 - nameW - stateW - 3);
|
|
16132
|
+
const askW = Math.max(6, inner - 2 - nameW - 9 - 7);
|
|
16133
|
+
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) => {
|
|
16134
|
+
const st = botState(s);
|
|
16135
|
+
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: s.id === sel ? C.sel : void 0, children: [
|
|
16136
|
+
/* @__PURE__ */ jsxs6(Text7, { color: STATE_COLOR[st], children: [
|
|
16137
|
+
g[st],
|
|
16122
16138
|
" "
|
|
16123
16139
|
] }),
|
|
16124
|
-
/* @__PURE__ */ jsx10(
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16140
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
16141
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.dim, children: fit(s.repo ?? s.project, 9) }),
|
|
16142
|
+
/* @__PURE__ */ jsx10(Text7, { color: STATE_COLOR[st], children: fit(oneLine(s.pending?.text ?? ""), askW) }),
|
|
16143
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
16144
|
+
" ",
|
|
16145
|
+
/* @__PURE__ */ jsx10(Ago, { iso: s.lastActivityAt })
|
|
16146
|
+
] })
|
|
16147
|
+
] }, s.id);
|
|
16148
|
+
}), ...decisions.slice(0, Math.max(0, yoursHeight - view.yours.length)).map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16149
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.red, children: [
|
|
16150
|
+
g.question,
|
|
16151
|
+
" "
|
|
16152
|
+
] }),
|
|
16153
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: t.overview.decision(x.giverName, x.title) }),
|
|
16154
|
+
x.text ? /* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16155
|
+
" \xB7 ",
|
|
16156
|
+
oneLine(x.text)
|
|
16157
|
+
] }) : null
|
|
16158
|
+
] }, `decision:${x.id}`)), ...closed.slice(0, Math.max(0, yoursHeight - view.yours.length - decisions.length)).map((x) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16159
|
+
/* @__PURE__ */ jsxs6(Text7, { color: x.state === "done" ? C.green : C.yellow, children: [
|
|
16160
|
+
x.state === "done" ? g.ok : g.fail,
|
|
16161
|
+
" "
|
|
16162
|
+
] }),
|
|
16163
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: t.overview.ledgeTask(x.ownerName, x.title, x.state) })
|
|
16164
|
+
] }, 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: [
|
|
16165
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.yellow, children: [
|
|
16166
|
+
s.handover?.state === "failed" ? g.fail : "\u25D4",
|
|
16167
|
+
" "
|
|
16168
|
+
] }),
|
|
16169
|
+
/* @__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)) })
|
|
16170
|
+
] }, `near:${s.id}`))] });
|
|
16171
|
+
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: [
|
|
16172
|
+
/* @__PURE__ */ jsx10(Text7, { color: r.color, children: g.project }),
|
|
16173
|
+
" ",
|
|
16174
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, bold: true, children: r.label }),
|
|
16175
|
+
" ",
|
|
16176
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.sessions(r.count) })
|
|
16177
|
+
] }, r.key) : r.kind === "common" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
|
|
16178
|
+
" ",
|
|
16179
|
+
g.common,
|
|
16180
|
+
" ",
|
|
16181
|
+
t.overview.common
|
|
16182
|
+
] }, r.key) : r.kind === "team" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16183
|
+
/* @__PURE__ */ jsx10(Text7, { children: " " }),
|
|
16184
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
|
|
16185
|
+
" ",
|
|
16186
|
+
/* @__PURE__ */ jsx10(Text7, { color: r.team.state === "resting" ? C.dim : C.fg, bold: true, children: r.team.name }),
|
|
16187
|
+
" ",
|
|
16188
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: r.team.state === "resting" ? t.overview.resting : t.overview.teamLine(r.done, r.given) })
|
|
16189
|
+
] }, r.key) : r.kind === "branch" ? /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", color: C.faint, children: [
|
|
16190
|
+
" ".repeat(r.indent),
|
|
16191
|
+
g.branch,
|
|
16192
|
+
" ",
|
|
16193
|
+
r.branch
|
|
16194
|
+
] }, 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)) });
|
|
16195
|
+
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]] : [
|
|
16196
|
+
["j k", t.keys.navigate],
|
|
16197
|
+
["\u21B5", t.keys.open],
|
|
16198
|
+
...view.yours.length ? [["tab", t.keys.next], ["y n", t.keys.answer]] : [],
|
|
16199
|
+
["a", t.keys.attach],
|
|
16200
|
+
...seatTeam ? [["T", t.keys.tasks]] : [],
|
|
16201
|
+
...choices2.length ? [["J", t.keys.join]] : [],
|
|
16202
|
+
...view.tree.some((n) => n.teams.some((x) => x.team.state === "resting")) ? [["R", t.keys.resume]] : [],
|
|
16203
|
+
...decisions.length ? [["D", t.keys.decide]] : [],
|
|
16204
|
+
...near.length ? [["H", t.keys.handover]] : [],
|
|
16205
|
+
["/", t.keys.ask],
|
|
16206
|
+
["+", t.keys.launch],
|
|
16207
|
+
[",", t.keys.settings],
|
|
16208
|
+
["?", t.keys.help],
|
|
16209
|
+
["q", t.keys.quit]
|
|
16210
|
+
];
|
|
16211
|
+
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) : [];
|
|
16212
|
+
return /* @__PURE__ */ jsxs6(Frame, { crumb: tui.machines.map((m) => `${m.label} ${m.ready ? g.machine : "\u25CB"}`).join(" "), keys, children: [
|
|
16213
|
+
/* @__PURE__ */ jsxs6(Box8, { flexGrow: 1, children: [
|
|
16214
|
+
/* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", width: left2, flexShrink: 0, children: [
|
|
16215
|
+
yoursBox,
|
|
16216
|
+
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: [
|
|
16217
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.coral, children: g.team }),
|
|
16218
|
+
" ",
|
|
16219
|
+
x.name
|
|
16220
|
+
] }, x.id)) }) : null,
|
|
16221
|
+
treeBox
|
|
16222
|
+
] }),
|
|
16223
|
+
wide ? /* @__PURE__ */ jsxs6(Box8, { flexDirection: "column", flexGrow: 1, children: [
|
|
16224
|
+
selected ? /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
16225
|
+
/* @__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 }) }),
|
|
16226
|
+
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: [
|
|
16227
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: x.id }),
|
|
16228
|
+
" ",
|
|
16229
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.title }),
|
|
16230
|
+
" ",
|
|
16231
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16232
|
+
x.giverName,
|
|
16233
|
+
" ",
|
|
16234
|
+
g.arrow,
|
|
16235
|
+
" ",
|
|
16236
|
+
x.ownerName
|
|
16237
|
+
] }),
|
|
16238
|
+
" ",
|
|
16239
|
+
/* @__PURE__ */ jsx10(Text7, { color: x.state === "done" ? C.green : x.state === "taken" ? C.blue : C.faint, children: t.ask.taskState[x.state] })
|
|
16240
|
+
] }, x.id)) }) : null,
|
|
16241
|
+
/* @__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 }) })
|
|
16242
|
+
] }) : /* @__PURE__ */ jsx10(TitledBox, { title: t.tabs.session, flexGrow: 1, children: /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.session.noSession }) }),
|
|
16243
|
+
/* @__PURE__ */ jsx10(TitledBox, { title: t.overview.exchanges, height: 5, children: (() => {
|
|
16244
|
+
const list3 = exchanges(d.sessions, 3, tasks, t.overview.ledge);
|
|
16245
|
+
return list3.length === 0 ? /* @__PURE__ */ jsx10(Text7, { color: C.faint, children: t.overview.noExchange }) : list3.map((x, i) => /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", children: [
|
|
16246
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.faint, children: clock(x.at) }),
|
|
16247
|
+
" ",
|
|
16248
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.from }),
|
|
16249
|
+
" ",
|
|
16250
|
+
/* @__PURE__ */ jsx10(Text7, { color: x.kind === "task" ? C.green : C.violet, children: g.arrow }),
|
|
16251
|
+
" ",
|
|
16252
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.fg, children: x.to }),
|
|
16253
|
+
" ",
|
|
16254
|
+
x.kind === "task" ? /* @__PURE__ */ jsxs6(Text7, { color: C.green, children: [
|
|
16255
|
+
g.task,
|
|
16256
|
+
" "
|
|
16257
|
+
] }) : null,
|
|
16258
|
+
/* @__PURE__ */ jsx10(Text7, { color: C.dim, children: oneLine(x.summary) })
|
|
16259
|
+
] }, i));
|
|
16260
|
+
})() })
|
|
16128
16261
|
] }) : null
|
|
16129
|
-
] },
|
|
16262
|
+
] }),
|
|
16263
|
+
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(
|
|
16264
|
+
Field,
|
|
16265
|
+
{
|
|
16266
|
+
title: t.overview.decisionTitle(decision.giverName, decision.title),
|
|
16267
|
+
value: deciding.draft,
|
|
16268
|
+
onChange: (v) => setDeciding((x) => x ? { ...x, draft: v } : x),
|
|
16269
|
+
onSubmit: (v) => {
|
|
16270
|
+
if (!v.trim()) return false;
|
|
16271
|
+
setDeciding((x) => x ? { ...x, draft: v, confirm: true } : x);
|
|
16272
|
+
},
|
|
16273
|
+
onEscape: () => setDeciding(null),
|
|
16274
|
+
active: true,
|
|
16275
|
+
placeholder: t.overview.decisionHint
|
|
16276
|
+
}
|
|
16277
|
+
) : null
|
|
16130
16278
|
] });
|
|
16131
16279
|
}
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
}
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16280
|
+
function SessionRow({ s, on, nameW, topicW, stateW, indent: indent2 = 0 }) {
|
|
16281
|
+
const { t, g } = useTui();
|
|
16282
|
+
const st = botState(s);
|
|
16283
|
+
const n = helperCount(s);
|
|
16284
|
+
return /* @__PURE__ */ jsxs6(Text7, { wrap: "truncate", backgroundColor: on ? C.sel : void 0, children: [
|
|
16285
|
+
/* @__PURE__ */ jsx10(Text7, { color: on ? C.coral : C.faint, children: on ? "\u258C" : " " }),
|
|
16286
|
+
/* @__PURE__ */ jsx10(Text7, { children: " ".repeat(2 + indent2) }),
|
|
16287
|
+
/* @__PURE__ */ jsxs6(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: [
|
|
16288
|
+
g[st],
|
|
16289
|
+
" "
|
|
16290
|
+
] }),
|
|
16291
|
+
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.dim : C.fg, bold: true, children: fit(s.name, nameW) }),
|
|
16292
|
+
s.role ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
16293
|
+
"@",
|
|
16294
|
+
s.role,
|
|
16295
|
+
" "
|
|
16296
|
+
] }) : null,
|
|
16297
|
+
/* @__PURE__ */ jsxs6(Text7, { color: C.dim, children: [
|
|
16298
|
+
fit(oneLine(s.topic ?? s.title), topicW),
|
|
16299
|
+
" "
|
|
16300
|
+
] }),
|
|
16301
|
+
/* @__PURE__ */ jsx10(Text7, { color: s.offline ? C.faint : STATE_COLOR[st], children: fit(s.offline ? t.overview.offline : t.state[st], stateW).trimEnd() }),
|
|
16302
|
+
n > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: C.violet, children: [
|
|
16303
|
+
" +",
|
|
16304
|
+
n
|
|
16305
|
+
] }) : null,
|
|
16306
|
+
s.machineName && s.machineId !== "local" ? /* @__PURE__ */ jsxs6(Text7, { color: C.faint, children: [
|
|
16307
|
+
" \xB7 ",
|
|
16308
|
+
machineLabel(s)
|
|
16309
|
+
] }) : null
|
|
16148
16310
|
] });
|
|
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
16311
|
}
|
|
16161
16312
|
|
|
16162
|
-
// src/tui/
|
|
16163
|
-
import {
|
|
16164
|
-
import { Box as
|
|
16313
|
+
// src/tui/screens/Session.tsx
|
|
16314
|
+
import { useEffect as useEffect8, useMemo as useMemo3, useState as useState7 } from "react";
|
|
16315
|
+
import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
|
|
16165
16316
|
|
|
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++;
|
|
16317
|
+
// src/tui/images.tsx
|
|
16318
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
16319
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
16320
|
+
import { join as join3 } from "path";
|
|
16321
|
+
import { useRef as useRef8, useState as useState4 } from "react";
|
|
16322
|
+
import { Text as Text8 } from "ink";
|
|
16323
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
16324
|
+
var counter2 = 0;
|
|
16325
|
+
var INLINE_BUDGET = MAX_FRAME - 64 * 1024;
|
|
16326
|
+
function useChips(put) {
|
|
16327
|
+
const { t, toast } = useTui();
|
|
16328
|
+
const [items, setItems] = useState4([]);
|
|
16329
|
+
const held = useRef8([]);
|
|
16330
|
+
const commit = (next2) => {
|
|
16331
|
+
held.current = next2;
|
|
16332
|
+
setItems(next2);
|
|
16333
|
+
};
|
|
16334
|
+
const patch = (key, over) => commit(held.current.map((c) => c.key === key ? { ...c, ...over } : c));
|
|
16335
|
+
const reasonOf = (e) => {
|
|
16336
|
+
const reason = e instanceof AttachmentFailure ? e.reason : null;
|
|
16337
|
+
return reason === "too_large" ? t.images.tooLarge : reason === "not_image" ? t.images.notImage : t.images.failed;
|
|
16338
|
+
};
|
|
16339
|
+
return {
|
|
16340
|
+
items,
|
|
16341
|
+
add({ image: image2, width, height, bytes }) {
|
|
16342
|
+
if (held.current.length >= ATTACHMENTS_MAX) {
|
|
16343
|
+
toast({ text: t.images.max, tone: "info" });
|
|
16344
|
+
return;
|
|
16226
16345
|
}
|
|
16227
|
-
|
|
16228
|
-
const
|
|
16229
|
-
|
|
16230
|
-
|
|
16346
|
+
const key = counter2++;
|
|
16347
|
+
const inline2 = held.current.reduce((n, c) => n + (c.image?.data.length ?? 0), 0);
|
|
16348
|
+
if (bytes > ATTACHMENT_LOCAL_MAX || !put && inline2 + image2.data.length > INLINE_BUDGET) {
|
|
16349
|
+
commit([...held.current, { key, width, height, state: "refused", reason: t.images.tooLarge }]);
|
|
16350
|
+
return;
|
|
16351
|
+
}
|
|
16352
|
+
if (!put) {
|
|
16353
|
+
commit([...held.current, { key, width, height, state: "ready", image: image2 }]);
|
|
16354
|
+
return;
|
|
16355
|
+
}
|
|
16356
|
+
commit([...held.current, { key, width, height, state: "uploading" }]);
|
|
16357
|
+
put(image2).then((ref) => patch(key, { state: "ready", id: ref.id }), (e) => patch(key, { state: "refused", reason: reasonOf(e) }));
|
|
16358
|
+
},
|
|
16359
|
+
removeLast() {
|
|
16360
|
+
commit(held.current.slice(0, -1));
|
|
16361
|
+
},
|
|
16362
|
+
ready: items.every((c) => c.state === "ready"),
|
|
16363
|
+
ids: () => held.current.flatMap((c) => c.state === "ready" && c.id ? [c.id] : []),
|
|
16364
|
+
uploads: () => held.current.flatMap((c) => c.state === "ready" && c.image ? [c.image] : []),
|
|
16365
|
+
clear() {
|
|
16366
|
+
const taken = held.current;
|
|
16367
|
+
commit([]);
|
|
16368
|
+
return taken;
|
|
16369
|
+
},
|
|
16370
|
+
putBack(taken) {
|
|
16371
|
+
commit([...taken, ...held.current].slice(0, ATTACHMENTS_MAX));
|
|
16231
16372
|
}
|
|
16232
|
-
|
|
16233
|
-
}
|
|
16234
|
-
return rows;
|
|
16373
|
+
};
|
|
16235
16374
|
}
|
|
16236
|
-
|
|
16237
|
-
const
|
|
16238
|
-
|
|
16239
|
-
|
|
16240
|
-
|
|
16241
|
-
|
|
16242
|
-
|
|
16243
|
-
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16375
|
+
function ChipsLine({ chips }) {
|
|
16376
|
+
const { t } = useTui();
|
|
16377
|
+
if (chips.length === 0) return null;
|
|
16378
|
+
return /* @__PURE__ */ jsx11(Text8, { wrap: "truncate", children: chips.map((c, i) => /* @__PURE__ */ jsxs7(Text8, { children: [
|
|
16379
|
+
i ? " " : "",
|
|
16380
|
+
/* @__PURE__ */ jsxs7(Text8, { color: c.state === "refused" ? C.red : C.dim, children: [
|
|
16381
|
+
"\u25A3 ",
|
|
16382
|
+
t.images.capture,
|
|
16383
|
+
" ",
|
|
16384
|
+
c.width,
|
|
16385
|
+
"\xD7",
|
|
16386
|
+
c.height,
|
|
16387
|
+
" ",
|
|
16388
|
+
c.state === "uploading" ? "\u2026" : "\u2715"
|
|
16389
|
+
] }),
|
|
16390
|
+
c.reason ? /* @__PURE__ */ jsxs7(Text8, { color: C.red, children: [
|
|
16391
|
+
" ",
|
|
16392
|
+
c.reason
|
|
16393
|
+
] }) : null
|
|
16394
|
+
] }, c.key)) });
|
|
16395
|
+
}
|
|
16396
|
+
var EXT = { "image/png": "png", "image/jpeg": "jpg", "image/gif": "gif", "image/webp": "webp" };
|
|
16397
|
+
function usePaste2(chips) {
|
|
16398
|
+
const { readImage, toast, t } = useTui();
|
|
16399
|
+
const reading = useRef8(false);
|
|
16400
|
+
return () => {
|
|
16401
|
+
if (reading.current) return;
|
|
16402
|
+
reading.current = true;
|
|
16403
|
+
void readImage().then((got) => {
|
|
16404
|
+
if (got) chips.add(got);
|
|
16405
|
+
else toast({ text: t.images.none, tone: "info" });
|
|
16406
|
+
}, () => toast({ text: t.images.none, tone: "info" })).finally(() => {
|
|
16407
|
+
reading.current = false;
|
|
16408
|
+
});
|
|
16409
|
+
};
|
|
16251
16410
|
}
|
|
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 });
|
|
16411
|
+
function useOpenImage() {
|
|
16412
|
+
const { client, openFile, toast, t } = useTui();
|
|
16413
|
+
const presses = useRef8(0);
|
|
16414
|
+
const last = useRef8("");
|
|
16415
|
+
return (items) => {
|
|
16416
|
+
const signature = items.map((x) => x.ref.id).join(",");
|
|
16417
|
+
if (signature !== last.current) {
|
|
16418
|
+
last.current = signature;
|
|
16419
|
+
presses.current = 0;
|
|
16266
16420
|
}
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16421
|
+
const item = items[items.length - 1 - presses.current++ % items.length];
|
|
16422
|
+
if (!item) return;
|
|
16423
|
+
const { ref, route } = item;
|
|
16424
|
+
void (async () => {
|
|
16425
|
+
const { mime, data } = await getImage(client, ref.id, route);
|
|
16426
|
+
const dir = join3(tmpdir2(), "ledge-images");
|
|
16427
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
16428
|
+
const file = join3(dir, `${ref.id}.${EXT[mime]}`);
|
|
16429
|
+
await writeFile(file, Buffer.from(data, "base64"), { mode: 384 });
|
|
16430
|
+
await openFile(file);
|
|
16431
|
+
})().catch((e) => toast({ text: t.images.openFailed(e instanceof AttachmentFailure ? e.reason : e.message), tone: "error" }));
|
|
16432
|
+
};
|
|
16270
16433
|
}
|
|
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
16434
|
|
|
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
|
-
|
|
16435
|
+
// src/tui/ui/ModelPicker.tsx
|
|
16436
|
+
import { useState as useState6 } from "react";
|
|
16437
|
+
import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
|
|
16438
|
+
|
|
16439
|
+
// ../fleet/src/models.ts
|
|
16440
|
+
import { useEffect as useEffect7, useState as useState5 } from "react";
|
|
16441
|
+
var IDLE = { status: "idle", models: [] };
|
|
16442
|
+
var LOADING = { status: "loading", models: [] };
|
|
16443
|
+
var known = /* @__PURE__ */ new Map();
|
|
16444
|
+
var next = 0;
|
|
16445
|
+
function useModels(client, machine, tool) {
|
|
16446
|
+
const key = machine === null || tool === null ? null : `${machine}|${tool}`;
|
|
16447
|
+
const [answer, setAnswer] = useState5(null);
|
|
16448
|
+
useEffect7(() => {
|
|
16449
|
+
if (key === null || machine === null || tool === null || known.has(key)) return;
|
|
16450
|
+
const requestId = `models-${++next}`;
|
|
16451
|
+
const off = client.onMessage((m) => {
|
|
16452
|
+
if (m.type !== "models.listing" || m.requestId !== requestId) return;
|
|
16453
|
+
const state2 = { status: "ready", models: m.models, ...m.error ? { error: m.error } : {} };
|
|
16454
|
+
if (!m.error) known.set(key, state2);
|
|
16455
|
+
setAnswer({ key, state: state2 });
|
|
16456
|
+
});
|
|
16457
|
+
client.send({ type: "models.list", requestId, tool, machineId: machine });
|
|
16458
|
+
return off;
|
|
16459
|
+
}, [client, key, machine, tool]);
|
|
16460
|
+
if (key === null) return IDLE;
|
|
16461
|
+
return known.get(key) ?? (answer && answer.key === key ? answer.state : LOADING);
|
|
16462
|
+
}
|
|
16463
|
+
|
|
16464
|
+
// src/tui/ui/ModelPicker.tsx
|
|
16465
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
16466
|
+
function ModelPicker({ machine, tool, current: current2, onPick, onCancel }) {
|
|
16467
|
+
const { client, t, g } = useTui();
|
|
16468
|
+
useEditing(true);
|
|
16469
|
+
const models = useModels(client, machine, tool);
|
|
16470
|
+
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 }))];
|
|
16471
|
+
const [at, setAt] = useState6(() => Math.max(0, rows.findIndex((r) => r.id === current2.model)));
|
|
16472
|
+
const [effort, setEffort] = useState6(current2.effort);
|
|
16473
|
+
const row2 = rows[Math.min(at, rows.length - 1)];
|
|
16316
16474
|
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
16475
|
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));
|
|
16476
|
+
onCancel();
|
|
16365
16477
|
return;
|
|
16366
16478
|
}
|
|
16367
|
-
if (key.
|
|
16368
|
-
|
|
16479
|
+
if (key.upArrow || input === "k") {
|
|
16480
|
+
setAt((i) => (i - 1 + rows.length) % rows.length);
|
|
16481
|
+
setEffort(null);
|
|
16369
16482
|
return;
|
|
16370
16483
|
}
|
|
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);
|
|
16484
|
+
if (key.downArrow || input === "j") {
|
|
16485
|
+
setAt((i) => (i + 1) % rows.length);
|
|
16486
|
+
setEffort(null);
|
|
16379
16487
|
return;
|
|
16380
16488
|
}
|
|
16381
|
-
if (key.
|
|
16382
|
-
const
|
|
16383
|
-
|
|
16384
|
-
set(down);
|
|
16385
|
-
return;
|
|
16386
|
-
}
|
|
16387
|
-
if (inHistory(browse2)) show(browse2, browse2.i + 1);
|
|
16489
|
+
if ((key.leftArrow || key.rightArrow) && row2.efforts.length) {
|
|
16490
|
+
const cur = Math.max(0, row2.efforts.indexOf(effort ?? row2.defaultEffort ?? row2.efforts[0]));
|
|
16491
|
+
setEffort(row2.efforts[(cur + (key.rightArrow ? 1 : -1) + row2.efforts.length) % row2.efforts.length]);
|
|
16388
16492
|
return;
|
|
16389
16493
|
}
|
|
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)) });
|
|
16494
|
+
if (key.return) onPick({ model: row2.id, effort: row2.efforts.length ? effort ?? null : null });
|
|
16495
|
+
});
|
|
16496
|
+
if (models.status !== "ready") return /* @__PURE__ */ jsx12(Text9, { color: C.faint, children: t.launch.loading });
|
|
16497
|
+
return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", children: [
|
|
16498
|
+
models.error ? /* @__PURE__ */ jsx12(Text9, { color: C.yellow, children: models.error }) : null,
|
|
16499
|
+
rows.map((r, i) => /* @__PURE__ */ jsxs8(Text9, { wrap: "truncate", backgroundColor: i === at ? C.sel : void 0, children: [
|
|
16500
|
+
/* @__PURE__ */ jsxs8(Text9, { color: i === at ? C.coral : C.faint, children: [
|
|
16501
|
+
i === at ? g.cursor : " ",
|
|
16502
|
+
" "
|
|
16503
|
+
] }),
|
|
16504
|
+
/* @__PURE__ */ jsx12(Text9, { color: C.fg, children: r.label }),
|
|
16505
|
+
i === at && r.efforts.length ? /* @__PURE__ */ jsxs8(Text9, { color: C.dim, children: [
|
|
16506
|
+
" ",
|
|
16507
|
+
r.efforts.map((e) => e === (effort ?? r.defaultEffort) ? `${g.prev} ${e} ${g.next}` : e).join(" ")
|
|
16508
|
+
] }) : null
|
|
16509
|
+
] }, r.id ?? "-"))
|
|
16510
|
+
] });
|
|
16414
16511
|
}
|
|
16415
16512
|
|
|
16416
16513
|
// src/tui/screens/Session.tsx
|
|
@@ -16438,7 +16535,7 @@ function SessionView({ s }) {
|
|
|
16438
16535
|
const [anchor, setAnchor] = useState7(null);
|
|
16439
16536
|
useEditing(mode === "reply" || mode === "note");
|
|
16440
16537
|
const items = d.threads.get(s.id);
|
|
16441
|
-
|
|
16538
|
+
useEffect8(() => {
|
|
16442
16539
|
if (!items && !s.offline) client.send({ type: "session.thread", id: s.id });
|
|
16443
16540
|
}, [items === void 0, s.id, d.snapshotSeq]);
|
|
16444
16541
|
const width = Math.max(20, columns - 6);
|
|
@@ -16460,7 +16557,7 @@ function SessionView({ s }) {
|
|
|
16460
16557
|
};
|
|
16461
16558
|
const images = useMemo3(() => (items ? threadImages(items, unfolded) : []).map((ref) => ({ ref, route: { id: s.id } })), [items, unfolded, s.id]);
|
|
16462
16559
|
const chips = useChips((image2) => putImage(client, image2, { id: s.id }));
|
|
16463
|
-
const paste =
|
|
16560
|
+
const paste = usePaste2(chips);
|
|
16464
16561
|
const openImage2 = useOpenImage();
|
|
16465
16562
|
const machine = splitId(s.id)?.machine ?? "local";
|
|
16466
16563
|
const canSwitch = machines.find((m) => m.id === machine)?.daemon?.modelSwitch === true;
|
|
@@ -16673,7 +16770,7 @@ function PendingBox({ s, a }) {
|
|
|
16673
16770
|
|
|
16674
16771
|
// src/tui/screens/Ask.tsx
|
|
16675
16772
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
16676
|
-
import { useEffect as
|
|
16773
|
+
import { useEffect as useEffect10, useMemo as useMemo4, useRef as useRef10, useState as useState9 } from "react";
|
|
16677
16774
|
import { Box as Box12, Text as Text12, useInput as useInput4 } from "ink";
|
|
16678
16775
|
|
|
16679
16776
|
// ../ui/src/chat/actionRow.ts
|
|
@@ -16830,11 +16927,11 @@ function openCards(turns2) {
|
|
|
16830
16927
|
}
|
|
16831
16928
|
|
|
16832
16929
|
// src/tui/ui/throttle.ts
|
|
16833
|
-
import { useEffect as
|
|
16930
|
+
import { useEffect as useEffect9, useRef as useRef9, useState as useState8 } from "react";
|
|
16834
16931
|
function useThrottled(value, ms) {
|
|
16835
16932
|
const [shown, setShown] = useState8(value);
|
|
16836
16933
|
const last = useRef9(0);
|
|
16837
|
-
|
|
16934
|
+
useEffect9(() => {
|
|
16838
16935
|
const show = () => {
|
|
16839
16936
|
last.current = Date.now();
|
|
16840
16937
|
setShown(value);
|
|
@@ -17013,7 +17110,7 @@ function Ask() {
|
|
|
17013
17110
|
const [anchor, setAnchor] = useState9(null);
|
|
17014
17111
|
const [view, setView] = useState9(10);
|
|
17015
17112
|
const chips = useChips(null);
|
|
17016
|
-
const paste =
|
|
17113
|
+
const paste = usePaste2(chips);
|
|
17017
17114
|
const openImage2 = useOpenImage();
|
|
17018
17115
|
const [busy, setBusy] = useState9(null);
|
|
17019
17116
|
const [rowOf2, setRowOf] = useState9({});
|
|
@@ -17021,7 +17118,7 @@ function Ask() {
|
|
|
17021
17118
|
const [review, setReview] = useState9(null);
|
|
17022
17119
|
useEditing(focus === -1 && !confirm);
|
|
17023
17120
|
const sentAsk = useRef10(null);
|
|
17024
|
-
|
|
17121
|
+
useEffect10(() => {
|
|
17025
17122
|
const s = sentAsk.current;
|
|
17026
17123
|
const turn = s && d.chat.find((x) => x.qid === s.qid);
|
|
17027
17124
|
if (!s || !turn) return;
|
|
@@ -17037,10 +17134,10 @@ function Ask() {
|
|
|
17037
17134
|
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
17135
|
};
|
|
17039
17136
|
const planOf = (turn) => cardPlan(turn.actions ?? [], room);
|
|
17040
|
-
|
|
17137
|
+
useEffect10(() => {
|
|
17041
17138
|
if (focus >= cards.length) setFocus(cards.length ? cards.length - 1 : -1);
|
|
17042
17139
|
}, [cards.length, focus]);
|
|
17043
|
-
|
|
17140
|
+
useEffect10(() => {
|
|
17044
17141
|
if (!review) return;
|
|
17045
17142
|
const i = cards.findIndex((c) => c.qid === review);
|
|
17046
17143
|
if (i >= 0) {
|
|
@@ -17048,21 +17145,21 @@ function Ask() {
|
|
|
17048
17145
|
setReview(null);
|
|
17049
17146
|
}
|
|
17050
17147
|
}, [cards, review]);
|
|
17051
|
-
|
|
17148
|
+
useEffect10(() => {
|
|
17052
17149
|
if (confirming && !cards.some((c) => c.kind === "action" && c.qid === confirming.qid)) setConfirming(null);
|
|
17053
17150
|
}, [confirming, cards]);
|
|
17054
|
-
|
|
17151
|
+
useEffect10(() => {
|
|
17055
17152
|
const turn = confirming && d.chat.find((x) => x.qid === confirming.qid);
|
|
17056
17153
|
if (turn && !confirming.rows.every((i) => appliable(planOf(turn).shown[i]))) setConfirming(null);
|
|
17057
17154
|
});
|
|
17058
17155
|
const ignored = [...d.chat].filter((x) => x.ignoredAt).sort((a, b2) => b2.ignoredAt.localeCompare(a.ignoredAt))[0];
|
|
17059
|
-
|
|
17156
|
+
useEffect10(() => {
|
|
17060
17157
|
if (!busy?.rid) return;
|
|
17061
17158
|
return client.onMessage((m) => {
|
|
17062
17159
|
if (m.type === "chat.acted" && m.rid === busy.rid) setBusy(null);
|
|
17063
17160
|
});
|
|
17064
17161
|
}, [busy?.rid]);
|
|
17065
|
-
|
|
17162
|
+
useEffect10(() => {
|
|
17066
17163
|
if (!busy) return;
|
|
17067
17164
|
const turn = d.chat.find((x) => x.qid === busy.qid);
|
|
17068
17165
|
if (turn !== busy.turn && !busy.rid || (d.errors.at(-1)?.seq ?? 0) > busy.errorSeq) {
|
|
@@ -17278,7 +17375,7 @@ function Ask() {
|
|
|
17278
17375
|
}
|
|
17279
17376
|
if (input === "s" && cards[focus].kind === "team") {
|
|
17280
17377
|
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 } : {} })) });
|
|
17378
|
+
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
17379
|
return true;
|
|
17283
17380
|
}
|
|
17284
17381
|
if (key.escape) {
|
|
@@ -17395,6 +17492,7 @@ function CardBox({ card, turn, focused, busy }) {
|
|
|
17395
17492
|
] }, x.role)),
|
|
17396
17493
|
auto.length ? /* @__PURE__ */ jsx15(Text12, { color: C.yellow, wrap: "truncate", children: t.ask.autoWarning(auto.join(", ")) }) : null,
|
|
17397
17494
|
team.error ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.error }) : null,
|
|
17495
|
+
team.saveError && !team.savedPath ? /* @__PURE__ */ jsx15(Text12, { color: C.red, wrap: "truncate", children: team.saveError }) : null,
|
|
17398
17496
|
/* @__PURE__ */ jsxs11(Box12, { columnGap: 2, children: [
|
|
17399
17497
|
/* @__PURE__ */ jsx15(Button, { label: busy ? `${g.live} ${t.ask.launchTeam}` : t.ask.launchTeam, k: "\u21B5", primary: focused }),
|
|
17400
17498
|
!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 +17579,7 @@ function CardBox({ card, turn, focused, busy }) {
|
|
|
17481
17579
|
}
|
|
17482
17580
|
|
|
17483
17581
|
// src/tui/screens/Launch.tsx
|
|
17484
|
-
import { useEffect as
|
|
17582
|
+
import { useEffect as useEffect11, useMemo as useMemo5, useRef as useRef11, useState as useState10 } from "react";
|
|
17485
17583
|
import { Box as Box13, Text as Text14, useInput as useInput6 } from "ink";
|
|
17486
17584
|
|
|
17487
17585
|
// ../fleet/src/launch.ts
|
|
@@ -17633,7 +17731,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17633
17731
|
const [repoAt, setRepoAt] = useState10(0);
|
|
17634
17732
|
const [launching, setLaunching] = useState10(null);
|
|
17635
17733
|
useEditing(true);
|
|
17636
|
-
|
|
17734
|
+
useEffect11(() => {
|
|
17637
17735
|
if (form.machine === null) {
|
|
17638
17736
|
const up = machines.find((m) => m.ready);
|
|
17639
17737
|
if (up) setForm((f) => withMachine(f, list3, up.id));
|
|
@@ -17656,7 +17754,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17656
17754
|
const readHere = sameMachine && target !== null && target === prefill.cwd;
|
|
17657
17755
|
const asked2 = sameMachine && !readHere && target !== null ? `${withFolder.machine}\0${target}` : null;
|
|
17658
17756
|
const [seen, setSeen] = useState10(null);
|
|
17659
|
-
|
|
17757
|
+
useEffect11(() => {
|
|
17660
17758
|
if (!asked2) return;
|
|
17661
17759
|
const [machineId, cwd] = asked2.split("\0");
|
|
17662
17760
|
const requestId = `tui-role-${randomUUID3()}`;
|
|
@@ -17683,7 +17781,7 @@ function SessionLaunch({ onTeam, prefill }) {
|
|
|
17683
17781
|
const [name, setName] = useState10(null);
|
|
17684
17782
|
const shownName = name ?? drawn.name;
|
|
17685
17783
|
const missing = missingOf(withFolder, machines);
|
|
17686
|
-
|
|
17784
|
+
useEffect11(() => {
|
|
17687
17785
|
if (!launching) return;
|
|
17688
17786
|
if ((d.lastLaunched?.seq ?? 0) > launching.seq) {
|
|
17689
17787
|
select(d.lastLaunched.id);
|
|
@@ -17894,7 +17992,7 @@ function TeamLaunch({ onSession, prefill }) {
|
|
|
17894
17992
|
const [name, setName] = useState10("");
|
|
17895
17993
|
const [launching, setLaunching] = useState10(false);
|
|
17896
17994
|
useEditing(true);
|
|
17897
|
-
|
|
17995
|
+
useEffect11(() => {
|
|
17898
17996
|
if (!cwd) {
|
|
17899
17997
|
setDefs({ roles: [], templates: [] });
|
|
17900
17998
|
return;
|
|
@@ -18180,7 +18278,7 @@ function Help() {
|
|
|
18180
18278
|
const k = t.keys;
|
|
18181
18279
|
const groups = [
|
|
18182
18280
|
[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]]],
|
|
18281
|
+
[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
18282
|
[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
18283
|
[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
18284
|
[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 +18316,7 @@ function Help() {
|
|
|
18218
18316
|
}
|
|
18219
18317
|
|
|
18220
18318
|
// src/tui/screens/Teams.tsx
|
|
18221
|
-
import { useEffect as
|
|
18319
|
+
import { useEffect as useEffect12, useMemo as useMemo7, useState as useState13 } from "react";
|
|
18222
18320
|
import { Box as Box16, Text as Text17 } from "ink";
|
|
18223
18321
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
18224
18322
|
|
|
@@ -18298,7 +18396,7 @@ function Teams() {
|
|
|
18298
18396
|
const [problems, setProblems] = useState13(false);
|
|
18299
18397
|
const [silent, setSilent] = useState13(false);
|
|
18300
18398
|
const [reads, setReads] = useState13(0);
|
|
18301
|
-
|
|
18399
|
+
useEffect12(() => {
|
|
18302
18400
|
setDefs(null);
|
|
18303
18401
|
setSilent(false);
|
|
18304
18402
|
const requestId = `tui-catalog-${randomUUID4()}`;
|
|
@@ -18579,7 +18677,7 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
18579
18677
|
const kept = useRef13({ drafts: /* @__PURE__ */ new Map(), sent: /* @__PURE__ */ new Map(), asked: [] }).current;
|
|
18580
18678
|
const armed = useRef13(0);
|
|
18581
18679
|
const catalog = useRef13({});
|
|
18582
|
-
|
|
18680
|
+
useEffect13(() => {
|
|
18583
18681
|
if (!lastToast) return;
|
|
18584
18682
|
const timer2 = setTimeout(() => setToast(null), lastToast.ms ?? TOAST_MS);
|
|
18585
18683
|
return () => clearTimeout(timer2);
|
|
@@ -18588,22 +18686,22 @@ function Shell({ client, lang, setLang: setLangState, saveLang, theme, setThemeS
|
|
|
18588
18686
|
const setEditing = useCallback((on) => setEditingState(on), []);
|
|
18589
18687
|
const view = useMemo8(() => overview(d.sessions, projects, teams ?? [], tasks), [d.sessions, projects, teams, tasks]);
|
|
18590
18688
|
const order2 = useRef13([]);
|
|
18591
|
-
|
|
18689
|
+
useEffect13(() => {
|
|
18592
18690
|
setSelectedId((cur) => cur === null && view.yours[0] ? view.yours[0].id : keepSelection(view.order, cur, order2.current));
|
|
18593
18691
|
order2.current = view.order;
|
|
18594
18692
|
}, [view]);
|
|
18595
18693
|
const selected = (selectedId ? d.byId.get(selectedId) : void 0) ?? null;
|
|
18596
18694
|
const seen = useRef13(null);
|
|
18597
18695
|
const titled = useRef13("");
|
|
18598
|
-
|
|
18696
|
+
useEffect13(() => {
|
|
18599
18697
|
if (seen.current) for (const text5 of asked(seen.current, d.sessions, t.notify)) write(notification(text5, notify, env));
|
|
18600
18698
|
seen.current = d.byId;
|
|
18601
|
-
const next2 = title(t.notify.title(listed(d.sessions).filter(isPending).length));
|
|
18699
|
+
const next2 = title(t.notify.title(listed(d.sessions).filter(isPending).length + openDecisions(tasks).length));
|
|
18602
18700
|
if (next2 !== titled.current) {
|
|
18603
18701
|
titled.current = next2;
|
|
18604
18702
|
write(next2);
|
|
18605
18703
|
}
|
|
18606
|
-
}, [d.byId]);
|
|
18704
|
+
}, [d.byId, tasks]);
|
|
18607
18705
|
const actions = useActions({ client, sessions: d.sessions, errors: d.errors, t, toast });
|
|
18608
18706
|
const go = useCallback((next2) => {
|
|
18609
18707
|
setScreen((cur) => {
|