kandown 0.7.0 → 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 +288 -412
- package/dist/index.html +1 -1
- package/package.json +1 -1
package/bin/tui.js
CHANGED
|
@@ -27968,7 +27968,7 @@ var require_backend = __commonJS({
|
|
|
27968
27968
|
return [initialArg, function() {
|
|
27969
27969
|
}];
|
|
27970
27970
|
},
|
|
27971
|
-
useRef: function
|
|
27971
|
+
useRef: function useRef5(initialValue) {
|
|
27972
27972
|
var hook = nextHook();
|
|
27973
27973
|
initialValue = null !== hook ? hook.memoizedState : {
|
|
27974
27974
|
current: initialValue
|
|
@@ -27983,7 +27983,7 @@ var require_backend = __commonJS({
|
|
|
27983
27983
|
});
|
|
27984
27984
|
return initialValue;
|
|
27985
27985
|
},
|
|
27986
|
-
useState: function
|
|
27986
|
+
useState: function useState9(initialState) {
|
|
27987
27987
|
var hook = nextHook();
|
|
27988
27988
|
initialState = null !== hook ? hook.memoizedState : "function" === typeof initialState ? initialState() : initialState;
|
|
27989
27989
|
hookLog.push({
|
|
@@ -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";
|
|
@@ -56757,222 +56757,76 @@ function AgentPicker({ agents, taskId, onSelect, onCancel }) {
|
|
|
56757
56757
|
|
|
56758
56758
|
// src/cli/hooks/use-mouse.ts
|
|
56759
56759
|
var import_react36 = __toESM(require_react(), 1);
|
|
56760
|
-
var
|
|
56761
|
-
var
|
|
56762
|
-
var
|
|
56763
|
-
|
|
56764
|
-
|
|
56765
|
-
const sgrRegex = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])/;
|
|
56766
|
-
const match = buffer.match(sgrRegex);
|
|
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);
|
|
56767
56765
|
if (!match) return null;
|
|
56768
56766
|
const cb = parseInt(match[1], 10);
|
|
56769
56767
|
const cx = parseInt(match[2], 10);
|
|
56770
56768
|
const cy = parseInt(match[3], 10);
|
|
56771
56769
|
const isPress = match[4] === "M";
|
|
56772
|
-
const button = cb & 3;
|
|
56773
|
-
const shift = (cb & 4) !== 0;
|
|
56774
|
-
const meta = (cb & 8) !== 0;
|
|
56775
|
-
const ctrl = (cb & 16) !== 0;
|
|
56776
56770
|
return {
|
|
56777
|
-
|
|
56778
|
-
|
|
56779
|
-
|
|
56780
|
-
|
|
56781
|
-
action: isPress ? "press" : "release",
|
|
56782
|
-
shift,
|
|
56783
|
-
meta,
|
|
56784
|
-
ctrl
|
|
56785
|
-
},
|
|
56786
|
-
endIndex: match[0].length
|
|
56771
|
+
x: cx,
|
|
56772
|
+
y: cy,
|
|
56773
|
+
button: cb & 3,
|
|
56774
|
+
action: isPress ? "press" : "release"
|
|
56787
56775
|
};
|
|
56788
56776
|
}
|
|
56789
|
-
function
|
|
56790
|
-
|
|
56791
|
-
if (buffer.length < 6) return null;
|
|
56792
|
-
const cb = buffer.charCodeAt(3) - 32;
|
|
56793
|
-
const cx = buffer.charCodeAt(4) - 32;
|
|
56794
|
-
const cy = buffer.charCodeAt(5) - 32;
|
|
56795
|
-
if (cb < 0 || cx < 0 || cy < 0) return null;
|
|
56796
|
-
const button = cb & 3;
|
|
56797
|
-
const shift = (cb & 4) !== 0;
|
|
56798
|
-
const meta = (cb & 8) !== 0;
|
|
56799
|
-
const ctrl = (cb & 16) !== 0;
|
|
56800
|
-
return {
|
|
56801
|
-
event: {
|
|
56802
|
-
x: cx,
|
|
56803
|
-
y: cy,
|
|
56804
|
-
button,
|
|
56805
|
-
action: "press",
|
|
56806
|
-
// X10 only reports press
|
|
56807
|
-
shift,
|
|
56808
|
-
meta,
|
|
56809
|
-
ctrl
|
|
56810
|
-
},
|
|
56811
|
-
endIndex: 6
|
|
56812
|
-
};
|
|
56777
|
+
function isMouseInput(input) {
|
|
56778
|
+
return input.startsWith("[<") || input.startsWith("[M");
|
|
56813
56779
|
}
|
|
56814
|
-
function
|
|
56815
|
-
const { enabled = true, pressOnly = true } = options;
|
|
56816
|
-
const handlerRef = (0, import_react36.useRef)(handler);
|
|
56817
|
-
handlerRef.current = handler;
|
|
56780
|
+
function useMouseMode(enabled = true) {
|
|
56818
56781
|
(0, import_react36.useEffect)(() => {
|
|
56819
56782
|
if (!enabled) return;
|
|
56820
|
-
|
|
56821
|
-
|
|
56822
|
-
process.stdout.write(MOUSE_ENABLE_SGR + MOUSE_ENABLE_X10);
|
|
56823
|
-
let buffer = "";
|
|
56824
|
-
const onData = (data) => {
|
|
56825
|
-
buffer += data.toString("utf8");
|
|
56826
|
-
while (buffer.length > 0) {
|
|
56827
|
-
let parsed = parseSGRMouse(buffer);
|
|
56828
|
-
if (!parsed) {
|
|
56829
|
-
parsed = parseX10Mouse(buffer);
|
|
56830
|
-
}
|
|
56831
|
-
if (parsed) {
|
|
56832
|
-
const { event, endIndex } = parsed;
|
|
56833
|
-
buffer = buffer.slice(endIndex);
|
|
56834
|
-
if (pressOnly && event.action === "release") continue;
|
|
56835
|
-
if (event.button === 3 && event.action === "press") continue;
|
|
56836
|
-
handlerRef.current(event);
|
|
56837
|
-
} else if (buffer.startsWith("\x1B[")) {
|
|
56838
|
-
if (buffer.length >= 3 && buffer[2] !== "<" && buffer[2] !== "M") {
|
|
56839
|
-
const leftover = buffer;
|
|
56840
|
-
buffer = "";
|
|
56841
|
-
stdin.unshift(Buffer.from(leftover, "utf8"));
|
|
56842
|
-
break;
|
|
56843
|
-
}
|
|
56844
|
-
if (buffer.length > 32) {
|
|
56845
|
-
const leftover = buffer;
|
|
56846
|
-
buffer = "";
|
|
56847
|
-
stdin.unshift(Buffer.from(leftover, "utf8"));
|
|
56848
|
-
}
|
|
56849
|
-
break;
|
|
56850
|
-
} else if (buffer.startsWith("\x1B") && buffer.length > 1 && buffer[1] !== "[") {
|
|
56851
|
-
const leftover = buffer;
|
|
56852
|
-
buffer = "";
|
|
56853
|
-
stdin.unshift(Buffer.from(leftover, "utf8"));
|
|
56854
|
-
break;
|
|
56855
|
-
} else if (buffer.startsWith("\x1B") && buffer.length === 1) {
|
|
56856
|
-
break;
|
|
56857
|
-
} else {
|
|
56858
|
-
const leftover = buffer;
|
|
56859
|
-
buffer = "";
|
|
56860
|
-
stdin.unshift(Buffer.from(leftover, "utf8"));
|
|
56861
|
-
break;
|
|
56862
|
-
}
|
|
56863
|
-
}
|
|
56864
|
-
if (buffer.length > 256) {
|
|
56865
|
-
const leftover = buffer;
|
|
56866
|
-
buffer = "";
|
|
56867
|
-
stdin.unshift(Buffer.from(leftover, "utf8"));
|
|
56868
|
-
}
|
|
56869
|
-
};
|
|
56870
|
-
stdin.prependListener("data", onData);
|
|
56783
|
+
if (!process.stdin.isTTY) return;
|
|
56784
|
+
process.stdout.write(MOUSE_ENABLE);
|
|
56871
56785
|
const cleanup = () => {
|
|
56872
|
-
process.stdout.write(
|
|
56786
|
+
process.stdout.write(MOUSE_DISABLE);
|
|
56873
56787
|
};
|
|
56874
56788
|
process.on("exit", cleanup);
|
|
56875
56789
|
return () => {
|
|
56876
|
-
stdin.removeListener("data", onData);
|
|
56877
56790
|
process.removeListener("exit", cleanup);
|
|
56878
|
-
process.stdout.write(
|
|
56791
|
+
process.stdout.write(MOUSE_DISABLE);
|
|
56879
56792
|
};
|
|
56880
|
-
}, [enabled
|
|
56793
|
+
}, [enabled]);
|
|
56881
56794
|
}
|
|
56882
56795
|
|
|
56883
56796
|
// src/cli/components/task-context-menu.tsx
|
|
56884
|
-
var import_react37 = __toESM(require_react(), 1);
|
|
56885
56797
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
56886
|
-
function
|
|
56887
|
-
|
|
56888
|
-
|
|
56889
|
-
|
|
56890
|
-
|
|
56891
|
-
|
|
56892
|
-
|
|
56893
|
-
|
|
56894
|
-
|
|
56895
|
-
|
|
56896
|
-
|
|
56897
|
-
|
|
56898
|
-
|
|
56899
|
-
|
|
56900
|
-
|
|
56901
|
-
|
|
56902
|
-
|
|
56903
|
-
|
|
56904
|
-
|
|
56905
|
-
|
|
56906
|
-
|
|
56907
|
-
|
|
56908
|
-
|
|
56909
|
-
|
|
56910
|
-
|
|
56911
|
-
|
|
56912
|
-
if (opt) onSelect(opt.id);
|
|
56913
|
-
return;
|
|
56914
|
-
}
|
|
56915
|
-
});
|
|
56916
|
-
const maxLabelLen = Math.max(...options.map((o) => o.label.length));
|
|
56917
|
-
const menuWidth = Math.max(24, maxLabelLen + 8);
|
|
56918
|
-
const lines = [];
|
|
56919
|
-
const startRow = menuStartRow ?? 0;
|
|
56920
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
56921
|
-
Box_default,
|
|
56922
|
-
{
|
|
56923
|
-
flexDirection: "column",
|
|
56924
|
-
paddingLeft: 2,
|
|
56925
|
-
marginTop: 0,
|
|
56926
|
-
children: [
|
|
56927
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
56928
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: "\u250C\u2500 " }),
|
|
56929
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "cyan", bold: true, children: taskId }),
|
|
56930
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: " \u2500\u2510" })
|
|
56931
|
-
] }),
|
|
56932
|
-
options.map((opt, idx) => {
|
|
56933
|
-
const focused = idx === cursor;
|
|
56934
|
-
const lineY = startRow + 1 + idx;
|
|
56935
|
-
lines.push({ optionId: opt.id, y: lineY });
|
|
56936
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
56937
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "gray", dimColor: true, children: "\u2502 " }),
|
|
56938
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
56939
|
-
Text,
|
|
56940
|
-
{
|
|
56941
|
-
color: focused ? "black" : "gray",
|
|
56942
|
-
backgroundColor: focused ? "cyan" : void 0,
|
|
56943
|
-
bold: focused,
|
|
56944
|
-
children: [
|
|
56945
|
-
focused ? "\u25B8" : " ",
|
|
56946
|
-
" ",
|
|
56947
|
-
opt.icon,
|
|
56948
|
-
" ",
|
|
56949
|
-
opt.label
|
|
56950
|
-
]
|
|
56951
|
-
}
|
|
56952
|
-
),
|
|
56953
|
-
!focused && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
56954
|
-
" ".repeat(Math.max(0, menuWidth - opt.label.length - 4)),
|
|
56955
|
-
"\u2502"
|
|
56956
|
-
] }),
|
|
56957
|
-
focused && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
56958
|
-
" ".repeat(Math.max(0, menuWidth - opt.label.length - 4)),
|
|
56959
|
-
"\u2502"
|
|
56960
|
-
] })
|
|
56961
|
-
] }, opt.id);
|
|
56962
|
-
}),
|
|
56963
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
56964
|
-
"\u2514",
|
|
56965
|
-
"\u2500".repeat(menuWidth),
|
|
56966
|
-
"\u2518"
|
|
56967
|
-
] }) })
|
|
56968
|
-
]
|
|
56969
|
-
}
|
|
56970
|
-
);
|
|
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
|
+
}) });
|
|
56971
56824
|
}
|
|
56825
|
+
var MENU_HEIGHT = 2;
|
|
56972
56826
|
|
|
56973
56827
|
// src/cli/screens/board.tsx
|
|
56974
56828
|
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
56975
|
-
var
|
|
56829
|
+
var TASKS_START_Y = 5;
|
|
56976
56830
|
function truncate(str, maxLen) {
|
|
56977
56831
|
if (str.length <= maxLen) return str;
|
|
56978
56832
|
return str.slice(0, maxLen - 1) + "\u2026";
|
|
@@ -56990,23 +56844,18 @@ function calcColWidth(numCols) {
|
|
|
56990
56844
|
}
|
|
56991
56845
|
var RE_HEADER = /^#{1,3}\s/;
|
|
56992
56846
|
var RE_SUBTASK = /^\s*-\s+\[([ xX])\]/;
|
|
56993
|
-
var
|
|
56847
|
+
var RE_DONE = /^\s*-\s+\[x\]/i;
|
|
56994
56848
|
var RE_BRACKET_TAG = /^\[([^\]]+)\]\s*/;
|
|
56995
|
-
function TaskRow({
|
|
56996
|
-
task,
|
|
56997
|
-
focused,
|
|
56998
|
-
colWidth
|
|
56999
|
-
}) {
|
|
56849
|
+
function TaskRow({ task, focused, colWidth }) {
|
|
57000
56850
|
const cursor = focused ? "\u25B8" : " ";
|
|
57001
56851
|
const check2 = task.checked ? "\u2713" : "\u25CB";
|
|
57002
56852
|
const idStr = task.id;
|
|
57003
56853
|
const tagMatch = task.title.match(RE_BRACKET_TAG);
|
|
57004
56854
|
const tag = tagMatch ? `[${tagMatch[1]}]` : "";
|
|
57005
|
-
const
|
|
56855
|
+
const titleClean = tagMatch ? task.title.slice(tagMatch[0].length) : task.title;
|
|
57006
56856
|
const fixedChars = 4 + idStr.length + 1;
|
|
57007
56857
|
const tagChars = tag ? tag.length + 1 : 0;
|
|
57008
|
-
const
|
|
57009
|
-
const titleStr = truncate(titleWithoutTag, Math.max(4, available));
|
|
56858
|
+
const titleStr = truncate(titleClean, Math.max(4, colWidth - fixedChars - tagChars));
|
|
57010
56859
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { children: [
|
|
57011
56860
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: focused ? "cyan" : void 0, bold: focused, children: [
|
|
57012
56861
|
cursor,
|
|
@@ -57027,12 +56876,7 @@ function TaskRow({
|
|
|
57027
56876
|
] })
|
|
57028
56877
|
] });
|
|
57029
56878
|
}
|
|
57030
|
-
function MovePlaceholder({
|
|
57031
|
-
name,
|
|
57032
|
-
focused,
|
|
57033
|
-
colWidth
|
|
57034
|
-
}) {
|
|
57035
|
-
const label = `\u2193 ${name}`;
|
|
56879
|
+
function MovePlaceholder({ name, focused, colWidth }) {
|
|
57036
56880
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
57037
56881
|
Text,
|
|
57038
56882
|
{
|
|
@@ -57041,7 +56885,7 @@ function MovePlaceholder({
|
|
|
57041
56885
|
bold: focused,
|
|
57042
56886
|
children: [
|
|
57043
56887
|
" ",
|
|
57044
|
-
pad(
|
|
56888
|
+
pad(`\u2193 ${name}`, colWidth - 2)
|
|
57045
56889
|
]
|
|
57046
56890
|
}
|
|
57047
56891
|
) });
|
|
@@ -57052,35 +56896,54 @@ function KanbanColumn({
|
|
|
57052
56896
|
focusedRow,
|
|
57053
56897
|
isFocused,
|
|
57054
56898
|
colWidth,
|
|
56899
|
+
contextMenuRow,
|
|
56900
|
+
contextMenuCursor,
|
|
57055
56901
|
showMoveTarget,
|
|
57056
56902
|
isMoveFocused
|
|
57057
56903
|
}) {
|
|
57058
56904
|
const headerBg = isFocused ? "cyan" : void 0;
|
|
57059
56905
|
const headerColor = isFocused ? "black" : "cyan";
|
|
57060
56906
|
const countStr = tasks.length > 0 ? ` (${tasks.length})` : "";
|
|
57061
|
-
const
|
|
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
|
+
});
|
|
57062
56933
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", width: colWidth, marginRight: 1, children: [
|
|
57063
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { backgroundColor: headerBg, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: headerColor, bold: true, children: pad(
|
|
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) }) }),
|
|
57064
56935
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: isFocused ? "cyan" : "gray", children: "\u2500".repeat(colWidth) }),
|
|
57065
56936
|
tasks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
57066
56937
|
" ".repeat(2),
|
|
57067
56938
|
"(empty)"
|
|
57068
|
-
] }) :
|
|
57069
|
-
TaskRow,
|
|
57070
|
-
{
|
|
57071
|
-
task,
|
|
57072
|
-
focused: isFocused && idx === focusedRow,
|
|
57073
|
-
colWidth
|
|
57074
|
-
},
|
|
57075
|
-
task.id
|
|
57076
|
-
)),
|
|
56939
|
+
] }) : rows,
|
|
57077
56940
|
showMoveTarget && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MovePlaceholder, { name, focused: !!isMoveFocused, colWidth })
|
|
57078
56941
|
] });
|
|
57079
56942
|
}
|
|
57080
56943
|
function BoardHeader({ title, inTmux, modeHint, version }) {
|
|
57081
56944
|
const tmuxHint = inTmux ? " tmux" : "";
|
|
57082
|
-
const hint = modeHint || "h/l cols j/k tasks Enter detail a agent r reload q quit";
|
|
57083
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";
|
|
57084
56947
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
57085
56948
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { bold: true, color: "cyan", children: [
|
|
57086
56949
|
" ",
|
|
@@ -57099,17 +56962,13 @@ function StatusBar({ message, task }) {
|
|
|
57099
56962
|
}
|
|
57100
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: " " }) });
|
|
57101
56964
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
57102
|
-
task.id
|
|
56965
|
+
task.id,
|
|
57103
56966
|
task.progress ? ` (${task.progress.done}/${task.progress.total})` : "",
|
|
57104
56967
|
" ",
|
|
57105
56968
|
task.checked ? "\u2713 done" : "\u25CB open"
|
|
57106
56969
|
] }) });
|
|
57107
56970
|
}
|
|
57108
|
-
function TaskDetail({
|
|
57109
|
-
task,
|
|
57110
|
-
taskId,
|
|
57111
|
-
scrollOffset
|
|
57112
|
-
}) {
|
|
56971
|
+
function TaskDetail({ task, taskId, scrollOffset }) {
|
|
57113
56972
|
const fm = task.frontmatter;
|
|
57114
56973
|
const bodyLines = task.body.split("\n");
|
|
57115
56974
|
const maxVisible = (process.stdout.rows || 24) - 10;
|
|
@@ -57131,14 +56990,14 @@ function TaskDetail({
|
|
|
57131
56990
|
] }) }),
|
|
57132
56991
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "\u2500".repeat(termWidth() - 4) }),
|
|
57133
56992
|
visibleLines.map((line, idx) => {
|
|
57134
|
-
const
|
|
57135
|
-
const
|
|
57136
|
-
const
|
|
56993
|
+
const isH = RE_HEADER.test(line);
|
|
56994
|
+
const isS = RE_SUBTASK.test(line);
|
|
56995
|
+
const isD = RE_DONE.test(line);
|
|
57137
56996
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57138
56997
|
Text,
|
|
57139
56998
|
{
|
|
57140
|
-
color:
|
|
57141
|
-
bold:
|
|
56999
|
+
color: isH ? "cyan" : isD ? "green" : isS ? "white" : "gray",
|
|
57000
|
+
bold: isH,
|
|
57142
57001
|
children: line || " "
|
|
57143
57002
|
},
|
|
57144
57003
|
scrollOffset + idx
|
|
@@ -57158,25 +57017,25 @@ function TaskDetail({
|
|
|
57158
57017
|
}
|
|
57159
57018
|
function Board({ kandownDir, version }) {
|
|
57160
57019
|
const { exit } = use_app_default();
|
|
57161
|
-
const [board, setBoard] = (0,
|
|
57162
|
-
const [colIndex, setColIndex] = (0,
|
|
57163
|
-
const [rowIndex, setRowIndex] = (0,
|
|
57164
|
-
const [mode, setMode] = (0,
|
|
57165
|
-
const [
|
|
57166
|
-
const [
|
|
57167
|
-
const [
|
|
57168
|
-
const [
|
|
57169
|
-
const [
|
|
57170
|
-
const [
|
|
57171
|
-
const [moveTaskId, setMoveTaskId] = (0,
|
|
57172
|
-
const [moveTargetCol, setMoveTargetCol] = (0,
|
|
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)([]);
|
|
57173
57033
|
const inTmux = isInTmux();
|
|
57174
|
-
const layoutRef = (0,
|
|
57034
|
+
const layoutRef = (0, import_react37.useRef)({
|
|
57175
57035
|
colStarts: [],
|
|
57176
|
-
colWidth: 0
|
|
57177
|
-
colTaskCounts: []
|
|
57036
|
+
colWidth: 0
|
|
57178
57037
|
});
|
|
57179
|
-
const updateLayout = (0,
|
|
57038
|
+
const updateLayout = (0, import_react37.useCallback)((b) => {
|
|
57180
57039
|
if (!b) return;
|
|
57181
57040
|
const cw = calcColWidth(b.columns.length);
|
|
57182
57041
|
const starts = [];
|
|
@@ -57185,19 +57044,15 @@ function Board({ kandownDir, version }) {
|
|
|
57185
57044
|
starts.push(x);
|
|
57186
57045
|
x += cw + 1;
|
|
57187
57046
|
}
|
|
57188
|
-
layoutRef.current = {
|
|
57189
|
-
colStarts: starts,
|
|
57190
|
-
colWidth: cw,
|
|
57191
|
-
colTaskCounts: b.columns.map((c) => c.tasks.length)
|
|
57192
|
-
};
|
|
57047
|
+
layoutRef.current = { colStarts: starts, colWidth: cw };
|
|
57193
57048
|
}, []);
|
|
57194
|
-
(0,
|
|
57049
|
+
(0, import_react37.useEffect)(() => {
|
|
57195
57050
|
const loaded = readBoard(kandownDir);
|
|
57196
57051
|
setBoard(loaded);
|
|
57197
57052
|
updateLayout(loaded);
|
|
57198
57053
|
setInstalledAgents(detectInstalledAgents());
|
|
57199
57054
|
}, [kandownDir, updateLayout]);
|
|
57200
|
-
(0,
|
|
57055
|
+
(0, import_react37.useEffect)(() => {
|
|
57201
57056
|
const watcher = createWatcher();
|
|
57202
57057
|
watcher.on("taskChanged", () => {
|
|
57203
57058
|
const loaded = readBoard(kandownDir);
|
|
@@ -57221,27 +57076,31 @@ function Board({ kandownDir, version }) {
|
|
|
57221
57076
|
watcher.stop();
|
|
57222
57077
|
};
|
|
57223
57078
|
}, [kandownDir, updateLayout]);
|
|
57224
|
-
const reloadBoard = (0,
|
|
57079
|
+
const reloadBoard = (0, import_react37.useCallback)(() => {
|
|
57225
57080
|
const loaded = readBoard(kandownDir);
|
|
57226
57081
|
setBoard(loaded);
|
|
57227
57082
|
updateLayout(loaded);
|
|
57228
57083
|
setStatusMsg("Board reloaded");
|
|
57229
57084
|
setTimeout(() => setStatusMsg(""), 1500);
|
|
57230
57085
|
}, [kandownDir, updateLayout]);
|
|
57231
|
-
const getFocusedTask = (0,
|
|
57086
|
+
const getFocusedTask = (0, import_react37.useCallback)(() => {
|
|
57232
57087
|
if (!board) return null;
|
|
57233
57088
|
const col = board.columns[colIndex];
|
|
57234
57089
|
if (!col || col.tasks.length === 0) return null;
|
|
57235
57090
|
return col.tasks[Math.min(rowIndex, col.tasks.length - 1)] ?? null;
|
|
57236
57091
|
}, [board, colIndex, rowIndex]);
|
|
57237
|
-
const openDetail = (0,
|
|
57092
|
+
const openDetail = (0, import_react37.useCallback)((taskId) => {
|
|
57238
57093
|
const task = readTask(kandownDir, taskId);
|
|
57239
57094
|
setDetailTask(task);
|
|
57240
57095
|
setDetailTaskId(taskId);
|
|
57241
57096
|
setDetailScroll(0);
|
|
57242
57097
|
setMode("detail");
|
|
57243
57098
|
}, [kandownDir]);
|
|
57244
|
-
const
|
|
57099
|
+
const closeContextMenu = (0, import_react37.useCallback)(() => {
|
|
57100
|
+
setCtxMenuRow(-1);
|
|
57101
|
+
setCtxMenuCursor(0);
|
|
57102
|
+
}, []);
|
|
57103
|
+
const handleAgentSelect = (0, import_react37.useCallback)((agentId) => {
|
|
57245
57104
|
const task = getFocusedTask();
|
|
57246
57105
|
const taskId = mode === "detail" ? detailTaskId : task?.id;
|
|
57247
57106
|
if (!taskId) return;
|
|
@@ -57249,12 +57108,7 @@ function Board({ kandownDir, version }) {
|
|
|
57249
57108
|
setStatusMsg(`Launching ${agentId} for ${taskId}\u2026`);
|
|
57250
57109
|
setTimeout(() => {
|
|
57251
57110
|
try {
|
|
57252
|
-
launchAgent({
|
|
57253
|
-
taskId,
|
|
57254
|
-
agentId,
|
|
57255
|
-
kandownDir,
|
|
57256
|
-
onBeforeExec: () => exit()
|
|
57257
|
-
});
|
|
57111
|
+
launchAgent({ taskId, agentId, kandownDir, onBeforeExec: () => exit() });
|
|
57258
57112
|
reloadBoard();
|
|
57259
57113
|
setStatusMsg(`${agentId} launched in tmux pane`);
|
|
57260
57114
|
setTimeout(() => setStatusMsg(""), 3e3);
|
|
@@ -57264,122 +57118,135 @@ function Board({ kandownDir, version }) {
|
|
|
57264
57118
|
}
|
|
57265
57119
|
}, 50);
|
|
57266
57120
|
}, [mode, detailTaskId, getFocusedTask, kandownDir, exit, reloadBoard]);
|
|
57267
|
-
|
|
57268
|
-
|
|
57121
|
+
useMouseMode(mode !== "agent-picker");
|
|
57122
|
+
const handleMouseClick = (0, import_react37.useCallback)((x, y) => {
|
|
57269
57123
|
if (!board) return;
|
|
57270
|
-
const { x, y } = evt;
|
|
57271
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
|
+
}
|
|
57272
57133
|
if (mode === "browse") {
|
|
57273
|
-
|
|
57274
|
-
|
|
57275
|
-
|
|
57276
|
-
|
|
57277
|
-
|
|
57278
|
-
|
|
57279
|
-
|
|
57280
|
-
|
|
57281
|
-
|
|
57282
|
-
if (task) {
|
|
57283
|
-
setContextTaskId(task.id);
|
|
57284
|
-
setMode("context-menu");
|
|
57285
|
-
}
|
|
57286
|
-
return;
|
|
57287
|
-
}
|
|
57288
|
-
}
|
|
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");
|
|
57289
57143
|
}
|
|
57290
57144
|
return;
|
|
57291
57145
|
}
|
|
57292
57146
|
if (mode === "context-menu") {
|
|
57293
|
-
|
|
57294
|
-
|
|
57295
|
-
|
|
57296
|
-
if (menuRow === 1) {
|
|
57297
|
-
if (contextTaskId) {
|
|
57298
|
-
setContextTaskId(null);
|
|
57299
|
-
openDetail(contextTaskId);
|
|
57300
|
-
}
|
|
57147
|
+
if (clickedCol < 0) {
|
|
57148
|
+
closeContextMenu();
|
|
57149
|
+
setMode("browse");
|
|
57301
57150
|
return;
|
|
57302
57151
|
}
|
|
57303
|
-
|
|
57304
|
-
|
|
57305
|
-
|
|
57306
|
-
|
|
57307
|
-
|
|
57308
|
-
|
|
57309
|
-
|
|
57310
|
-
|
|
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;
|
|
57311
57161
|
}
|
|
57312
|
-
|
|
57313
|
-
|
|
57314
|
-
|
|
57315
|
-
|
|
57316
|
-
|
|
57317
|
-
|
|
57318
|
-
|
|
57319
|
-
|
|
57320
|
-
|
|
57321
|
-
|
|
57322
|
-
|
|
57323
|
-
|
|
57324
|
-
|
|
57325
|
-
|
|
57326
|
-
|
|
57327
|
-
|
|
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");
|
|
57328
57183
|
}
|
|
57329
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;
|
|
57330
57204
|
}
|
|
57331
|
-
setContextTaskId(null);
|
|
57332
|
-
setMode("browse");
|
|
57333
57205
|
}
|
|
57206
|
+
closeContextMenu();
|
|
57207
|
+
setMode("browse");
|
|
57334
57208
|
return;
|
|
57335
57209
|
}
|
|
57336
57210
|
if (mode === "move-target") {
|
|
57337
|
-
|
|
57338
|
-
|
|
57339
|
-
|
|
57340
|
-
|
|
57341
|
-
const colEnd = colStart + layout.colWidth;
|
|
57342
|
-
const colTaskCount = board.columns[c].tasks.length;
|
|
57343
|
-
const placeholderRow = HEADER_LINES + colTaskCount;
|
|
57344
|
-
if (x >= colStart && x <= colEnd && y === placeholderRow) {
|
|
57345
|
-
const targetColName = board.columns[c].name;
|
|
57346
|
-
if (moveTaskId && targetColName) {
|
|
57347
|
-
moveTaskToColumn(kandownDir, moveTaskId, targetColName);
|
|
57348
|
-
const loaded = readBoard(kandownDir);
|
|
57349
|
-
setBoard(loaded);
|
|
57350
|
-
updateLayout(loaded);
|
|
57351
|
-
setStatusMsg(`Moved ${moveTaskId} \u2192 ${targetColName}`);
|
|
57352
|
-
setTimeout(() => setStatusMsg(""), 2e3);
|
|
57353
|
-
}
|
|
57354
|
-
setMoveTaskId(null);
|
|
57355
|
-
setMode("browse");
|
|
57356
|
-
clickedPlaceholder = true;
|
|
57357
|
-
break;
|
|
57358
|
-
}
|
|
57211
|
+
if (clickedCol < 0) {
|
|
57212
|
+
setMoveTaskId(null);
|
|
57213
|
+
setMode("browse");
|
|
57214
|
+
return;
|
|
57359
57215
|
}
|
|
57360
|
-
if (
|
|
57361
|
-
|
|
57362
|
-
|
|
57363
|
-
|
|
57364
|
-
|
|
57365
|
-
|
|
57366
|
-
|
|
57367
|
-
|
|
57368
|
-
|
|
57369
|
-
|
|
57370
|
-
|
|
57371
|
-
|
|
57372
|
-
|
|
57373
|
-
|
|
57374
|
-
|
|
57375
|
-
|
|
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);
|
|
57376
57232
|
}
|
|
57233
|
+
setMoveTaskId(null);
|
|
57234
|
+
setMode("browse");
|
|
57235
|
+
return;
|
|
57377
57236
|
}
|
|
57237
|
+
setMoveTaskId(null);
|
|
57238
|
+
setMode("browse");
|
|
57378
57239
|
return;
|
|
57379
57240
|
}
|
|
57380
|
-
}, [board, mode, colIndex,
|
|
57381
|
-
useMouse(handleMouseClick, { enabled: mode !== "agent-picker" });
|
|
57241
|
+
}, [board, mode, colIndex, rowIndex, ctxMenuRow, moveTaskId, kandownDir, updateLayout, openDetail, closeContextMenu]);
|
|
57382
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
|
+
}
|
|
57383
57250
|
if (mode === "browse") {
|
|
57384
57251
|
if (input === "q" || key.escape) {
|
|
57385
57252
|
exit();
|
|
@@ -57415,9 +57282,18 @@ function Board({ kandownDir, version }) {
|
|
|
57415
57282
|
if (task) openDetail(task.id);
|
|
57416
57283
|
return;
|
|
57417
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
|
+
}
|
|
57418
57294
|
if (input === "a") {
|
|
57419
57295
|
if (installedAgents.length === 0) {
|
|
57420
|
-
setStatusMsg("No AI agents found in PATH
|
|
57296
|
+
setStatusMsg("No AI agents found in PATH");
|
|
57421
57297
|
setTimeout(() => setStatusMsg(""), 3e3);
|
|
57422
57298
|
return;
|
|
57423
57299
|
}
|
|
@@ -57428,7 +57304,36 @@ function Board({ kandownDir, version }) {
|
|
|
57428
57304
|
}
|
|
57429
57305
|
}
|
|
57430
57306
|
if (mode === "context-menu") {
|
|
57431
|
-
|
|
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
|
+
}
|
|
57432
57337
|
}
|
|
57433
57338
|
if (mode === "move-target") {
|
|
57434
57339
|
if (key.escape || input === "q") {
|
|
@@ -57438,29 +57343,27 @@ function Board({ kandownDir, version }) {
|
|
|
57438
57343
|
}
|
|
57439
57344
|
if (input === "l" || key.rightArrow) {
|
|
57440
57345
|
if (!board) return;
|
|
57441
|
-
const
|
|
57442
|
-
const
|
|
57443
|
-
|
|
57444
|
-
setMoveTargetCol(otherCols[nextIdx] ?? 0);
|
|
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);
|
|
57445
57349
|
return;
|
|
57446
57350
|
}
|
|
57447
57351
|
if (input === "h" || key.leftArrow) {
|
|
57448
57352
|
if (!board) return;
|
|
57449
|
-
const
|
|
57450
|
-
const
|
|
57451
|
-
|
|
57452
|
-
setMoveTargetCol(otherCols[prevIdx] ?? 0);
|
|
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);
|
|
57453
57356
|
return;
|
|
57454
57357
|
}
|
|
57455
57358
|
if (key.return) {
|
|
57456
57359
|
if (!board || !moveTaskId) return;
|
|
57457
|
-
const
|
|
57458
|
-
if (
|
|
57459
|
-
moveTaskToColumn(kandownDir, moveTaskId,
|
|
57360
|
+
const name = board.columns[moveTargetCol]?.name;
|
|
57361
|
+
if (name) {
|
|
57362
|
+
moveTaskToColumn(kandownDir, moveTaskId, name);
|
|
57460
57363
|
const loaded = readBoard(kandownDir);
|
|
57461
57364
|
setBoard(loaded);
|
|
57462
57365
|
updateLayout(loaded);
|
|
57463
|
-
setStatusMsg(`Moved ${moveTaskId} \u2192 ${
|
|
57366
|
+
setStatusMsg(`Moved ${moveTaskId} \u2192 ${name}`);
|
|
57464
57367
|
setTimeout(() => setStatusMsg(""), 2e3);
|
|
57465
57368
|
}
|
|
57466
57369
|
setMoveTaskId(null);
|
|
@@ -57504,12 +57407,18 @@ function Board({ kandownDir, version }) {
|
|
|
57504
57407
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", children: [
|
|
57505
57408
|
"Run ",
|
|
57506
57409
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "cyan", children: "kandown init" }),
|
|
57507
|
-
" to set up
|
|
57410
|
+
" to set up."
|
|
57508
57411
|
] })
|
|
57509
57412
|
] });
|
|
57510
57413
|
}
|
|
57511
57414
|
const colWidth = calcColWidth(board.columns.length);
|
|
57512
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
|
+
}
|
|
57513
57422
|
if (mode === "agent-picker") {
|
|
57514
57423
|
const taskId = detailTaskId || focusedTask?.id || "";
|
|
57515
57424
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
@@ -57528,7 +57437,7 @@ function Board({ kandownDir, version }) {
|
|
|
57528
57437
|
if (mode === "detail" && detailTask) {
|
|
57529
57438
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
57530
57439
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { marginBottom: 1, justifyContent: "space-between", children: [
|
|
57531
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "Esc back
|
|
57440
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "Esc back \xB7 a agent \xB7 j/k scroll" }),
|
|
57532
57441
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: "gray", dimColor: true, children: [
|
|
57533
57442
|
"KANDOWN ",
|
|
57534
57443
|
board.title
|
|
@@ -57538,16 +57447,6 @@ function Board({ kandownDir, version }) {
|
|
|
57538
57447
|
statusMsg && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: statusMsg }) })
|
|
57539
57448
|
] });
|
|
57540
57449
|
}
|
|
57541
|
-
const contextMenuOptions = [
|
|
57542
|
-
{ id: "open", label: "Open task", icon: "\u{1F4D6}" },
|
|
57543
|
-
{ id: "move", label: "Move task", icon: "\u2197" }
|
|
57544
|
-
];
|
|
57545
|
-
let modeHint;
|
|
57546
|
-
if (mode === "context-menu") {
|
|
57547
|
-
modeHint = "\u2191/\u2193 navigate Enter confirm Esc cancel (or click)";
|
|
57548
|
-
} else if (mode === "move-target") {
|
|
57549
|
-
modeHint = "\u2190/\u2192 pick column Enter confirm Esc cancel (or click \u2193 placeholder)";
|
|
57550
|
-
}
|
|
57551
57450
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
57552
57451
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BoardHeader, { title: board.title, inTmux, modeHint, version }),
|
|
57553
57452
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { flexDirection: "row", children: board.columns.map((col, cIdx) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -57558,42 +57457,19 @@ function Board({ kandownDir, version }) {
|
|
|
57558
57457
|
focusedRow: cIdx === colIndex ? rowIndex : -1,
|
|
57559
57458
|
isFocused: cIdx === colIndex,
|
|
57560
57459
|
colWidth,
|
|
57460
|
+
contextMenuRow: mode === "context-menu" && cIdx === colIndex ? ctxMenuRow : -1,
|
|
57461
|
+
contextMenuCursor: ctxMenuCursor,
|
|
57561
57462
|
showMoveTarget: mode === "move-target" && cIdx !== colIndex,
|
|
57562
57463
|
isMoveFocused: mode === "move-target" && cIdx === moveTargetCol
|
|
57563
57464
|
},
|
|
57564
57465
|
col.name
|
|
57565
57466
|
)) }),
|
|
57566
|
-
mode === "context-menu" && contextTaskId && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { marginTop: 0, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57567
|
-
TaskContextMenu,
|
|
57568
|
-
{
|
|
57569
|
-
taskId: contextTaskId,
|
|
57570
|
-
options: contextMenuOptions,
|
|
57571
|
-
onSelect: (optionId) => {
|
|
57572
|
-
if (optionId === "open") {
|
|
57573
|
-
setContextTaskId(null);
|
|
57574
|
-
openDetail(contextTaskId);
|
|
57575
|
-
} else if (optionId === "move") {
|
|
57576
|
-
setContextTaskId(null);
|
|
57577
|
-
setMoveTaskId(contextTaskId);
|
|
57578
|
-
const target = colIndex === 0 ? Math.min(1, board.columns.length - 1) : 0;
|
|
57579
|
-
setMoveTargetCol(target);
|
|
57580
|
-
setMode("move-target");
|
|
57581
|
-
}
|
|
57582
|
-
},
|
|
57583
|
-
onCancel: () => {
|
|
57584
|
-
setContextTaskId(null);
|
|
57585
|
-
setMode("browse");
|
|
57586
|
-
},
|
|
57587
|
-
mouseX: layoutRef.current.colStarts[colIndex],
|
|
57588
|
-
mouseY: HEADER_LINES + rowIndex
|
|
57589
|
-
}
|
|
57590
|
-
) }),
|
|
57591
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: [
|
|
57592
57468
|
"Moving ",
|
|
57593
57469
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "cyan", children: moveTaskId }),
|
|
57594
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " \u2014 click
|
|
57470
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " \u2014 click " }),
|
|
57595
57471
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", bold: true, children: "\u2193" }),
|
|
57596
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: "
|
|
57472
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "gray", children: " or \u2190/\u2192 + Enter" })
|
|
57597
57473
|
] }) }),
|
|
57598
57474
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StatusBar, { message: statusMsg, task: focusedTask })
|
|
57599
57475
|
] });
|
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.7.0",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.
|