bertrand 0.20.0 → 0.20.1
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/bertrand.js +39 -12
- package/dist/dashboard/assets/index-D_l3EPhC.css +1 -0
- package/dist/dashboard/assets/index-mg96b7q-.js +684 -0
- package/dist/dashboard/index.html +3 -3
- package/dist/run-screen.js +308 -455
- package/package.json +1 -1
- package/dist/dashboard/assets/index-DgvcDkSY.js +0 -673
- package/dist/dashboard/assets/index-qVHXGTGL.css +0 -1
- package/dist/dashboard/assets/workbox-window.prod.es5-BBnX5xw4.js +0 -2
- package/dist/dashboard/manifest.webmanifest +0 -1
- package/dist/dashboard/sw.js +0 -1
- package/dist/dashboard/workbox-e4022e15.js +0 -1
package/dist/run-screen.js
CHANGED
|
@@ -18,14 +18,9 @@ var __export = (target, all) => {
|
|
|
18
18
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
19
19
|
import { render } from "@orchetron/storm";
|
|
20
20
|
|
|
21
|
-
// src/tui/screens/
|
|
22
|
-
import { useState as
|
|
23
|
-
import { useTui } from "@orchetron/storm";
|
|
24
|
-
|
|
25
|
-
// src/tui/screens/project-picker/index.tsx
|
|
26
|
-
import { useMemo as useMemo2 } from "react";
|
|
27
|
-
import { Box as Box5, Text as Text6 } from "@orchetron/storm";
|
|
28
|
-
import { existsSync as existsSync2 } from "fs";
|
|
21
|
+
// src/tui/screens/launch/index.tsx
|
|
22
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
23
|
+
import { Box as Box5, Text as Text6, useTui } from "@orchetron/storm";
|
|
29
24
|
|
|
30
25
|
// src/tui/components/app-details.tsx
|
|
31
26
|
import { Box, Text } from "@orchetron/storm";
|
|
@@ -565,6 +560,16 @@ function StatusDot({ status }) {
|
|
|
565
560
|
children: "\u25CF"
|
|
566
561
|
}, undefined, false, undefined, this);
|
|
567
562
|
}
|
|
563
|
+
// src/db/queries/sessions.ts
|
|
564
|
+
import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
565
|
+
|
|
566
|
+
// src/db/client.ts
|
|
567
|
+
import { Database } from "bun:sqlite";
|
|
568
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
569
|
+
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
|
570
|
+
import { mkdirSync as mkdirSync2 } from "fs";
|
|
571
|
+
import { dirname } from "path";
|
|
572
|
+
|
|
568
573
|
// src/lib/projects/registry.ts
|
|
569
574
|
import {
|
|
570
575
|
existsSync,
|
|
@@ -575,7 +580,6 @@ import {
|
|
|
575
580
|
statSync,
|
|
576
581
|
writeFileSync
|
|
577
582
|
} from "fs";
|
|
578
|
-
import { randomBytes } from "crypto";
|
|
579
583
|
import { join as join2 } from "path";
|
|
580
584
|
|
|
581
585
|
// src/lib/paths.ts
|
|
@@ -616,14 +620,6 @@ function readRegistry() {
|
|
|
616
620
|
return null;
|
|
617
621
|
}
|
|
618
622
|
}
|
|
619
|
-
function writeRegistry(registry) {
|
|
620
|
-
const path = registryPath();
|
|
621
|
-
mkdirSync(_registryDir, { recursive: true });
|
|
622
|
-
const tmp = `${path}.tmp-${process.pid}-${process.hrtime.bigint()}-${randomBytes(4).toString("hex")}`;
|
|
623
|
-
writeFileSync(tmp, JSON.stringify(registry, null, 2) + `
|
|
624
|
-
`);
|
|
625
|
-
renameSync(tmp, path);
|
|
626
|
-
}
|
|
627
623
|
function recoverFromDisk() {
|
|
628
624
|
const dir = projectsDir();
|
|
629
625
|
if (!existsSync(dir))
|
|
@@ -673,52 +669,6 @@ function listProjects() {
|
|
|
673
669
|
function getActiveProjectSlug() {
|
|
674
670
|
return loadRegistry()?.activeProjectSlug ?? DEFAULT_PROJECT_SLUG;
|
|
675
671
|
}
|
|
676
|
-
function setActiveProjectSlug(slug) {
|
|
677
|
-
const registry = loadRegistry();
|
|
678
|
-
if (!registry) {
|
|
679
|
-
throw new Error(`No registry to update \u2014 create a project first (no ${registryPath()} and no ${projectsDir()}/* to recover from)`);
|
|
680
|
-
}
|
|
681
|
-
if (!registry.projects.some((p) => p.slug === slug)) {
|
|
682
|
-
throw new Error(`Unknown project slug "${slug}"`);
|
|
683
|
-
}
|
|
684
|
-
registry.activeProjectSlug = slug;
|
|
685
|
-
const idx = registry.projects.findIndex((p) => p.slug === slug);
|
|
686
|
-
if (idx !== -1) {
|
|
687
|
-
registry.projects[idx].lastUsedAt = new Date().toISOString();
|
|
688
|
-
}
|
|
689
|
-
writeRegistry(registry);
|
|
690
|
-
}
|
|
691
|
-
function registerProject(opts) {
|
|
692
|
-
const registry = loadRegistry() ?? {
|
|
693
|
-
activeProjectSlug: opts.slug,
|
|
694
|
-
projects: []
|
|
695
|
-
};
|
|
696
|
-
if (registry.projects.some((p) => p.slug === opts.slug)) {
|
|
697
|
-
throw new Error(`Project "${opts.slug}" already exists`);
|
|
698
|
-
}
|
|
699
|
-
const now = new Date().toISOString();
|
|
700
|
-
const entry = {
|
|
701
|
-
slug: opts.slug,
|
|
702
|
-
name: opts.name,
|
|
703
|
-
color: opts.color,
|
|
704
|
-
createdAt: now,
|
|
705
|
-
lastUsedAt: now
|
|
706
|
-
};
|
|
707
|
-
registry.projects.push(entry);
|
|
708
|
-
writeRegistry(registry);
|
|
709
|
-
return entry;
|
|
710
|
-
}
|
|
711
|
-
function removeProject(slug) {
|
|
712
|
-
const registry = loadRegistry();
|
|
713
|
-
if (!registry)
|
|
714
|
-
return;
|
|
715
|
-
registry.projects = registry.projects.filter((p) => p.slug !== slug);
|
|
716
|
-
if (registry.activeProjectSlug === slug) {
|
|
717
|
-
const survivor = [...registry.projects].sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt))[0];
|
|
718
|
-
registry.activeProjectSlug = survivor?.slug ?? DEFAULT_PROJECT_SLUG;
|
|
719
|
-
}
|
|
720
|
-
writeRegistry(registry);
|
|
721
|
-
}
|
|
722
672
|
function isRegistry(value) {
|
|
723
673
|
if (typeof value !== "object" || value === null)
|
|
724
674
|
return false;
|
|
@@ -747,13 +697,6 @@ function projectPaths(slug) {
|
|
|
747
697
|
};
|
|
748
698
|
}
|
|
749
699
|
|
|
750
|
-
// src/db/client.ts
|
|
751
|
-
import { Database } from "bun:sqlite";
|
|
752
|
-
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
753
|
-
import { migrate } from "drizzle-orm/bun-sqlite/migrator";
|
|
754
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
755
|
-
import { dirname } from "path";
|
|
756
|
-
|
|
757
700
|
// src/lib/projects/resolve.ts
|
|
758
701
|
var _cached = null;
|
|
759
702
|
function resolveActiveProject() {
|
|
@@ -771,9 +714,6 @@ function resolveActiveProject() {
|
|
|
771
714
|
_cached = { slug, name, ...projectPaths(slug) };
|
|
772
715
|
return _cached;
|
|
773
716
|
}
|
|
774
|
-
function _resetActiveProjectCache() {
|
|
775
|
-
_cached = null;
|
|
776
|
-
}
|
|
777
717
|
|
|
778
718
|
// src/db/schema.ts
|
|
779
719
|
var exports_schema = {};
|
|
@@ -936,207 +876,6 @@ function hasSessionsTable(sqlite) {
|
|
|
936
876
|
return row !== null;
|
|
937
877
|
}
|
|
938
878
|
|
|
939
|
-
// src/lib/format.ts
|
|
940
|
-
var SECOND = 1000;
|
|
941
|
-
var MINUTE = 60 * SECOND;
|
|
942
|
-
var HOUR = 60 * MINUTE;
|
|
943
|
-
var DAY = 24 * HOUR;
|
|
944
|
-
function formatDuration(ms) {
|
|
945
|
-
if (ms < MINUTE)
|
|
946
|
-
return `${Math.round(ms / SECOND)}s`;
|
|
947
|
-
const days = Math.floor(ms / DAY);
|
|
948
|
-
const hours = Math.floor(ms % DAY / HOUR);
|
|
949
|
-
const minutes = Math.floor(ms % HOUR / MINUTE);
|
|
950
|
-
if (days > 0)
|
|
951
|
-
return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
952
|
-
if (hours > 0)
|
|
953
|
-
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
954
|
-
return `${minutes}m`;
|
|
955
|
-
}
|
|
956
|
-
function formatAgo(isoOrDate) {
|
|
957
|
-
const date = typeof isoOrDate === "string" ? new Date(isoOrDate) : isoOrDate;
|
|
958
|
-
const ms = Date.now() - date.getTime();
|
|
959
|
-
if (ms < MINUTE)
|
|
960
|
-
return "just now";
|
|
961
|
-
if (ms < HOUR)
|
|
962
|
-
return `${Math.floor(ms / MINUTE)}m ago`;
|
|
963
|
-
if (ms < DAY)
|
|
964
|
-
return `${Math.floor(ms / HOUR)}h ago`;
|
|
965
|
-
if (ms < 2 * DAY)
|
|
966
|
-
return "yesterday";
|
|
967
|
-
if (ms < 7 * DAY)
|
|
968
|
-
return `${Math.floor(ms / DAY)}d ago`;
|
|
969
|
-
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// src/tui/screens/project-picker/index.tsx
|
|
973
|
-
import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
|
|
974
|
-
var SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
975
|
-
function statsFor(slug) {
|
|
976
|
-
const dbFile = projectPaths(slug).db;
|
|
977
|
-
if (!existsSync2(dbFile))
|
|
978
|
-
return { total: 0, active: 0, unreadable: false };
|
|
979
|
-
try {
|
|
980
|
-
const db = getDbForProject(slug);
|
|
981
|
-
const all = db.select({ status: sessions.status }).from(sessions).all();
|
|
982
|
-
return {
|
|
983
|
-
total: all.length,
|
|
984
|
-
active: all.filter((s) => s.status === "active" || s.status === "waiting").length,
|
|
985
|
-
unreadable: false
|
|
986
|
-
};
|
|
987
|
-
} catch {
|
|
988
|
-
return { total: 0, active: 0, unreadable: true };
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
function projectRow(entry, isActive) {
|
|
992
|
-
const stats = statsFor(entry.slug);
|
|
993
|
-
const sessionsLabel = stats.unreadable ? "?" : `${stats.active}/${stats.total}`;
|
|
994
|
-
const lastUsed = formatAgo(entry.lastUsedAt);
|
|
995
|
-
return {
|
|
996
|
-
value: entry.slug,
|
|
997
|
-
label: `${entry.slug} ${entry.name} ${sessionsLabel}`,
|
|
998
|
-
meta: lastUsed,
|
|
999
|
-
display: (isCursor) => {
|
|
1000
|
-
const cursorColor = "green";
|
|
1001
|
-
const slugColor = isCursor ? cursorColor : undefined;
|
|
1002
|
-
const nameColor = isCursor ? cursorColor : undefined;
|
|
1003
|
-
return /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1004
|
-
children: [
|
|
1005
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1006
|
-
color: cursorColor,
|
|
1007
|
-
bold: true,
|
|
1008
|
-
children: isCursor ? "\u276F " : " "
|
|
1009
|
-
}, undefined, false, undefined, this),
|
|
1010
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1011
|
-
bold: isActive,
|
|
1012
|
-
color: isActive ? "gold" : slugColor,
|
|
1013
|
-
children: isActive ? "\u25CF " : " "
|
|
1014
|
-
}, undefined, false, undefined, this),
|
|
1015
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1016
|
-
color: slugColor,
|
|
1017
|
-
bold: isCursor || isActive,
|
|
1018
|
-
children: entry.slug.padEnd(16)
|
|
1019
|
-
}, undefined, false, undefined, this),
|
|
1020
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1021
|
-
color: nameColor,
|
|
1022
|
-
dim: !isCursor,
|
|
1023
|
-
children: entry.name.padEnd(20)
|
|
1024
|
-
}, undefined, false, undefined, this),
|
|
1025
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1026
|
-
dim: true,
|
|
1027
|
-
children: stats.unreadable ? " (unreadable)" : ` ${sessionsLabel} active/total`
|
|
1028
|
-
}, undefined, false, undefined, this)
|
|
1029
|
-
]
|
|
1030
|
-
}, undefined, true, undefined, this);
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
function ProjectPicker({ onSelect }) {
|
|
1035
|
-
const allProjects = useMemo2(() => listProjects(), []);
|
|
1036
|
-
const activeSlug = useMemo2(() => getActiveProjectSlug(), []);
|
|
1037
|
-
const items = useMemo2(() => {
|
|
1038
|
-
return allProjects.slice().sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt)).map((p) => projectRow(p, p.slug === activeSlug));
|
|
1039
|
-
}, [allProjects, activeSlug]);
|
|
1040
|
-
const suggestions = useMemo2(() => allProjects.map((p) => p.slug), [allProjects]);
|
|
1041
|
-
const knownSlugs = useMemo2(() => new Set(allProjects.map((p) => p.slug)), [allProjects]);
|
|
1042
|
-
const select = (selection) => {
|
|
1043
|
-
onSelect(selection);
|
|
1044
|
-
};
|
|
1045
|
-
const handleSubmit = (value) => {
|
|
1046
|
-
const trimmed = value.trim();
|
|
1047
|
-
if (!trimmed)
|
|
1048
|
-
return;
|
|
1049
|
-
if (knownSlugs.has(trimmed)) {
|
|
1050
|
-
select({ type: "select", slug: trimmed });
|
|
1051
|
-
return;
|
|
1052
|
-
}
|
|
1053
|
-
if (!SLUG_PATTERN.test(trimmed)) {
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
select({ type: "create", slug: trimmed });
|
|
1057
|
-
};
|
|
1058
|
-
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
1059
|
-
flexDirection: "column",
|
|
1060
|
-
paddingY: 1,
|
|
1061
|
-
gap: 1,
|
|
1062
|
-
children: [
|
|
1063
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1064
|
-
marginX: 1,
|
|
1065
|
-
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
1066
|
-
}, undefined, false, undefined, this),
|
|
1067
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1068
|
-
flexDirection: "column",
|
|
1069
|
-
marginX: 2,
|
|
1070
|
-
gap: 1,
|
|
1071
|
-
children: [
|
|
1072
|
-
/* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
|
|
1073
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1074
|
-
flexDirection: "column",
|
|
1075
|
-
gap: 1,
|
|
1076
|
-
children: [
|
|
1077
|
-
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1078
|
-
flexDirection: "row",
|
|
1079
|
-
gap: 1,
|
|
1080
|
-
children: [
|
|
1081
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1082
|
-
bold: true,
|
|
1083
|
-
children: "Projects"
|
|
1084
|
-
}, undefined, false, undefined, this),
|
|
1085
|
-
allProjects.length > 0 ? /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1086
|
-
children: [
|
|
1087
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1088
|
-
dim: true,
|
|
1089
|
-
children: "\xB7"
|
|
1090
|
-
}, undefined, false, undefined, this),
|
|
1091
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1092
|
-
dim: true,
|
|
1093
|
-
children: [
|
|
1094
|
-
allProjects.length,
|
|
1095
|
-
" total",
|
|
1096
|
-
activeSlug ? ` \xB7 active: ${activeSlug}` : ""
|
|
1097
|
-
]
|
|
1098
|
-
}, undefined, true, undefined, this)
|
|
1099
|
-
]
|
|
1100
|
-
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1101
|
-
dim: true,
|
|
1102
|
-
children: "\xB7 none \u2014 type a slug to create your first project"
|
|
1103
|
-
}, undefined, false, undefined, this)
|
|
1104
|
-
]
|
|
1105
|
-
}, undefined, true, undefined, this),
|
|
1106
|
-
/* @__PURE__ */ jsxDEV6(Picker, {
|
|
1107
|
-
mode: "single",
|
|
1108
|
-
items,
|
|
1109
|
-
isFocused: true,
|
|
1110
|
-
maxVisible: 16,
|
|
1111
|
-
suggest: suggestions,
|
|
1112
|
-
placeholder: "Filter or type a new slug to create\u2026",
|
|
1113
|
-
emptyHint: "No projects. Type a slug to create one.",
|
|
1114
|
-
onSubmit: handleSubmit,
|
|
1115
|
-
onKey: (e) => {
|
|
1116
|
-
if (e.key === "c" && e.ctrl) {
|
|
1117
|
-
select({ type: "quit" });
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
}, undefined, false, undefined, this),
|
|
1121
|
-
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1122
|
-
dim: true,
|
|
1123
|
-
children: "\u2191\u2193 navigate \xB7 enter select \xB7 type to filter or create new \xB7 ctrl+c quit"
|
|
1124
|
-
}, undefined, false, undefined, this)
|
|
1125
|
-
]
|
|
1126
|
-
}, undefined, true, undefined, this)
|
|
1127
|
-
]
|
|
1128
|
-
}, undefined, true, undefined, this)
|
|
1129
|
-
]
|
|
1130
|
-
}, undefined, true, undefined, this);
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// src/tui/screens/launch/index.tsx
|
|
1134
|
-
import { useMemo as useMemo3, useState as useState3 } from "react";
|
|
1135
|
-
import { Box as Box6, Text as Text7 } from "@orchetron/storm";
|
|
1136
|
-
|
|
1137
|
-
// src/db/queries/sessions.ts
|
|
1138
|
-
import { eq, and, inArray, sql as sql2 } from "drizzle-orm";
|
|
1139
|
-
|
|
1140
879
|
// src/lib/id.ts
|
|
1141
880
|
import { nanoid } from "nanoid";
|
|
1142
881
|
|
|
@@ -1186,6 +925,39 @@ function unarchiveSession(id) {
|
|
|
1186
925
|
return { ok: true, session: updated };
|
|
1187
926
|
}
|
|
1188
927
|
|
|
928
|
+
// src/lib/format.ts
|
|
929
|
+
var SECOND = 1000;
|
|
930
|
+
var MINUTE = 60 * SECOND;
|
|
931
|
+
var HOUR = 60 * MINUTE;
|
|
932
|
+
var DAY = 24 * HOUR;
|
|
933
|
+
function formatDuration(ms) {
|
|
934
|
+
if (ms < MINUTE)
|
|
935
|
+
return `${Math.round(ms / SECOND)}s`;
|
|
936
|
+
const days = Math.floor(ms / DAY);
|
|
937
|
+
const hours = Math.floor(ms % DAY / HOUR);
|
|
938
|
+
const minutes = Math.floor(ms % HOUR / MINUTE);
|
|
939
|
+
if (days > 0)
|
|
940
|
+
return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
941
|
+
if (hours > 0)
|
|
942
|
+
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
943
|
+
return `${minutes}m`;
|
|
944
|
+
}
|
|
945
|
+
function formatAgo(isoOrDate) {
|
|
946
|
+
const date = typeof isoOrDate === "string" ? new Date(isoOrDate) : isoOrDate;
|
|
947
|
+
const ms = Date.now() - date.getTime();
|
|
948
|
+
if (ms < MINUTE)
|
|
949
|
+
return "just now";
|
|
950
|
+
if (ms < HOUR)
|
|
951
|
+
return `${Math.floor(ms / MINUTE)}m ago`;
|
|
952
|
+
if (ms < DAY)
|
|
953
|
+
return `${Math.floor(ms / HOUR)}h ago`;
|
|
954
|
+
if (ms < 2 * DAY)
|
|
955
|
+
return "yesterday";
|
|
956
|
+
if (ms < 7 * DAY)
|
|
957
|
+
return `${Math.floor(ms / DAY)}d ago`;
|
|
958
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
959
|
+
}
|
|
960
|
+
|
|
1189
961
|
// src/lib/parse-session-name.ts
|
|
1190
962
|
function parseSessionName(input) {
|
|
1191
963
|
const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -1207,7 +979,7 @@ function parseSessionName(input) {
|
|
|
1207
979
|
}
|
|
1208
980
|
|
|
1209
981
|
// src/tui/screens/launch/index.tsx
|
|
1210
|
-
import { jsxDEV as
|
|
982
|
+
import { jsxDEV as jsxDEV6, Fragment } from "react/jsx-dev-runtime";
|
|
1211
983
|
var STATUS_COLOR = {
|
|
1212
984
|
paused: "gold",
|
|
1213
985
|
waiting: "red",
|
|
@@ -1241,14 +1013,14 @@ function sessionRow(s) {
|
|
|
1241
1013
|
const textColor = isCursor ? cursorColor : color;
|
|
1242
1014
|
const slugColor = isCursor ? cursorColor : undefined;
|
|
1243
1015
|
const dimText = !isCursor && (disabled || isArchived);
|
|
1244
|
-
return /* @__PURE__ */
|
|
1016
|
+
return /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1245
1017
|
children: [
|
|
1246
|
-
/* @__PURE__ */
|
|
1018
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1247
1019
|
color: cursorColor,
|
|
1248
1020
|
bold: true,
|
|
1249
1021
|
children: isCursor ? "\u276F " : " "
|
|
1250
1022
|
}, undefined, false, undefined, this),
|
|
1251
|
-
/* @__PURE__ */
|
|
1023
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1252
1024
|
color: dotColor,
|
|
1253
1025
|
bold: isCursor,
|
|
1254
1026
|
children: [
|
|
@@ -1256,17 +1028,17 @@ function sessionRow(s) {
|
|
|
1256
1028
|
" "
|
|
1257
1029
|
]
|
|
1258
1030
|
}, undefined, true, undefined, this),
|
|
1259
|
-
/* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1260
1032
|
color: textColor,
|
|
1261
1033
|
bold: isCursor,
|
|
1262
1034
|
dim: dimText,
|
|
1263
1035
|
children: status.padEnd(8)
|
|
1264
1036
|
}, undefined, false, undefined, this),
|
|
1265
|
-
/* @__PURE__ */
|
|
1037
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1266
1038
|
color: slugColor,
|
|
1267
1039
|
children: " "
|
|
1268
1040
|
}, undefined, false, undefined, this),
|
|
1269
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1270
1042
|
color: slugColor,
|
|
1271
1043
|
bold: isCursor,
|
|
1272
1044
|
dim: dimText,
|
|
@@ -1285,12 +1057,13 @@ function categoryHeader(categoryPath) {
|
|
|
1285
1057
|
};
|
|
1286
1058
|
}
|
|
1287
1059
|
function Launch({ onSelect }) {
|
|
1060
|
+
const { exit } = useTui();
|
|
1288
1061
|
const [error, setError] = useState3(null);
|
|
1289
1062
|
const [notice, setNotice] = useState3(null);
|
|
1290
1063
|
const [showArchived, setShowArchived] = useState3(false);
|
|
1291
1064
|
const [refreshKey, setRefreshKey] = useState3(0);
|
|
1292
|
-
const allSessions =
|
|
1293
|
-
const visibleSessions =
|
|
1065
|
+
const allSessions = useMemo2(() => getAllSessions({ excludeArchived: !showArchived }), [showArchived, refreshKey]);
|
|
1066
|
+
const visibleSessions = useMemo2(() => {
|
|
1294
1067
|
return allSessions.filter((s) => {
|
|
1295
1068
|
const st = s.session.status;
|
|
1296
1069
|
if (st === "paused" || st === "waiting")
|
|
@@ -1308,7 +1081,7 @@ function Launch({ onSelect }) {
|
|
|
1308
1081
|
return recencyKey(b).localeCompare(recencyKey(a));
|
|
1309
1082
|
});
|
|
1310
1083
|
}, [allSessions, showArchived]);
|
|
1311
|
-
const items =
|
|
1084
|
+
const items = useMemo2(() => {
|
|
1312
1085
|
const rows = [];
|
|
1313
1086
|
let lastCategory = null;
|
|
1314
1087
|
for (const s of visibleSessions) {
|
|
@@ -1320,7 +1093,7 @@ function Launch({ onSelect }) {
|
|
|
1320
1093
|
}
|
|
1321
1094
|
return rows;
|
|
1322
1095
|
}, [visibleSessions]);
|
|
1323
|
-
const suggestions =
|
|
1096
|
+
const suggestions = useMemo2(() => {
|
|
1324
1097
|
const categories2 = new Set;
|
|
1325
1098
|
const names = [];
|
|
1326
1099
|
for (const s of allSessions) {
|
|
@@ -1329,7 +1102,7 @@ function Launch({ onSelect }) {
|
|
|
1329
1102
|
}
|
|
1330
1103
|
return [...categories2, ...names];
|
|
1331
1104
|
}, [allSessions]);
|
|
1332
|
-
const sessionByValue =
|
|
1105
|
+
const sessionByValue = useMemo2(() => {
|
|
1333
1106
|
const map = new Map;
|
|
1334
1107
|
for (const s of allSessions) {
|
|
1335
1108
|
map.set(`${s.categoryPath}/${s.session.slug}`, s);
|
|
@@ -1363,6 +1136,7 @@ function Launch({ onSelect }) {
|
|
|
1363
1136
|
};
|
|
1364
1137
|
const select = (selection) => {
|
|
1365
1138
|
onSelect(selection);
|
|
1139
|
+
exit();
|
|
1366
1140
|
};
|
|
1367
1141
|
const handleSubmit = (value) => {
|
|
1368
1142
|
const existing = sessionByValue.get(value);
|
|
@@ -1381,7 +1155,7 @@ function Launch({ onSelect }) {
|
|
|
1381
1155
|
setError(e instanceof Error ? e.message : "Invalid name");
|
|
1382
1156
|
}
|
|
1383
1157
|
};
|
|
1384
|
-
const counts =
|
|
1158
|
+
const counts = useMemo2(() => {
|
|
1385
1159
|
let paused = 0;
|
|
1386
1160
|
let waiting = 0;
|
|
1387
1161
|
let archived = 0;
|
|
@@ -1395,56 +1169,56 @@ function Launch({ onSelect }) {
|
|
|
1395
1169
|
}
|
|
1396
1170
|
return { paused, waiting, archived };
|
|
1397
1171
|
}, [visibleSessions]);
|
|
1398
|
-
return /* @__PURE__ */
|
|
1172
|
+
return /* @__PURE__ */ jsxDEV6(Box5, {
|
|
1399
1173
|
flexDirection: "column",
|
|
1400
1174
|
paddingY: 1,
|
|
1401
1175
|
gap: 1,
|
|
1402
1176
|
children: [
|
|
1403
|
-
/* @__PURE__ */
|
|
1177
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1404
1178
|
marginX: 1,
|
|
1405
|
-
children: /* @__PURE__ */
|
|
1179
|
+
children: /* @__PURE__ */ jsxDEV6(Logo, {}, undefined, false, undefined, this)
|
|
1406
1180
|
}, undefined, false, undefined, this),
|
|
1407
|
-
/* @__PURE__ */
|
|
1181
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1408
1182
|
flexDirection: "column",
|
|
1409
1183
|
marginX: 2,
|
|
1410
1184
|
gap: 1,
|
|
1411
1185
|
children: [
|
|
1412
|
-
/* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1186
|
+
/* @__PURE__ */ jsxDEV6(AppDetails, {}, undefined, false, undefined, this),
|
|
1187
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1414
1188
|
flexDirection: "column",
|
|
1415
1189
|
gap: 1,
|
|
1416
1190
|
children: [
|
|
1417
|
-
/* @__PURE__ */
|
|
1191
|
+
/* @__PURE__ */ jsxDEV6(Box5, {
|
|
1418
1192
|
flexDirection: "row",
|
|
1419
1193
|
gap: 1,
|
|
1420
1194
|
children: [
|
|
1421
|
-
/* @__PURE__ */
|
|
1195
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1422
1196
|
bold: true,
|
|
1423
1197
|
children: "Sessions"
|
|
1424
1198
|
}, undefined, false, undefined, this),
|
|
1425
|
-
visibleSessions.length === 0 ? /* @__PURE__ */
|
|
1199
|
+
visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1426
1200
|
dim: true,
|
|
1427
1201
|
children: "\xB7 none \u2014 type category/slug to create"
|
|
1428
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
1202
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1429
1203
|
children: [
|
|
1430
|
-
/* @__PURE__ */
|
|
1204
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1431
1205
|
dim: true,
|
|
1432
1206
|
children: "\xB7"
|
|
1433
1207
|
}, undefined, false, undefined, this),
|
|
1434
|
-
/* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1435
1209
|
color: "gold",
|
|
1436
1210
|
children: [
|
|
1437
1211
|
counts.paused,
|
|
1438
1212
|
" paused"
|
|
1439
1213
|
]
|
|
1440
1214
|
}, undefined, true, undefined, this),
|
|
1441
|
-
counts.waiting > 0 && /* @__PURE__ */
|
|
1215
|
+
counts.waiting > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1442
1216
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
1217
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1444
1218
|
dim: true,
|
|
1445
1219
|
children: "\xB7"
|
|
1446
1220
|
}, undefined, false, undefined, this),
|
|
1447
|
-
/* @__PURE__ */
|
|
1221
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1448
1222
|
color: "red",
|
|
1449
1223
|
dim: true,
|
|
1450
1224
|
children: [
|
|
@@ -1454,13 +1228,13 @@ function Launch({ onSelect }) {
|
|
|
1454
1228
|
}, undefined, true, undefined, this)
|
|
1455
1229
|
]
|
|
1456
1230
|
}, undefined, true, undefined, this),
|
|
1457
|
-
showArchived && counts.archived > 0 && /* @__PURE__ */
|
|
1231
|
+
showArchived && counts.archived > 0 && /* @__PURE__ */ jsxDEV6(Fragment, {
|
|
1458
1232
|
children: [
|
|
1459
|
-
/* @__PURE__ */
|
|
1233
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1460
1234
|
dim: true,
|
|
1461
1235
|
children: "\xB7"
|
|
1462
1236
|
}, undefined, false, undefined, this),
|
|
1463
|
-
/* @__PURE__ */
|
|
1237
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1464
1238
|
color: "purple",
|
|
1465
1239
|
dim: true,
|
|
1466
1240
|
children: [
|
|
@@ -1474,7 +1248,7 @@ function Launch({ onSelect }) {
|
|
|
1474
1248
|
}, undefined, true, undefined, this)
|
|
1475
1249
|
]
|
|
1476
1250
|
}, undefined, true, undefined, this),
|
|
1477
|
-
/* @__PURE__ */
|
|
1251
|
+
/* @__PURE__ */ jsxDEV6(Picker, {
|
|
1478
1252
|
mode: "single",
|
|
1479
1253
|
items,
|
|
1480
1254
|
isFocused: true,
|
|
@@ -1495,15 +1269,15 @@ function Launch({ onSelect }) {
|
|
|
1495
1269
|
}
|
|
1496
1270
|
}
|
|
1497
1271
|
}, undefined, false, undefined, this),
|
|
1498
|
-
notice && /* @__PURE__ */
|
|
1272
|
+
notice && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1499
1273
|
color: "green",
|
|
1500
1274
|
children: notice
|
|
1501
1275
|
}, undefined, false, undefined, this),
|
|
1502
|
-
error && /* @__PURE__ */
|
|
1276
|
+
error && /* @__PURE__ */ jsxDEV6(Text6, {
|
|
1503
1277
|
color: "red",
|
|
1504
1278
|
children: error
|
|
1505
1279
|
}, undefined, false, undefined, this),
|
|
1506
|
-
/* @__PURE__ */
|
|
1280
|
+
/* @__PURE__ */ jsxDEV6(Text6, {
|
|
1507
1281
|
dim: true,
|
|
1508
1282
|
children: [
|
|
1509
1283
|
"\u2191\u2193 navigate \xB7 \u2190\u2192 skip category \xB7 enter continue/create \xB7 ctrl+a",
|
|
@@ -1523,104 +1297,9 @@ function Launch({ onSelect }) {
|
|
|
1523
1297
|
}, undefined, true, undefined, this);
|
|
1524
1298
|
}
|
|
1525
1299
|
|
|
1526
|
-
// src/lib/projects/create.ts
|
|
1527
|
-
import { mkdirSync as mkdirSync4 } from "fs";
|
|
1528
|
-
|
|
1529
|
-
// src/db/migrate.ts
|
|
1530
|
-
import { Database as Database2 } from "bun:sqlite";
|
|
1531
|
-
import { drizzle as drizzle2 } from "drizzle-orm/bun-sqlite";
|
|
1532
|
-
import { migrate as migrate2 } from "drizzle-orm/bun-sqlite/migrator";
|
|
1533
|
-
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1534
|
-
import { dirname as dirname2 } from "path";
|
|
1535
|
-
var MIGRATIONS_FOLDER2 = import.meta.dir + "/migrations";
|
|
1536
|
-
function runMigrations(dbPath) {
|
|
1537
|
-
const target = dbPath ?? resolveActiveProject().db;
|
|
1538
|
-
mkdirSync3(dirname2(target), { recursive: true });
|
|
1539
|
-
const sqlite = new Database2(target);
|
|
1540
|
-
sqlite.exec("PRAGMA busy_timeout = 5000");
|
|
1541
|
-
sqlite.exec("PRAGMA journal_mode = WAL");
|
|
1542
|
-
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
1543
|
-
const db = drizzle2(sqlite);
|
|
1544
|
-
migrate2(db, { migrationsFolder: MIGRATIONS_FOLDER2 });
|
|
1545
|
-
sqlite.close();
|
|
1546
|
-
}
|
|
1547
|
-
if (false) {}
|
|
1548
|
-
|
|
1549
|
-
// src/lib/projects/create.ts
|
|
1550
|
-
function createProject(opts) {
|
|
1551
|
-
registerProject({ slug: opts.slug, name: opts.name ?? opts.slug });
|
|
1552
|
-
try {
|
|
1553
|
-
const paths2 = projectPaths(opts.slug);
|
|
1554
|
-
mkdirSync4(paths2.root, { recursive: true });
|
|
1555
|
-
runMigrations(paths2.db);
|
|
1556
|
-
} catch (err) {
|
|
1557
|
-
try {
|
|
1558
|
-
removeProject(opts.slug);
|
|
1559
|
-
} catch {}
|
|
1560
|
-
throw err;
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
// src/tui/screens/startup/index.tsx
|
|
1565
|
-
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
1566
|
-
function Startup({
|
|
1567
|
-
skipProjectPicker,
|
|
1568
|
-
initialProjectSlug,
|
|
1569
|
-
onSelect
|
|
1570
|
-
}) {
|
|
1571
|
-
const { exit } = useTui();
|
|
1572
|
-
const [activeSlug, setActiveSlug] = useState4(skipProjectPicker ? initialProjectSlug : null);
|
|
1573
|
-
const finish = (s) => {
|
|
1574
|
-
onSelect(s);
|
|
1575
|
-
exit();
|
|
1576
|
-
};
|
|
1577
|
-
const handleProjectSelect = (s) => {
|
|
1578
|
-
switch (s.type) {
|
|
1579
|
-
case "quit":
|
|
1580
|
-
finish({ type: "quit" });
|
|
1581
|
-
return;
|
|
1582
|
-
case "create":
|
|
1583
|
-
createProject({ slug: s.slug });
|
|
1584
|
-
setActiveProjectSlug(s.slug);
|
|
1585
|
-
_resetActiveProjectCache();
|
|
1586
|
-
setActiveSlug(s.slug);
|
|
1587
|
-
return;
|
|
1588
|
-
case "select":
|
|
1589
|
-
setActiveProjectSlug(s.slug);
|
|
1590
|
-
_resetActiveProjectCache();
|
|
1591
|
-
setActiveSlug(s.slug);
|
|
1592
|
-
return;
|
|
1593
|
-
}
|
|
1594
|
-
};
|
|
1595
|
-
const handleLaunchSelect = (s) => {
|
|
1596
|
-
if (s.type === "quit" || activeSlug === null) {
|
|
1597
|
-
finish({ type: "quit" });
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
if (s.type === "create") {
|
|
1601
|
-
finish({
|
|
1602
|
-
type: "create",
|
|
1603
|
-
projectSlug: activeSlug,
|
|
1604
|
-
categoryPath: s.categoryPath,
|
|
1605
|
-
slug: s.slug
|
|
1606
|
-
});
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
finish({ type: "pick", projectSlug: activeSlug, sessionId: s.sessionId });
|
|
1610
|
-
};
|
|
1611
|
-
if (activeSlug === null) {
|
|
1612
|
-
return /* @__PURE__ */ jsxDEV8(ProjectPicker, {
|
|
1613
|
-
onSelect: handleProjectSelect
|
|
1614
|
-
}, undefined, false, undefined, this);
|
|
1615
|
-
}
|
|
1616
|
-
return /* @__PURE__ */ jsxDEV8(Launch, {
|
|
1617
|
-
onSelect: handleLaunchSelect
|
|
1618
|
-
}, undefined, false, undefined, this);
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
1300
|
// src/tui/screens/Exit.tsx
|
|
1622
|
-
import { useState as
|
|
1623
|
-
import { Box as
|
|
1301
|
+
import { useState as useState4 } from "react";
|
|
1302
|
+
import { Box as Box6, Text as Text7, useInput as useInput3, useTui as useTui2 } from "@orchetron/storm";
|
|
1624
1303
|
|
|
1625
1304
|
// src/db/queries/conversations.ts
|
|
1626
1305
|
import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
|
|
@@ -1629,7 +1308,7 @@ function getConversationsBySession(sessionId) {
|
|
|
1629
1308
|
}
|
|
1630
1309
|
|
|
1631
1310
|
// src/tui/screens/Exit.tsx
|
|
1632
|
-
import { jsxDEV as
|
|
1311
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
1633
1312
|
var OPTIONS = [
|
|
1634
1313
|
{ action: "save", label: "Save", hint: "Keep session paused for later" },
|
|
1635
1314
|
{
|
|
@@ -1650,7 +1329,7 @@ var OPTIONS = [
|
|
|
1650
1329
|
];
|
|
1651
1330
|
function Exit({ sessionId, onAction }) {
|
|
1652
1331
|
const { exit } = useTui2();
|
|
1653
|
-
const [cursor, setCursor] =
|
|
1332
|
+
const [cursor, setCursor] = useState4(0);
|
|
1654
1333
|
const session = getSession(sessionId);
|
|
1655
1334
|
const conversations2 = session ? getConversationsBySession(session.id) : [];
|
|
1656
1335
|
useInput3((e) => {
|
|
@@ -1670,37 +1349,37 @@ function Exit({ sessionId, onAction }) {
|
|
|
1670
1349
|
}
|
|
1671
1350
|
});
|
|
1672
1351
|
if (!session) {
|
|
1673
|
-
return /* @__PURE__ */
|
|
1352
|
+
return /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1674
1353
|
color: "red",
|
|
1675
1354
|
children: "Session not found"
|
|
1676
1355
|
}, undefined, false, undefined, this);
|
|
1677
1356
|
}
|
|
1678
1357
|
const duration = session.endedAt && session.startedAt ? formatDuration(new Date(session.endedAt).getTime() - new Date(session.startedAt).getTime()) : null;
|
|
1679
|
-
return /* @__PURE__ */
|
|
1358
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
1680
1359
|
flexDirection: "column",
|
|
1681
1360
|
padding: 1,
|
|
1682
1361
|
gap: 1,
|
|
1683
1362
|
children: [
|
|
1684
|
-
/* @__PURE__ */
|
|
1363
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1685
1364
|
bold: true,
|
|
1686
1365
|
color: "#82AAFF",
|
|
1687
1366
|
children: "Session ended"
|
|
1688
1367
|
}, undefined, false, undefined, this),
|
|
1689
|
-
/* @__PURE__ */
|
|
1368
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1690
1369
|
flexDirection: "column",
|
|
1691
1370
|
children: [
|
|
1692
|
-
/* @__PURE__ */
|
|
1371
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1693
1372
|
flexDirection: "row",
|
|
1694
1373
|
gap: 1,
|
|
1695
1374
|
children: [
|
|
1696
|
-
/* @__PURE__ */
|
|
1375
|
+
/* @__PURE__ */ jsxDEV7(StatusDot, {
|
|
1697
1376
|
status: session.status
|
|
1698
1377
|
}, undefined, false, undefined, this),
|
|
1699
|
-
/* @__PURE__ */
|
|
1378
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1700
1379
|
bold: true,
|
|
1701
1380
|
children: session.name
|
|
1702
1381
|
}, undefined, false, undefined, this),
|
|
1703
|
-
duration && /* @__PURE__ */
|
|
1382
|
+
duration && /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1704
1383
|
dim: true,
|
|
1705
1384
|
children: [
|
|
1706
1385
|
"(",
|
|
@@ -1710,7 +1389,7 @@ function Exit({ sessionId, onAction }) {
|
|
|
1710
1389
|
}, undefined, true, undefined, this)
|
|
1711
1390
|
]
|
|
1712
1391
|
}, undefined, true, undefined, this),
|
|
1713
|
-
/* @__PURE__ */
|
|
1392
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1714
1393
|
dim: true,
|
|
1715
1394
|
children: [
|
|
1716
1395
|
conversations2.length,
|
|
@@ -1720,28 +1399,28 @@ function Exit({ sessionId, onAction }) {
|
|
|
1720
1399
|
}, undefined, true, undefined, this)
|
|
1721
1400
|
]
|
|
1722
1401
|
}, undefined, true, undefined, this),
|
|
1723
|
-
/* @__PURE__ */
|
|
1402
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1724
1403
|
flexDirection: "column",
|
|
1725
|
-
children: OPTIONS.map((opt, i) => /* @__PURE__ */
|
|
1404
|
+
children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV7(Box6, {
|
|
1726
1405
|
flexDirection: "row",
|
|
1727
1406
|
gap: 1,
|
|
1728
1407
|
children: [
|
|
1729
|
-
/* @__PURE__ */
|
|
1408
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1730
1409
|
children: i === cursor ? "\u276F" : " "
|
|
1731
1410
|
}, undefined, false, undefined, this),
|
|
1732
|
-
/* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1733
1412
|
bold: i === cursor,
|
|
1734
1413
|
children: opt.label
|
|
1735
1414
|
}, undefined, false, undefined, this),
|
|
1736
|
-
/* @__PURE__ */
|
|
1415
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1737
1416
|
dim: true,
|
|
1738
1417
|
children: opt.hint
|
|
1739
1418
|
}, undefined, false, undefined, this)
|
|
1740
1419
|
]
|
|
1741
1420
|
}, opt.action, true, undefined, this))
|
|
1742
1421
|
}, undefined, false, undefined, this),
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
children: /* @__PURE__ */
|
|
1422
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1423
|
+
children: /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1745
1424
|
dim: true,
|
|
1746
1425
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q save & quit"
|
|
1747
1426
|
}, undefined, false, undefined, this)
|
|
@@ -1751,12 +1430,12 @@ function Exit({ sessionId, onAction }) {
|
|
|
1751
1430
|
}
|
|
1752
1431
|
|
|
1753
1432
|
// src/tui/screens/Resume.tsx
|
|
1754
|
-
import { useState as
|
|
1755
|
-
import { Box as
|
|
1756
|
-
import { jsxDEV as
|
|
1433
|
+
import { useState as useState5 } from "react";
|
|
1434
|
+
import { Box as Box7, Text as Text8, useInput as useInput4, useTui as useTui3 } from "@orchetron/storm";
|
|
1435
|
+
import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
|
|
1757
1436
|
function Resume({ sessionId, onSelect }) {
|
|
1758
1437
|
const { exit } = useTui3();
|
|
1759
|
-
const [cursor, setCursor] =
|
|
1438
|
+
const [cursor, setCursor] = useState5(0);
|
|
1760
1439
|
const session = getSession(sessionId);
|
|
1761
1440
|
const conversations2 = getConversationsBySession(sessionId);
|
|
1762
1441
|
const totalOptions = conversations2.length + 1;
|
|
@@ -1783,17 +1462,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1783
1462
|
}
|
|
1784
1463
|
});
|
|
1785
1464
|
if (!session) {
|
|
1786
|
-
return /* @__PURE__ */
|
|
1465
|
+
return /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1787
1466
|
color: "red",
|
|
1788
1467
|
children: "Session not found"
|
|
1789
1468
|
}, undefined, false, undefined, this);
|
|
1790
1469
|
}
|
|
1791
|
-
return /* @__PURE__ */
|
|
1470
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
1792
1471
|
flexDirection: "column",
|
|
1793
1472
|
padding: 1,
|
|
1794
1473
|
gap: 1,
|
|
1795
1474
|
children: [
|
|
1796
|
-
/* @__PURE__ */
|
|
1475
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1797
1476
|
bold: true,
|
|
1798
1477
|
color: "#82AAFF",
|
|
1799
1478
|
children: [
|
|
@@ -1801,17 +1480,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1801
1480
|
session.name
|
|
1802
1481
|
]
|
|
1803
1482
|
}, undefined, true, undefined, this),
|
|
1804
|
-
/* @__PURE__ */
|
|
1483
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1805
1484
|
flexDirection: "column",
|
|
1806
1485
|
children: [
|
|
1807
|
-
/* @__PURE__ */
|
|
1486
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1808
1487
|
flexDirection: "row",
|
|
1809
1488
|
gap: 1,
|
|
1810
1489
|
children: [
|
|
1811
|
-
/* @__PURE__ */
|
|
1490
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1812
1491
|
children: cursor === 0 ? "\u276F" : " "
|
|
1813
1492
|
}, undefined, false, undefined, this),
|
|
1814
|
-
/* @__PURE__ */
|
|
1493
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1815
1494
|
bold: cursor === 0,
|
|
1816
1495
|
color: "#34D399",
|
|
1817
1496
|
children: "+ New conversation"
|
|
@@ -1823,42 +1502,42 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1823
1502
|
const isSelected = cursor === idx;
|
|
1824
1503
|
const duration = conv.endedAt ? formatDuration(new Date(conv.endedAt).getTime() - new Date(conv.startedAt).getTime()) : "active";
|
|
1825
1504
|
const ago = formatAgo(conv.startedAt);
|
|
1826
|
-
return /* @__PURE__ */
|
|
1505
|
+
return /* @__PURE__ */ jsxDEV8(Box7, {
|
|
1827
1506
|
flexDirection: "row",
|
|
1828
1507
|
gap: 1,
|
|
1829
1508
|
children: [
|
|
1830
|
-
/* @__PURE__ */
|
|
1509
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1831
1510
|
children: isSelected ? "\u276F" : " "
|
|
1832
1511
|
}, undefined, false, undefined, this),
|
|
1833
|
-
/* @__PURE__ */
|
|
1512
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1834
1513
|
bold: isSelected,
|
|
1835
1514
|
dim: conv.discarded,
|
|
1836
1515
|
children: conv.id.slice(0, 8)
|
|
1837
1516
|
}, undefined, false, undefined, this),
|
|
1838
|
-
/* @__PURE__ */
|
|
1517
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1839
1518
|
dim: true,
|
|
1840
1519
|
children: ago
|
|
1841
1520
|
}, undefined, false, undefined, this),
|
|
1842
|
-
/* @__PURE__ */
|
|
1521
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1843
1522
|
dim: true,
|
|
1844
1523
|
children: "\xB7"
|
|
1845
1524
|
}, undefined, false, undefined, this),
|
|
1846
|
-
/* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1847
1526
|
dim: true,
|
|
1848
1527
|
children: [
|
|
1849
1528
|
conv.eventCount,
|
|
1850
1529
|
" events"
|
|
1851
1530
|
]
|
|
1852
1531
|
}, undefined, true, undefined, this),
|
|
1853
|
-
/* @__PURE__ */
|
|
1532
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1854
1533
|
dim: true,
|
|
1855
1534
|
children: "\xB7"
|
|
1856
1535
|
}, undefined, false, undefined, this),
|
|
1857
|
-
/* @__PURE__ */
|
|
1536
|
+
/* @__PURE__ */ jsxDEV8(Text8, {
|
|
1858
1537
|
dim: true,
|
|
1859
1538
|
children: duration
|
|
1860
1539
|
}, undefined, false, undefined, this),
|
|
1861
|
-
conv.discarded && /* @__PURE__ */
|
|
1540
|
+
conv.discarded && /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1862
1541
|
color: "red",
|
|
1863
1542
|
dim: true,
|
|
1864
1543
|
children: "(discarded)"
|
|
@@ -1868,8 +1547,8 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1868
1547
|
})
|
|
1869
1548
|
]
|
|
1870
1549
|
}, undefined, true, undefined, this),
|
|
1871
|
-
/* @__PURE__ */
|
|
1872
|
-
children: /* @__PURE__ */
|
|
1550
|
+
/* @__PURE__ */ jsxDEV8(Box7, {
|
|
1551
|
+
children: /* @__PURE__ */ jsxDEV8(Text8, {
|
|
1873
1552
|
dim: true,
|
|
1874
1553
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q back"
|
|
1875
1554
|
}, undefined, false, undefined, this)
|
|
@@ -1878,8 +1557,174 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1878
1557
|
}, undefined, true, undefined, this);
|
|
1879
1558
|
}
|
|
1880
1559
|
|
|
1560
|
+
// src/tui/screens/project-picker/index.tsx
|
|
1561
|
+
import { useMemo as useMemo3 } from "react";
|
|
1562
|
+
import { Box as Box8, Text as Text9, useTui as useTui4 } from "@orchetron/storm";
|
|
1563
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1564
|
+
import { jsxDEV as jsxDEV9, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
1565
|
+
var SLUG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
1566
|
+
function statsFor(slug) {
|
|
1567
|
+
const dbFile = projectPaths(slug).db;
|
|
1568
|
+
if (!existsSync2(dbFile))
|
|
1569
|
+
return { total: 0, active: 0, unreadable: false };
|
|
1570
|
+
try {
|
|
1571
|
+
const db = getDbForProject(slug);
|
|
1572
|
+
const all = db.select({ status: sessions.status }).from(sessions).all();
|
|
1573
|
+
return {
|
|
1574
|
+
total: all.length,
|
|
1575
|
+
active: all.filter((s) => s.status === "active" || s.status === "waiting").length,
|
|
1576
|
+
unreadable: false
|
|
1577
|
+
};
|
|
1578
|
+
} catch {
|
|
1579
|
+
return { total: 0, active: 0, unreadable: true };
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
function projectRow(entry, isActive) {
|
|
1583
|
+
const stats = statsFor(entry.slug);
|
|
1584
|
+
const sessionsLabel = stats.unreadable ? "?" : `${stats.active}/${stats.total}`;
|
|
1585
|
+
const lastUsed = formatAgo(entry.lastUsedAt);
|
|
1586
|
+
return {
|
|
1587
|
+
value: entry.slug,
|
|
1588
|
+
label: `${entry.slug} ${entry.name} ${sessionsLabel}`,
|
|
1589
|
+
meta: lastUsed,
|
|
1590
|
+
display: (isCursor) => {
|
|
1591
|
+
const cursorColor = "green";
|
|
1592
|
+
const slugColor = isCursor ? cursorColor : undefined;
|
|
1593
|
+
const nameColor = isCursor ? cursorColor : undefined;
|
|
1594
|
+
return /* @__PURE__ */ jsxDEV9(Fragment2, {
|
|
1595
|
+
children: [
|
|
1596
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1597
|
+
color: cursorColor,
|
|
1598
|
+
bold: true,
|
|
1599
|
+
children: isCursor ? "\u276F " : " "
|
|
1600
|
+
}, undefined, false, undefined, this),
|
|
1601
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1602
|
+
bold: isActive,
|
|
1603
|
+
color: isActive ? "gold" : slugColor,
|
|
1604
|
+
children: isActive ? "\u25CF " : " "
|
|
1605
|
+
}, undefined, false, undefined, this),
|
|
1606
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1607
|
+
color: slugColor,
|
|
1608
|
+
bold: isCursor || isActive,
|
|
1609
|
+
children: entry.slug.padEnd(16)
|
|
1610
|
+
}, undefined, false, undefined, this),
|
|
1611
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1612
|
+
color: nameColor,
|
|
1613
|
+
dim: !isCursor,
|
|
1614
|
+
children: entry.name.padEnd(20)
|
|
1615
|
+
}, undefined, false, undefined, this),
|
|
1616
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1617
|
+
dim: true,
|
|
1618
|
+
children: stats.unreadable ? " (unreadable)" : ` ${sessionsLabel} active/total`
|
|
1619
|
+
}, undefined, false, undefined, this)
|
|
1620
|
+
]
|
|
1621
|
+
}, undefined, true, undefined, this);
|
|
1622
|
+
}
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
function ProjectPicker({ onSelect }) {
|
|
1626
|
+
const { exit } = useTui4();
|
|
1627
|
+
const allProjects = useMemo3(() => listProjects(), []);
|
|
1628
|
+
const activeSlug = useMemo3(() => getActiveProjectSlug(), []);
|
|
1629
|
+
const items = useMemo3(() => {
|
|
1630
|
+
return allProjects.slice().sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt)).map((p) => projectRow(p, p.slug === activeSlug));
|
|
1631
|
+
}, [allProjects, activeSlug]);
|
|
1632
|
+
const suggestions = useMemo3(() => allProjects.map((p) => p.slug), [allProjects]);
|
|
1633
|
+
const knownSlugs = useMemo3(() => new Set(allProjects.map((p) => p.slug)), [allProjects]);
|
|
1634
|
+
const select = (selection) => {
|
|
1635
|
+
onSelect(selection);
|
|
1636
|
+
exit();
|
|
1637
|
+
};
|
|
1638
|
+
const handleSubmit = (value) => {
|
|
1639
|
+
const trimmed = value.trim();
|
|
1640
|
+
if (!trimmed)
|
|
1641
|
+
return;
|
|
1642
|
+
if (knownSlugs.has(trimmed)) {
|
|
1643
|
+
select({ type: "select", slug: trimmed });
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
if (!SLUG_PATTERN.test(trimmed)) {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
select({ type: "create", slug: trimmed });
|
|
1650
|
+
};
|
|
1651
|
+
return /* @__PURE__ */ jsxDEV9(Box8, {
|
|
1652
|
+
flexDirection: "column",
|
|
1653
|
+
paddingY: 1,
|
|
1654
|
+
gap: 1,
|
|
1655
|
+
children: [
|
|
1656
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1657
|
+
marginX: 1,
|
|
1658
|
+
children: /* @__PURE__ */ jsxDEV9(Logo, {}, undefined, false, undefined, this)
|
|
1659
|
+
}, undefined, false, undefined, this),
|
|
1660
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1661
|
+
flexDirection: "column",
|
|
1662
|
+
marginX: 2,
|
|
1663
|
+
gap: 1,
|
|
1664
|
+
children: [
|
|
1665
|
+
/* @__PURE__ */ jsxDEV9(AppDetails, {}, undefined, false, undefined, this),
|
|
1666
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1667
|
+
flexDirection: "column",
|
|
1668
|
+
gap: 1,
|
|
1669
|
+
children: [
|
|
1670
|
+
/* @__PURE__ */ jsxDEV9(Box8, {
|
|
1671
|
+
flexDirection: "row",
|
|
1672
|
+
gap: 1,
|
|
1673
|
+
children: [
|
|
1674
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1675
|
+
bold: true,
|
|
1676
|
+
children: "Projects"
|
|
1677
|
+
}, undefined, false, undefined, this),
|
|
1678
|
+
allProjects.length > 0 ? /* @__PURE__ */ jsxDEV9(Fragment2, {
|
|
1679
|
+
children: [
|
|
1680
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1681
|
+
dim: true,
|
|
1682
|
+
children: "\xB7"
|
|
1683
|
+
}, undefined, false, undefined, this),
|
|
1684
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1685
|
+
dim: true,
|
|
1686
|
+
children: [
|
|
1687
|
+
allProjects.length,
|
|
1688
|
+
" total",
|
|
1689
|
+
activeSlug ? ` \xB7 active: ${activeSlug}` : ""
|
|
1690
|
+
]
|
|
1691
|
+
}, undefined, true, undefined, this)
|
|
1692
|
+
]
|
|
1693
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsxDEV9(Text9, {
|
|
1694
|
+
dim: true,
|
|
1695
|
+
children: "\xB7 none \u2014 type a slug to create your first project"
|
|
1696
|
+
}, undefined, false, undefined, this)
|
|
1697
|
+
]
|
|
1698
|
+
}, undefined, true, undefined, this),
|
|
1699
|
+
/* @__PURE__ */ jsxDEV9(Picker, {
|
|
1700
|
+
mode: "single",
|
|
1701
|
+
items,
|
|
1702
|
+
isFocused: true,
|
|
1703
|
+
maxVisible: 16,
|
|
1704
|
+
suggest: suggestions,
|
|
1705
|
+
placeholder: "Filter or type a new slug to create\u2026",
|
|
1706
|
+
emptyHint: "No projects. Type a slug to create one.",
|
|
1707
|
+
onSubmit: handleSubmit,
|
|
1708
|
+
onKey: (e) => {
|
|
1709
|
+
if (e.key === "c" && e.ctrl) {
|
|
1710
|
+
select({ type: "quit" });
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}, undefined, false, undefined, this),
|
|
1714
|
+
/* @__PURE__ */ jsxDEV9(Text9, {
|
|
1715
|
+
dim: true,
|
|
1716
|
+
children: "\u2191\u2193 navigate \xB7 enter select \xB7 type to filter or create new \xB7 ctrl+c quit"
|
|
1717
|
+
}, undefined, false, undefined, this)
|
|
1718
|
+
]
|
|
1719
|
+
}, undefined, true, undefined, this)
|
|
1720
|
+
]
|
|
1721
|
+
}, undefined, true, undefined, this)
|
|
1722
|
+
]
|
|
1723
|
+
}, undefined, true, undefined, this);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1881
1726
|
// src/tui/run-screen.tsx
|
|
1882
|
-
import { jsxDEV as
|
|
1727
|
+
import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
|
|
1883
1728
|
var [, , screen, outputPath, ...args] = process.argv;
|
|
1884
1729
|
if (!screen || !outputPath) {
|
|
1885
1730
|
console.error("Usage: run-screen <screen> <outputPath> [args...]");
|
|
@@ -1887,13 +1732,21 @@ if (!screen || !outputPath) {
|
|
|
1887
1732
|
}
|
|
1888
1733
|
var result;
|
|
1889
1734
|
switch (screen) {
|
|
1890
|
-
case "
|
|
1891
|
-
|
|
1892
|
-
const
|
|
1735
|
+
case "launch": {
|
|
1736
|
+
let selection = { type: "quit" };
|
|
1737
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Launch, {
|
|
1738
|
+
onSelect: (s) => {
|
|
1739
|
+
selection = s;
|
|
1740
|
+
}
|
|
1741
|
+
}, undefined, false, undefined, this), { alternateScreen: true, patchConsole: true });
|
|
1742
|
+
await app.waitUntilExit();
|
|
1743
|
+
app.unmount();
|
|
1744
|
+
result = selection;
|
|
1745
|
+
break;
|
|
1746
|
+
}
|
|
1747
|
+
case "project-picker": {
|
|
1893
1748
|
let selection = { type: "quit" };
|
|
1894
|
-
const app = render(/* @__PURE__ */
|
|
1895
|
-
skipProjectPicker,
|
|
1896
|
-
initialProjectSlug,
|
|
1749
|
+
const app = render(/* @__PURE__ */ jsxDEV10(ProjectPicker, {
|
|
1897
1750
|
onSelect: (s) => {
|
|
1898
1751
|
selection = s;
|
|
1899
1752
|
}
|
|
@@ -1910,7 +1763,7 @@ switch (screen) {
|
|
|
1910
1763
|
process.exit(1);
|
|
1911
1764
|
}
|
|
1912
1765
|
let action = "save";
|
|
1913
|
-
const app = render(/* @__PURE__ */
|
|
1766
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Exit, {
|
|
1914
1767
|
sessionId,
|
|
1915
1768
|
onAction: (a) => {
|
|
1916
1769
|
action = a;
|
|
@@ -1928,7 +1781,7 @@ switch (screen) {
|
|
|
1928
1781
|
process.exit(1);
|
|
1929
1782
|
}
|
|
1930
1783
|
let selection = { type: "back" };
|
|
1931
|
-
const app = render(/* @__PURE__ */
|
|
1784
|
+
const app = render(/* @__PURE__ */ jsxDEV10(Resume, {
|
|
1932
1785
|
sessionId,
|
|
1933
1786
|
onSelect: (s) => {
|
|
1934
1787
|
selection = s;
|