kandown 0.7.4 → 0.8.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/README.md +15 -1
- package/bin/kandown.js +255 -30
- package/bin/tui.js +422 -83
- package/dist/index.html +2 -2
- package/package.json +1 -1
package/bin/tui.js
CHANGED
|
@@ -27895,7 +27895,7 @@ var require_backend = __commonJS({
|
|
|
27895
27895
|
});
|
|
27896
27896
|
return value;
|
|
27897
27897
|
},
|
|
27898
|
-
useEffect: function
|
|
27898
|
+
useEffect: function useEffect11(create3) {
|
|
27899
27899
|
nextHook();
|
|
27900
27900
|
hookLog.push({
|
|
27901
27901
|
displayName: null,
|
|
@@ -27983,7 +27983,7 @@ var require_backend = __commonJS({
|
|
|
27983
27983
|
});
|
|
27984
27984
|
return initialValue;
|
|
27985
27985
|
},
|
|
27986
|
-
useState: function
|
|
27986
|
+
useState: function useState10(initialState) {
|
|
27987
27987
|
var hook = nextHook();
|
|
27988
27988
|
initialState = null !== hook ? hook.memoizedState : "function" === typeof initialState ? initialState() : initialState;
|
|
27989
27989
|
hookLog.push({
|
|
@@ -53951,6 +53951,8 @@ var use_app_default = useApp;
|
|
|
53951
53951
|
|
|
53952
53952
|
// node_modules/.pnpm/ink@7.0.1_@types+react@19.2.14_react-devtools-core@7.0.1_react@19.2.5/node_modules/ink/build/hooks/use-stdout.js
|
|
53953
53953
|
var import_react25 = __toESM(require_react(), 1);
|
|
53954
|
+
var useStdout = () => (0, import_react25.useContext)(StdoutContext_default);
|
|
53955
|
+
var use_stdout_default = useStdout;
|
|
53954
53956
|
|
|
53955
53957
|
// node_modules/.pnpm/ink@7.0.1_@types+react@19.2.14_react-devtools-core@7.0.1_react@19.2.5/node_modules/ink/build/hooks/use-stderr.js
|
|
53956
53958
|
var import_react26 = __toESM(require_react(), 1);
|
|
@@ -53976,6 +53978,9 @@ var import_react32 = __toESM(require_react(), 1);
|
|
|
53976
53978
|
// node_modules/.pnpm/ink@7.0.1_@types+react@19.2.14_react-devtools-core@7.0.1_react@19.2.5/node_modules/ink/build/hooks/use-box-metrics.js
|
|
53977
53979
|
var import_react33 = __toESM(require_react(), 1);
|
|
53978
53980
|
|
|
53981
|
+
// src/cli/app.tsx
|
|
53982
|
+
var import_react38 = __toESM(require_react(), 1);
|
|
53983
|
+
|
|
53979
53984
|
// src/cli/screens/settings.tsx
|
|
53980
53985
|
var import_react34 = __toESM(require_react(), 1);
|
|
53981
53986
|
|
|
@@ -54584,10 +54589,137 @@ function moveTaskToColumn(kandownDir, taskId, targetColumn) {
|
|
|
54584
54589
|
return true;
|
|
54585
54590
|
}
|
|
54586
54591
|
|
|
54592
|
+
// src/cli/lib/daemon.ts
|
|
54593
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, unlinkSync } from "fs";
|
|
54594
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
54595
|
+
import { spawn } from "child_process";
|
|
54596
|
+
function metadataPath(kandownDir) {
|
|
54597
|
+
return join3(kandownDir, "daemon.json");
|
|
54598
|
+
}
|
|
54599
|
+
function isRecord(value) {
|
|
54600
|
+
return typeof value === "object" && value !== null;
|
|
54601
|
+
}
|
|
54602
|
+
function parseMetadata(value) {
|
|
54603
|
+
if (!isRecord(value)) return null;
|
|
54604
|
+
const { pid, port, url, kandownDir, startedAt, version } = value;
|
|
54605
|
+
if (typeof pid !== "number" || !Number.isInteger(pid)) return null;
|
|
54606
|
+
if (typeof port !== "number" || !Number.isInteger(port)) return null;
|
|
54607
|
+
if (typeof url !== "string" || typeof kandownDir !== "string") return null;
|
|
54608
|
+
if (typeof startedAt !== "string") return null;
|
|
54609
|
+
if (version !== null && typeof version !== "string" && version !== void 0) return null;
|
|
54610
|
+
return { pid, port, url, kandownDir, startedAt, version: version ?? null };
|
|
54611
|
+
}
|
|
54612
|
+
function readDaemonMetadata(kandownDir) {
|
|
54613
|
+
const path = metadataPath(kandownDir);
|
|
54614
|
+
if (!existsSync4(path)) return null;
|
|
54615
|
+
try {
|
|
54616
|
+
return parseMetadata(JSON.parse(readFileSync4(path, "utf8")));
|
|
54617
|
+
} catch {
|
|
54618
|
+
return null;
|
|
54619
|
+
}
|
|
54620
|
+
}
|
|
54621
|
+
function removeDaemonMetadata(kandownDir) {
|
|
54622
|
+
try {
|
|
54623
|
+
const path = metadataPath(kandownDir);
|
|
54624
|
+
if (existsSync4(path)) unlinkSync(path);
|
|
54625
|
+
} catch {
|
|
54626
|
+
}
|
|
54627
|
+
}
|
|
54628
|
+
function isProcessAlive(pid) {
|
|
54629
|
+
try {
|
|
54630
|
+
process.kill(pid, 0);
|
|
54631
|
+
return true;
|
|
54632
|
+
} catch {
|
|
54633
|
+
return false;
|
|
54634
|
+
}
|
|
54635
|
+
}
|
|
54636
|
+
function parseRemoteDaemonInfo(value) {
|
|
54637
|
+
if (!isRecord(value)) return null;
|
|
54638
|
+
const { ok, pid, kandownDir } = value;
|
|
54639
|
+
if (ok !== true || typeof pid !== "number" || !Number.isInteger(pid) || typeof kandownDir !== "string") return null;
|
|
54640
|
+
return { ok, pid, kandownDir };
|
|
54641
|
+
}
|
|
54642
|
+
async function fetchDaemonInfo(port) {
|
|
54643
|
+
try {
|
|
54644
|
+
const response = await fetch(`http://127.0.0.1:${port}/api/daemon`, {
|
|
54645
|
+
signal: AbortSignal.timeout(700)
|
|
54646
|
+
});
|
|
54647
|
+
if (!response.ok) return null;
|
|
54648
|
+
return parseRemoteDaemonInfo(await response.json());
|
|
54649
|
+
} catch {
|
|
54650
|
+
return null;
|
|
54651
|
+
}
|
|
54652
|
+
}
|
|
54653
|
+
async function getDaemonStatus(kandownDir) {
|
|
54654
|
+
const metadata = readDaemonMetadata(kandownDir);
|
|
54655
|
+
if (!metadata) return { running: false, metadata: null };
|
|
54656
|
+
if (!isProcessAlive(metadata.pid)) {
|
|
54657
|
+
removeDaemonMetadata(kandownDir);
|
|
54658
|
+
return { running: false, metadata: null };
|
|
54659
|
+
}
|
|
54660
|
+
const remote = await fetchDaemonInfo(metadata.port);
|
|
54661
|
+
if (!remote || remote.pid !== metadata.pid || remote.kandownDir !== kandownDir) {
|
|
54662
|
+
removeDaemonMetadata(kandownDir);
|
|
54663
|
+
return { running: false, metadata: null };
|
|
54664
|
+
}
|
|
54665
|
+
return { running: true, metadata };
|
|
54666
|
+
}
|
|
54667
|
+
async function waitForDaemon(kandownDir, timeoutMs = 5e3) {
|
|
54668
|
+
const started = Date.now();
|
|
54669
|
+
while (Date.now() - started < timeoutMs) {
|
|
54670
|
+
const status = await getDaemonStatus(kandownDir);
|
|
54671
|
+
if (status.running) return status;
|
|
54672
|
+
await new Promise((resolve3) => setTimeout(resolve3, 150));
|
|
54673
|
+
}
|
|
54674
|
+
return { running: false, metadata: null };
|
|
54675
|
+
}
|
|
54676
|
+
async function startProjectDaemon(kandownDir, preferredPort) {
|
|
54677
|
+
const current = await getDaemonStatus(kandownDir);
|
|
54678
|
+
if (current.running) return current;
|
|
54679
|
+
const cliPath = process.argv[1];
|
|
54680
|
+
if (!cliPath) throw new Error("Cannot locate kandown CLI entrypoint");
|
|
54681
|
+
const args = [
|
|
54682
|
+
cliPath,
|
|
54683
|
+
"--no-update-check",
|
|
54684
|
+
"daemon",
|
|
54685
|
+
"run",
|
|
54686
|
+
"--path",
|
|
54687
|
+
kandownDir
|
|
54688
|
+
];
|
|
54689
|
+
if (typeof preferredPort === "number" && Number.isInteger(preferredPort)) {
|
|
54690
|
+
args.push("--port", String(preferredPort));
|
|
54691
|
+
}
|
|
54692
|
+
const child = spawn(process.execPath, args, {
|
|
54693
|
+
cwd: dirname2(kandownDir),
|
|
54694
|
+
detached: true,
|
|
54695
|
+
stdio: "ignore",
|
|
54696
|
+
env: { ...process.env, KANDOWN_DAEMON: "1" }
|
|
54697
|
+
});
|
|
54698
|
+
child.unref();
|
|
54699
|
+
return waitForDaemon(kandownDir);
|
|
54700
|
+
}
|
|
54701
|
+
async function stopProjectDaemon(kandownDir) {
|
|
54702
|
+
const status = await getDaemonStatus(kandownDir);
|
|
54703
|
+
if (!status.running || !status.metadata) {
|
|
54704
|
+
removeDaemonMetadata(kandownDir);
|
|
54705
|
+
return false;
|
|
54706
|
+
}
|
|
54707
|
+
try {
|
|
54708
|
+
process.kill(status.metadata.pid, "SIGTERM");
|
|
54709
|
+
} catch {
|
|
54710
|
+
}
|
|
54711
|
+
const started = Date.now();
|
|
54712
|
+
while (Date.now() - started < 2500 && isProcessAlive(status.metadata.pid)) {
|
|
54713
|
+
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
54714
|
+
}
|
|
54715
|
+
removeDaemonMetadata(kandownDir);
|
|
54716
|
+
return true;
|
|
54717
|
+
}
|
|
54718
|
+
|
|
54587
54719
|
// src/cli/lib/file-watcher.ts
|
|
54588
54720
|
import { createReadStream, statSync } from "fs";
|
|
54589
54721
|
import { createHash } from "crypto";
|
|
54590
|
-
import { join as
|
|
54722
|
+
import { join as join6 } from "path";
|
|
54591
54723
|
|
|
54592
54724
|
// node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
|
|
54593
54725
|
import { stat as statcb } from "fs";
|
|
@@ -55318,9 +55450,9 @@ var NodeFsHandler = class {
|
|
|
55318
55450
|
if (this.fsw.closed) {
|
|
55319
55451
|
return;
|
|
55320
55452
|
}
|
|
55321
|
-
const
|
|
55453
|
+
const dirname5 = sysPath.dirname(file);
|
|
55322
55454
|
const basename3 = sysPath.basename(file);
|
|
55323
|
-
const parent = this.fsw._getWatchedDir(
|
|
55455
|
+
const parent = this.fsw._getWatchedDir(dirname5);
|
|
55324
55456
|
let prevStats = stats;
|
|
55325
55457
|
if (parent.has(basename3))
|
|
55326
55458
|
return;
|
|
@@ -55347,7 +55479,7 @@ var NodeFsHandler = class {
|
|
|
55347
55479
|
prevStats = newStats2;
|
|
55348
55480
|
}
|
|
55349
55481
|
} catch (error) {
|
|
55350
|
-
this.fsw._remove(
|
|
55482
|
+
this.fsw._remove(dirname5, basename3);
|
|
55351
55483
|
}
|
|
55352
55484
|
} else if (parent.has(basename3)) {
|
|
55353
55485
|
const at = newStats.atimeMs;
|
|
@@ -56309,18 +56441,18 @@ var FileWatcher = class {
|
|
|
56309
56441
|
* poll to catch any races or network-mounted file changes.
|
|
56310
56442
|
*/
|
|
56311
56443
|
start(kandownDir) {
|
|
56312
|
-
const tasksDir =
|
|
56313
|
-
const configPath =
|
|
56444
|
+
const tasksDir = join6(kandownDir, "tasks");
|
|
56445
|
+
const configPath = join6(kandownDir, "kandown.json");
|
|
56314
56446
|
const existingIds = listTaskIds(kandownDir);
|
|
56315
56447
|
for (const id of existingIds) {
|
|
56316
56448
|
this.knownTaskIds.add(id);
|
|
56317
56449
|
try {
|
|
56318
|
-
const filePath =
|
|
56450
|
+
const filePath = join6(tasksDir, `${id}.md`);
|
|
56319
56451
|
this.taskHashes.set(id, hashFileSync(filePath));
|
|
56320
56452
|
} catch {
|
|
56321
56453
|
}
|
|
56322
56454
|
}
|
|
56323
|
-
this.watcher = watch([
|
|
56455
|
+
this.watcher = watch([join6(tasksDir, "*.md"), configPath], {
|
|
56324
56456
|
ignoreInitial: true,
|
|
56325
56457
|
awaitWriteFinish: { stabilityThreshold: 25, pollInterval: 25 },
|
|
56326
56458
|
alwaysStat: true
|
|
@@ -56368,8 +56500,8 @@ var FileWatcher = class {
|
|
|
56368
56500
|
// ─── Private ───────────────────────────────────────────────────────────────
|
|
56369
56501
|
handleFsEvent(event, filePath, kandownDir) {
|
|
56370
56502
|
if (this.stopped) return;
|
|
56371
|
-
const tasksDir =
|
|
56372
|
-
const configPath =
|
|
56503
|
+
const tasksDir = join6(kandownDir, "tasks");
|
|
56504
|
+
const configPath = join6(kandownDir, "kandown.json");
|
|
56373
56505
|
if (filePath === configPath) {
|
|
56374
56506
|
const key = `config:${event}`;
|
|
56375
56507
|
const existing = this.debounceTimers.get(key);
|
|
@@ -56421,14 +56553,14 @@ var FileWatcher = class {
|
|
|
56421
56553
|
/** 📖 Fallback poll — catches changes that chokidar missed (network mounts, exotic FS). */
|
|
56422
56554
|
async pollHashes(kandownDir) {
|
|
56423
56555
|
if (this.stopped) return;
|
|
56424
|
-
const tasksDir =
|
|
56425
|
-
const configPath =
|
|
56556
|
+
const tasksDir = join6(kandownDir, "tasks");
|
|
56557
|
+
const configPath = join6(kandownDir, "kandown.json");
|
|
56426
56558
|
try {
|
|
56427
56559
|
const newHash = hashFileSync(configPath);
|
|
56428
56560
|
} catch {
|
|
56429
56561
|
}
|
|
56430
56562
|
for (const taskId of this.knownTaskIds) {
|
|
56431
|
-
const filePath =
|
|
56563
|
+
const filePath = join6(tasksDir, `${taskId}.md`);
|
|
56432
56564
|
try {
|
|
56433
56565
|
statSync(filePath);
|
|
56434
56566
|
const newHash = await hashFile(filePath);
|
|
@@ -56445,7 +56577,7 @@ var FileWatcher = class {
|
|
|
56445
56577
|
const currentIds = listTaskIds(kandownDir);
|
|
56446
56578
|
for (const id of currentIds) {
|
|
56447
56579
|
if (!this.knownTaskIds.has(id)) {
|
|
56448
|
-
const filePath =
|
|
56580
|
+
const filePath = join6(tasksDir, `${id}.md`);
|
|
56449
56581
|
try {
|
|
56450
56582
|
const newHash = await hashFile(filePath);
|
|
56451
56583
|
this.knownTaskIds.add(id);
|
|
@@ -56615,9 +56747,9 @@ function buildPrompt(agentDoc, taskContent, taskId, kandownDir) {
|
|
|
56615
56747
|
}
|
|
56616
56748
|
|
|
56617
56749
|
// src/cli/lib/launcher.ts
|
|
56618
|
-
import { execSync, spawn } from "child_process";
|
|
56750
|
+
import { execSync, spawn as spawn2 } from "child_process";
|
|
56619
56751
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
56620
|
-
import { join as
|
|
56752
|
+
import { join as join7 } from "path";
|
|
56621
56753
|
import { tmpdir } from "os";
|
|
56622
56754
|
function isInTmux() {
|
|
56623
56755
|
return !!process.env.TMUX;
|
|
@@ -56641,7 +56773,7 @@ function launchAgent(opts) {
|
|
|
56641
56773
|
].join("\n");
|
|
56642
56774
|
const { systemPrompt, taskPrompt } = buildPrompt(agentDoc, taskFileContent, taskId, kandownDir);
|
|
56643
56775
|
moveTaskToColumn(kandownDir, taskId, "In Progress");
|
|
56644
|
-
const contextFile =
|
|
56776
|
+
const contextFile = join7(tmpdir(), `kandown-${taskId}-context.md`);
|
|
56645
56777
|
writeFileSync3(contextFile, `${systemPrompt}
|
|
56646
56778
|
|
|
56647
56779
|
---
|
|
@@ -56659,7 +56791,7 @@ ${taskPrompt}`, "utf8");
|
|
|
56659
56791
|
});
|
|
56660
56792
|
} else {
|
|
56661
56793
|
onBeforeExec?.();
|
|
56662
|
-
const child =
|
|
56794
|
+
const child = spawn2(binary, args, {
|
|
56663
56795
|
stdio: "inherit",
|
|
56664
56796
|
env: {
|
|
56665
56797
|
...process.env,
|
|
@@ -56757,8 +56889,8 @@ function AgentPicker({ agents, taskId, onSelect, onCancel }) {
|
|
|
56757
56889
|
|
|
56758
56890
|
// src/cli/hooks/use-mouse.ts
|
|
56759
56891
|
var import_react36 = __toESM(require_react(), 1);
|
|
56760
|
-
var MOUSE_ENABLE = "\x1B[?1000h\x1B[?1006h";
|
|
56761
|
-
var MOUSE_DISABLE = "\x1B[?1006l\x1B[?1000l";
|
|
56892
|
+
var MOUSE_ENABLE = "\x1B[?1000h\x1B[?1002h\x1B[?1006h";
|
|
56893
|
+
var MOUSE_DISABLE = "\x1B[?1006l\x1B[?1002l\x1B[?1000l";
|
|
56762
56894
|
var RE_SGR_MOUSE = /^\[<(\d+);(\d+);(\d+)([Mm])/;
|
|
56763
56895
|
function parseMouseInput(input) {
|
|
56764
56896
|
const match = input.match(RE_SGR_MOUSE);
|
|
@@ -56766,31 +56898,44 @@ function parseMouseInput(input) {
|
|
|
56766
56898
|
const cb = parseInt(match[1], 10);
|
|
56767
56899
|
const cx = parseInt(match[2], 10);
|
|
56768
56900
|
const cy = parseInt(match[3], 10);
|
|
56769
|
-
const
|
|
56901
|
+
const final = match[4];
|
|
56902
|
+
const buttonNumber = cb & 3 | (cb & 192) >> 4;
|
|
56903
|
+
const dragging = (cb & 32) === 32;
|
|
56904
|
+
let action;
|
|
56905
|
+
if (final === "m") action = "release";
|
|
56906
|
+
else if (dragging && buttonNumber <= 2) action = "drag";
|
|
56907
|
+
else if (dragging) action = "move";
|
|
56908
|
+
else if (buttonNumber >= 4 && buttonNumber <= 7) action = "scroll";
|
|
56909
|
+
else action = "press";
|
|
56770
56910
|
return {
|
|
56771
56911
|
x: cx,
|
|
56772
56912
|
y: cy,
|
|
56773
|
-
button:
|
|
56774
|
-
action
|
|
56913
|
+
button: buttonNumber <= 2 ? buttonNumber : 0,
|
|
56914
|
+
action
|
|
56775
56915
|
};
|
|
56776
56916
|
}
|
|
56777
56917
|
function isMouseInput(input) {
|
|
56778
56918
|
return input.startsWith("[<") || input.startsWith("[M");
|
|
56779
56919
|
}
|
|
56920
|
+
function safeWrite(write, data) {
|
|
56921
|
+
try {
|
|
56922
|
+
write(data);
|
|
56923
|
+
} catch {
|
|
56924
|
+
}
|
|
56925
|
+
}
|
|
56780
56926
|
function useMouseMode(enabled = true) {
|
|
56927
|
+
const { write } = use_stdout_default();
|
|
56781
56928
|
(0, import_react36.useEffect)(() => {
|
|
56782
56929
|
if (!enabled) return;
|
|
56783
56930
|
if (!process.stdin.isTTY) return;
|
|
56784
|
-
|
|
56785
|
-
const cleanup = () =>
|
|
56786
|
-
process.stdout.write(MOUSE_DISABLE);
|
|
56787
|
-
};
|
|
56931
|
+
safeWrite(write, MOUSE_ENABLE);
|
|
56932
|
+
const cleanup = () => safeWrite(write, MOUSE_DISABLE);
|
|
56788
56933
|
process.on("exit", cleanup);
|
|
56789
56934
|
return () => {
|
|
56790
56935
|
process.removeListener("exit", cleanup);
|
|
56791
|
-
|
|
56936
|
+
safeWrite(write, MOUSE_DISABLE);
|
|
56792
56937
|
};
|
|
56793
|
-
}, [enabled]);
|
|
56938
|
+
}, [enabled, write]);
|
|
56794
56939
|
}
|
|
56795
56940
|
|
|
56796
56941
|
// src/cli/components/task-context-menu.tsx
|
|
@@ -56846,8 +56991,17 @@ var RE_HEADER = /^#{1,3}\s/;
|
|
|
56846
56991
|
var RE_SUBTASK = /^\s*-\s+\[([ xX])\]/;
|
|
56847
56992
|
var RE_DONE = /^\s*-\s+\[x\]/i;
|
|
56848
56993
|
var RE_BRACKET_TAG = /^\[([^\]]+)\]\s*/;
|
|
56849
|
-
function
|
|
56850
|
-
const
|
|
56994
|
+
function columnAccentColor(name) {
|
|
56995
|
+
const normalized = name.toLowerCase();
|
|
56996
|
+
if (/backlog/.test(normalized)) return "magenta";
|
|
56997
|
+
if (/todo/.test(normalized)) return "cyan";
|
|
56998
|
+
if (/progress|doing/.test(normalized)) return "yellow";
|
|
56999
|
+
if (/review/.test(normalized)) return "blue";
|
|
57000
|
+
if (/done|archive|closed|complete/.test(normalized)) return "green";
|
|
57001
|
+
return "cyan";
|
|
57002
|
+
}
|
|
57003
|
+
function TaskRow({ task, focused, dragging, colWidth }) {
|
|
57004
|
+
const cursor = dragging ? "\u2195" : focused ? "\u25B8" : " ";
|
|
56851
57005
|
const check2 = task.checked ? "\u2713" : "\u25CB";
|
|
56852
57006
|
const idStr = task.id;
|
|
56853
57007
|
const tagMatch = task.title.match(RE_BRACKET_TAG);
|
|
@@ -56857,15 +57011,15 @@ function TaskRow({ task, focused, colWidth }) {
|
|
|
56857
57011
|
const tagChars = tag ? tag.length + 1 : 0;
|
|
56858
57012
|
const titleStr = truncate(titleClean, Math.max(4, colWidth - fixedChars - tagChars));
|
|
56859
57013
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { children: [
|
|
56860
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "cyan" : void 0, bold: focused, children: [
|
|
57014
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: dragging ? "yellow" : focused ? "cyan" : void 0, bold: focused || dragging, children: [
|
|
56861
57015
|
cursor,
|
|
56862
57016
|
" "
|
|
56863
57017
|
] }),
|
|
56864
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: task.checked ? "green" : focused ? "white" : "gray", children: [
|
|
57018
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: dragging ? "yellow" : task.checked ? "green" : focused ? "white" : "gray", children: [
|
|
56865
57019
|
check2,
|
|
56866
57020
|
" "
|
|
56867
57021
|
] }),
|
|
56868
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: focused ? "cyan" : "yellow", bold: focused, children: idStr }),
|
|
57022
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: dragging ? "yellow" : focused ? "cyan" : "yellow", bold: focused || dragging, children: idStr }),
|
|
56869
57023
|
tag && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "white" : "magenta", bold: true, children: [
|
|
56870
57024
|
" ",
|
|
56871
57025
|
tag
|
|
@@ -56899,10 +57053,12 @@ function KanbanColumn({
|
|
|
56899
57053
|
contextMenuRow,
|
|
56900
57054
|
contextMenuCursor,
|
|
56901
57055
|
showMoveTarget,
|
|
56902
|
-
isMoveFocused
|
|
57056
|
+
isMoveFocused,
|
|
57057
|
+
draggedTaskId
|
|
56903
57058
|
}) {
|
|
56904
|
-
const
|
|
56905
|
-
const
|
|
57059
|
+
const accent = columnAccentColor(name);
|
|
57060
|
+
const headerBg = isFocused ? accent : void 0;
|
|
57061
|
+
const headerColor = isFocused ? "black" : accent;
|
|
56906
57062
|
const countStr = tasks.length > 0 ? ` (${tasks.length})` : "";
|
|
56907
57063
|
const rows = [];
|
|
56908
57064
|
tasks.forEach((task, idx) => {
|
|
@@ -56912,6 +57068,7 @@ function KanbanColumn({
|
|
|
56912
57068
|
{
|
|
56913
57069
|
task,
|
|
56914
57070
|
focused: isFocused && idx === focusedRow,
|
|
57071
|
+
dragging: task.id === draggedTaskId,
|
|
56915
57072
|
colWidth
|
|
56916
57073
|
},
|
|
56917
57074
|
task.id
|
|
@@ -56940,34 +57097,46 @@ function KanbanColumn({
|
|
|
56940
57097
|
showMoveTarget && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MovePlaceholder, { name, focused: !!isMoveFocused, colWidth })
|
|
56941
57098
|
] });
|
|
56942
57099
|
}
|
|
56943
|
-
function BoardHeader({ title, inTmux, modeHint, version }) {
|
|
57100
|
+
function BoardHeader({ title, inTmux, modeHint, version, daemonStatus, daemonBusy }) {
|
|
56944
57101
|
const tmuxHint = inTmux ? " tmux" : "";
|
|
56945
57102
|
const versionTag = version ? ` v${version}` : "";
|
|
56946
|
-
const
|
|
56947
|
-
|
|
56948
|
-
|
|
57103
|
+
const daemonLabel = daemonBusy ? "\u25CC daemon\u2026" : daemonStatus.running ? `\u25CF web ${daemonStatus.metadata?.port ?? ""}` : "\u25CB web off";
|
|
57104
|
+
const hint = modeHint || "h/l cols \xB7 j/k tasks \xB7 drag tasks \xB7 d daemon \xB7 r reload \xB7 q quit";
|
|
57105
|
+
const width = termWidth();
|
|
57106
|
+
const leftWidth = Math.min(Math.max(34, Math.floor(width * 0.46)), width);
|
|
57107
|
+
const daemonWidth = Math.min(16, Math.max(0, width - leftWidth));
|
|
57108
|
+
const rightWidth = Math.max(0, width - leftWidth - daemonWidth);
|
|
57109
|
+
const left = pad(` \u25C6 KANDOWN${tmuxHint}${versionTag} ${title}`, leftWidth);
|
|
57110
|
+
const daemon = pad(daemonLabel, daemonWidth);
|
|
57111
|
+
const right = truncate(hint, rightWidth).padStart(rightWidth, " ");
|
|
57112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, children: [
|
|
57113
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { bold: true, color: "cyan", children: left }),
|
|
57114
|
+
daemonWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: daemonStatus.running ? "green" : "yellow", bold: true, children: daemon }),
|
|
57115
|
+
rightWidth > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", dimColor: true, children: right })
|
|
57116
|
+
] });
|
|
57117
|
+
}
|
|
57118
|
+
function StatusBar({ message, task, daemonStatus }) {
|
|
57119
|
+
const daemonText = daemonStatus.running && daemonStatus.metadata ? `web daemon ON \xB7 ${daemonStatus.metadata.url}` : "web daemon OFF \xB7 press d to start";
|
|
57120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
|
|
57121
|
+
message ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "yellow", bold: true, children: [
|
|
57122
|
+
" \u2726 ",
|
|
57123
|
+
message
|
|
57124
|
+
] }) : task ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
56949
57125
|
" ",
|
|
56950
|
-
"
|
|
56951
|
-
|
|
56952
|
-
|
|
57126
|
+
"\u25C6 ",
|
|
57127
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", bold: true, children: task.id }),
|
|
57128
|
+
task.progress ? ` checklist ${task.progress.done}/${task.progress.total}` : "",
|
|
56953
57129
|
" ",
|
|
56954
|
-
|
|
56955
|
-
] }),
|
|
56956
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
57130
|
+
task.checked ? "\u2713 done" : "\u25CB open"
|
|
57131
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " " }),
|
|
57132
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: daemonStatus.running ? "green" : "yellow", dimColor: !daemonStatus.running, children: [
|
|
57133
|
+
" ",
|
|
57134
|
+
daemonStatus.running ? "\u25CF" : "\u25CB",
|
|
57135
|
+
" ",
|
|
57136
|
+
daemonText
|
|
57137
|
+
] })
|
|
56957
57138
|
] });
|
|
56958
57139
|
}
|
|
56959
|
-
function StatusBar({ message, task }) {
|
|
56960
|
-
if (message) {
|
|
56961
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: message }) });
|
|
56962
|
-
}
|
|
56963
|
-
if (!task) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " " }) });
|
|
56964
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
56965
|
-
task.id,
|
|
56966
|
-
task.progress ? ` (${task.progress.done}/${task.progress.total})` : "",
|
|
56967
|
-
" ",
|
|
56968
|
-
task.checked ? "\u2713 done" : "\u25CB open"
|
|
56969
|
-
] }) });
|
|
56970
|
-
}
|
|
56971
57140
|
function TaskDetail({ task, taskId, scrollOffset }) {
|
|
56972
57141
|
const fm = task.frontmatter;
|
|
56973
57142
|
const bodyLines = task.body.split("\n");
|
|
@@ -57029,6 +57198,11 @@ function Board({ kandownDir, version }) {
|
|
|
57029
57198
|
const [ctxMenuCursor, setCtxMenuCursor] = (0, import_react37.useState)(0);
|
|
57030
57199
|
const [moveTaskId, setMoveTaskId] = (0, import_react37.useState)(null);
|
|
57031
57200
|
const [moveTargetCol, setMoveTargetCol] = (0, import_react37.useState)(0);
|
|
57201
|
+
const [mousePress, setMousePress] = (0, import_react37.useState)(null);
|
|
57202
|
+
const [taskDrag, setTaskDrag] = (0, import_react37.useState)(null);
|
|
57203
|
+
const [daemonStatus, setDaemonStatus] = (0, import_react37.useState)({ running: false, metadata: null });
|
|
57204
|
+
const [daemonBusy, setDaemonBusy] = (0, import_react37.useState)(false);
|
|
57205
|
+
const [preferredDaemonPort, setPreferredDaemonPort] = (0, import_react37.useState)(null);
|
|
57032
57206
|
const [installedAgents, setInstalledAgents] = (0, import_react37.useState)([]);
|
|
57033
57207
|
const inTmux = isInTmux();
|
|
57034
57208
|
const layoutRef = (0, import_react37.useRef)({
|
|
@@ -57046,6 +57220,25 @@ function Board({ kandownDir, version }) {
|
|
|
57046
57220
|
}
|
|
57047
57221
|
layoutRef.current = { colStarts: starts, colWidth: cw };
|
|
57048
57222
|
}, []);
|
|
57223
|
+
const columnAtX = (0, import_react37.useCallback)((x) => {
|
|
57224
|
+
const layout = layoutRef.current;
|
|
57225
|
+
for (let c = 0; c < layout.colStarts.length; c++) {
|
|
57226
|
+
const start = layout.colStarts[c];
|
|
57227
|
+
if (x >= start && x < start + layout.colWidth) return c;
|
|
57228
|
+
}
|
|
57229
|
+
return -1;
|
|
57230
|
+
}, []);
|
|
57231
|
+
const taskHitAt = (0, import_react37.useCallback)((x, y) => {
|
|
57232
|
+
if (!board) return null;
|
|
57233
|
+
const clickedCol = columnAtX(x);
|
|
57234
|
+
if (clickedCol < 0) return null;
|
|
57235
|
+
const col = board.columns[clickedCol];
|
|
57236
|
+
if (!col) return null;
|
|
57237
|
+
const taskIdx = y - TASKS_START_Y;
|
|
57238
|
+
const task = taskIdx >= 0 ? col.tasks[taskIdx] : void 0;
|
|
57239
|
+
if (!task) return null;
|
|
57240
|
+
return { taskId: task.id, colIndex: clickedCol, rowIndex: taskIdx, startX: x, startY: y };
|
|
57241
|
+
}, [board, columnAtX]);
|
|
57049
57242
|
(0, import_react37.useEffect)(() => {
|
|
57050
57243
|
const loaded = readBoard(kandownDir);
|
|
57051
57244
|
setBoard(loaded);
|
|
@@ -57083,6 +57276,42 @@ function Board({ kandownDir, version }) {
|
|
|
57083
57276
|
setStatusMsg("Board reloaded");
|
|
57084
57277
|
setTimeout(() => setStatusMsg(""), 1500);
|
|
57085
57278
|
}, [kandownDir, updateLayout]);
|
|
57279
|
+
const refreshDaemonStatus = (0, import_react37.useCallback)(async () => {
|
|
57280
|
+
const next = await getDaemonStatus(kandownDir);
|
|
57281
|
+
setDaemonStatus(next);
|
|
57282
|
+
if (next.running && next.metadata) setPreferredDaemonPort(next.metadata.port);
|
|
57283
|
+
}, [kandownDir]);
|
|
57284
|
+
(0, import_react37.useEffect)(() => {
|
|
57285
|
+
void refreshDaemonStatus();
|
|
57286
|
+
const timer = setInterval(() => {
|
|
57287
|
+
void refreshDaemonStatus();
|
|
57288
|
+
}, 2e3);
|
|
57289
|
+
return () => clearInterval(timer);
|
|
57290
|
+
}, [refreshDaemonStatus]);
|
|
57291
|
+
const toggleDaemon = (0, import_react37.useCallback)(async () => {
|
|
57292
|
+
if (daemonBusy) return;
|
|
57293
|
+
setDaemonBusy(true);
|
|
57294
|
+
try {
|
|
57295
|
+
const current = await getDaemonStatus(kandownDir);
|
|
57296
|
+
if (current.running) {
|
|
57297
|
+
if (current.metadata) setPreferredDaemonPort(current.metadata.port);
|
|
57298
|
+
await stopProjectDaemon(kandownDir);
|
|
57299
|
+
const next = await getDaemonStatus(kandownDir);
|
|
57300
|
+
setDaemonStatus(next);
|
|
57301
|
+
setStatusMsg("Web daemon stopped");
|
|
57302
|
+
} else {
|
|
57303
|
+
const next = await startProjectDaemon(kandownDir, preferredDaemonPort);
|
|
57304
|
+
setDaemonStatus(next);
|
|
57305
|
+
if (next.running && next.metadata) setPreferredDaemonPort(next.metadata.port);
|
|
57306
|
+
setStatusMsg(next.running ? "Web daemon started" : "Web daemon failed to start");
|
|
57307
|
+
}
|
|
57308
|
+
} catch (error) {
|
|
57309
|
+
setStatusMsg(`Daemon error: ${error instanceof Error ? error.message : String(error)}`);
|
|
57310
|
+
} finally {
|
|
57311
|
+
setDaemonBusy(false);
|
|
57312
|
+
setTimeout(() => setStatusMsg(""), 2500);
|
|
57313
|
+
}
|
|
57314
|
+
}, [daemonBusy, kandownDir, preferredDaemonPort]);
|
|
57086
57315
|
const getFocusedTask = (0, import_react37.useCallback)(() => {
|
|
57087
57316
|
if (!board) return null;
|
|
57088
57317
|
const col = board.columns[colIndex];
|
|
@@ -57239,12 +57468,87 @@ function Board({ kandownDir, version }) {
|
|
|
57239
57468
|
return;
|
|
57240
57469
|
}
|
|
57241
57470
|
}, [board, mode, colIndex, rowIndex, ctxMenuRow, moveTaskId, kandownDir, updateLayout, openDetail, closeContextMenu]);
|
|
57471
|
+
const handleMouseEvent = (0, import_react37.useCallback)((mouse) => {
|
|
57472
|
+
if (!board) return;
|
|
57473
|
+
if (mode === "browse") {
|
|
57474
|
+
if (mouse.action === "press" && mouse.button === 0) {
|
|
57475
|
+
const hit = taskHitAt(mouse.x, mouse.y);
|
|
57476
|
+
if (hit) {
|
|
57477
|
+
setMousePress(hit);
|
|
57478
|
+
setColIndex(hit.colIndex);
|
|
57479
|
+
setRowIndex(hit.rowIndex);
|
|
57480
|
+
}
|
|
57481
|
+
return;
|
|
57482
|
+
}
|
|
57483
|
+
if (mouse.action === "drag" && mousePress) {
|
|
57484
|
+
const delta = Math.max(Math.abs(mouse.x - mousePress.startX), Math.abs(mouse.y - mousePress.startY));
|
|
57485
|
+
if (delta < 1) return;
|
|
57486
|
+
const hoverCol = columnAtX(mouse.x);
|
|
57487
|
+
setTaskDrag({ taskId: mousePress.taskId, sourceCol: mousePress.colIndex, hoverCol });
|
|
57488
|
+
setMoveTargetCol(hoverCol >= 0 ? hoverCol : mousePress.colIndex);
|
|
57489
|
+
setMoveTaskId(mousePress.taskId);
|
|
57490
|
+
setMode("dragging");
|
|
57491
|
+
closeContextMenu();
|
|
57492
|
+
return;
|
|
57493
|
+
}
|
|
57494
|
+
if (mouse.action === "release" && mousePress) {
|
|
57495
|
+
const hit = taskHitAt(mouse.x, mouse.y);
|
|
57496
|
+
if (hit && hit.taskId === mousePress.taskId) {
|
|
57497
|
+
setCtxMenuRow(mousePress.rowIndex);
|
|
57498
|
+
setCtxMenuCursor(0);
|
|
57499
|
+
setMode("context-menu");
|
|
57500
|
+
}
|
|
57501
|
+
setMousePress(null);
|
|
57502
|
+
return;
|
|
57503
|
+
}
|
|
57504
|
+
if (mouse.action === "press") handleMouseClick(mouse.x, mouse.y);
|
|
57505
|
+
return;
|
|
57506
|
+
}
|
|
57507
|
+
if (mode === "dragging") {
|
|
57508
|
+
if (!taskDrag) {
|
|
57509
|
+
setMode("browse");
|
|
57510
|
+
setMoveTaskId(null);
|
|
57511
|
+
setMousePress(null);
|
|
57512
|
+
return;
|
|
57513
|
+
}
|
|
57514
|
+
if (mouse.action === "drag") {
|
|
57515
|
+
const hoverCol = columnAtX(mouse.x);
|
|
57516
|
+
setTaskDrag((current) => current ? { ...current, hoverCol } : current);
|
|
57517
|
+
setMoveTargetCol(hoverCol >= 0 ? hoverCol : taskDrag.sourceCol);
|
|
57518
|
+
return;
|
|
57519
|
+
}
|
|
57520
|
+
if (mouse.action === "release") {
|
|
57521
|
+
const targetCol = columnAtX(mouse.x);
|
|
57522
|
+
if (targetCol >= 0 && targetCol !== taskDrag.sourceCol) {
|
|
57523
|
+
const targetColName = board.columns[targetCol]?.name;
|
|
57524
|
+
if (targetColName) {
|
|
57525
|
+
moveTaskToColumn(kandownDir, taskDrag.taskId, targetColName);
|
|
57526
|
+
const loaded = readBoard(kandownDir);
|
|
57527
|
+
setBoard(loaded);
|
|
57528
|
+
updateLayout(loaded);
|
|
57529
|
+
setColIndex(targetCol);
|
|
57530
|
+
const movedRow = loaded.columns[targetCol]?.tasks.findIndex((task) => task.id === taskDrag.taskId) ?? 0;
|
|
57531
|
+
setRowIndex(Math.max(0, movedRow));
|
|
57532
|
+
setStatusMsg(`Dragged ${taskDrag.taskId} \u2192 ${targetColName}`);
|
|
57533
|
+
setTimeout(() => setStatusMsg(""), 2e3);
|
|
57534
|
+
}
|
|
57535
|
+
}
|
|
57536
|
+
setTaskDrag(null);
|
|
57537
|
+
setMousePress(null);
|
|
57538
|
+
setMoveTaskId(null);
|
|
57539
|
+
setMode("browse");
|
|
57540
|
+
return;
|
|
57541
|
+
}
|
|
57542
|
+
return;
|
|
57543
|
+
}
|
|
57544
|
+
if (mouse.action === "press" && mouse.button === 0) {
|
|
57545
|
+
handleMouseClick(mouse.x, mouse.y);
|
|
57546
|
+
}
|
|
57547
|
+
}, [board, mode, mousePress, taskDrag, taskHitAt, columnAtX, closeContextMenu, handleMouseClick, kandownDir, updateLayout]);
|
|
57242
57548
|
use_input_default((input, key) => {
|
|
57243
57549
|
if (isMouseInput(input)) {
|
|
57244
57550
|
const mouse = parseMouseInput(input);
|
|
57245
|
-
if (mouse
|
|
57246
|
-
handleMouseClick(mouse.x, mouse.y);
|
|
57247
|
-
}
|
|
57551
|
+
if (mouse) handleMouseEvent(mouse);
|
|
57248
57552
|
return;
|
|
57249
57553
|
}
|
|
57250
57554
|
if (mode === "browse") {
|
|
@@ -57256,6 +57560,10 @@ function Board({ kandownDir, version }) {
|
|
|
57256
57560
|
reloadBoard();
|
|
57257
57561
|
return;
|
|
57258
57562
|
}
|
|
57563
|
+
if (input === "d") {
|
|
57564
|
+
void toggleDaemon();
|
|
57565
|
+
return;
|
|
57566
|
+
}
|
|
57259
57567
|
if (input === "l" || key.rightArrow) {
|
|
57260
57568
|
const maxCol = (board?.columns.length ?? 1) - 1;
|
|
57261
57569
|
setColIndex((c) => Math.min(c + 1, maxCol));
|
|
@@ -57335,6 +57643,16 @@ function Board({ kandownDir, version }) {
|
|
|
57335
57643
|
return;
|
|
57336
57644
|
}
|
|
57337
57645
|
}
|
|
57646
|
+
if (mode === "dragging") {
|
|
57647
|
+
if (key.escape || input === "q") {
|
|
57648
|
+
setTaskDrag(null);
|
|
57649
|
+
setMousePress(null);
|
|
57650
|
+
setMoveTaskId(null);
|
|
57651
|
+
setMode("browse");
|
|
57652
|
+
return;
|
|
57653
|
+
}
|
|
57654
|
+
return;
|
|
57655
|
+
}
|
|
57338
57656
|
if (mode === "move-target") {
|
|
57339
57657
|
if (key.escape || input === "q") {
|
|
57340
57658
|
setMoveTaskId(null);
|
|
@@ -57418,11 +57736,13 @@ function Board({ kandownDir, version }) {
|
|
|
57418
57736
|
modeHint = "j/k choose \xB7 Enter confirm \xB7 Esc cancel \xB7 or click";
|
|
57419
57737
|
} else if (mode === "move-target") {
|
|
57420
57738
|
modeHint = "\u2190/\u2192 pick column \xB7 Enter confirm \xB7 Esc cancel \xB7 or click \u2193";
|
|
57739
|
+
} else if (mode === "dragging") {
|
|
57740
|
+
modeHint = "drag over target column \xB7 release to drop \xB7 Esc cancel";
|
|
57421
57741
|
}
|
|
57422
57742
|
if (mode === "agent-picker") {
|
|
57423
57743
|
const taskId = detailTaskId || focusedTask?.id || "";
|
|
57424
57744
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
57425
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BoardHeader, { title: board.title, inTmux, version }),
|
|
57745
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BoardHeader, { title: board.title, inTmux, version, daemonStatus, daemonBusy }),
|
|
57426
57746
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57427
57747
|
AgentPicker,
|
|
57428
57748
|
{
|
|
@@ -57448,7 +57768,7 @@ function Board({ kandownDir, version }) {
|
|
|
57448
57768
|
] });
|
|
57449
57769
|
}
|
|
57450
57770
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
57451
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BoardHeader, { title: board.title, inTmux, modeHint, version }),
|
|
57771
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BoardHeader, { title: board.title, inTmux, modeHint, version, daemonStatus, daemonBusy }),
|
|
57452
57772
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { flexDirection: "row", children: board.columns.map((col, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57453
57773
|
KanbanColumn,
|
|
57454
57774
|
{
|
|
@@ -57459,36 +57779,57 @@ function Board({ kandownDir, version }) {
|
|
|
57459
57779
|
colWidth,
|
|
57460
57780
|
contextMenuRow: mode === "context-menu" && cIdx === colIndex ? ctxMenuRow : -1,
|
|
57461
57781
|
contextMenuCursor: ctxMenuCursor,
|
|
57462
|
-
showMoveTarget: mode === "move-target" && cIdx !== colIndex,
|
|
57463
|
-
isMoveFocused: mode === "move-target" && cIdx === moveTargetCol
|
|
57782
|
+
showMoveTarget: mode === "move-target" && cIdx !== colIndex || mode === "dragging" && cIdx !== taskDrag?.sourceCol,
|
|
57783
|
+
isMoveFocused: (mode === "move-target" || mode === "dragging") && cIdx === moveTargetCol,
|
|
57784
|
+
draggedTaskId: taskDrag?.taskId ?? null
|
|
57464
57785
|
},
|
|
57465
57786
|
col.name
|
|
57466
57787
|
)) }),
|
|
57467
|
-
mode === "move-target" && moveTaskId && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "yellow", bold: true, children: [
|
|
57788
|
+
(mode === "move-target" || mode === "dragging") && moveTaskId && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "yellow", bold: true, children: [
|
|
57468
57789
|
"Moving ",
|
|
57469
57790
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "cyan", children: moveTaskId }),
|
|
57470
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " \u2014
|
|
57471
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", bold: true, children: "\u2193" }),
|
|
57472
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " or \u2190/\u2192 + Enter" })
|
|
57791
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " \u2014 " }),
|
|
57792
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", bold: true, children: mode === "dragging" ? "release over a column" : "click \u2193" }),
|
|
57793
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: mode === "dragging" ? " \xB7 Esc cancel" : " or \u2190/\u2192 + Enter" })
|
|
57473
57794
|
] }) }),
|
|
57474
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBar, { message: statusMsg, task: focusedTask })
|
|
57795
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBar, { message: statusMsg, task: focusedTask, daemonStatus })
|
|
57475
57796
|
] });
|
|
57476
57797
|
}
|
|
57477
57798
|
|
|
57478
57799
|
// src/cli/app.tsx
|
|
57479
57800
|
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
57801
|
+
function getTerminalRows() {
|
|
57802
|
+
const rows = process.stdout.rows;
|
|
57803
|
+
return typeof rows === "number" && Number.isFinite(rows) && rows > 0 ? rows : 24;
|
|
57804
|
+
}
|
|
57805
|
+
function useTerminalRows() {
|
|
57806
|
+
const [rows, setRows] = (0, import_react38.useState)(getTerminalRows);
|
|
57807
|
+
(0, import_react38.useEffect)(() => {
|
|
57808
|
+
const handleResize = () => setRows(getTerminalRows());
|
|
57809
|
+
process.stdout.on("resize", handleResize);
|
|
57810
|
+
return () => {
|
|
57811
|
+
process.stdout.off("resize", handleResize);
|
|
57812
|
+
};
|
|
57813
|
+
}, []);
|
|
57814
|
+
return rows;
|
|
57815
|
+
}
|
|
57480
57816
|
function App2({ screen, kandownDir, version }) {
|
|
57817
|
+
const rows = useTerminalRows();
|
|
57818
|
+
let content;
|
|
57481
57819
|
switch (screen) {
|
|
57482
57820
|
case "settings":
|
|
57483
|
-
|
|
57821
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Settings, { kandownDir, version });
|
|
57822
|
+
break;
|
|
57484
57823
|
case "board":
|
|
57485
|
-
|
|
57824
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Board, { kandownDir, version });
|
|
57825
|
+
break;
|
|
57486
57826
|
default:
|
|
57487
|
-
|
|
57827
|
+
content = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "red", bold: true, children: [
|
|
57488
57828
|
"Unknown screen: ",
|
|
57489
57829
|
screen
|
|
57490
57830
|
] }) });
|
|
57491
57831
|
}
|
|
57832
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { flexDirection: "column", height: rows, overflow: "hidden", children: content });
|
|
57492
57833
|
}
|
|
57493
57834
|
|
|
57494
57835
|
// src/cli/tui.tsx
|
|
@@ -57499,15 +57840,13 @@ async function run(screen, kandownDir, version) {
|
|
|
57499
57840
|
"kandown TUI requires an interactive terminal. Run this command directly in your terminal."
|
|
57500
57841
|
);
|
|
57501
57842
|
}
|
|
57502
|
-
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
57503
57843
|
const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(App2, { screen, kandownDir, version }), {
|
|
57504
|
-
exitOnCtrlC: true
|
|
57844
|
+
exitOnCtrlC: true,
|
|
57845
|
+
interactive: true,
|
|
57846
|
+
alternateScreen: true,
|
|
57847
|
+
maxFps: 30
|
|
57505
57848
|
});
|
|
57506
|
-
|
|
57507
|
-
await instance.waitUntilExit();
|
|
57508
|
-
} finally {
|
|
57509
|
-
process.stdout.write("\x1B[?1049l");
|
|
57510
|
-
}
|
|
57849
|
+
await instance.waitUntilExit();
|
|
57511
57850
|
}
|
|
57512
57851
|
export {
|
|
57513
57852
|
run
|