kandown 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tui.js +457 -114
- package/dist/index.html +1 -1
- 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 useEffect10(create3) {
|
|
27899
27899
|
nextHook();
|
|
27900
27900
|
hookLog.push({
|
|
27901
27901
|
displayName: null,
|
|
@@ -54326,7 +54326,7 @@ function ValueDisplay({ setting, value, focused }) {
|
|
|
54326
54326
|
}
|
|
54327
54327
|
|
|
54328
54328
|
// src/cli/screens/board.tsx
|
|
54329
|
-
var
|
|
54329
|
+
var import_react37 = __toESM(require_react(), 1);
|
|
54330
54330
|
|
|
54331
54331
|
// src/cli/lib/board-reader.ts
|
|
54332
54332
|
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -56755,8 +56755,78 @@ function AgentPicker({ agents, taskId, onSelect, onCancel }) {
|
|
|
56755
56755
|
);
|
|
56756
56756
|
}
|
|
56757
56757
|
|
|
56758
|
-
// src/cli/
|
|
56758
|
+
// src/cli/hooks/use-mouse.ts
|
|
56759
|
+
var import_react36 = __toESM(require_react(), 1);
|
|
56760
|
+
var MOUSE_ENABLE = "\x1B[?1000h\x1B[?1006h";
|
|
56761
|
+
var MOUSE_DISABLE = "\x1B[?1006l\x1B[?1000l";
|
|
56762
|
+
var RE_SGR_MOUSE = /^\[<(\d+);(\d+);(\d+)([Mm])/;
|
|
56763
|
+
function parseMouseInput(input) {
|
|
56764
|
+
const match = input.match(RE_SGR_MOUSE);
|
|
56765
|
+
if (!match) return null;
|
|
56766
|
+
const cb = parseInt(match[1], 10);
|
|
56767
|
+
const cx = parseInt(match[2], 10);
|
|
56768
|
+
const cy = parseInt(match[3], 10);
|
|
56769
|
+
const isPress = match[4] === "M";
|
|
56770
|
+
return {
|
|
56771
|
+
x: cx,
|
|
56772
|
+
y: cy,
|
|
56773
|
+
button: cb & 3,
|
|
56774
|
+
action: isPress ? "press" : "release"
|
|
56775
|
+
};
|
|
56776
|
+
}
|
|
56777
|
+
function isMouseInput(input) {
|
|
56778
|
+
return input.startsWith("[<") || input.startsWith("[M");
|
|
56779
|
+
}
|
|
56780
|
+
function useMouseMode(enabled = true) {
|
|
56781
|
+
(0, import_react36.useEffect)(() => {
|
|
56782
|
+
if (!enabled) return;
|
|
56783
|
+
if (!process.stdin.isTTY) return;
|
|
56784
|
+
process.stdout.write(MOUSE_ENABLE);
|
|
56785
|
+
const cleanup = () => {
|
|
56786
|
+
process.stdout.write(MOUSE_DISABLE);
|
|
56787
|
+
};
|
|
56788
|
+
process.on("exit", cleanup);
|
|
56789
|
+
return () => {
|
|
56790
|
+
process.removeListener("exit", cleanup);
|
|
56791
|
+
process.stdout.write(MOUSE_DISABLE);
|
|
56792
|
+
};
|
|
56793
|
+
}, [enabled]);
|
|
56794
|
+
}
|
|
56795
|
+
|
|
56796
|
+
// src/cli/components/task-context-menu.tsx
|
|
56759
56797
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
56798
|
+
function InlineContextMenu({ cursor, colWidth }) {
|
|
56799
|
+
const options = [
|
|
56800
|
+
{ label: "Open task", icon: "\u{1F4D6}" },
|
|
56801
|
+
{ label: "Move task", icon: "\u2197" }
|
|
56802
|
+
];
|
|
56803
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { flexDirection: "column", children: options.map((opt, idx) => {
|
|
56804
|
+
const focused = idx === cursor;
|
|
56805
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
56806
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: " " }),
|
|
56807
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
56808
|
+
Text,
|
|
56809
|
+
{
|
|
56810
|
+
color: focused ? "black" : "gray",
|
|
56811
|
+
backgroundColor: focused ? "cyan" : void 0,
|
|
56812
|
+
bold: focused,
|
|
56813
|
+
children: [
|
|
56814
|
+
focused ? "\u25B8" : " ",
|
|
56815
|
+
" ",
|
|
56816
|
+
opt.icon,
|
|
56817
|
+
" ",
|
|
56818
|
+
opt.label
|
|
56819
|
+
]
|
|
56820
|
+
}
|
|
56821
|
+
)
|
|
56822
|
+
] }, opt.label);
|
|
56823
|
+
}) });
|
|
56824
|
+
}
|
|
56825
|
+
var MENU_HEIGHT = 2;
|
|
56826
|
+
|
|
56827
|
+
// src/cli/screens/board.tsx
|
|
56828
|
+
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
56829
|
+
var TASKS_START_Y = 5;
|
|
56760
56830
|
function truncate(str, maxLen) {
|
|
56761
56831
|
if (str.length <= maxLen) return str;
|
|
56762
56832
|
return str.slice(0, maxLen - 1) + "\u2026";
|
|
@@ -56774,136 +56844,166 @@ function calcColWidth(numCols) {
|
|
|
56774
56844
|
}
|
|
56775
56845
|
var RE_HEADER = /^#{1,3}\s/;
|
|
56776
56846
|
var RE_SUBTASK = /^\s*-\s+\[([ xX])\]/;
|
|
56777
|
-
var
|
|
56847
|
+
var RE_DONE = /^\s*-\s+\[x\]/i;
|
|
56778
56848
|
var RE_BRACKET_TAG = /^\[([^\]]+)\]\s*/;
|
|
56779
|
-
function TaskRow({
|
|
56780
|
-
task,
|
|
56781
|
-
focused,
|
|
56782
|
-
colWidth
|
|
56783
|
-
}) {
|
|
56849
|
+
function TaskRow({ task, focused, colWidth }) {
|
|
56784
56850
|
const cursor = focused ? "\u25B8" : " ";
|
|
56785
56851
|
const check2 = task.checked ? "\u2713" : "\u25CB";
|
|
56786
56852
|
const idStr = task.id;
|
|
56787
56853
|
const tagMatch = task.title.match(RE_BRACKET_TAG);
|
|
56788
56854
|
const tag = tagMatch ? `[${tagMatch[1]}]` : "";
|
|
56789
|
-
const
|
|
56855
|
+
const titleClean = tagMatch ? task.title.slice(tagMatch[0].length) : task.title;
|
|
56790
56856
|
const fixedChars = 4 + idStr.length + 1;
|
|
56791
56857
|
const tagChars = tag ? tag.length + 1 : 0;
|
|
56792
|
-
const
|
|
56793
|
-
|
|
56794
|
-
|
|
56795
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: focused ? "cyan" : void 0, bold: focused, children: [
|
|
56858
|
+
const titleStr = truncate(titleClean, Math.max(4, colWidth - fixedChars - tagChars));
|
|
56859
|
+
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: [
|
|
56796
56861
|
cursor,
|
|
56797
56862
|
" "
|
|
56798
56863
|
] }),
|
|
56799
|
-
/* @__PURE__ */ (0,
|
|
56864
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: task.checked ? "green" : focused ? "white" : "gray", children: [
|
|
56800
56865
|
check2,
|
|
56801
56866
|
" "
|
|
56802
56867
|
] }),
|
|
56803
|
-
/* @__PURE__ */ (0,
|
|
56804
|
-
tag && /* @__PURE__ */ (0,
|
|
56868
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: focused ? "cyan" : "yellow", bold: focused, children: idStr }),
|
|
56869
|
+
tag && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "white" : "magenta", bold: true, children: [
|
|
56805
56870
|
" ",
|
|
56806
56871
|
tag
|
|
56807
56872
|
] }),
|
|
56808
|
-
/* @__PURE__ */ (0,
|
|
56873
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "white" : "gray", children: [
|
|
56809
56874
|
" ",
|
|
56810
56875
|
titleStr
|
|
56811
56876
|
] })
|
|
56812
56877
|
] });
|
|
56813
56878
|
}
|
|
56879
|
+
function MovePlaceholder({ name, focused, colWidth }) {
|
|
56880
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
56881
|
+
Text,
|
|
56882
|
+
{
|
|
56883
|
+
color: focused ? "black" : "yellow",
|
|
56884
|
+
backgroundColor: focused ? "yellow" : void 0,
|
|
56885
|
+
bold: focused,
|
|
56886
|
+
children: [
|
|
56887
|
+
" ",
|
|
56888
|
+
pad(`\u2193 ${name}`, colWidth - 2)
|
|
56889
|
+
]
|
|
56890
|
+
}
|
|
56891
|
+
) });
|
|
56892
|
+
}
|
|
56814
56893
|
function KanbanColumn({
|
|
56815
56894
|
name,
|
|
56816
56895
|
tasks,
|
|
56817
56896
|
focusedRow,
|
|
56818
56897
|
isFocused,
|
|
56819
|
-
colWidth
|
|
56898
|
+
colWidth,
|
|
56899
|
+
contextMenuRow,
|
|
56900
|
+
contextMenuCursor,
|
|
56901
|
+
showMoveTarget,
|
|
56902
|
+
isMoveFocused
|
|
56820
56903
|
}) {
|
|
56821
56904
|
const headerBg = isFocused ? "cyan" : void 0;
|
|
56822
56905
|
const headerColor = isFocused ? "black" : "cyan";
|
|
56823
56906
|
const countStr = tasks.length > 0 ? ` (${tasks.length})` : "";
|
|
56824
|
-
const
|
|
56825
|
-
|
|
56826
|
-
|
|
56827
|
-
|
|
56828
|
-
|
|
56907
|
+
const rows = [];
|
|
56908
|
+
tasks.forEach((task, idx) => {
|
|
56909
|
+
rows.push(
|
|
56910
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
56911
|
+
TaskRow,
|
|
56912
|
+
{
|
|
56913
|
+
task,
|
|
56914
|
+
focused: isFocused && idx === focusedRow,
|
|
56915
|
+
colWidth
|
|
56916
|
+
},
|
|
56917
|
+
task.id
|
|
56918
|
+
)
|
|
56919
|
+
);
|
|
56920
|
+
if (contextMenuRow === idx) {
|
|
56921
|
+
rows.push(
|
|
56922
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
56923
|
+
InlineContextMenu,
|
|
56924
|
+
{
|
|
56925
|
+
cursor: contextMenuCursor ?? 0,
|
|
56926
|
+
colWidth
|
|
56927
|
+
},
|
|
56928
|
+
"ctx-menu"
|
|
56929
|
+
)
|
|
56930
|
+
);
|
|
56931
|
+
}
|
|
56932
|
+
});
|
|
56933
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", width: colWidth, marginRight: 1, children: [
|
|
56934
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { backgroundColor: headerBg, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: headerColor, bold: true, children: pad(`${name}${countStr}`, colWidth) }) }),
|
|
56935
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: isFocused ? "cyan" : "gray", children: "\u2500".repeat(colWidth) }),
|
|
56936
|
+
tasks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
56829
56937
|
" ".repeat(2),
|
|
56830
56938
|
"(empty)"
|
|
56831
|
-
] }) :
|
|
56832
|
-
|
|
56833
|
-
{
|
|
56834
|
-
task,
|
|
56835
|
-
focused: isFocused && idx === focusedRow,
|
|
56836
|
-
colWidth
|
|
56837
|
-
},
|
|
56838
|
-
task.id
|
|
56839
|
-
))
|
|
56939
|
+
] }) : rows,
|
|
56940
|
+
showMoveTarget && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MovePlaceholder, { name, focused: !!isMoveFocused, colWidth })
|
|
56840
56941
|
] });
|
|
56841
56942
|
}
|
|
56842
|
-
function BoardHeader({ title, inTmux }) {
|
|
56943
|
+
function BoardHeader({ title, inTmux, modeHint, version }) {
|
|
56843
56944
|
const tmuxHint = inTmux ? " tmux" : "";
|
|
56844
|
-
|
|
56845
|
-
|
|
56945
|
+
const versionTag = version ? ` v${version}` : "";
|
|
56946
|
+
const hint = modeHint || "h/l cols \xB7 j/k tasks \xB7 Enter detail \xB7 m menu \xB7 a agent \xB7 r reload \xB7 q quit";
|
|
56947
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
56948
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { bold: true, color: "cyan", children: [
|
|
56846
56949
|
" ",
|
|
56847
56950
|
"KANDOWN",
|
|
56848
56951
|
tmuxHint,
|
|
56952
|
+
versionTag,
|
|
56849
56953
|
" ",
|
|
56850
56954
|
title
|
|
56851
56955
|
] }),
|
|
56852
|
-
/* @__PURE__ */ (0,
|
|
56956
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", dimColor: true, children: hint })
|
|
56853
56957
|
] });
|
|
56854
56958
|
}
|
|
56855
56959
|
function StatusBar({ message, task }) {
|
|
56856
56960
|
if (message) {
|
|
56857
|
-
return /* @__PURE__ */ (0,
|
|
56961
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: message }) });
|
|
56858
56962
|
}
|
|
56859
|
-
if (!task) return /* @__PURE__ */ (0,
|
|
56860
|
-
return /* @__PURE__ */ (0,
|
|
56861
|
-
task.id
|
|
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,
|
|
56862
56966
|
task.progress ? ` (${task.progress.done}/${task.progress.total})` : "",
|
|
56863
56967
|
" ",
|
|
56864
56968
|
task.checked ? "\u2713 done" : "\u25CB open"
|
|
56865
56969
|
] }) });
|
|
56866
56970
|
}
|
|
56867
|
-
function TaskDetail({
|
|
56868
|
-
task,
|
|
56869
|
-
taskId,
|
|
56870
|
-
scrollOffset
|
|
56871
|
-
}) {
|
|
56971
|
+
function TaskDetail({ task, taskId, scrollOffset }) {
|
|
56872
56972
|
const fm = task.frontmatter;
|
|
56873
56973
|
const bodyLines = task.body.split("\n");
|
|
56874
56974
|
const maxVisible = (process.stdout.rows || 24) - 10;
|
|
56875
56975
|
const visibleLines = bodyLines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
56876
|
-
return /* @__PURE__ */ (0,
|
|
56877
|
-
/* @__PURE__ */ (0,
|
|
56878
|
-
/* @__PURE__ */ (0,
|
|
56879
|
-
/* @__PURE__ */ (0,
|
|
56976
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", paddingX: 2, children: [
|
|
56977
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, children: [
|
|
56978
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { bold: true, color: "cyan", children: taskId }),
|
|
56979
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "white", bold: true, children: [
|
|
56880
56980
|
" ",
|
|
56881
56981
|
fm.title
|
|
56882
56982
|
] })
|
|
56883
56983
|
] }),
|
|
56884
|
-
/* @__PURE__ */ (0,
|
|
56984
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
56885
56985
|
"status: ",
|
|
56886
|
-
/* @__PURE__ */ (0,
|
|
56986
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: fm.status ?? "\u2014" }),
|
|
56887
56987
|
fm.priority ? ` priority: ${fm.priority}` : "",
|
|
56888
56988
|
fm.assignee ? ` assignee: ${fm.assignee}` : "",
|
|
56889
56989
|
fm.due ? ` due: ${fm.due}` : ""
|
|
56890
56990
|
] }) }),
|
|
56891
|
-
/* @__PURE__ */ (0,
|
|
56991
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "\u2500".repeat(termWidth() - 4) }),
|
|
56892
56992
|
visibleLines.map((line, idx) => {
|
|
56893
|
-
const
|
|
56894
|
-
const
|
|
56895
|
-
const
|
|
56896
|
-
return /* @__PURE__ */ (0,
|
|
56993
|
+
const isH = RE_HEADER.test(line);
|
|
56994
|
+
const isS = RE_SUBTASK.test(line);
|
|
56995
|
+
const isD = RE_DONE.test(line);
|
|
56996
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
56897
56997
|
Text,
|
|
56898
56998
|
{
|
|
56899
|
-
color:
|
|
56900
|
-
bold:
|
|
56999
|
+
color: isH ? "cyan" : isD ? "green" : isS ? "white" : "gray",
|
|
57000
|
+
bold: isH,
|
|
56901
57001
|
children: line || " "
|
|
56902
57002
|
},
|
|
56903
57003
|
scrollOffset + idx
|
|
56904
57004
|
);
|
|
56905
57005
|
}),
|
|
56906
|
-
bodyLines.length > maxVisible && /* @__PURE__ */ (0,
|
|
57006
|
+
bodyLines.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
56907
57007
|
" ",
|
|
56908
57008
|
"\u2191\u2193 scroll (",
|
|
56909
57009
|
scrollOffset + 1,
|
|
@@ -56915,66 +57015,92 @@ function TaskDetail({
|
|
|
56915
57015
|
] })
|
|
56916
57016
|
] });
|
|
56917
57017
|
}
|
|
56918
|
-
function Board({ kandownDir }) {
|
|
57018
|
+
function Board({ kandownDir, version }) {
|
|
56919
57019
|
const { exit } = use_app_default();
|
|
56920
|
-
const [board, setBoard] = (0,
|
|
56921
|
-
const [colIndex, setColIndex] = (0,
|
|
56922
|
-
const [rowIndex, setRowIndex] = (0,
|
|
56923
|
-
const [mode, setMode] = (0,
|
|
56924
|
-
const [
|
|
56925
|
-
const [
|
|
56926
|
-
const [
|
|
56927
|
-
const [
|
|
56928
|
-
const [
|
|
57020
|
+
const [board, setBoard] = (0, import_react37.useState)(null);
|
|
57021
|
+
const [colIndex, setColIndex] = (0, import_react37.useState)(0);
|
|
57022
|
+
const [rowIndex, setRowIndex] = (0, import_react37.useState)(0);
|
|
57023
|
+
const [mode, setMode] = (0, import_react37.useState)("browse");
|
|
57024
|
+
const [statusMsg, setStatusMsg] = (0, import_react37.useState)("");
|
|
57025
|
+
const [detailTask, setDetailTask] = (0, import_react37.useState)(null);
|
|
57026
|
+
const [detailTaskId, setDetailTaskId] = (0, import_react37.useState)("");
|
|
57027
|
+
const [detailScroll, setDetailScroll] = (0, import_react37.useState)(0);
|
|
57028
|
+
const [ctxMenuRow, setCtxMenuRow] = (0, import_react37.useState)(-1);
|
|
57029
|
+
const [ctxMenuCursor, setCtxMenuCursor] = (0, import_react37.useState)(0);
|
|
57030
|
+
const [moveTaskId, setMoveTaskId] = (0, import_react37.useState)(null);
|
|
57031
|
+
const [moveTargetCol, setMoveTargetCol] = (0, import_react37.useState)(0);
|
|
57032
|
+
const [installedAgents, setInstalledAgents] = (0, import_react37.useState)([]);
|
|
56929
57033
|
const inTmux = isInTmux();
|
|
56930
|
-
(0,
|
|
57034
|
+
const layoutRef = (0, import_react37.useRef)({
|
|
57035
|
+
colStarts: [],
|
|
57036
|
+
colWidth: 0
|
|
57037
|
+
});
|
|
57038
|
+
const updateLayout = (0, import_react37.useCallback)((b) => {
|
|
57039
|
+
if (!b) return;
|
|
57040
|
+
const cw = calcColWidth(b.columns.length);
|
|
57041
|
+
const starts = [];
|
|
57042
|
+
let x = 1;
|
|
57043
|
+
for (let i = 0; i < b.columns.length; i++) {
|
|
57044
|
+
starts.push(x);
|
|
57045
|
+
x += cw + 1;
|
|
57046
|
+
}
|
|
57047
|
+
layoutRef.current = { colStarts: starts, colWidth: cw };
|
|
57048
|
+
}, []);
|
|
57049
|
+
(0, import_react37.useEffect)(() => {
|
|
56931
57050
|
const loaded = readBoard(kandownDir);
|
|
56932
57051
|
setBoard(loaded);
|
|
57052
|
+
updateLayout(loaded);
|
|
56933
57053
|
setInstalledAgents(detectInstalledAgents());
|
|
56934
|
-
}, [kandownDir]);
|
|
56935
|
-
|
|
56936
|
-
const rowIndexRef = (0, import_react36.useRef)(0);
|
|
56937
|
-
(0, import_react36.useEffect)(() => {
|
|
57054
|
+
}, [kandownDir, updateLayout]);
|
|
57055
|
+
(0, import_react37.useEffect)(() => {
|
|
56938
57056
|
const watcher = createWatcher();
|
|
56939
57057
|
watcher.on("taskChanged", () => {
|
|
56940
57058
|
const loaded = readBoard(kandownDir);
|
|
56941
57059
|
setBoard(loaded);
|
|
57060
|
+
updateLayout(loaded);
|
|
56942
57061
|
});
|
|
56943
57062
|
watcher.on("newTaskDetected", (taskId) => {
|
|
56944
57063
|
const loaded = readBoard(kandownDir);
|
|
56945
57064
|
setBoard(loaded);
|
|
57065
|
+
updateLayout(loaded);
|
|
56946
57066
|
setStatusMsg(`New task: ${taskId}`);
|
|
56947
57067
|
setTimeout(() => setStatusMsg(""), 2e3);
|
|
56948
57068
|
});
|
|
56949
57069
|
watcher.on("configChanged", () => {
|
|
56950
57070
|
const loaded = readBoard(kandownDir);
|
|
56951
57071
|
setBoard(loaded);
|
|
57072
|
+
updateLayout(loaded);
|
|
56952
57073
|
});
|
|
56953
57074
|
watcher.start(kandownDir);
|
|
56954
57075
|
return () => {
|
|
56955
57076
|
watcher.stop();
|
|
56956
57077
|
};
|
|
56957
|
-
}, [kandownDir]);
|
|
56958
|
-
const reloadBoard = (0,
|
|
57078
|
+
}, [kandownDir, updateLayout]);
|
|
57079
|
+
const reloadBoard = (0, import_react37.useCallback)(() => {
|
|
56959
57080
|
const loaded = readBoard(kandownDir);
|
|
56960
57081
|
setBoard(loaded);
|
|
57082
|
+
updateLayout(loaded);
|
|
56961
57083
|
setStatusMsg("Board reloaded");
|
|
56962
57084
|
setTimeout(() => setStatusMsg(""), 1500);
|
|
56963
|
-
}, [kandownDir]);
|
|
56964
|
-
const getFocusedTask = (0,
|
|
57085
|
+
}, [kandownDir, updateLayout]);
|
|
57086
|
+
const getFocusedTask = (0, import_react37.useCallback)(() => {
|
|
56965
57087
|
if (!board) return null;
|
|
56966
57088
|
const col = board.columns[colIndex];
|
|
56967
57089
|
if (!col || col.tasks.length === 0) return null;
|
|
56968
57090
|
return col.tasks[Math.min(rowIndex, col.tasks.length - 1)] ?? null;
|
|
56969
57091
|
}, [board, colIndex, rowIndex]);
|
|
56970
|
-
const openDetail = (0,
|
|
57092
|
+
const openDetail = (0, import_react37.useCallback)((taskId) => {
|
|
56971
57093
|
const task = readTask(kandownDir, taskId);
|
|
56972
57094
|
setDetailTask(task);
|
|
56973
57095
|
setDetailTaskId(taskId);
|
|
56974
57096
|
setDetailScroll(0);
|
|
56975
57097
|
setMode("detail");
|
|
56976
57098
|
}, [kandownDir]);
|
|
56977
|
-
const
|
|
57099
|
+
const closeContextMenu = (0, import_react37.useCallback)(() => {
|
|
57100
|
+
setCtxMenuRow(-1);
|
|
57101
|
+
setCtxMenuCursor(0);
|
|
57102
|
+
}, []);
|
|
57103
|
+
const handleAgentSelect = (0, import_react37.useCallback)((agentId) => {
|
|
56978
57104
|
const task = getFocusedTask();
|
|
56979
57105
|
const taskId = mode === "detail" ? detailTaskId : task?.id;
|
|
56980
57106
|
if (!taskId) return;
|
|
@@ -56982,12 +57108,7 @@ function Board({ kandownDir }) {
|
|
|
56982
57108
|
setStatusMsg(`Launching ${agentId} for ${taskId}\u2026`);
|
|
56983
57109
|
setTimeout(() => {
|
|
56984
57110
|
try {
|
|
56985
|
-
launchAgent({
|
|
56986
|
-
taskId,
|
|
56987
|
-
agentId,
|
|
56988
|
-
kandownDir,
|
|
56989
|
-
onBeforeExec: () => exit()
|
|
56990
|
-
});
|
|
57111
|
+
launchAgent({ taskId, agentId, kandownDir, onBeforeExec: () => exit() });
|
|
56991
57112
|
reloadBoard();
|
|
56992
57113
|
setStatusMsg(`${agentId} launched in tmux pane`);
|
|
56993
57114
|
setTimeout(() => setStatusMsg(""), 3e3);
|
|
@@ -56997,7 +57118,135 @@ function Board({ kandownDir }) {
|
|
|
56997
57118
|
}
|
|
56998
57119
|
}, 50);
|
|
56999
57120
|
}, [mode, detailTaskId, getFocusedTask, kandownDir, exit, reloadBoard]);
|
|
57121
|
+
useMouseMode(mode !== "agent-picker");
|
|
57122
|
+
const handleMouseClick = (0, import_react37.useCallback)((x, y) => {
|
|
57123
|
+
if (!board) return;
|
|
57124
|
+
const layout = layoutRef.current;
|
|
57125
|
+
let clickedCol = -1;
|
|
57126
|
+
for (let c = 0; c < layout.colStarts.length; c++) {
|
|
57127
|
+
const start = layout.colStarts[c];
|
|
57128
|
+
if (x >= start && x < start + layout.colWidth) {
|
|
57129
|
+
clickedCol = c;
|
|
57130
|
+
break;
|
|
57131
|
+
}
|
|
57132
|
+
}
|
|
57133
|
+
if (mode === "browse") {
|
|
57134
|
+
if (clickedCol < 0) return;
|
|
57135
|
+
const col = board.columns[clickedCol];
|
|
57136
|
+
const taskIdx = y - TASKS_START_Y;
|
|
57137
|
+
if (taskIdx >= 0 && taskIdx < col.tasks.length) {
|
|
57138
|
+
setColIndex(clickedCol);
|
|
57139
|
+
setRowIndex(taskIdx);
|
|
57140
|
+
setCtxMenuRow(taskIdx);
|
|
57141
|
+
setCtxMenuCursor(0);
|
|
57142
|
+
setMode("context-menu");
|
|
57143
|
+
}
|
|
57144
|
+
return;
|
|
57145
|
+
}
|
|
57146
|
+
if (mode === "context-menu") {
|
|
57147
|
+
if (clickedCol < 0) {
|
|
57148
|
+
closeContextMenu();
|
|
57149
|
+
setMode("browse");
|
|
57150
|
+
return;
|
|
57151
|
+
}
|
|
57152
|
+
const col = board.columns[clickedCol];
|
|
57153
|
+
const hasMenu = clickedCol === colIndex && ctxMenuRow >= 0;
|
|
57154
|
+
if (hasMenu) {
|
|
57155
|
+
const taskIdx = y - TASKS_START_Y;
|
|
57156
|
+
if (taskIdx >= 0 && taskIdx < ctxMenuRow) {
|
|
57157
|
+
setRowIndex(taskIdx);
|
|
57158
|
+
setCtxMenuRow(taskIdx);
|
|
57159
|
+
setCtxMenuCursor(0);
|
|
57160
|
+
return;
|
|
57161
|
+
}
|
|
57162
|
+
if (taskIdx === ctxMenuRow) {
|
|
57163
|
+
closeContextMenu();
|
|
57164
|
+
setMode("browse");
|
|
57165
|
+
return;
|
|
57166
|
+
}
|
|
57167
|
+
const menuOffset = taskIdx - ctxMenuRow - 1;
|
|
57168
|
+
if (menuOffset >= 0 && menuOffset < MENU_HEIGHT) {
|
|
57169
|
+
if (menuOffset === 0) {
|
|
57170
|
+
const task = col.tasks[ctxMenuRow];
|
|
57171
|
+
if (task) {
|
|
57172
|
+
closeContextMenu();
|
|
57173
|
+
openDetail(task.id);
|
|
57174
|
+
}
|
|
57175
|
+
} else {
|
|
57176
|
+
const task = col.tasks[ctxMenuRow];
|
|
57177
|
+
if (task) {
|
|
57178
|
+
setMoveTaskId(task.id);
|
|
57179
|
+
const target = colIndex === 0 ? Math.min(1, board.columns.length - 1) : 0;
|
|
57180
|
+
setMoveTargetCol(target);
|
|
57181
|
+
closeContextMenu();
|
|
57182
|
+
setMode("move-target");
|
|
57183
|
+
}
|
|
57184
|
+
}
|
|
57185
|
+
return;
|
|
57186
|
+
}
|
|
57187
|
+
const belowIdx = taskIdx - MENU_HEIGHT;
|
|
57188
|
+
if (belowIdx >= 0 && belowIdx < col.tasks.length) {
|
|
57189
|
+
setRowIndex(belowIdx);
|
|
57190
|
+
closeContextMenu();
|
|
57191
|
+
setCtxMenuRow(belowIdx);
|
|
57192
|
+
setCtxMenuCursor(0);
|
|
57193
|
+
return;
|
|
57194
|
+
}
|
|
57195
|
+
} else {
|
|
57196
|
+
const taskIdx = y - TASKS_START_Y;
|
|
57197
|
+
if (taskIdx >= 0 && taskIdx < col.tasks.length) {
|
|
57198
|
+
closeContextMenu();
|
|
57199
|
+
setColIndex(clickedCol);
|
|
57200
|
+
setRowIndex(taskIdx);
|
|
57201
|
+
setCtxMenuRow(taskIdx);
|
|
57202
|
+
setCtxMenuCursor(0);
|
|
57203
|
+
return;
|
|
57204
|
+
}
|
|
57205
|
+
}
|
|
57206
|
+
closeContextMenu();
|
|
57207
|
+
setMode("browse");
|
|
57208
|
+
return;
|
|
57209
|
+
}
|
|
57210
|
+
if (mode === "move-target") {
|
|
57211
|
+
if (clickedCol < 0) {
|
|
57212
|
+
setMoveTaskId(null);
|
|
57213
|
+
setMode("browse");
|
|
57214
|
+
return;
|
|
57215
|
+
}
|
|
57216
|
+
if (clickedCol === colIndex) {
|
|
57217
|
+
setMoveTaskId(null);
|
|
57218
|
+
setMode("browse");
|
|
57219
|
+
return;
|
|
57220
|
+
}
|
|
57221
|
+
const col = board.columns[clickedCol];
|
|
57222
|
+
const placeholderY = TASKS_START_Y + col.tasks.length;
|
|
57223
|
+
if (y === placeholderY) {
|
|
57224
|
+
const targetColName = col.name;
|
|
57225
|
+
if (moveTaskId) {
|
|
57226
|
+
moveTaskToColumn(kandownDir, moveTaskId, targetColName);
|
|
57227
|
+
const loaded = readBoard(kandownDir);
|
|
57228
|
+
setBoard(loaded);
|
|
57229
|
+
updateLayout(loaded);
|
|
57230
|
+
setStatusMsg(`Moved ${moveTaskId} \u2192 ${targetColName}`);
|
|
57231
|
+
setTimeout(() => setStatusMsg(""), 2e3);
|
|
57232
|
+
}
|
|
57233
|
+
setMoveTaskId(null);
|
|
57234
|
+
setMode("browse");
|
|
57235
|
+
return;
|
|
57236
|
+
}
|
|
57237
|
+
setMoveTaskId(null);
|
|
57238
|
+
setMode("browse");
|
|
57239
|
+
return;
|
|
57240
|
+
}
|
|
57241
|
+
}, [board, mode, colIndex, rowIndex, ctxMenuRow, moveTaskId, kandownDir, updateLayout, openDetail, closeContextMenu]);
|
|
57000
57242
|
use_input_default((input, key) => {
|
|
57243
|
+
if (isMouseInput(input)) {
|
|
57244
|
+
const mouse = parseMouseInput(input);
|
|
57245
|
+
if (mouse && mouse.action === "press" && mouse.button === 0) {
|
|
57246
|
+
handleMouseClick(mouse.x, mouse.y);
|
|
57247
|
+
}
|
|
57248
|
+
return;
|
|
57249
|
+
}
|
|
57001
57250
|
if (mode === "browse") {
|
|
57002
57251
|
if (input === "q" || key.escape) {
|
|
57003
57252
|
exit();
|
|
@@ -57033,9 +57282,18 @@ function Board({ kandownDir }) {
|
|
|
57033
57282
|
if (task) openDetail(task.id);
|
|
57034
57283
|
return;
|
|
57035
57284
|
}
|
|
57285
|
+
if (input === "m") {
|
|
57286
|
+
const col = board?.columns[colIndex];
|
|
57287
|
+
if (col && col.tasks.length > 0) {
|
|
57288
|
+
setCtxMenuRow(rowIndex);
|
|
57289
|
+
setCtxMenuCursor(0);
|
|
57290
|
+
setMode("context-menu");
|
|
57291
|
+
}
|
|
57292
|
+
return;
|
|
57293
|
+
}
|
|
57036
57294
|
if (input === "a") {
|
|
57037
57295
|
if (installedAgents.length === 0) {
|
|
57038
|
-
setStatusMsg("No AI agents found in PATH
|
|
57296
|
+
setStatusMsg("No AI agents found in PATH");
|
|
57039
57297
|
setTimeout(() => setStatusMsg(""), 3e3);
|
|
57040
57298
|
return;
|
|
57041
57299
|
}
|
|
@@ -57045,6 +57303,74 @@ function Board({ kandownDir }) {
|
|
|
57045
57303
|
return;
|
|
57046
57304
|
}
|
|
57047
57305
|
}
|
|
57306
|
+
if (mode === "context-menu") {
|
|
57307
|
+
if (key.escape || input === "q") {
|
|
57308
|
+
closeContextMenu();
|
|
57309
|
+
setMode("browse");
|
|
57310
|
+
return;
|
|
57311
|
+
}
|
|
57312
|
+
if (input === "j" || key.downArrow) {
|
|
57313
|
+
setCtxMenuCursor((c) => Math.min(c + 1, 1));
|
|
57314
|
+
return;
|
|
57315
|
+
}
|
|
57316
|
+
if (input === "k" || key.upArrow) {
|
|
57317
|
+
setCtxMenuCursor((c) => Math.max(c - 1, 0));
|
|
57318
|
+
return;
|
|
57319
|
+
}
|
|
57320
|
+
if (key.return) {
|
|
57321
|
+
const col = board?.columns[colIndex];
|
|
57322
|
+
if (!col) return;
|
|
57323
|
+
const task = col.tasks[ctxMenuRow];
|
|
57324
|
+
if (!task) return;
|
|
57325
|
+
if (ctxMenuCursor === 0) {
|
|
57326
|
+
closeContextMenu();
|
|
57327
|
+
openDetail(task.id);
|
|
57328
|
+
} else {
|
|
57329
|
+
setMoveTaskId(task.id);
|
|
57330
|
+
const target = colIndex === 0 ? Math.min(1, (board?.columns.length ?? 1) - 1) : 0;
|
|
57331
|
+
setMoveTargetCol(target);
|
|
57332
|
+
closeContextMenu();
|
|
57333
|
+
setMode("move-target");
|
|
57334
|
+
}
|
|
57335
|
+
return;
|
|
57336
|
+
}
|
|
57337
|
+
}
|
|
57338
|
+
if (mode === "move-target") {
|
|
57339
|
+
if (key.escape || input === "q") {
|
|
57340
|
+
setMoveTaskId(null);
|
|
57341
|
+
setMode("browse");
|
|
57342
|
+
return;
|
|
57343
|
+
}
|
|
57344
|
+
if (input === "l" || key.rightArrow) {
|
|
57345
|
+
if (!board) return;
|
|
57346
|
+
const others = board.columns.map((_, i) => i).filter((i) => i !== colIndex);
|
|
57347
|
+
const cur = others.indexOf(moveTargetCol);
|
|
57348
|
+
setMoveTargetCol(others[Math.min(cur + 1, others.length - 1)] ?? 0);
|
|
57349
|
+
return;
|
|
57350
|
+
}
|
|
57351
|
+
if (input === "h" || key.leftArrow) {
|
|
57352
|
+
if (!board) return;
|
|
57353
|
+
const others = board.columns.map((_, i) => i).filter((i) => i !== colIndex);
|
|
57354
|
+
const cur = others.indexOf(moveTargetCol);
|
|
57355
|
+
setMoveTargetCol(others[Math.max(cur - 1, 0)] ?? 0);
|
|
57356
|
+
return;
|
|
57357
|
+
}
|
|
57358
|
+
if (key.return) {
|
|
57359
|
+
if (!board || !moveTaskId) return;
|
|
57360
|
+
const name = board.columns[moveTargetCol]?.name;
|
|
57361
|
+
if (name) {
|
|
57362
|
+
moveTaskToColumn(kandownDir, moveTaskId, name);
|
|
57363
|
+
const loaded = readBoard(kandownDir);
|
|
57364
|
+
setBoard(loaded);
|
|
57365
|
+
updateLayout(loaded);
|
|
57366
|
+
setStatusMsg(`Moved ${moveTaskId} \u2192 ${name}`);
|
|
57367
|
+
setTimeout(() => setStatusMsg(""), 2e3);
|
|
57368
|
+
}
|
|
57369
|
+
setMoveTaskId(null);
|
|
57370
|
+
setMode("browse");
|
|
57371
|
+
return;
|
|
57372
|
+
}
|
|
57373
|
+
}
|
|
57048
57374
|
if (mode === "detail") {
|
|
57049
57375
|
if (key.escape || input === "q") {
|
|
57050
57376
|
setMode("browse");
|
|
@@ -57070,28 +57396,34 @@ function Board({ kandownDir }) {
|
|
|
57070
57396
|
}
|
|
57071
57397
|
});
|
|
57072
57398
|
if (!board) {
|
|
57073
|
-
return /* @__PURE__ */ (0,
|
|
57399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "Loading board\u2026" }) });
|
|
57074
57400
|
}
|
|
57075
57401
|
if (board.columns.length === 0) {
|
|
57076
|
-
return /* @__PURE__ */ (0,
|
|
57077
|
-
/* @__PURE__ */ (0,
|
|
57402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", padding: 2, children: [
|
|
57403
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "red", bold: true, children: [
|
|
57078
57404
|
"No board found at ",
|
|
57079
57405
|
kandownDir
|
|
57080
57406
|
] }),
|
|
57081
|
-
/* @__PURE__ */ (0,
|
|
57407
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
57082
57408
|
"Run ",
|
|
57083
|
-
/* @__PURE__ */ (0,
|
|
57084
|
-
" to set up
|
|
57409
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "cyan", children: "kandown init" }),
|
|
57410
|
+
" to set up."
|
|
57085
57411
|
] })
|
|
57086
57412
|
] });
|
|
57087
57413
|
}
|
|
57088
57414
|
const colWidth = calcColWidth(board.columns.length);
|
|
57089
57415
|
const focusedTask = getFocusedTask();
|
|
57416
|
+
let modeHint;
|
|
57417
|
+
if (mode === "context-menu") {
|
|
57418
|
+
modeHint = "j/k choose \xB7 Enter confirm \xB7 Esc cancel \xB7 or click";
|
|
57419
|
+
} else if (mode === "move-target") {
|
|
57420
|
+
modeHint = "\u2190/\u2192 pick column \xB7 Enter confirm \xB7 Esc cancel \xB7 or click \u2193";
|
|
57421
|
+
}
|
|
57090
57422
|
if (mode === "agent-picker") {
|
|
57091
57423
|
const taskId = detailTaskId || focusedTask?.id || "";
|
|
57092
|
-
return /* @__PURE__ */ (0,
|
|
57093
|
-
/* @__PURE__ */ (0,
|
|
57094
|
-
/* @__PURE__ */ (0,
|
|
57424
|
+
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 }),
|
|
57426
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57095
57427
|
AgentPicker,
|
|
57096
57428
|
{
|
|
57097
57429
|
agents: installedAgents,
|
|
@@ -57103,45 +57435,56 @@ function Board({ kandownDir }) {
|
|
|
57103
57435
|
] });
|
|
57104
57436
|
}
|
|
57105
57437
|
if (mode === "detail" && detailTask) {
|
|
57106
|
-
return /* @__PURE__ */ (0,
|
|
57107
|
-
/* @__PURE__ */ (0,
|
|
57108
|
-
/* @__PURE__ */ (0,
|
|
57109
|
-
/* @__PURE__ */ (0,
|
|
57438
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
57439
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
57440
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "Esc back \xB7 a agent \xB7 j/k scroll" }),
|
|
57441
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
57110
57442
|
"KANDOWN ",
|
|
57111
57443
|
board.title
|
|
57112
57444
|
] })
|
|
57113
57445
|
] }),
|
|
57114
|
-
/* @__PURE__ */ (0,
|
|
57115
|
-
statusMsg && /* @__PURE__ */ (0,
|
|
57446
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TaskDetail, { task: detailTask, taskId: detailTaskId, scrollOffset: detailScroll }),
|
|
57447
|
+
statusMsg && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: statusMsg }) })
|
|
57116
57448
|
] });
|
|
57117
57449
|
}
|
|
57118
|
-
return /* @__PURE__ */ (0,
|
|
57119
|
-
/* @__PURE__ */ (0,
|
|
57120
|
-
/* @__PURE__ */ (0,
|
|
57450
|
+
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 }),
|
|
57452
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { flexDirection: "row", children: board.columns.map((col, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57121
57453
|
KanbanColumn,
|
|
57122
57454
|
{
|
|
57123
57455
|
name: col.name,
|
|
57124
57456
|
tasks: col.tasks,
|
|
57125
57457
|
focusedRow: cIdx === colIndex ? rowIndex : -1,
|
|
57126
57458
|
isFocused: cIdx === colIndex,
|
|
57127
|
-
colWidth
|
|
57459
|
+
colWidth,
|
|
57460
|
+
contextMenuRow: mode === "context-menu" && cIdx === colIndex ? ctxMenuRow : -1,
|
|
57461
|
+
contextMenuCursor: ctxMenuCursor,
|
|
57462
|
+
showMoveTarget: mode === "move-target" && cIdx !== colIndex,
|
|
57463
|
+
isMoveFocused: mode === "move-target" && cIdx === moveTargetCol
|
|
57128
57464
|
},
|
|
57129
57465
|
col.name
|
|
57130
57466
|
)) }),
|
|
57131
|
-
/* @__PURE__ */ (0,
|
|
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: [
|
|
57468
|
+
"Moving ",
|
|
57469
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "cyan", children: moveTaskId }),
|
|
57470
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " \u2014 click " }),
|
|
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" })
|
|
57473
|
+
] }) }),
|
|
57474
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBar, { message: statusMsg, task: focusedTask })
|
|
57132
57475
|
] });
|
|
57133
57476
|
}
|
|
57134
57477
|
|
|
57135
57478
|
// src/cli/app.tsx
|
|
57136
|
-
var
|
|
57479
|
+
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
57137
57480
|
function App2({ screen, kandownDir, version }) {
|
|
57138
57481
|
switch (screen) {
|
|
57139
57482
|
case "settings":
|
|
57140
|
-
return /* @__PURE__ */ (0,
|
|
57483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Settings, { kandownDir, version });
|
|
57141
57484
|
case "board":
|
|
57142
|
-
return /* @__PURE__ */ (0,
|
|
57485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Board, { kandownDir, version });
|
|
57143
57486
|
default:
|
|
57144
|
-
return /* @__PURE__ */ (0,
|
|
57487
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "red", bold: true, children: [
|
|
57145
57488
|
"Unknown screen: ",
|
|
57146
57489
|
screen
|
|
57147
57490
|
] }) });
|
|
@@ -57149,7 +57492,7 @@ function App2({ screen, kandownDir, version }) {
|
|
|
57149
57492
|
}
|
|
57150
57493
|
|
|
57151
57494
|
// src/cli/tui.tsx
|
|
57152
|
-
var
|
|
57495
|
+
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
|
|
57153
57496
|
async function run(screen, kandownDir, version) {
|
|
57154
57497
|
if (!process.stdin.isTTY) {
|
|
57155
57498
|
throw new Error(
|
|
@@ -57157,7 +57500,7 @@ async function run(screen, kandownDir, version) {
|
|
|
57157
57500
|
);
|
|
57158
57501
|
}
|
|
57159
57502
|
process.stdout.write("\x1B[?1049h\x1B[H");
|
|
57160
|
-
const instance = render_default(/* @__PURE__ */ (0,
|
|
57503
|
+
const instance = render_default(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(App2, { screen, kandownDir, version }), {
|
|
57161
57504
|
exitOnCtrlC: true
|
|
57162
57505
|
});
|
|
57163
57506
|
try {
|
package/dist/index.html
CHANGED
|
@@ -112,7 +112,7 @@ Error generating stack: `+k.message+`
|
|
|
112
112
|
|
|
113
113
|
## Subtasks
|
|
114
114
|
|
|
115
|
-
`}}}async function sf(e,t,n,a){if(ba())return x1e(t,QI(n,a));const r=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await r.write(QI(n,a)),await r.close()}async function nR(e,t){if(ba())return C1e(t);try{await e.removeEntry(`${t}.md`)}catch{}}const $1e="kanban-md",P1e=1,Uf="recentProjects";function $Y(){return new Promise((e,t)=>{const n=indexedDB.open($1e,P1e);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Uf)||a.createObjectStore(Uf,{keyPath:"id"})}})}async function kN(e){const t=await $Y();return new Promise((n,a)=>{const i=t.transaction(Uf,"readwrite");i.objectStore(Uf).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function L$(){const e=await $Y();return new Promise((t,n)=>{const i=e.transaction(Uf,"readonly").objectStore(Uf).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function aR(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}class T1e{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=200;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),500)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await iR(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await iR(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await rR(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await rR(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function iR(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function rR(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const of=new T1e,M1e={soft:[{frequency:520,durationMs:90,delayMs:0,type:"sine"},{frequency:720,durationMs:120,delayMs:95,type:"sine"}],chime:[{frequency:660,durationMs:120,delayMs:0,type:"triangle"},{frequency:880,durationMs:160,delayMs:125,type:"triangle"}],ping:[{frequency:920,durationMs:110,delayMs:0,type:"sine"}],pop:[{frequency:260,durationMs:55,delayMs:0,type:"square"},{frequency:520,durationMs:80,delayMs:60,type:"square"}]};let sR=null;function PY(){return"Notification"in window?Notification.permission:"unsupported"}async function L1e(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function wN({title:e,body:t,config:n}){if(n.notifications.sound&&j1e(n.notifications.soundId),!(!n.notifications.browser||PY()!=="granted"))try{new Notification(e,{body:t})}catch{}}function j1e(e){const t=M1e[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{sR??=new n;const a=sR;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function D1e(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function F1e(){const e=await NY();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await EY(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Jc(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function oR(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await pl(e,s.id);await sf(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function Vu(e){y1e(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function j$(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function cR(e){yf.clear(),e.forEach(t=>{yf.set(t.id,j$(t))})}function I1e(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function R1e(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function O1e(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let B1e=0;const yf=new Map,tb=new Map,Me=p1e((e,t)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],taskContents:new Map,searchMatches:new Map,viewMode:localStorage.getItem("kandown:view")||"board",density:localStorage.getItem("kandown:density")||"comfortable",filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},commandOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:bs,recentProjects:[],toasts:[],drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await N1e();if(!n)return;const{projectHandle:a,kandownHandle:i}=n,r=await Hb(i),s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=ba()?_N():null;await kN({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await L$();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await aR(n.handle,!0)){t().toast("Permission denied","error");return}const i=await tR(n.handle),r=await Hb(i),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await kN({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=_N();if(!n)throw new Error("No server root");const a=I1e(n),i=await AY();Vu(i);const r=await NY(),s=await Promise.all(r.map(async u=>{const{frontmatter:m,body:g}=await EY(u),h={...m,id:m.id||u,status:m.status||"Backlog"},{subtasks:v,bodyWithoutSubtasks:w}=Jc(g);return{id:u,frontmatter:h,body:w,subtasks:v}}));cR(s);const o=s.map(u=>({frontmatter:u.frontmatter,body:Qh(u.body,u.subtasks)})),c=eR(o,i.board.columns),p=c.reduce((u,m)=>u+m.tasks.length,0),l=new Map;if(p<=10)for(const u of s)l.set(u.frontmatter.id,{frontmatter:u.frontmatter,subtasks:u.subtasks,body:u.body});e({loading:!1,isOpen:!0,config:i,columns:c,boardTitle:"Project Kanban",projectName:a,taskContents:l,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`)}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!ba())return;const n=_N();if(!n)return;const a=await L$(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await aR(i.handle,!0)){await t().openServerProject();return}const s=await tR(i.handle),o=await Hb(s),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await kN({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!ba()))try{const a=await E1e(n);a?(e({config:a}),Vu(a)):(e({config:bs}),Vu(bs))}catch{e({config:bs}),Vu(bs)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!ba())return;const r=n(i);e({config:r}),Vu(r);try{await S1e(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(ba())try{const i=await F1e();cR(i);const r=i.map(p=>({frontmatter:p.frontmatter,body:Qh(p.body,p.subtasks)})),s=eR(r,a.board.columns);e({boardTitle:"Project Kanban",columns:s});const o=s.reduce((p,l)=>p+l.tasks.length,0),c=new Map;if(o<=10)for(const p of i)c.set(p.frontmatter.id,{frontmatter:p.frontmatter,subtasks:p.subtasks,body:p.body});e({taskContents:c,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t();if(!ba()&&!t().tasksDirHandle)return;const p=s.find(w=>w.name===a),l=s.find(w=>w.name===i);if(!p||!l)return;const u=p.tasks.findIndex(w=>w.id===n);if(u===-1)return;const m=s.map(w=>({...w,tasks:[...w.tasks]})),g=m.find(w=>w.name===a),h=m.find(w=>w.name===i),[v]=g.tasks.splice(u,1);/done|termin|closed|complet/i.test(i)?v.checked=!0:v.checked=!1,r!==void 0?h.tasks.splice(r,0,v):h.tasks.push(v),e({columns:m});try{if(ba())return;const{tasksDirHandle:w}=t();if(!w)return;const x=a===i?m.filter(A=>A.name===i):m.filter(A=>A.name===a||A.name===i);await oR(w,x,o.board.columns)}catch(w){t().toast("Failed to save: "+w.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!ba())return;const c=r.map(u=>({...u,tasks:[...u.tasks]})),p=c.find(u=>u.name===n);if(!p)return;const[l]=p.tasks.splice(a,1);p.tasks.splice(i,0,l),e({columns:c});try{if(ba())return;const{tasksDirHandle:u}=t();if(!u)return;await oR(u,[p],o.board.columns)}catch(u){t().toast("Failed to save: "+u.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(l=>l.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(l=>l.name===n?{...l,name:i}:l);e({columns:p});try{const l=c.find(u=>u.name===n);l&&await Promise.all(l.tasks.map(async(u,m)=>{const{frontmatter:g,body:h}=await pl(s,u.id);await sf(s,u.id,{...g,id:u.id,status:i,order:m},h)})),await t().updateConfig(u=>{const m={...u.board.columnColors??{}},g=m[n.toLowerCase()];g&&(m[i.toLowerCase()]=g,delete m[n.toLowerCase()]);const h=u.board.columns.some(v=>v.toLowerCase()===n.toLowerCase())?u.board.columns:[...u.board.columns,n];return{...u,board:{...u.board,columns:h.map(v=>v.toLowerCase()===n.toLowerCase()?i:v),columnColors:m}}}),await t().reloadBoard()}catch(l){t().toast("Failed to rename column: "+l.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!ba())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>nR(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!ba()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=D1e(a),p=a.find(m=>m.name===o)?.tasks.length??0,l={id:c,title:"",checked:!1,tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null},u=a.map(m=>m.name===o?{...m,tasks:[...m.tasks,l]}:m);e({columns:u});try{const m={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},g="";await sf(i||null,c,m,g);const v=new Map(s);return v.set(c,{frontmatter:m,subtasks:[],body:g}),e({taskContents:v}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(m){return t().toast("Failed to create: "+m.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!ba())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(l=>l.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await nR(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!ba()))try{const{frontmatter:i,body:r}=await pl(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Jc(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=Qh(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await sf(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=Qh(a.body,a.subtasks),o={...a.frontmatter,id:n};await sf(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(l=>l.id));if(r){const p=c.filter(l=>!s.has(l));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await pl(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Jc(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=CY(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++B1e;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await pl(r,s),{subtasks:l,bodyWithoutSubtasks:u}=Jc(p);e({drawerData:{frontmatter:c,subtasks:l,body:u},drawerBaseVersion:{frontmatter:c,subtasks:l,body:u,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=Qh(i.body,i.subtasks),p={...i.frontmatter,id:s};await sf(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;of.stop(),tb.forEach(s=>clearTimeout(s)),tb.clear(),of.start(n,a);const i=(s,o)=>{const c=tb.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),l=setTimeout(()=>{tb.delete(s);const u=t().config;u.notifications.taskEdits&&wN({title:"Task edited",body:`${o} changed on disk.`,config:u})},p);tb.set(s,l)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:l}=await pl(o,s),{subtasks:u,bodyWithoutSubtasks:m}=Jc(l),g={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:m,subtasks:u},h=j$(g),v=yf.get(s);if(!v){yf.set(s,h);return}c.notifications.statusChanges&&v.status!==h.status&&wN({title:"Task status changed",body:`${h.title}: ${v.status} → ${h.status}`,config:c});const w=R1e(v.subtasks,h.subtasks);c.notifications.subtaskCompletions&&w>0&&wN({title:"Subtask completed",body:w===1?`${h.title}: 1 subtask completed.`:`${h.title}: ${w} subtasks completed.`,config:c}),O1e(v,h)&&i(s,h.title),yf.set(s,h)};of.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),of.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:l,body:u}=await pl(p,s),{subtasks:m,bodyWithoutSubtasks:g}=Jc(u),h=c,v=JSON.stringify(h.frontmatter)!==JSON.stringify(l),w=h.body!==g,x=JSON.stringify(h.subtasks)!==JSON.stringify(m);if(!v&&!w&&!x)return;let A="none";v&&(w||x)?A="full":v?A="metadata-only":(w||x)&&(A="body-only"),e({conflictState:{taskId:s,type:A,local:h,remote:{frontmatter:l,body:g,subtasks:m}},showConflictModal:A==="full"})}else t().reloadBoard()}),of.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await pl(o,s),{subtasks:l,bodyWithoutSubtasks:u}=Jc(p);yf.set(s,j$({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:u,subtasks:l}))}t().reloadBoard()})}}));L$().then(e=>{Me.setState({recentProjects:e})});Vu(bs);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{Vu(Me.getState().config)});function z1e(){const e=Me(o=>o.config),t=Me(o=>o.updateConfig),[n,a]=S.useState(!1);if(S.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return y.jsx($t.button,{onClick:s,className:P$("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:y.jsx($t.div,{className:P$("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:y.jsx($t.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?y.jsx(va.Moon,{size:13,className:"text-sky-300"}):y.jsx(va.Sun,{size:13,className:"text-amber-400"})},i)})})}function H1e(e){const t=_ve(e,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{t.set(e)},[e,t]),eY(t,a=>Math.round(a).toString())}const D$="0.6.1",q1e=({className:e})=>y.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[y.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),y.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),y.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function U1e(){const{t:e}=za(),t=Me(q=>q.dirHandle),n=Me(q=>q.isOpen);Me(q=>q.projectName);const a=Me(q=>q.columns),i=Me(q=>q.openFolder),r=Me(q=>q.reloadBoard),s=Me(q=>q.createTask),o=Me(q=>q.setCommandOpen),c=Me(q=>q.viewMode),p=Me(q=>q.setViewMode),l=Me(q=>q.density),u=Me(q=>q.setDensity),m=Me(q=>q.setCurrentPage),g=Me(q=>q.recentProjects),h=Me(q=>q.openRecentProject),v=Me(q=>q.filters),w=Me(q=>q.setFilter),x=Me(q=>q.clearFilters),A=Me(q=>q.config.fields),[N,M]=S.useState(!1),T=S.useRef(null),P=S.useRef(null),F=a.reduce((q,W)=>q+W.tasks.length,0),R=H1e(F),B=[];A.priority&&v.priority&&B.push({type:"priority",label:v.priority,value:v.priority}),A.tags&&v.tag&&B.push({type:"tag",label:"#"+v.tag,value:v.tag}),A.assignee&&v.assignee&&B.push({type:"assignee",label:"@"+v.assignee,value:v.assignee});const j=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],V=B.length>0||v.search||A.ownerType&&v.ownerType;return S.useEffect(()=>{if(!N)return;const q=W=>{T.current&&!T.current.contains(W.target)&&M(!1)};return document.addEventListener("mousedown",q),()=>document.removeEventListener("mousedown",q)},[N]),y.jsxs("header",{className:"flex items-center justify-between px-5 h-[64px] border-b border-border bg-card/80 backdrop-blur-xl relative z-10",children:[y.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[y.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:y.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[y.jsx(q1e,{className:"w-[34px] h-[34px] dark:text-white text-black"}),y.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),y.jsxs("span",{className:"inline-flex items-center h-5 px-1.5 text-[10.5px] font-semibold text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-500/15 rounded-md",children:["v",D$]})]})}),t&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),y.jsxs("div",{className:"flex items-center gap-2 px-3 h-9 bg-secondary/60 border border-border rounded-xl min-w-[200px] max-w-[280px] focus-within:border-border-focus focus-within:bg-secondary transition-all",children:[y.jsx(va.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),y.jsx("input",{ref:P,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:v.search,onChange:q=>w("search",q.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),v.search?y.jsx("button",{onClick:()=>w("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:y.jsx(va.X,{size:14})}):y.jsx("kbd",{className:"inline-flex items-center h-5 px-1.5 text-[10px] font-medium text-fg-muted/50 bg-black/[0.04] dark:bg-white/[0.08] rounded border border-black/[0.06] dark:border-white/[0.1]",children:"⌘K"})]}),y.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[y.jsx(Zr,{children:B.map(q=>y.jsxs($t.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>w(q.type,null),className:"inline-flex items-center gap-1 h-6 px-2.5 text-[12px] text-fg bg-black/[0.05] dark:bg-white/[0.1] border border-black/[0.08] dark:border-white/[0.12] rounded-lg hover:bg-black/[0.08] dark:hover:bg-white/[0.15] transition-colors",children:[q.label,y.jsx(va.X,{size:10,className:"text-fg-muted/60"})]},q.type+q.value))}),A.ownerType&&y.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:j.map(q=>y.jsx("button",{onClick:()=>w("ownerType",q.value),className:`h-full px-2.5 text-[12px] transition-colors ${v.ownerType===q.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:q.label},q.value))}),V&&y.jsx("button",{onClick:x,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),y.jsxs("div",{className:"relative flex-shrink-0",ref:T,children:[y.jsxs("button",{onClick:()=>M(q=>!q),className:"flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] text-fg-muted hover:text-fg hover:bg-black/[0.04] dark:hover:bg-white/[0.06] rounded-lg transition-colors border border-transparent hover:border-black/[0.06] dark:hover:border-white/[0.1]",children:[y.jsx(va.Folder,{size:13,className:"text-fg-muted/70"}),y.jsxs("span",{className:"font-medium",children:[".",t.name]}),y.jsx(va.ChevronDown,{size:11,className:"opacity-50"})]}),y.jsx(Zr,{children:N&&y.jsx($t.div,{initial:{opacity:0,y:-4,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:-4,scale:.98},transition:{duration:.12},className:"absolute top-full left-0 mt-2 min-w-[240px] glass rounded-xl shadow-[0_16px_48px_rgba(0,0,0,0.5)] overflow-hidden z-50",children:y.jsxs("div",{className:"py-1.5",children:[g.length>0&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),g.map(q=>y.jsxs("button",{onClick:()=>{M(!1),h(q)},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[y.jsx(va.Folder,{size:12,className:"text-fg-muted/60"}),y.jsx("span",{className:"truncate",children:q.name}),q.id===t.name&&y.jsx(va.Check,{size:12,className:"ml-auto text-emerald-500"})]},q.id)),y.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),y.jsxs("button",{onClick:()=>{M(!1),i()},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[y.jsx(va.Plus,{size:12,className:"text-fg-muted/60"}),y.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),y.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?y.jsxs(y.Fragment,{children:[y.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[y.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),y.jsx($t.span,{className:"tabular-nums font-medium",children:R}),y.jsx("span",{children:e("header.tasks")})]}),y.jsxs("div",{className:"flex items-center bg-black/[0.04] dark:bg-white/[0.06] border border-black/[0.06] dark:border-white/[0.1] rounded-xl p-0.5 h-10",children:[y.jsx("button",{onClick:()=>p("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${c==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:y.jsx(va.LayoutBoard,{size:18})}),y.jsx("button",{onClick:()=>p("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${c==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:y.jsx(va.LayoutList,{size:18})})]}),y.jsx(dr,{variant:"icon",icon:"Density",onClick:()=>u(l==="compact"?"comfortable":"compact"),title:`Density: ${l}`}),y.jsx(dr,{variant:"icon",icon:"Settings",onClick:()=>m("settings"),title:e("common.settings")}),y.jsx(z1e,{}),y.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),y.jsx(dr,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>o(!0),title:"Command palette (⌘K)"}),y.jsx(dr,{variant:"icon",icon:"Refresh",onClick:r,title:`${e("common.reload")} (R)`}),y.jsx(dr,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>s()})]}):y.jsx(dr,{variant:"primary",label:e("common.openFolder"),onClick:i})})]})}/**
|
|
115
|
+
`}}}async function sf(e,t,n,a){if(ba())return x1e(t,QI(n,a));const r=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await r.write(QI(n,a)),await r.close()}async function nR(e,t){if(ba())return C1e(t);try{await e.removeEntry(`${t}.md`)}catch{}}const $1e="kanban-md",P1e=1,Uf="recentProjects";function $Y(){return new Promise((e,t)=>{const n=indexedDB.open($1e,P1e);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Uf)||a.createObjectStore(Uf,{keyPath:"id"})}})}async function kN(e){const t=await $Y();return new Promise((n,a)=>{const i=t.transaction(Uf,"readwrite");i.objectStore(Uf).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function L$(){const e=await $Y();return new Promise((t,n)=>{const i=e.transaction(Uf,"readonly").objectStore(Uf).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function aR(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}class T1e{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=200;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),500)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await iR(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await iR(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await rR(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await rR(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function iR(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function rR(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const of=new T1e,M1e={soft:[{frequency:520,durationMs:90,delayMs:0,type:"sine"},{frequency:720,durationMs:120,delayMs:95,type:"sine"}],chime:[{frequency:660,durationMs:120,delayMs:0,type:"triangle"},{frequency:880,durationMs:160,delayMs:125,type:"triangle"}],ping:[{frequency:920,durationMs:110,delayMs:0,type:"sine"}],pop:[{frequency:260,durationMs:55,delayMs:0,type:"square"},{frequency:520,durationMs:80,delayMs:60,type:"square"}]};let sR=null;function PY(){return"Notification"in window?Notification.permission:"unsupported"}async function L1e(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function wN({title:e,body:t,config:n}){if(n.notifications.sound&&j1e(n.notifications.soundId),!(!n.notifications.browser||PY()!=="granted"))try{new Notification(e,{body:t})}catch{}}function j1e(e){const t=M1e[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{sR??=new n;const a=sR;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function D1e(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function F1e(){const e=await NY();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await EY(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Jc(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function oR(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await pl(e,s.id);await sf(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function Vu(e){y1e(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function j$(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function cR(e){yf.clear(),e.forEach(t=>{yf.set(t.id,j$(t))})}function I1e(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function R1e(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function O1e(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let B1e=0;const yf=new Map,tb=new Map,Me=p1e((e,t)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],taskContents:new Map,searchMatches:new Map,viewMode:localStorage.getItem("kandown:view")||"board",density:localStorage.getItem("kandown:density")||"comfortable",filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},commandOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:bs,recentProjects:[],toasts:[],drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await N1e();if(!n)return;const{projectHandle:a,kandownHandle:i}=n,r=await Hb(i),s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=ba()?_N():null;await kN({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await L$();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await aR(n.handle,!0)){t().toast("Permission denied","error");return}const i=await tR(n.handle),r=await Hb(i),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await kN({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=_N();if(!n)throw new Error("No server root");const a=I1e(n),i=await AY();Vu(i);const r=await NY(),s=await Promise.all(r.map(async u=>{const{frontmatter:m,body:g}=await EY(u),h={...m,id:m.id||u,status:m.status||"Backlog"},{subtasks:v,bodyWithoutSubtasks:w}=Jc(g);return{id:u,frontmatter:h,body:w,subtasks:v}}));cR(s);const o=s.map(u=>({frontmatter:u.frontmatter,body:Qh(u.body,u.subtasks)})),c=eR(o,i.board.columns),p=c.reduce((u,m)=>u+m.tasks.length,0),l=new Map;if(p<=10)for(const u of s)l.set(u.frontmatter.id,{frontmatter:u.frontmatter,subtasks:u.subtasks,body:u.body});e({loading:!1,isOpen:!0,config:i,columns:c,boardTitle:"Project Kanban",projectName:a,taskContents:l,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`)}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!ba())return;const n=_N();if(!n)return;const a=await L$(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await aR(i.handle,!0)){await t().openServerProject();return}const s=await tR(i.handle),o=await Hb(s),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await kN({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!ba()))try{const a=await E1e(n);a?(e({config:a}),Vu(a)):(e({config:bs}),Vu(bs))}catch{e({config:bs}),Vu(bs)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!ba())return;const r=n(i);e({config:r}),Vu(r);try{await S1e(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(ba())try{const i=await F1e();cR(i);const r=i.map(p=>({frontmatter:p.frontmatter,body:Qh(p.body,p.subtasks)})),s=eR(r,a.board.columns);e({boardTitle:"Project Kanban",columns:s});const o=s.reduce((p,l)=>p+l.tasks.length,0),c=new Map;if(o<=10)for(const p of i)c.set(p.frontmatter.id,{frontmatter:p.frontmatter,subtasks:p.subtasks,body:p.body});e({taskContents:c,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t();if(!ba()&&!t().tasksDirHandle)return;const p=s.find(w=>w.name===a),l=s.find(w=>w.name===i);if(!p||!l)return;const u=p.tasks.findIndex(w=>w.id===n);if(u===-1)return;const m=s.map(w=>({...w,tasks:[...w.tasks]})),g=m.find(w=>w.name===a),h=m.find(w=>w.name===i),[v]=g.tasks.splice(u,1);/done|termin|closed|complet/i.test(i)?v.checked=!0:v.checked=!1,r!==void 0?h.tasks.splice(r,0,v):h.tasks.push(v),e({columns:m});try{if(ba())return;const{tasksDirHandle:w}=t();if(!w)return;const x=a===i?m.filter(A=>A.name===i):m.filter(A=>A.name===a||A.name===i);await oR(w,x,o.board.columns)}catch(w){t().toast("Failed to save: "+w.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!ba())return;const c=r.map(u=>({...u,tasks:[...u.tasks]})),p=c.find(u=>u.name===n);if(!p)return;const[l]=p.tasks.splice(a,1);p.tasks.splice(i,0,l),e({columns:c});try{if(ba())return;const{tasksDirHandle:u}=t();if(!u)return;await oR(u,[p],o.board.columns)}catch(u){t().toast("Failed to save: "+u.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(l=>l.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(l=>l.name===n?{...l,name:i}:l);e({columns:p});try{const l=c.find(u=>u.name===n);l&&await Promise.all(l.tasks.map(async(u,m)=>{const{frontmatter:g,body:h}=await pl(s,u.id);await sf(s,u.id,{...g,id:u.id,status:i,order:m},h)})),await t().updateConfig(u=>{const m={...u.board.columnColors??{}},g=m[n.toLowerCase()];g&&(m[i.toLowerCase()]=g,delete m[n.toLowerCase()]);const h=u.board.columns.some(v=>v.toLowerCase()===n.toLowerCase())?u.board.columns:[...u.board.columns,n];return{...u,board:{...u.board,columns:h.map(v=>v.toLowerCase()===n.toLowerCase()?i:v),columnColors:m}}}),await t().reloadBoard()}catch(l){t().toast("Failed to rename column: "+l.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!ba())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>nR(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!ba()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=D1e(a),p=a.find(m=>m.name===o)?.tasks.length??0,l={id:c,title:"",checked:!1,tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null},u=a.map(m=>m.name===o?{...m,tasks:[...m.tasks,l]}:m);e({columns:u});try{const m={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},g="";await sf(i||null,c,m,g);const v=new Map(s);return v.set(c,{frontmatter:m,subtasks:[],body:g}),e({taskContents:v}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(m){return t().toast("Failed to create: "+m.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!ba())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(l=>l.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await nR(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!ba()))try{const{frontmatter:i,body:r}=await pl(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Jc(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=Qh(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await sf(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=Qh(a.body,a.subtasks),o={...a.frontmatter,id:n};await sf(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(l=>l.id));if(r){const p=c.filter(l=>!s.has(l));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await pl(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Jc(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=CY(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++B1e;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await pl(r,s),{subtasks:l,bodyWithoutSubtasks:u}=Jc(p);e({drawerData:{frontmatter:c,subtasks:l,body:u},drawerBaseVersion:{frontmatter:c,subtasks:l,body:u,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=Qh(i.body,i.subtasks),p={...i.frontmatter,id:s};await sf(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;of.stop(),tb.forEach(s=>clearTimeout(s)),tb.clear(),of.start(n,a);const i=(s,o)=>{const c=tb.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),l=setTimeout(()=>{tb.delete(s);const u=t().config;u.notifications.taskEdits&&wN({title:"Task edited",body:`${o} changed on disk.`,config:u})},p);tb.set(s,l)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:l}=await pl(o,s),{subtasks:u,bodyWithoutSubtasks:m}=Jc(l),g={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:m,subtasks:u},h=j$(g),v=yf.get(s);if(!v){yf.set(s,h);return}c.notifications.statusChanges&&v.status!==h.status&&wN({title:"Task status changed",body:`${h.title}: ${v.status} → ${h.status}`,config:c});const w=R1e(v.subtasks,h.subtasks);c.notifications.subtaskCompletions&&w>0&&wN({title:"Subtask completed",body:w===1?`${h.title}: 1 subtask completed.`:`${h.title}: ${w} subtasks completed.`,config:c}),O1e(v,h)&&i(s,h.title),yf.set(s,h)};of.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),of.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:l,body:u}=await pl(p,s),{subtasks:m,bodyWithoutSubtasks:g}=Jc(u),h=c,v=JSON.stringify(h.frontmatter)!==JSON.stringify(l),w=h.body!==g,x=JSON.stringify(h.subtasks)!==JSON.stringify(m);if(!v&&!w&&!x)return;let A="none";v&&(w||x)?A="full":v?A="metadata-only":(w||x)&&(A="body-only"),e({conflictState:{taskId:s,type:A,local:h,remote:{frontmatter:l,body:g,subtasks:m}},showConflictModal:A==="full"})}else t().reloadBoard()}),of.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await pl(o,s),{subtasks:l,bodyWithoutSubtasks:u}=Jc(p);yf.set(s,j$({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:u,subtasks:l}))}t().reloadBoard()})}}));L$().then(e=>{Me.setState({recentProjects:e})});Vu(bs);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{Vu(Me.getState().config)});function z1e(){const e=Me(o=>o.config),t=Me(o=>o.updateConfig),[n,a]=S.useState(!1);if(S.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return y.jsx($t.button,{onClick:s,className:P$("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:y.jsx($t.div,{className:P$("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:y.jsx($t.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?y.jsx(va.Moon,{size:13,className:"text-sky-300"}):y.jsx(va.Sun,{size:13,className:"text-amber-400"})},i)})})}function H1e(e){const t=_ve(e,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{t.set(e)},[e,t]),eY(t,a=>Math.round(a).toString())}const D$="0.7.1",q1e=({className:e})=>y.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[y.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),y.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),y.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function U1e(){const{t:e}=za(),t=Me(q=>q.dirHandle),n=Me(q=>q.isOpen);Me(q=>q.projectName);const a=Me(q=>q.columns),i=Me(q=>q.openFolder),r=Me(q=>q.reloadBoard),s=Me(q=>q.createTask),o=Me(q=>q.setCommandOpen),c=Me(q=>q.viewMode),p=Me(q=>q.setViewMode),l=Me(q=>q.density),u=Me(q=>q.setDensity),m=Me(q=>q.setCurrentPage),g=Me(q=>q.recentProjects),h=Me(q=>q.openRecentProject),v=Me(q=>q.filters),w=Me(q=>q.setFilter),x=Me(q=>q.clearFilters),A=Me(q=>q.config.fields),[N,M]=S.useState(!1),T=S.useRef(null),P=S.useRef(null),F=a.reduce((q,W)=>q+W.tasks.length,0),R=H1e(F),B=[];A.priority&&v.priority&&B.push({type:"priority",label:v.priority,value:v.priority}),A.tags&&v.tag&&B.push({type:"tag",label:"#"+v.tag,value:v.tag}),A.assignee&&v.assignee&&B.push({type:"assignee",label:"@"+v.assignee,value:v.assignee});const j=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],V=B.length>0||v.search||A.ownerType&&v.ownerType;return S.useEffect(()=>{if(!N)return;const q=W=>{T.current&&!T.current.contains(W.target)&&M(!1)};return document.addEventListener("mousedown",q),()=>document.removeEventListener("mousedown",q)},[N]),y.jsxs("header",{className:"flex items-center justify-between px-5 h-[64px] border-b border-border bg-card/80 backdrop-blur-xl relative z-10",children:[y.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[y.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:y.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[y.jsx(q1e,{className:"w-[34px] h-[34px] dark:text-white text-black"}),y.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),y.jsxs("span",{className:"inline-flex items-center h-5 px-1.5 text-[10.5px] font-semibold text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-500/15 rounded-md",children:["v",D$]})]})}),t&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),y.jsxs("div",{className:"flex items-center gap-2 px-3 h-9 bg-secondary/60 border border-border rounded-xl min-w-[200px] max-w-[280px] focus-within:border-border-focus focus-within:bg-secondary transition-all",children:[y.jsx(va.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),y.jsx("input",{ref:P,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:v.search,onChange:q=>w("search",q.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),v.search?y.jsx("button",{onClick:()=>w("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:y.jsx(va.X,{size:14})}):y.jsx("kbd",{className:"inline-flex items-center h-5 px-1.5 text-[10px] font-medium text-fg-muted/50 bg-black/[0.04] dark:bg-white/[0.08] rounded border border-black/[0.06] dark:border-white/[0.1]",children:"⌘K"})]}),y.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[y.jsx(Zr,{children:B.map(q=>y.jsxs($t.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>w(q.type,null),className:"inline-flex items-center gap-1 h-6 px-2.5 text-[12px] text-fg bg-black/[0.05] dark:bg-white/[0.1] border border-black/[0.08] dark:border-white/[0.12] rounded-lg hover:bg-black/[0.08] dark:hover:bg-white/[0.15] transition-colors",children:[q.label,y.jsx(va.X,{size:10,className:"text-fg-muted/60"})]},q.type+q.value))}),A.ownerType&&y.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:j.map(q=>y.jsx("button",{onClick:()=>w("ownerType",q.value),className:`h-full px-2.5 text-[12px] transition-colors ${v.ownerType===q.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:q.label},q.value))}),V&&y.jsx("button",{onClick:x,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),y.jsxs("div",{className:"relative flex-shrink-0",ref:T,children:[y.jsxs("button",{onClick:()=>M(q=>!q),className:"flex items-center gap-1.5 px-2.5 py-1.5 text-[13px] text-fg-muted hover:text-fg hover:bg-black/[0.04] dark:hover:bg-white/[0.06] rounded-lg transition-colors border border-transparent hover:border-black/[0.06] dark:hover:border-white/[0.1]",children:[y.jsx(va.Folder,{size:13,className:"text-fg-muted/70"}),y.jsxs("span",{className:"font-medium",children:[".",t.name]}),y.jsx(va.ChevronDown,{size:11,className:"opacity-50"})]}),y.jsx(Zr,{children:N&&y.jsx($t.div,{initial:{opacity:0,y:-4,scale:.98},animate:{opacity:1,y:0,scale:1},exit:{opacity:0,y:-4,scale:.98},transition:{duration:.12},className:"absolute top-full left-0 mt-2 min-w-[240px] glass rounded-xl shadow-[0_16px_48px_rgba(0,0,0,0.5)] overflow-hidden z-50",children:y.jsxs("div",{className:"py-1.5",children:[g.length>0&&y.jsxs(y.Fragment,{children:[y.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),g.map(q=>y.jsxs("button",{onClick:()=>{M(!1),h(q)},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[y.jsx(va.Folder,{size:12,className:"text-fg-muted/60"}),y.jsx("span",{className:"truncate",children:q.name}),q.id===t.name&&y.jsx(va.Check,{size:12,className:"ml-auto text-emerald-500"})]},q.id)),y.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),y.jsxs("button",{onClick:()=>{M(!1),i()},className:"w-full flex items-center gap-2 px-3 py-2 text-[13.5px] text-left hover:bg-black/[0.04] dark:hover:bg-white/[0.06] transition-colors",children:[y.jsx(va.Plus,{size:12,className:"text-fg-muted/60"}),y.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),y.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?y.jsxs(y.Fragment,{children:[y.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[y.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),y.jsx($t.span,{className:"tabular-nums font-medium",children:R}),y.jsx("span",{children:e("header.tasks")})]}),y.jsxs("div",{className:"flex items-center bg-black/[0.04] dark:bg-white/[0.06] border border-black/[0.06] dark:border-white/[0.1] rounded-xl p-0.5 h-10",children:[y.jsx("button",{onClick:()=>p("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${c==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:y.jsx(va.LayoutBoard,{size:18})}),y.jsx("button",{onClick:()=>p("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${c==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:y.jsx(va.LayoutList,{size:18})})]}),y.jsx(dr,{variant:"icon",icon:"Density",onClick:()=>u(l==="compact"?"comfortable":"compact"),title:`Density: ${l}`}),y.jsx(dr,{variant:"icon",icon:"Settings",onClick:()=>m("settings"),title:e("common.settings")}),y.jsx(z1e,{}),y.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),y.jsx(dr,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>o(!0),title:"Command palette (⌘K)"}),y.jsx(dr,{variant:"icon",icon:"Refresh",onClick:r,title:`${e("common.reload")} (R)`}),y.jsx(dr,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>s()})]}):y.jsx(dr,{variant:"primary",label:e("common.openFolder"),onClick:i})})]})}/**
|
|
116
116
|
* @license @tabler/icons-react v3.41.1 - MIT
|
|
117
117
|
*
|
|
118
118
|
* This source code is licensed under the MIT license.
|