bertrand 0.18.0 → 0.20.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/bertrand.js +1735 -490
- package/dist/dashboard/assets/{index-e53khqNa.js → index-DgvcDkSY.js} +103 -103
- package/dist/dashboard/index.html +1 -1
- package/dist/dashboard/sw.js +1 -1
- package/dist/migrations/0003_categories_rename.sql +8 -0
- package/dist/migrations/meta/0003_snapshot.json +777 -0
- package/dist/migrations/meta/_journal.json +7 -0
- package/dist/run-screen.js +663 -171
- package/package.json +1 -1
package/dist/run-screen.js
CHANGED
|
@@ -15,12 +15,17 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// src/tui/run-screen.tsx
|
|
18
|
-
import { writeFileSync } from "fs";
|
|
18
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
19
19
|
import { render } from "@orchetron/storm";
|
|
20
20
|
|
|
21
|
-
// src/tui/screens/
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
21
|
+
// src/tui/screens/startup/index.tsx
|
|
22
|
+
import { useState as useState4 } from "react";
|
|
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";
|
|
24
29
|
|
|
25
30
|
// src/tui/components/app-details.tsx
|
|
26
31
|
import { Box, Text } from "@orchetron/storm";
|
|
@@ -560,14 +565,18 @@ function StatusDot({ status }) {
|
|
|
560
565
|
children: "\u25CF"
|
|
561
566
|
}, undefined, false, undefined, this);
|
|
562
567
|
}
|
|
563
|
-
// src/
|
|
564
|
-
import {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
568
|
+
// src/lib/projects/registry.ts
|
|
569
|
+
import {
|
|
570
|
+
existsSync,
|
|
571
|
+
mkdirSync,
|
|
572
|
+
readFileSync,
|
|
573
|
+
readdirSync,
|
|
574
|
+
renameSync,
|
|
575
|
+
statSync,
|
|
576
|
+
writeFileSync
|
|
577
|
+
} from "fs";
|
|
578
|
+
import { randomBytes } from "crypto";
|
|
579
|
+
import { join as join2 } from "path";
|
|
571
580
|
|
|
572
581
|
// src/lib/paths.ts
|
|
573
582
|
import { homedir } from "os";
|
|
@@ -575,12 +584,197 @@ import { join } from "path";
|
|
|
575
584
|
var BERTRAND_DIR = ".bertrand";
|
|
576
585
|
var paths = {
|
|
577
586
|
root: join(homedir(), BERTRAND_DIR),
|
|
578
|
-
db: join(homedir(), BERTRAND_DIR, "bertrand.db"),
|
|
579
587
|
hooks: join(homedir(), BERTRAND_DIR, "hooks"),
|
|
580
588
|
sessions: join(homedir(), BERTRAND_DIR, "sessions"),
|
|
589
|
+
db: join(homedir(), BERTRAND_DIR, "bertrand.db"),
|
|
581
590
|
syncEnv: join(homedir(), BERTRAND_DIR, "sync.env")
|
|
582
591
|
};
|
|
583
592
|
|
|
593
|
+
// src/lib/projects/registry.ts
|
|
594
|
+
var DEFAULT_PROJECT_SLUG = "default";
|
|
595
|
+
var _registryDir = paths.root;
|
|
596
|
+
function _getRegistryDir() {
|
|
597
|
+
return _registryDir;
|
|
598
|
+
}
|
|
599
|
+
function registryPath() {
|
|
600
|
+
return join2(_registryDir, "projects.json");
|
|
601
|
+
}
|
|
602
|
+
function projectsDir() {
|
|
603
|
+
return join2(_registryDir, "projects");
|
|
604
|
+
}
|
|
605
|
+
function readRegistry() {
|
|
606
|
+
const path = registryPath();
|
|
607
|
+
if (!existsSync(path))
|
|
608
|
+
return null;
|
|
609
|
+
try {
|
|
610
|
+
const raw = readFileSync(path, "utf8");
|
|
611
|
+
const parsed = JSON.parse(raw);
|
|
612
|
+
if (!isRegistry(parsed))
|
|
613
|
+
return null;
|
|
614
|
+
return parsed;
|
|
615
|
+
} catch {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
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
|
+
function recoverFromDisk() {
|
|
628
|
+
const dir = projectsDir();
|
|
629
|
+
if (!existsSync(dir))
|
|
630
|
+
return null;
|
|
631
|
+
let entries;
|
|
632
|
+
try {
|
|
633
|
+
entries = readdirSync(dir);
|
|
634
|
+
} catch {
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
const projects = [];
|
|
638
|
+
for (const slug of entries) {
|
|
639
|
+
const subdir = join2(dir, slug);
|
|
640
|
+
let mtime;
|
|
641
|
+
try {
|
|
642
|
+
const s = statSync(subdir);
|
|
643
|
+
if (!s.isDirectory())
|
|
644
|
+
continue;
|
|
645
|
+
mtime = new Date(s.mtimeMs);
|
|
646
|
+
} catch {
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
if (!existsSync(join2(subdir, "bertrand.db")))
|
|
650
|
+
continue;
|
|
651
|
+
projects.push({
|
|
652
|
+
slug,
|
|
653
|
+
name: slug,
|
|
654
|
+
createdAt: mtime.toISOString(),
|
|
655
|
+
lastUsedAt: mtime.toISOString()
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
if (projects.length === 0)
|
|
659
|
+
return null;
|
|
660
|
+
projects.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
661
|
+
const envSlug = process.env.BERTRAND_PROJECT;
|
|
662
|
+
const hasEnv = !!envSlug && projects.some((p) => p.slug === envSlug);
|
|
663
|
+
const hasDefault = projects.some((p) => p.slug === DEFAULT_PROJECT_SLUG);
|
|
664
|
+
const activeProjectSlug = hasEnv ? envSlug : hasDefault ? DEFAULT_PROJECT_SLUG : projects[0].slug;
|
|
665
|
+
return { activeProjectSlug, projects };
|
|
666
|
+
}
|
|
667
|
+
function loadRegistry() {
|
|
668
|
+
return readRegistry() ?? recoverFromDisk();
|
|
669
|
+
}
|
|
670
|
+
function listProjects() {
|
|
671
|
+
return loadRegistry()?.projects ?? [];
|
|
672
|
+
}
|
|
673
|
+
function getActiveProjectSlug() {
|
|
674
|
+
return loadRegistry()?.activeProjectSlug ?? DEFAULT_PROJECT_SLUG;
|
|
675
|
+
}
|
|
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
|
+
function isRegistry(value) {
|
|
723
|
+
if (typeof value !== "object" || value === null)
|
|
724
|
+
return false;
|
|
725
|
+
const r = value;
|
|
726
|
+
if (typeof r.activeProjectSlug !== "string")
|
|
727
|
+
return false;
|
|
728
|
+
if (!Array.isArray(r.projects))
|
|
729
|
+
return false;
|
|
730
|
+
return r.projects.every((p) => {
|
|
731
|
+
if (typeof p !== "object" || p === null)
|
|
732
|
+
return false;
|
|
733
|
+
const e = p;
|
|
734
|
+
return typeof e.slug === "string" && typeof e.name === "string" && typeof e.createdAt === "string" && typeof e.lastUsedAt === "string";
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/lib/projects/paths.ts
|
|
739
|
+
import { join as join3 } from "path";
|
|
740
|
+
function projectPaths(slug) {
|
|
741
|
+
const root = join3(_getRegistryDir(), "projects", slug);
|
|
742
|
+
return {
|
|
743
|
+
root,
|
|
744
|
+
db: join3(root, "bertrand.db"),
|
|
745
|
+
syncEnv: join3(root, "sync.env"),
|
|
746
|
+
snapshots: join3(root, "snapshots")
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
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
|
+
// src/lib/projects/resolve.ts
|
|
758
|
+
var _cached = null;
|
|
759
|
+
function resolveActiveProject() {
|
|
760
|
+
if (_cached)
|
|
761
|
+
return _cached;
|
|
762
|
+
const envSlug = process.env.BERTRAND_PROJECT;
|
|
763
|
+
let slug;
|
|
764
|
+
if (envSlug && envSlug.trim()) {
|
|
765
|
+
slug = envSlug.trim();
|
|
766
|
+
} else {
|
|
767
|
+
slug = getActiveProjectSlug();
|
|
768
|
+
}
|
|
769
|
+
const entry = listProjects().find((p) => p.slug === slug);
|
|
770
|
+
const name = entry?.name ?? slug;
|
|
771
|
+
_cached = { slug, name, ...projectPaths(slug) };
|
|
772
|
+
return _cached;
|
|
773
|
+
}
|
|
774
|
+
function _resetActiveProjectCache() {
|
|
775
|
+
_cached = null;
|
|
776
|
+
}
|
|
777
|
+
|
|
584
778
|
// src/db/schema.ts
|
|
585
779
|
var exports_schema = {};
|
|
586
780
|
__export(exports_schema, {
|
|
@@ -589,15 +783,15 @@ __export(exports_schema, {
|
|
|
589
783
|
sessionStats: () => sessionStats,
|
|
590
784
|
sessionLabels: () => sessionLabels,
|
|
591
785
|
labels: () => labels,
|
|
592
|
-
groups: () => groups,
|
|
593
786
|
events: () => events,
|
|
594
|
-
conversations: () => conversations
|
|
787
|
+
conversations: () => conversations,
|
|
788
|
+
categories: () => categories
|
|
595
789
|
});
|
|
596
790
|
import { sqliteTable, text, integer, index, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
597
791
|
import { sql } from "drizzle-orm";
|
|
598
|
-
var
|
|
792
|
+
var categories = sqliteTable("categories", {
|
|
599
793
|
id: text("id").primaryKey(),
|
|
600
|
-
parentId: text("parent_id").references(() =>
|
|
794
|
+
parentId: text("parent_id").references(() => categories.id, {
|
|
601
795
|
onDelete: "cascade"
|
|
602
796
|
}),
|
|
603
797
|
slug: text("slug").notNull(),
|
|
@@ -607,8 +801,8 @@ var groups = sqliteTable("groups", {
|
|
|
607
801
|
color: text("color"),
|
|
608
802
|
createdAt: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
609
803
|
}, (t) => [
|
|
610
|
-
uniqueIndex("
|
|
611
|
-
index("
|
|
804
|
+
uniqueIndex("categories_parent_slug").on(t.parentId, t.slug),
|
|
805
|
+
index("categories_path").on(t.path)
|
|
612
806
|
]);
|
|
613
807
|
var labels = sqliteTable("labels", {
|
|
614
808
|
id: text("id").primaryKey(),
|
|
@@ -618,7 +812,7 @@ var labels = sqliteTable("labels", {
|
|
|
618
812
|
});
|
|
619
813
|
var sessions = sqliteTable("sessions", {
|
|
620
814
|
id: text("id").primaryKey(),
|
|
621
|
-
|
|
815
|
+
categoryId: text("category_id").notNull().references(() => categories.id, { onDelete: "cascade" }),
|
|
622
816
|
slug: text("slug").notNull(),
|
|
623
817
|
name: text("name").notNull(),
|
|
624
818
|
status: text("status", {
|
|
@@ -631,7 +825,7 @@ var sessions = sqliteTable("sessions", {
|
|
|
631
825
|
createdAt: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
632
826
|
updatedAt: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
633
827
|
}, (t) => [
|
|
634
|
-
uniqueIndex("
|
|
828
|
+
uniqueIndex("sessions_category_slug").on(t.categoryId, t.slug),
|
|
635
829
|
index("sessions_status").on(t.status),
|
|
636
830
|
index("sessions_started").on(t.startedAt)
|
|
637
831
|
]);
|
|
@@ -694,21 +888,255 @@ var sessionStats = sqliteTable("session_stats", {
|
|
|
694
888
|
});
|
|
695
889
|
|
|
696
890
|
// src/db/client.ts
|
|
697
|
-
var
|
|
891
|
+
var MIGRATIONS_FOLDER = import.meta.dir + "/migrations";
|
|
892
|
+
var _cache = new Map;
|
|
893
|
+
var _migrated = new Set;
|
|
894
|
+
var _testDb = null;
|
|
698
895
|
function getDb() {
|
|
699
|
-
if (
|
|
700
|
-
return
|
|
701
|
-
|
|
702
|
-
|
|
896
|
+
if (_testDb)
|
|
897
|
+
return _testDb;
|
|
898
|
+
return openDb(resolveActiveProject().db);
|
|
899
|
+
}
|
|
900
|
+
function getDbForProject(slug) {
|
|
901
|
+
if (_testDb)
|
|
902
|
+
return _testDb;
|
|
903
|
+
return openDb(projectPaths(slug).db);
|
|
904
|
+
}
|
|
905
|
+
function openDb(dbPath) {
|
|
906
|
+
const cached = _cache.get(dbPath);
|
|
907
|
+
if (cached)
|
|
908
|
+
return cached;
|
|
909
|
+
mkdirSync2(dirname(dbPath), { recursive: true });
|
|
910
|
+
const sqlite = new Database(dbPath);
|
|
911
|
+
sqlite.exec("PRAGMA busy_timeout = 5000");
|
|
703
912
|
sqlite.exec("PRAGMA journal_mode = WAL");
|
|
704
913
|
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
705
914
|
sqlite.exec("PRAGMA synchronous = NORMAL");
|
|
706
915
|
sqlite.exec("PRAGMA cache_size = -8000");
|
|
707
916
|
sqlite.exec("PRAGMA temp_store = MEMORY");
|
|
708
|
-
|
|
709
|
-
|
|
917
|
+
const db = drizzle(sqlite, { schema: exports_schema });
|
|
918
|
+
if (!_migrated.has(dbPath)) {
|
|
919
|
+
try {
|
|
920
|
+
migrate(db, { migrationsFolder: MIGRATIONS_FOLDER });
|
|
921
|
+
if (!hasSessionsTable(sqlite)) {
|
|
922
|
+
sqlite.exec("DROP TABLE IF EXISTS __drizzle_migrations");
|
|
923
|
+
migrate(db, { migrationsFolder: MIGRATIONS_FOLDER });
|
|
924
|
+
}
|
|
925
|
+
} catch (err) {
|
|
926
|
+
sqlite.close();
|
|
927
|
+
throw err;
|
|
928
|
+
}
|
|
929
|
+
_migrated.add(dbPath);
|
|
930
|
+
}
|
|
931
|
+
_cache.set(dbPath, db);
|
|
932
|
+
return db;
|
|
933
|
+
}
|
|
934
|
+
function hasSessionsTable(sqlite) {
|
|
935
|
+
const row = sqlite.query("SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'").get();
|
|
936
|
+
return row !== null;
|
|
937
|
+
}
|
|
938
|
+
|
|
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);
|
|
710
1131
|
}
|
|
711
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
|
+
|
|
712
1140
|
// src/lib/id.ts
|
|
713
1141
|
import { nanoid } from "nanoid";
|
|
714
1142
|
|
|
@@ -718,7 +1146,7 @@ function getSession(id) {
|
|
|
718
1146
|
}
|
|
719
1147
|
function getAllSessions(opts) {
|
|
720
1148
|
const db = getDb();
|
|
721
|
-
const query = db.select({ session: sessions,
|
|
1149
|
+
const query = db.select({ session: sessions, categoryPath: categories.path }).from(sessions).innerJoin(categories, eq(sessions.categoryId, categories.id));
|
|
722
1150
|
if (opts?.excludeArchived) {
|
|
723
1151
|
return query.where(inArray(sessions.status, [
|
|
724
1152
|
"active",
|
|
@@ -758,39 +1186,6 @@ function unarchiveSession(id) {
|
|
|
758
1186
|
return { ok: true, session: updated };
|
|
759
1187
|
}
|
|
760
1188
|
|
|
761
|
-
// src/lib/format.ts
|
|
762
|
-
var SECOND = 1000;
|
|
763
|
-
var MINUTE = 60 * SECOND;
|
|
764
|
-
var HOUR = 60 * MINUTE;
|
|
765
|
-
var DAY = 24 * HOUR;
|
|
766
|
-
function formatDuration(ms) {
|
|
767
|
-
if (ms < MINUTE)
|
|
768
|
-
return `${Math.round(ms / SECOND)}s`;
|
|
769
|
-
const days = Math.floor(ms / DAY);
|
|
770
|
-
const hours = Math.floor(ms % DAY / HOUR);
|
|
771
|
-
const minutes = Math.floor(ms % HOUR / MINUTE);
|
|
772
|
-
if (days > 0)
|
|
773
|
-
return hours > 0 ? `${days}d ${hours}h` : `${days}d`;
|
|
774
|
-
if (hours > 0)
|
|
775
|
-
return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
776
|
-
return `${minutes}m`;
|
|
777
|
-
}
|
|
778
|
-
function formatAgo(isoOrDate) {
|
|
779
|
-
const date = typeof isoOrDate === "string" ? new Date(isoOrDate) : isoOrDate;
|
|
780
|
-
const ms = Date.now() - date.getTime();
|
|
781
|
-
if (ms < MINUTE)
|
|
782
|
-
return "just now";
|
|
783
|
-
if (ms < HOUR)
|
|
784
|
-
return `${Math.floor(ms / MINUTE)}m ago`;
|
|
785
|
-
if (ms < DAY)
|
|
786
|
-
return `${Math.floor(ms / HOUR)}h ago`;
|
|
787
|
-
if (ms < 2 * DAY)
|
|
788
|
-
return "yesterday";
|
|
789
|
-
if (ms < 7 * DAY)
|
|
790
|
-
return `${Math.floor(ms / DAY)}d ago`;
|
|
791
|
-
return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
792
|
-
}
|
|
793
|
-
|
|
794
1189
|
// src/lib/parse-session-name.ts
|
|
795
1190
|
function parseSessionName(input) {
|
|
796
1191
|
const trimmed = input.trim().replace(/^\/+|\/+$/g, "");
|
|
@@ -799,7 +1194,7 @@ function parseSessionName(input) {
|
|
|
799
1194
|
}
|
|
800
1195
|
const segments = trimmed.split("/").filter(Boolean);
|
|
801
1196
|
if (segments.length < 2) {
|
|
802
|
-
throw new Error(`Session name must include at least one
|
|
1197
|
+
throw new Error(`Session name must include at least one category: "category/session" (got "${trimmed}")`);
|
|
803
1198
|
}
|
|
804
1199
|
for (const segment of segments) {
|
|
805
1200
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(segment)) {
|
|
@@ -807,12 +1202,12 @@ function parseSessionName(input) {
|
|
|
807
1202
|
}
|
|
808
1203
|
}
|
|
809
1204
|
const slug = segments[segments.length - 1];
|
|
810
|
-
const
|
|
811
|
-
return {
|
|
1205
|
+
const categoryPath = segments.slice(0, -1).join("/");
|
|
1206
|
+
return { categoryPath, slug };
|
|
812
1207
|
}
|
|
813
1208
|
|
|
814
1209
|
// src/tui/screens/launch/index.tsx
|
|
815
|
-
import { jsxDEV as
|
|
1210
|
+
import { jsxDEV as jsxDEV7, Fragment as Fragment2 } from "react/jsx-dev-runtime";
|
|
816
1211
|
var STATUS_COLOR = {
|
|
817
1212
|
paused: "gold",
|
|
818
1213
|
waiting: "red",
|
|
@@ -835,8 +1230,8 @@ function sessionRow(s) {
|
|
|
835
1230
|
const disabled = status === "waiting";
|
|
836
1231
|
const isArchived = status === "archived";
|
|
837
1232
|
return {
|
|
838
|
-
value: `${s.
|
|
839
|
-
label: `${s.
|
|
1233
|
+
value: `${s.categoryPath}/${s.session.slug}`,
|
|
1234
|
+
label: `${s.categoryPath}/${s.session.slug} ${status}`,
|
|
840
1235
|
meta: formatAgo(recencyKey(s)),
|
|
841
1236
|
disabled,
|
|
842
1237
|
dim: isArchived,
|
|
@@ -846,14 +1241,14 @@ function sessionRow(s) {
|
|
|
846
1241
|
const textColor = isCursor ? cursorColor : color;
|
|
847
1242
|
const slugColor = isCursor ? cursorColor : undefined;
|
|
848
1243
|
const dimText = !isCursor && (disabled || isArchived);
|
|
849
|
-
return /* @__PURE__ */
|
|
1244
|
+
return /* @__PURE__ */ jsxDEV7(Fragment2, {
|
|
850
1245
|
children: [
|
|
851
|
-
/* @__PURE__ */
|
|
1246
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
852
1247
|
color: cursorColor,
|
|
853
1248
|
bold: true,
|
|
854
1249
|
children: isCursor ? "\u276F " : " "
|
|
855
1250
|
}, undefined, false, undefined, this),
|
|
856
|
-
/* @__PURE__ */
|
|
1251
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
857
1252
|
color: dotColor,
|
|
858
1253
|
bold: isCursor,
|
|
859
1254
|
children: [
|
|
@@ -861,17 +1256,17 @@ function sessionRow(s) {
|
|
|
861
1256
|
" "
|
|
862
1257
|
]
|
|
863
1258
|
}, undefined, true, undefined, this),
|
|
864
|
-
/* @__PURE__ */
|
|
1259
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
865
1260
|
color: textColor,
|
|
866
1261
|
bold: isCursor,
|
|
867
1262
|
dim: dimText,
|
|
868
1263
|
children: status.padEnd(8)
|
|
869
1264
|
}, undefined, false, undefined, this),
|
|
870
|
-
/* @__PURE__ */
|
|
1265
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
871
1266
|
color: slugColor,
|
|
872
1267
|
children: " "
|
|
873
1268
|
}, undefined, false, undefined, this),
|
|
874
|
-
/* @__PURE__ */
|
|
1269
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
875
1270
|
color: slugColor,
|
|
876
1271
|
bold: isCursor,
|
|
877
1272
|
dim: dimText,
|
|
@@ -882,21 +1277,20 @@ function sessionRow(s) {
|
|
|
882
1277
|
}
|
|
883
1278
|
};
|
|
884
1279
|
}
|
|
885
|
-
function
|
|
1280
|
+
function categoryHeader(categoryPath) {
|
|
886
1281
|
return {
|
|
887
|
-
value: `
|
|
888
|
-
label:
|
|
1282
|
+
value: `__category:${categoryPath}`,
|
|
1283
|
+
label: categoryPath,
|
|
889
1284
|
kind: "header"
|
|
890
1285
|
};
|
|
891
1286
|
}
|
|
892
1287
|
function Launch({ onSelect }) {
|
|
893
|
-
const { exit } = useTui();
|
|
894
1288
|
const [error, setError] = useState3(null);
|
|
895
1289
|
const [notice, setNotice] = useState3(null);
|
|
896
1290
|
const [showArchived, setShowArchived] = useState3(false);
|
|
897
1291
|
const [refreshKey, setRefreshKey] = useState3(0);
|
|
898
|
-
const allSessions =
|
|
899
|
-
const visibleSessions =
|
|
1292
|
+
const allSessions = useMemo3(() => getAllSessions({ excludeArchived: !showArchived }), [showArchived, refreshKey]);
|
|
1293
|
+
const visibleSessions = useMemo3(() => {
|
|
900
1294
|
return allSessions.filter((s) => {
|
|
901
1295
|
const st = s.session.status;
|
|
902
1296
|
if (st === "paused" || st === "waiting")
|
|
@@ -905,7 +1299,7 @@ function Launch({ onSelect }) {
|
|
|
905
1299
|
return showArchived;
|
|
906
1300
|
return false;
|
|
907
1301
|
}).sort((a, b) => {
|
|
908
|
-
const g = a.
|
|
1302
|
+
const g = a.categoryPath.localeCompare(b.categoryPath);
|
|
909
1303
|
if (g !== 0)
|
|
910
1304
|
return g;
|
|
911
1305
|
const r = statusRank(a.session.status) - statusRank(b.session.status);
|
|
@@ -914,31 +1308,31 @@ function Launch({ onSelect }) {
|
|
|
914
1308
|
return recencyKey(b).localeCompare(recencyKey(a));
|
|
915
1309
|
});
|
|
916
1310
|
}, [allSessions, showArchived]);
|
|
917
|
-
const items =
|
|
1311
|
+
const items = useMemo3(() => {
|
|
918
1312
|
const rows = [];
|
|
919
|
-
let
|
|
1313
|
+
let lastCategory = null;
|
|
920
1314
|
for (const s of visibleSessions) {
|
|
921
|
-
if (s.
|
|
922
|
-
rows.push(
|
|
923
|
-
|
|
1315
|
+
if (s.categoryPath !== lastCategory) {
|
|
1316
|
+
rows.push(categoryHeader(s.categoryPath));
|
|
1317
|
+
lastCategory = s.categoryPath;
|
|
924
1318
|
}
|
|
925
1319
|
rows.push(sessionRow(s));
|
|
926
1320
|
}
|
|
927
1321
|
return rows;
|
|
928
1322
|
}, [visibleSessions]);
|
|
929
|
-
const suggestions =
|
|
930
|
-
const
|
|
1323
|
+
const suggestions = useMemo3(() => {
|
|
1324
|
+
const categories2 = new Set;
|
|
931
1325
|
const names = [];
|
|
932
1326
|
for (const s of allSessions) {
|
|
933
|
-
|
|
934
|
-
names.push(`${s.
|
|
1327
|
+
categories2.add(`${s.categoryPath}/`);
|
|
1328
|
+
names.push(`${s.categoryPath}/${s.session.slug}`);
|
|
935
1329
|
}
|
|
936
|
-
return [...
|
|
1330
|
+
return [...categories2, ...names];
|
|
937
1331
|
}, [allSessions]);
|
|
938
|
-
const sessionByValue =
|
|
1332
|
+
const sessionByValue = useMemo3(() => {
|
|
939
1333
|
const map = new Map;
|
|
940
1334
|
for (const s of allSessions) {
|
|
941
|
-
map.set(`${s.
|
|
1335
|
+
map.set(`${s.categoryPath}/${s.session.slug}`, s);
|
|
942
1336
|
}
|
|
943
1337
|
return map;
|
|
944
1338
|
}, [allSessions]);
|
|
@@ -969,7 +1363,6 @@ function Launch({ onSelect }) {
|
|
|
969
1363
|
};
|
|
970
1364
|
const select = (selection) => {
|
|
971
1365
|
onSelect(selection);
|
|
972
|
-
exit();
|
|
973
1366
|
};
|
|
974
1367
|
const handleSubmit = (value) => {
|
|
975
1368
|
const existing = sessionByValue.get(value);
|
|
@@ -982,13 +1375,13 @@ function Launch({ onSelect }) {
|
|
|
982
1375
|
return;
|
|
983
1376
|
}
|
|
984
1377
|
try {
|
|
985
|
-
const {
|
|
986
|
-
select({ type: "create",
|
|
1378
|
+
const { categoryPath, slug } = parseSessionName(value);
|
|
1379
|
+
select({ type: "create", categoryPath, slug });
|
|
987
1380
|
} catch (e) {
|
|
988
1381
|
setError(e instanceof Error ? e.message : "Invalid name");
|
|
989
1382
|
}
|
|
990
1383
|
};
|
|
991
|
-
const counts =
|
|
1384
|
+
const counts = useMemo3(() => {
|
|
992
1385
|
let paused = 0;
|
|
993
1386
|
let waiting = 0;
|
|
994
1387
|
let archived = 0;
|
|
@@ -1002,56 +1395,56 @@ function Launch({ onSelect }) {
|
|
|
1002
1395
|
}
|
|
1003
1396
|
return { paused, waiting, archived };
|
|
1004
1397
|
}, [visibleSessions]);
|
|
1005
|
-
return /* @__PURE__ */
|
|
1398
|
+
return /* @__PURE__ */ jsxDEV7(Box6, {
|
|
1006
1399
|
flexDirection: "column",
|
|
1007
1400
|
paddingY: 1,
|
|
1008
1401
|
gap: 1,
|
|
1009
1402
|
children: [
|
|
1010
|
-
/* @__PURE__ */
|
|
1403
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1011
1404
|
marginX: 1,
|
|
1012
|
-
children: /* @__PURE__ */
|
|
1405
|
+
children: /* @__PURE__ */ jsxDEV7(Logo, {}, undefined, false, undefined, this)
|
|
1013
1406
|
}, undefined, false, undefined, this),
|
|
1014
|
-
/* @__PURE__ */
|
|
1407
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1015
1408
|
flexDirection: "column",
|
|
1016
1409
|
marginX: 2,
|
|
1017
1410
|
gap: 1,
|
|
1018
1411
|
children: [
|
|
1019
|
-
/* @__PURE__ */
|
|
1020
|
-
/* @__PURE__ */
|
|
1412
|
+
/* @__PURE__ */ jsxDEV7(AppDetails, {}, undefined, false, undefined, this),
|
|
1413
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1021
1414
|
flexDirection: "column",
|
|
1022
1415
|
gap: 1,
|
|
1023
1416
|
children: [
|
|
1024
|
-
/* @__PURE__ */
|
|
1417
|
+
/* @__PURE__ */ jsxDEV7(Box6, {
|
|
1025
1418
|
flexDirection: "row",
|
|
1026
1419
|
gap: 1,
|
|
1027
1420
|
children: [
|
|
1028
|
-
/* @__PURE__ */
|
|
1421
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1029
1422
|
bold: true,
|
|
1030
1423
|
children: "Sessions"
|
|
1031
1424
|
}, undefined, false, undefined, this),
|
|
1032
|
-
visibleSessions.length === 0 ? /* @__PURE__ */
|
|
1425
|
+
visibleSessions.length === 0 ? /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1033
1426
|
dim: true,
|
|
1034
|
-
children: "\xB7 none \u2014 type
|
|
1035
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
1427
|
+
children: "\xB7 none \u2014 type category/slug to create"
|
|
1428
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV7(Fragment2, {
|
|
1036
1429
|
children: [
|
|
1037
|
-
/* @__PURE__ */
|
|
1430
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1038
1431
|
dim: true,
|
|
1039
1432
|
children: "\xB7"
|
|
1040
1433
|
}, undefined, false, undefined, this),
|
|
1041
|
-
/* @__PURE__ */
|
|
1434
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1042
1435
|
color: "gold",
|
|
1043
1436
|
children: [
|
|
1044
1437
|
counts.paused,
|
|
1045
1438
|
" paused"
|
|
1046
1439
|
]
|
|
1047
1440
|
}, undefined, true, undefined, this),
|
|
1048
|
-
counts.waiting > 0 && /* @__PURE__ */
|
|
1441
|
+
counts.waiting > 0 && /* @__PURE__ */ jsxDEV7(Fragment2, {
|
|
1049
1442
|
children: [
|
|
1050
|
-
/* @__PURE__ */
|
|
1443
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1051
1444
|
dim: true,
|
|
1052
1445
|
children: "\xB7"
|
|
1053
1446
|
}, undefined, false, undefined, this),
|
|
1054
|
-
/* @__PURE__ */
|
|
1447
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1055
1448
|
color: "red",
|
|
1056
1449
|
dim: true,
|
|
1057
1450
|
children: [
|
|
@@ -1061,13 +1454,13 @@ function Launch({ onSelect }) {
|
|
|
1061
1454
|
}, undefined, true, undefined, this)
|
|
1062
1455
|
]
|
|
1063
1456
|
}, undefined, true, undefined, this),
|
|
1064
|
-
showArchived && counts.archived > 0 && /* @__PURE__ */
|
|
1457
|
+
showArchived && counts.archived > 0 && /* @__PURE__ */ jsxDEV7(Fragment2, {
|
|
1065
1458
|
children: [
|
|
1066
|
-
/* @__PURE__ */
|
|
1459
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1067
1460
|
dim: true,
|
|
1068
1461
|
children: "\xB7"
|
|
1069
1462
|
}, undefined, false, undefined, this),
|
|
1070
|
-
/* @__PURE__ */
|
|
1463
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1071
1464
|
color: "purple",
|
|
1072
1465
|
dim: true,
|
|
1073
1466
|
children: [
|
|
@@ -1081,14 +1474,14 @@ function Launch({ onSelect }) {
|
|
|
1081
1474
|
}, undefined, true, undefined, this)
|
|
1082
1475
|
]
|
|
1083
1476
|
}, undefined, true, undefined, this),
|
|
1084
|
-
/* @__PURE__ */
|
|
1477
|
+
/* @__PURE__ */ jsxDEV7(Picker, {
|
|
1085
1478
|
mode: "single",
|
|
1086
1479
|
items,
|
|
1087
1480
|
isFocused: true,
|
|
1088
1481
|
maxVisible: 24,
|
|
1089
1482
|
suggest: suggestions,
|
|
1090
|
-
placeholder: "Filter or type
|
|
1091
|
-
emptyHint: showArchived ? "No sessions. Type
|
|
1483
|
+
placeholder: "Filter or type category/slug to create\u2026",
|
|
1484
|
+
emptyHint: showArchived ? "No sessions. Type category/slug to create one." : "No paused sessions. Type category/slug to create one.",
|
|
1092
1485
|
onSubmit: handleSubmit,
|
|
1093
1486
|
onKey: (e, cursorItem) => {
|
|
1094
1487
|
if (e.key === "c" && e.ctrl) {
|
|
@@ -1102,18 +1495,18 @@ function Launch({ onSelect }) {
|
|
|
1102
1495
|
}
|
|
1103
1496
|
}
|
|
1104
1497
|
}, undefined, false, undefined, this),
|
|
1105
|
-
notice && /* @__PURE__ */
|
|
1498
|
+
notice && /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1106
1499
|
color: "green",
|
|
1107
1500
|
children: notice
|
|
1108
1501
|
}, undefined, false, undefined, this),
|
|
1109
|
-
error && /* @__PURE__ */
|
|
1502
|
+
error && /* @__PURE__ */ jsxDEV7(Text7, {
|
|
1110
1503
|
color: "red",
|
|
1111
1504
|
children: error
|
|
1112
1505
|
}, undefined, false, undefined, this),
|
|
1113
|
-
/* @__PURE__ */
|
|
1506
|
+
/* @__PURE__ */ jsxDEV7(Text7, {
|
|
1114
1507
|
dim: true,
|
|
1115
1508
|
children: [
|
|
1116
|
-
"\u2191\u2193 navigate \xB7 \u2190\u2192 skip
|
|
1509
|
+
"\u2191\u2193 navigate \xB7 \u2190\u2192 skip category \xB7 enter continue/create \xB7 ctrl+a",
|
|
1117
1510
|
" ",
|
|
1118
1511
|
showArchived ? "(un)archive" : "archive",
|
|
1119
1512
|
" \xB7 tab",
|
|
@@ -1130,9 +1523,104 @@ function Launch({ onSelect }) {
|
|
|
1130
1523
|
}, undefined, true, undefined, this);
|
|
1131
1524
|
}
|
|
1132
1525
|
|
|
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
|
+
|
|
1133
1621
|
// src/tui/screens/Exit.tsx
|
|
1134
|
-
import { useState as
|
|
1135
|
-
import { Box as
|
|
1622
|
+
import { useState as useState5 } from "react";
|
|
1623
|
+
import { Box as Box7, Text as Text8, useInput as useInput3, useTui as useTui2 } from "@orchetron/storm";
|
|
1136
1624
|
|
|
1137
1625
|
// src/db/queries/conversations.ts
|
|
1138
1626
|
import { eq as eq2, and as and2, desc, sql as sql3 } from "drizzle-orm";
|
|
@@ -1141,7 +1629,7 @@ function getConversationsBySession(sessionId) {
|
|
|
1141
1629
|
}
|
|
1142
1630
|
|
|
1143
1631
|
// src/tui/screens/Exit.tsx
|
|
1144
|
-
import { jsxDEV as
|
|
1632
|
+
import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
|
|
1145
1633
|
var OPTIONS = [
|
|
1146
1634
|
{ action: "save", label: "Save", hint: "Keep session paused for later" },
|
|
1147
1635
|
{
|
|
@@ -1162,7 +1650,7 @@ var OPTIONS = [
|
|
|
1162
1650
|
];
|
|
1163
1651
|
function Exit({ sessionId, onAction }) {
|
|
1164
1652
|
const { exit } = useTui2();
|
|
1165
|
-
const [cursor, setCursor] =
|
|
1653
|
+
const [cursor, setCursor] = useState5(0);
|
|
1166
1654
|
const session = getSession(sessionId);
|
|
1167
1655
|
const conversations2 = session ? getConversationsBySession(session.id) : [];
|
|
1168
1656
|
useInput3((e) => {
|
|
@@ -1182,37 +1670,37 @@ function Exit({ sessionId, onAction }) {
|
|
|
1182
1670
|
}
|
|
1183
1671
|
});
|
|
1184
1672
|
if (!session) {
|
|
1185
|
-
return /* @__PURE__ */
|
|
1673
|
+
return /* @__PURE__ */ jsxDEV9(Text8, {
|
|
1186
1674
|
color: "red",
|
|
1187
1675
|
children: "Session not found"
|
|
1188
1676
|
}, undefined, false, undefined, this);
|
|
1189
1677
|
}
|
|
1190
1678
|
const duration = session.endedAt && session.startedAt ? formatDuration(new Date(session.endedAt).getTime() - new Date(session.startedAt).getTime()) : null;
|
|
1191
|
-
return /* @__PURE__ */
|
|
1679
|
+
return /* @__PURE__ */ jsxDEV9(Box7, {
|
|
1192
1680
|
flexDirection: "column",
|
|
1193
1681
|
padding: 1,
|
|
1194
1682
|
gap: 1,
|
|
1195
1683
|
children: [
|
|
1196
|
-
/* @__PURE__ */
|
|
1684
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1197
1685
|
bold: true,
|
|
1198
1686
|
color: "#82AAFF",
|
|
1199
1687
|
children: "Session ended"
|
|
1200
1688
|
}, undefined, false, undefined, this),
|
|
1201
|
-
/* @__PURE__ */
|
|
1689
|
+
/* @__PURE__ */ jsxDEV9(Box7, {
|
|
1202
1690
|
flexDirection: "column",
|
|
1203
1691
|
children: [
|
|
1204
|
-
/* @__PURE__ */
|
|
1692
|
+
/* @__PURE__ */ jsxDEV9(Box7, {
|
|
1205
1693
|
flexDirection: "row",
|
|
1206
1694
|
gap: 1,
|
|
1207
1695
|
children: [
|
|
1208
|
-
/* @__PURE__ */
|
|
1696
|
+
/* @__PURE__ */ jsxDEV9(StatusDot, {
|
|
1209
1697
|
status: session.status
|
|
1210
1698
|
}, undefined, false, undefined, this),
|
|
1211
|
-
/* @__PURE__ */
|
|
1699
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1212
1700
|
bold: true,
|
|
1213
1701
|
children: session.name
|
|
1214
1702
|
}, undefined, false, undefined, this),
|
|
1215
|
-
duration && /* @__PURE__ */
|
|
1703
|
+
duration && /* @__PURE__ */ jsxDEV9(Text8, {
|
|
1216
1704
|
dim: true,
|
|
1217
1705
|
children: [
|
|
1218
1706
|
"(",
|
|
@@ -1222,7 +1710,7 @@ function Exit({ sessionId, onAction }) {
|
|
|
1222
1710
|
}, undefined, true, undefined, this)
|
|
1223
1711
|
]
|
|
1224
1712
|
}, undefined, true, undefined, this),
|
|
1225
|
-
/* @__PURE__ */
|
|
1713
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1226
1714
|
dim: true,
|
|
1227
1715
|
children: [
|
|
1228
1716
|
conversations2.length,
|
|
@@ -1232,28 +1720,28 @@ function Exit({ sessionId, onAction }) {
|
|
|
1232
1720
|
}, undefined, true, undefined, this)
|
|
1233
1721
|
]
|
|
1234
1722
|
}, undefined, true, undefined, this),
|
|
1235
|
-
/* @__PURE__ */
|
|
1723
|
+
/* @__PURE__ */ jsxDEV9(Box7, {
|
|
1236
1724
|
flexDirection: "column",
|
|
1237
|
-
children: OPTIONS.map((opt, i) => /* @__PURE__ */
|
|
1725
|
+
children: OPTIONS.map((opt, i) => /* @__PURE__ */ jsxDEV9(Box7, {
|
|
1238
1726
|
flexDirection: "row",
|
|
1239
1727
|
gap: 1,
|
|
1240
1728
|
children: [
|
|
1241
|
-
/* @__PURE__ */
|
|
1729
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1242
1730
|
children: i === cursor ? "\u276F" : " "
|
|
1243
1731
|
}, undefined, false, undefined, this),
|
|
1244
|
-
/* @__PURE__ */
|
|
1732
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1245
1733
|
bold: i === cursor,
|
|
1246
1734
|
children: opt.label
|
|
1247
1735
|
}, undefined, false, undefined, this),
|
|
1248
|
-
/* @__PURE__ */
|
|
1736
|
+
/* @__PURE__ */ jsxDEV9(Text8, {
|
|
1249
1737
|
dim: true,
|
|
1250
1738
|
children: opt.hint
|
|
1251
1739
|
}, undefined, false, undefined, this)
|
|
1252
1740
|
]
|
|
1253
1741
|
}, opt.action, true, undefined, this))
|
|
1254
1742
|
}, undefined, false, undefined, this),
|
|
1255
|
-
/* @__PURE__ */
|
|
1256
|
-
children: /* @__PURE__ */
|
|
1743
|
+
/* @__PURE__ */ jsxDEV9(Box7, {
|
|
1744
|
+
children: /* @__PURE__ */ jsxDEV9(Text8, {
|
|
1257
1745
|
dim: true,
|
|
1258
1746
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q save & quit"
|
|
1259
1747
|
}, undefined, false, undefined, this)
|
|
@@ -1263,12 +1751,12 @@ function Exit({ sessionId, onAction }) {
|
|
|
1263
1751
|
}
|
|
1264
1752
|
|
|
1265
1753
|
// src/tui/screens/Resume.tsx
|
|
1266
|
-
import { useState as
|
|
1267
|
-
import { Box as
|
|
1268
|
-
import { jsxDEV as
|
|
1754
|
+
import { useState as useState6 } from "react";
|
|
1755
|
+
import { Box as Box8, Text as Text9, useInput as useInput4, useTui as useTui3 } from "@orchetron/storm";
|
|
1756
|
+
import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
|
|
1269
1757
|
function Resume({ sessionId, onSelect }) {
|
|
1270
1758
|
const { exit } = useTui3();
|
|
1271
|
-
const [cursor, setCursor] =
|
|
1759
|
+
const [cursor, setCursor] = useState6(0);
|
|
1272
1760
|
const session = getSession(sessionId);
|
|
1273
1761
|
const conversations2 = getConversationsBySession(sessionId);
|
|
1274
1762
|
const totalOptions = conversations2.length + 1;
|
|
@@ -1295,17 +1783,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1295
1783
|
}
|
|
1296
1784
|
});
|
|
1297
1785
|
if (!session) {
|
|
1298
|
-
return /* @__PURE__ */
|
|
1786
|
+
return /* @__PURE__ */ jsxDEV10(Text9, {
|
|
1299
1787
|
color: "red",
|
|
1300
1788
|
children: "Session not found"
|
|
1301
1789
|
}, undefined, false, undefined, this);
|
|
1302
1790
|
}
|
|
1303
|
-
return /* @__PURE__ */
|
|
1791
|
+
return /* @__PURE__ */ jsxDEV10(Box8, {
|
|
1304
1792
|
flexDirection: "column",
|
|
1305
1793
|
padding: 1,
|
|
1306
1794
|
gap: 1,
|
|
1307
1795
|
children: [
|
|
1308
|
-
/* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1309
1797
|
bold: true,
|
|
1310
1798
|
color: "#82AAFF",
|
|
1311
1799
|
children: [
|
|
@@ -1313,17 +1801,17 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1313
1801
|
session.name
|
|
1314
1802
|
]
|
|
1315
1803
|
}, undefined, true, undefined, this),
|
|
1316
|
-
/* @__PURE__ */
|
|
1804
|
+
/* @__PURE__ */ jsxDEV10(Box8, {
|
|
1317
1805
|
flexDirection: "column",
|
|
1318
1806
|
children: [
|
|
1319
|
-
/* @__PURE__ */
|
|
1807
|
+
/* @__PURE__ */ jsxDEV10(Box8, {
|
|
1320
1808
|
flexDirection: "row",
|
|
1321
1809
|
gap: 1,
|
|
1322
1810
|
children: [
|
|
1323
|
-
/* @__PURE__ */
|
|
1811
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1324
1812
|
children: cursor === 0 ? "\u276F" : " "
|
|
1325
1813
|
}, undefined, false, undefined, this),
|
|
1326
|
-
/* @__PURE__ */
|
|
1814
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1327
1815
|
bold: cursor === 0,
|
|
1328
1816
|
color: "#34D399",
|
|
1329
1817
|
children: "+ New conversation"
|
|
@@ -1335,42 +1823,42 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1335
1823
|
const isSelected = cursor === idx;
|
|
1336
1824
|
const duration = conv.endedAt ? formatDuration(new Date(conv.endedAt).getTime() - new Date(conv.startedAt).getTime()) : "active";
|
|
1337
1825
|
const ago = formatAgo(conv.startedAt);
|
|
1338
|
-
return /* @__PURE__ */
|
|
1826
|
+
return /* @__PURE__ */ jsxDEV10(Box8, {
|
|
1339
1827
|
flexDirection: "row",
|
|
1340
1828
|
gap: 1,
|
|
1341
1829
|
children: [
|
|
1342
|
-
/* @__PURE__ */
|
|
1830
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1343
1831
|
children: isSelected ? "\u276F" : " "
|
|
1344
1832
|
}, undefined, false, undefined, this),
|
|
1345
|
-
/* @__PURE__ */
|
|
1833
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1346
1834
|
bold: isSelected,
|
|
1347
1835
|
dim: conv.discarded,
|
|
1348
1836
|
children: conv.id.slice(0, 8)
|
|
1349
1837
|
}, undefined, false, undefined, this),
|
|
1350
|
-
/* @__PURE__ */
|
|
1838
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1351
1839
|
dim: true,
|
|
1352
1840
|
children: ago
|
|
1353
1841
|
}, undefined, false, undefined, this),
|
|
1354
|
-
/* @__PURE__ */
|
|
1842
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1355
1843
|
dim: true,
|
|
1356
1844
|
children: "\xB7"
|
|
1357
1845
|
}, undefined, false, undefined, this),
|
|
1358
|
-
/* @__PURE__ */
|
|
1846
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1359
1847
|
dim: true,
|
|
1360
1848
|
children: [
|
|
1361
1849
|
conv.eventCount,
|
|
1362
1850
|
" events"
|
|
1363
1851
|
]
|
|
1364
1852
|
}, undefined, true, undefined, this),
|
|
1365
|
-
/* @__PURE__ */
|
|
1853
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1366
1854
|
dim: true,
|
|
1367
1855
|
children: "\xB7"
|
|
1368
1856
|
}, undefined, false, undefined, this),
|
|
1369
|
-
/* @__PURE__ */
|
|
1857
|
+
/* @__PURE__ */ jsxDEV10(Text9, {
|
|
1370
1858
|
dim: true,
|
|
1371
1859
|
children: duration
|
|
1372
1860
|
}, undefined, false, undefined, this),
|
|
1373
|
-
conv.discarded && /* @__PURE__ */
|
|
1861
|
+
conv.discarded && /* @__PURE__ */ jsxDEV10(Text9, {
|
|
1374
1862
|
color: "red",
|
|
1375
1863
|
dim: true,
|
|
1376
1864
|
children: "(discarded)"
|
|
@@ -1380,8 +1868,8 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1380
1868
|
})
|
|
1381
1869
|
]
|
|
1382
1870
|
}, undefined, true, undefined, this),
|
|
1383
|
-
/* @__PURE__ */
|
|
1384
|
-
children: /* @__PURE__ */
|
|
1871
|
+
/* @__PURE__ */ jsxDEV10(Box8, {
|
|
1872
|
+
children: /* @__PURE__ */ jsxDEV10(Text9, {
|
|
1385
1873
|
dim: true,
|
|
1386
1874
|
children: "\u2191\u2193 navigate \xB7 enter select \xB7 q back"
|
|
1387
1875
|
}, undefined, false, undefined, this)
|
|
@@ -1391,7 +1879,7 @@ function Resume({ sessionId, onSelect }) {
|
|
|
1391
1879
|
}
|
|
1392
1880
|
|
|
1393
1881
|
// src/tui/run-screen.tsx
|
|
1394
|
-
import { jsxDEV as
|
|
1882
|
+
import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
|
|
1395
1883
|
var [, , screen, outputPath, ...args] = process.argv;
|
|
1396
1884
|
if (!screen || !outputPath) {
|
|
1397
1885
|
console.error("Usage: run-screen <screen> <outputPath> [args...]");
|
|
@@ -1399,9 +1887,13 @@ if (!screen || !outputPath) {
|
|
|
1399
1887
|
}
|
|
1400
1888
|
var result;
|
|
1401
1889
|
switch (screen) {
|
|
1402
|
-
case "
|
|
1890
|
+
case "startup": {
|
|
1891
|
+
const skipProjectPicker = args[0] === "true";
|
|
1892
|
+
const initialProjectSlug = args[1] ?? "";
|
|
1403
1893
|
let selection = { type: "quit" };
|
|
1404
|
-
const app = render(/* @__PURE__ */
|
|
1894
|
+
const app = render(/* @__PURE__ */ jsxDEV11(Startup, {
|
|
1895
|
+
skipProjectPicker,
|
|
1896
|
+
initialProjectSlug,
|
|
1405
1897
|
onSelect: (s) => {
|
|
1406
1898
|
selection = s;
|
|
1407
1899
|
}
|
|
@@ -1418,7 +1910,7 @@ switch (screen) {
|
|
|
1418
1910
|
process.exit(1);
|
|
1419
1911
|
}
|
|
1420
1912
|
let action = "save";
|
|
1421
|
-
const app = render(/* @__PURE__ */
|
|
1913
|
+
const app = render(/* @__PURE__ */ jsxDEV11(Exit, {
|
|
1422
1914
|
sessionId,
|
|
1423
1915
|
onAction: (a) => {
|
|
1424
1916
|
action = a;
|
|
@@ -1436,7 +1928,7 @@ switch (screen) {
|
|
|
1436
1928
|
process.exit(1);
|
|
1437
1929
|
}
|
|
1438
1930
|
let selection = { type: "back" };
|
|
1439
|
-
const app = render(/* @__PURE__ */
|
|
1931
|
+
const app = render(/* @__PURE__ */ jsxDEV11(Resume, {
|
|
1440
1932
|
sessionId,
|
|
1441
1933
|
onSelect: (s) => {
|
|
1442
1934
|
selection = s;
|
|
@@ -1451,5 +1943,5 @@ switch (screen) {
|
|
|
1451
1943
|
console.error(`Unknown screen: ${screen}`);
|
|
1452
1944
|
process.exit(1);
|
|
1453
1945
|
}
|
|
1454
|
-
|
|
1946
|
+
writeFileSync2(outputPath, JSON.stringify(result));
|
|
1455
1947
|
process.exit(0);
|