kandown 0.7.0 → 0.7.1

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