kandown 0.6.1 → 0.7.1

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