kandown 0.32.1 → 0.32.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.32.2 — 2026-07-21 — "Reliable Auto-Updater Engine"
4
+
5
+ - **Fixed**: **Auto-Updater Execution Gate** — removed command filtering logic in CLI main router that previously bypassed update checks for scripted commands like `kandown work`, `kandown list`, and `kandown doctor`.
6
+ - **Fixed**: **Home Directory Update Cache** — moved `.update-check.json` and `.update.lock` out of project/package directories to user homedir `~/.kandown/` so permissions and working directories never block registry checks.
7
+ - **Fixed**: **Compiled Version Token Precedence** — updated `getCurrentVersion()` to prioritize `KANDOWN_VERSION` over reading random `package.json` files in `CWD`, preventing false version match evaluation during auto-updates.
8
+
3
9
  ## 0.32.1 — 2026-07-21 — "CLI Modularization & Updater Speed"
4
10
 
5
11
  - **Fixed**: **Auto-Updater Check Throttle** — reduced update check throttle from 24h to 10 minutes (`UPDATE_CHECK_INTERVAL_MS = 10m`) and prioritized global `npm install -g kandown --force` for npm setups so release updates trigger promptly when launching Kandown.
package/bin/kandown.js CHANGED
@@ -6,28 +6,31 @@ import { join as join5, resolve as resolve2, basename } from "path";
6
6
  import { spawn as spawn3 } from "child_process";
7
7
 
8
8
  // src/cli/lib/updater.ts
9
- import { existsSync, readFileSync, writeFileSync, unlinkSync, statSync } from "fs";
9
+ import { existsSync, readFileSync, writeFileSync, unlinkSync, statSync, mkdirSync } from "fs";
10
10
  import { join, resolve } from "path";
11
11
  import { spawn, execSync } from "child_process";
12
12
  import { homedir } from "os";
13
13
 
14
14
  // src/lib/version.ts
15
- var KANDOWN_VERSION = "0.32.1";
15
+ var KANDOWN_VERSION = "0.32.2";
16
16
 
17
17
  // src/cli/lib/updater.ts
18
- var PKG_ROOT = resolve(import.meta.url ? new URL("../../..", import.meta.url).pathname : process.cwd());
19
- var UPDATE_CHECK_CACHE = join(PKG_ROOT, ".update-check.json");
20
- var UPDATE_CHECK_INTERVAL_MS = 10 * 60 * 1e3;
18
+ var CACHE_DIR = join(homedir(), ".kandown");
19
+ var UPDATE_CHECK_CACHE = join(CACHE_DIR, ".update-check.json");
20
+ var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
21
21
  function getCurrentVersion() {
22
+ if (KANDOWN_VERSION && KANDOWN_VERSION !== "0.0.0-dev") {
23
+ return KANDOWN_VERSION;
24
+ }
22
25
  try {
23
- const pkgPath = join(PKG_ROOT, "package.json");
26
+ const pkgPath = resolve(import.meta.url ? new URL("../../..", import.meta.url).pathname : process.cwd(), "package.json");
24
27
  if (existsSync(pkgPath)) {
25
28
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
26
29
  if (pkg.version) return pkg.version;
27
30
  }
28
31
  } catch {
29
32
  }
30
- return KANDOWN_VERSION || "0.32.0";
33
+ return KANDOWN_VERSION || "0.32.1";
31
34
  }
32
35
  function semverGt(a, b) {
33
36
  const parse = (v) => {
@@ -78,34 +81,9 @@ function resolveKandownBin() {
78
81
  }
79
82
  return null;
80
83
  }
81
- async function readInstalledKandownVersion(targetVersion) {
82
- const localVersion = getCurrentVersion();
83
- if (localVersion && semverGt(localVersion, targetVersion) >= 0) return localVersion;
84
- const bin = resolveKandownBin();
85
- if (!bin) return localVersion;
86
- return await new Promise((resolveVersion) => {
87
- const child = spawn(bin, ["--version"], {
88
- timeout: 5e3,
89
- stdio: ["pipe", "pipe", "pipe"],
90
- env: { ...process.env, KANDOWN_NO_UPDATE: "1" },
91
- detached: false
92
- });
93
- let stdout = "";
94
- child.stdout.on("data", (d) => {
95
- stdout += d;
96
- });
97
- child.stderr.on("data", () => {
98
- });
99
- child.on("error", () => resolveVersion(localVersion));
100
- child.on("close", (code) => {
101
- if (code !== 0) return resolveVersion(localVersion);
102
- const match = stdout.trim().match(/v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)/);
103
- resolveVersion(match ? match[1] : localVersion);
104
- });
105
- });
106
- }
107
84
  function updateCheckedRecently() {
108
85
  try {
86
+ if (!existsSync(UPDATE_CHECK_CACHE)) return false;
109
87
  const raw = JSON.parse(readFileSync(UPDATE_CHECK_CACHE, "utf8"));
110
88
  return Number.isFinite(raw?.lastCheck) && Date.now() - raw.lastCheck < UPDATE_CHECK_INTERVAL_MS;
111
89
  } catch {
@@ -114,7 +92,8 @@ function updateCheckedRecently() {
114
92
  }
115
93
  function rememberUpdateCheck() {
116
94
  try {
117
- writeFileSync(UPDATE_CHECK_CACHE, JSON.stringify({ lastCheck: Date.now() }), "utf8");
95
+ if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
96
+ writeFileSync(UPDATE_CHECK_CACHE, JSON.stringify({ lastCheck: Date.now(), version: getCurrentVersion() }), "utf8");
118
97
  } catch {
119
98
  }
120
99
  }
@@ -154,13 +133,11 @@ async function performGlobalPackageUpdate(packageSpec) {
154
133
  return await tryPkgCmd("bun", ["add", "-g", packageSpec]);
155
134
  }
156
135
  async function checkForUpdate(argv = process.argv) {
157
- if (existsSync(join(PKG_ROOT, "src")) && !process.env.KANDOWN_TEST_UPDATE) return;
158
136
  if (process.env.KANDOWN_NO_UPDATE === "1") return;
159
- if (!process.stdout.isTTY) return;
160
- if (updateCheckedRecently()) return;
137
+ if (updateCheckedRecently() && !process.env.KANDOWN_FORCE_UPDATE) return;
161
138
  const current = getCurrentVersion();
162
139
  if (!current) return;
163
- const lockFile = join(PKG_ROOT, ".update.lock");
140
+ const lockFile = join(CACHE_DIR, ".update.lock");
164
141
  const now = Date.now();
165
142
  try {
166
143
  if (existsSync(lockFile)) {
@@ -191,29 +168,29 @@ async function checkForUpdate(argv = process.argv) {
191
168
  });
192
169
  });
193
170
  if (!latest) return;
194
- if (semverGt(current, latest) >= 0) {
171
+ if (semverGt(latest, current) <= 0) {
195
172
  rememberUpdateCheck();
196
173
  return;
197
174
  }
198
- console.log(`\u26A1 Update available: kandown ${current} \u2192 ${latest}`);
175
+ console.log(`\x1B[36m\u26A1 Update available:\x1B[0m kandown \x1B[2mv${current}\x1B[0m \u2192 \x1B[32mv${latest}\x1B[0m`);
199
176
  try {
177
+ if (!existsSync(CACHE_DIR)) mkdirSync(CACHE_DIR, { recursive: true });
200
178
  writeFileSync(lockFile, `${process.pid}
201
179
  ${now}`, "utf8");
202
180
  } catch {
203
181
  }
182
+ console.log(`\x1B[32mInstalling kandown@${latest} globally\u2026\x1B[0m`);
204
183
  const updateOk = await performGlobalPackageUpdate(`kandown@${latest}`);
205
184
  try {
206
185
  if (existsSync(lockFile)) unlinkSync(lockFile);
207
186
  } catch {
208
187
  }
209
188
  if (!updateOk) {
210
- console.log(`\u2717 Auto-update failed \u2014 continuing with current version`);
189
+ console.log(`\x1B[33m\u2717 Auto-update failed\x1B[0m \u2014 continuing with current version`);
211
190
  return;
212
191
  }
213
- const postVersion = await readInstalledKandownVersion(latest);
214
- if (!postVersion || semverGt(postVersion, latest) < 0) return;
215
192
  rememberUpdateCheck();
216
- console.log(`\u2713 Updated to v${postVersion} \u2014 restarting\u2026`);
193
+ console.log(`\x1B[32m\u2713 Successfully updated kandown to v${latest}!\x1B[0m \u2014 restarting\u2026`);
217
194
  const bin = resolveKandownBin();
218
195
  const childArgs = ["--no-update-check", ...argv.slice(2)];
219
196
  const child = spawn(bin || process.argv[0], bin ? childArgs : [process.argv[1], ...childArgs], {
@@ -226,7 +203,7 @@ ${now}`, "utf8");
226
203
  }
227
204
 
228
205
  // src/cli/lib/board-reader.ts
229
- import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, mkdirSync, unlinkSync as unlinkSync3 } from "fs";
206
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, mkdirSync as mkdirSync2, unlinkSync as unlinkSync3 } from "fs";
230
207
  import { dirname, join as join3 } from "path";
231
208
  import { fileURLToPath } from "url";
232
209
  import { homedir as homedir2 } from "os";
@@ -566,11 +543,11 @@ function readTask(kandownDir, taskId) {
566
543
  }
567
544
  };
568
545
  }
569
- var PKG_ROOT2 = dirname(dirname(fileURLToPath(import.meta.url)));
546
+ var PKG_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
570
547
  function readAgentDoc(kandownDir) {
571
548
  const sections = [];
572
549
  try {
573
- sections.push(readFileSync3(join3(PKG_ROOT2, "templates", "AGENT_KANDOWN.md"), "utf8").trim());
550
+ sections.push(readFileSync3(join3(PKG_ROOT, "templates", "AGENT_KANDOWN.md"), "utf8").trim());
574
551
  } catch (e) {
575
552
  console.warn("[kandown] Could not read base agent rules:", e.message);
576
553
  }
@@ -637,7 +614,7 @@ function moveTaskToColumn(kandownDir, taskId, targetColumn) {
637
614
  function pushUndo(kandownDir, record) {
638
615
  try {
639
616
  const undoDir = join3(kandownDir, ".undo");
640
- if (!existsSync3(undoDir)) mkdirSync(undoDir, { recursive: true });
617
+ if (!existsSync3(undoDir)) mkdirSync2(undoDir, { recursive: true });
641
618
  const logPath = join3(undoDir, "log.json");
642
619
  let list = [];
643
620
  if (existsSync3(logPath)) {
@@ -1022,8 +999,7 @@ async function main() {
1022
999
  const cmd = args[0];
1023
1000
  const rest = args.slice(1);
1024
1001
  const skipUpdate = args.includes("--no-update-check") || process.env.KANDOWN_NO_UPDATE === "1";
1025
- const SCRIPTED = /* @__PURE__ */ new Set(["list", "ls", "show", "create", "new", "move", "assign", "commit", "work", "doctor", "projects", "export", "import"]);
1026
- if (!skipUpdate && !SCRIPTED.has(cmd)) {
1002
+ if (!skipUpdate) {
1027
1003
  await checkForUpdate(process.argv);
1028
1004
  }
1029
1005
  switch (cmd) {
package/dist/index.html CHANGED
@@ -176,7 +176,7 @@ Error generating stack: `+_.message+`
176
176
 
177
177
  ## Subtasks
178
178
 
179
- `}}async function bH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Yce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function vH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function Xce(t){if(Lt())return fS();const e=new Set;for await(const n of t.values())n.kind==="file"&&n.name.endsWith(".md")&&e.add(n.name.slice(0,-3));try{const n=await t.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&e.add(a.name.slice(0,-3))}catch{}return[...e].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Ps(t,e){if(Lt())try{const i=await gS(e);return Og(i)}catch{return FL(e)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}})(t)??await bH(t,e);return a!==null?Og(a):FL(e)}async function Qce(t,e){if(Lt())try{const a=await gS(e);return{ok:!0,task:Og(a)}}catch(a){return a.name==="NotFoundError"?{ok:!1,reason:"not-found"}:{ok:!1,reason:"unknown",error:a}}let n=null;try{n=await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{}if(n===null)try{n=await bH(t,e)}catch(a){return{ok:!1,reason:"unknown",error:a}}if(n===null)return{ok:!1,reason:"not-found"};if(n.trim()==="")return{ok:!1,reason:"corrupted",error:new Error(`Task file ${e}.md is empty`)};try{return{ok:!0,task:Og(n)}}catch(a){return{ok:!1,reason:"corrupted",error:a}}}async function cu(t,e,n,a){const i=dS(n,a);if(Lt())return zce(e,i);try{const o=await(await(await Yce(t,e)?await vH(t,!0):t).getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await o.write(i)}finally{await o.close()}}catch(r){throw Mh(r,`${e}.md`)}}async function zL(t,e){if(Lt())return Hce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Jce(t,e){await sr(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function epe(t,e){await sr(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function tpe(t,e,n,a){const i=dS(n,a);if(Lt())return Jce(e,i);try{const o=await(await(await vH(t,!0)).getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await o.write(i)}finally{await o.close()}try{await t.removeEntry(`${e}.md`)}catch{}}catch(r){throw Mh(r,`archive/${e}.md`)}}async function npe(t,e,n,a){const i=dS(n,a);if(Lt())return epe(e,i);try{const s=await(await t.getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await s.write(i)}finally{await s.close()}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}catch(r){throw Mh(r,`${e}.md`)}}const ape="kanban-md",ipe=1,sp="recentProjects";function hS(){return new Promise((t,e)=>{const n=indexedDB.open(ape,ipe);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(sp)||a.createObjectStore(sp,{keyPath:"id"})}})}async function z2(t){const e=await hS();return new Promise((n,a)=>{const i=e.transaction(sp,"readwrite");i.objectStore(sp).put(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Oy(){const t=await hS();return new Promise((e,n)=>{const i=t.transaction(sp,"readonly").objectStore(sp).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),e(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function rpe(t){const e=await hS();return new Promise((n,a)=>{const i=e.transaction(sp,"readwrite");i.objectStore(sp).delete(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function HL(t,e=!0){const n={mode:e?"readwrite":"read"};try{return await t.queryPermission(n)==="granted"||await t.requestPermission(n)==="granted"}catch(a){return console.warn("[FS] verifyPermission: handle is no longer valid:",a),!1}}function yH(t=$i){const e=t.board.columns;return e[e.length-1]??"Done"}function spe(t,e=$i){return t===yH(e)||t.toLowerCase()==="archived"}class ope{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;consecutiveErrors=0;maxConsecutiveErrors=5;disabled=!1;eventSource=null;start(e,n){this.dirHandle=e,this.tasksDirHandle=n,this.consecutiveErrors=0,this.disabled=!1,Lt()&&this.startServerSse(),e&&n&&(this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300))}startServerSse(){if(typeof window>"u"||!Lt()||this.eventSource)return;const e=window.__KANDOWN_TOKEN__,n=e?`/api/events?token=${encodeURIComponent(e)}`:"/api/events";try{this.eventSource=new EventSource(n),this.eventSource.onmessage=a=>{try{const i=JSON.parse(a.data);i.type==="change"&&(i.taskId?this.emit("taskChanged",i.taskId):(this.emit("configChanged"),this.emit("taskChanged","")))}catch{}}}catch(a){console.warn("[Watcher] EventSource init failed:",a)}}stop(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(e=>clearTimeout(e)),this.debounceTimers.clear(),this.consecutiveErrors=0,this.disabled=!1}isDisabled(){return this.disabled}on(e,n){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n),()=>{this.listeners.get(e)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!(!this.dirHandle||!this.tasksDirHandle))try{const e=await BL(this.dirHandle);e!==null&&(this.configHash=await this.hash(e)),await this.syncTaskDir(!1)}catch(e){console.warn("[Watcher] initHashes error:",e)}}async tick(){if(!(!this.dirHandle||!this.tasksDirHandle)&&!this.disabled)try{const e=await BL(this.dirHandle);if(e!==null){const n=await this.hash(e);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of[...this.knownTaskIds])try{const a=await UL(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))}}catch(a){console.warn(`[Watcher] Error reading task ${n}:`,a)}await this.syncTaskDir(!0),this.consecutiveErrors=0}catch(e){this.consecutiveErrors++,console.error(`[Watcher] Tick failed (${this.consecutiveErrors}/${this.maxConsecutiveErrors}):`,e),this.consecutiveErrors>=this.maxConsecutiveErrors&&this.disable(`File watcher stopped after ${this.maxConsecutiveErrors} consecutive errors: ${e.message}`)}}disable(e){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.disabled=!0,this.emit("watcherError",e)}debouncedEmit(e,...n){const a=e+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(e,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(e){if(!this.tasksDirHandle)return;let n;try{n=this.tasksDirHandle.values()}catch(a){throw a}for await(const a of n){if(a.kind!=="file"||!a.name.endsWith(".md"))continue;const i=a.name.replace(".md","");if(!this.knownTaskIds.has(i))try{this.knownTaskIds.add(i);const r=await UL(this.tasksDirHandle,i);r!==null&&(this.taskHashes.set(i,await this.hash(r)),e&&this.debouncedEmit("newTaskDetected",i))}catch(r){console.warn(`[Watcher] syncTaskDir: failed to read ${i}:`,r)}}}async hash(e){const a=new TextEncoder().encode(e),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(e,...n){this.listeners.get(e)?.forEach(i=>{if(e==="configChanged"){i();return}if(e==="taskChanged"){const[s]=n;i(s);return}if(e==="newTaskDetected"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function BL(t){try{return await(await(await t.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function UL(t,e){try{return await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}const Lo=new ope,cpe={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 qL=null;function _H(){return"Notification"in window?Notification.permission:"unsupported"}async function ppe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function H2({title:t,body:e,config:n}){if(n.notifications.webhookUrl&&fetch(n.notifications.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({event:"kandown_notification",title:t,body:e,timestamp:new Date().toISOString()})}).catch(()=>{}),n.notifications.sound&&lpe(n.notifications.soundId),!(!n.notifications.browser||_H()!=="granted"))try{new Notification(t,{body:e})}catch{}}function lpe(t){const e=cpe[t],n=window.AudioContext??window.webkitAudioContext;if(!(!n||e.length===0))try{qL??=new n;const a=qL;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;e.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{}}async function Lf(t,e={}){const{maxAttempts:n=3,baseDelayMs:a=500,retryableCheck:i=Dce}=e;let r;for(let s=1;s<=n;s++)try{return await t()}catch(o){if(r=o,!i(o))throw o;if(s<n){const c=a*Math.pow(2,s-1);await new Promise(p=>setTimeout(p,c))}}throw r}function dpe(t){const e=t.toLowerCase(),n=new Date;if(e==="today")return n.toISOString().split("T")[0];if(e==="tomorrow"){const i=new Date(n);return i.setDate(i.getDate()+1),i.toISOString().split("T")[0]}const a=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];if(a.includes(e)){const i=a.indexOf(e),r=new Date(n);let s=i-r.getDay();return s<=0&&(s+=7),r.setDate(r.getDate()+s),r.toISOString().split("T")[0]}return t}function upe(t){let e=t.trim(),n;const a=[];let i,r;const s=[];return e=e.replace(/(?:^|\s)p([1-4])(?:\s|$)/i,(c,p)=>(n=`P${p}`," ")),e=e.replace(/(?:^|\s)#([a-zA-Z0-9_-]+)/g,(c,p)=>(a.push(p.toLowerCase())," ")),e=e.replace(/(?:^|\s)@([a-zA-Z0-9_-]+)/g,(c,p)=>(i=p," ")),e=e.replace(/(?:^|\s)due:([^\s]+)/i,(c,p)=>(r=dpe(p)," ")),e=e.replace(/(?:^|\s)\+([a-zA-Z0-9_-]+)/g,(c,p)=>(s.push(p)," ")),{title:e.replace(/\s+/g," ").trim()||t,...n?{priority:n}:{},...a.length>0?{tags:a}:{},...i?{assignee:i}:{},...r?{due:r}:{},...s.length>0?{depends_on:s}:{}}}const mpe=/^[A-Za-z0-9_-]+$/;function B2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!mpe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function fpe(t){const e=new URLSearchParams(t.search).get("p");if(!e)return null;const[n]=e.split("/").filter(Boolean);return n||null}function Fy(t){const e=new URLSearchParams(t.search),n=B2(e.get("task"));if(n)return n;const a=e.get("p");if(a){const[,...r]=a.split("/").filter(Boolean),s=B2(r.join("/"));if(s)return s}return B2(decodeURIComponent(t.pathname))}function E3(t){return t?`/?p=${encodeURIComponent(t)}`:"/"}function bS(t,e){const n=t.replace(/^t(?=\d+$)/,""),a=`/${encodeURIComponent(n)}`;return e?`${a}?p=${encodeURIComponent(e)}`:a}function zy(t,e=!1){typeof window>"u"||`${window.location.pathname}${window.location.search}`===t||(e?window.history.replaceState({},"",t):window.history.pushState({},"",t),window.dispatchEvent(new Event("kandown:urlchange")))}function Dv(t){typeof window<"u"&&Fy(window.location)||zy(E3(t))}function gpe(t){let e=-1;for(const n of t)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>e&&(e=r)}}return"t"+(e+1)}async function hpe(){const t=await fS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await hH(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Rs(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function bpe(t){const e=await Xce(t),n=await Promise.all(e.map(async r=>{const s=await Qce(t,r);return{id:r,result:s}})),a=[],i=[];for(const{id:r,result:s}of n)if(s.ok){const o={...s.task.frontmatter,id:s.task.frontmatter.id||r,status:s.task.frontmatter.status||"Backlog"},{subtasks:c,bodyWithoutSubtasks:p}=Rs(s.task.body);a.push({id:r,frontmatter:o,body:p,subtasks:c})}else{if(s.reason==="not-found")continue;i.push(r)}return{tasks:a,failedIds:i}}async function VL(t,e,n){const a=[];for(const s of e){const o=s.name;s.tasks.forEach((c,p)=>{const d=(async()=>{const{frontmatter:m,body:f}=await Ps(t,c.id);await cu(t,c.id,{...m,id:c.id,status:o,order:p},f)})();a.push({id:c.id,promise:d})})}const i=await Promise.allSettled(a.map(s=>s.promise)),r=[];return i.forEach((s,o)=>{s.status==="rejected"&&r.push(a[o].id)}),{failedIds:r}}function cl(t){mH(t.ui.theme,t.ui.skin,t.ui.font,t.ui.background)}function T3(t){return{title:t.frontmatter.title||t.id,status:t.frontmatter.status||"Backlog",body:t.body,subtasks:t.subtasks}}function U2(t){vu.clear(),t.forEach(e=>{vu.set(e.id,T3(e))})}function vpe(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function ype(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function _pe(t,e){const n=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return t.title!==e.title||t.body!==e.body||JSON.stringify(n)!==JSON.stringify(a)}let kpe=0;const vu=new Map,jf=new Map;let q2=null;const se=wce((t,e)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],archivedTasks:[],showArchives:!1,showMetadata:!0,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,cheatsheetOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:$i,recentProjects:[],toasts:[],agentHook:null,isReloading:!1,lastReloadError:null,failedTaskIds:[],watcherError:null,hasUnsavedDrawerEdits:!1,lastSaveError:null,drawerRecoveryData:new Map,selectedTaskIds:[],toggleTaskSelection:n=>{t(a=>({selectedTaskIds:a.selectedTaskIds.includes(n)?a.selectedTaskIds.filter(s=>s!==n):[...a.selectedTaskIds,n]}))},clearTaskSelection:()=>t({selectedTaskIds:[]}),bulkMoveTasks:async n=>{const{selectedTaskIds:a,columns:i,moveTask:r}=e();for(const s of a){let o="Backlog";for(const c of i)if(c.tasks.some(p=>p.id===s)){o=c.name;break}await r(s,o,n)}t({selectedTaskIds:[]})},bulkDeleteTasks:async()=>{const{selectedTaskIds:n,deleteTask:a}=e();for(const i of n)await a(i);t({selectedTaskIds:[]})},drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{if(!fH()){const c=navigator.userAgent||"this browser";e().toast(new jce(c).message,"error",8e3);return}let n;try{n=await Vce()}catch(c){c instanceof mS?e().toast("Permission denied — please grant access to the project folder","error"):e().toast("Failed to open folder: "+c.message,"error");return}if(!n)return;const{projectHandle:a,kandownHandle:i,tasksHandle:r}=n,s=a.name;t({dirHandle:i,tasksDirHandle:r,projectName:s}),Dv(s);const o=Lt()?F2():null;try{await z2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}})}catch(c){console.warn("[Store] Failed to save recent project:",c)}await e().loadConfig(),await e().reloadBoard();try{const c=await Oy();t({recentProjects:c})}catch(c){console.warn("[Store] Failed to load recent projects:",c)}e().setupWatcher()},openRecentProject:async n=>{const a={dirHandle:e().dirHandle,tasksDirHandle:e().tasksDirHandle,projectName:e().projectName};if(!await HL(n.handle,!0)){let r=!1;try{await Ry(n.handle),r=!0}catch{r=!1}if(!r){e().toast(`"${n.name}" is no longer accessible. Removed from recent projects.`,"warning",8e3);try{await rpe(n.id);const s=await Oy();t({recentProjects:s})}catch{}return}e().toast("Permission denied — please grant access to the folder","error");return}try{const r=await Ry(n.handle),s=await $3(n.handle),o=n.handle.name;t({dirHandle:r,tasksDirHandle:s,projectName:o}),Dv(o);try{await z2({...n,lastOpened:Date.now()})}catch(c){console.warn("[Store] Failed to update recent project:",c)}await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()}catch(r){t(a),e().toast(`Failed to open project: ${r.message}`,"error")}},openServerProject:async()=>{t({loading:!0});try{const n=F2();if(!n)throw new Error("No server root");await OL();const a=vpe(n),i=await gH();cl(i);const r=await fS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:v}=await hH(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Rs(v);return{id:f,frontmatter:k,body:A,subtasks:w}}));U2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:Gd(f.body,f.subtasks)})),c=I2(o,i.board.columns),p=R2(o),d=c.reduce((f,h)=>f+h.tasks.length,0),m=new Map;if(d<=10)for(const f of s)m.set(f.frontmatter.id,{frontmatter:f.frontmatter,subtasks:f.subtasks,body:f.body});t({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),Dv(a),e().setupWatcher(),e().refreshAgentHook()}catch{t({loading:!1,isOpen:!1}),e().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!Lt())return;const n=F2();if(!n)return;await OL();const a=await Oy(),i=a.find(p=>p.kandownDir===n);if(!i){await e().openServerProject();return}if(!await HL(i.handle,!0)){await e().openServerProject();return}const s=await Ry(i.handle),o=await $3(i.handle),c=i.handle.name;t({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),Dv(c),await z2({...i,lastOpened:Date.now()}),await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=e();if(!(!n&&!Lt()))try{const a=await Zce(n);if(a.ok){t({config:a.config}),cl(a.config);return}if(a.reason==="corrupted"){if(a.rawContent&&n)try{const r=await(await n.getFileHandle("kandown.json.backup",{create:!0})).createWritable();try{await r.write(a.rawContent)}finally{await r.close()}}catch{}e().toast("kandown.json is corrupted — using default settings. A backup was saved as kandown.json.backup.","warning",1e4)}t({config:$i}),cl($i)}catch{t({config:$i}),cl($i)}},updateConfig:async n=>{const{dirHandle:a,config:i}=e();if(!a&&!Lt())return;const r=n(i);t({config:r}),cl(r);try{await Gce(a,r)}catch(s){const o=s;o instanceof To?e().toast("Disk is full — settings were not saved.","error",8e3):e().toast("Failed to save config: "+o.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=e();t({isReloading:!0,lastReloadError:null});try{if(Lt()){const i=await hpe();U2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:Gd(d.body,d.subtasks)})),s=I2(r,a.board.columns),o=R2(r);t({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});t({taskContents:p,searchMatches:new Map,failedTaskIds:[],isReloading:!1})}else if(n){const{tasks:i,failedIds:r}=await bpe(n);U2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:Gd(m.body,m.subtasks)})),o=I2(s,a.board.columns),c=R2(s);t({boardTitle:"Project Kanban",columns:o,archivedTasks:c});const p=o.reduce((m,f)=>m+f.tasks.length,0),d=new Map;if(p<=10)for(const m of i)d.set(m.frontmatter.id,{frontmatter:m.frontmatter,subtasks:m.subtasks,body:m.body});if(r.length>0){const m=r.length===1?`Task ${r[0]} could not be loaded`:`${r.length} tasks could not be loaded`;e().toast(m,"warning",8e3)}t({taskContents:d,searchMatches:new Map,failedTaskIds:r,isReloading:!1})}else t({isReloading:!1})}catch(i){const r=i.message||String(i);t({isReloading:!1,lastReloadError:`Failed to reload board: ${r}`}),e().toast(`Board reload failed — showing last loaded state (${r})`,"warning",8e3)}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o,taskContents:c,searchMatches:p}=e(),d=Lt();if(!d&&!e().tasksDirHandle)return;const m=s.find(T=>T.name===a),f=s.find(T=>T.name===i);if(!m||!f)return;const h=m.tasks.findIndex(T=>T.id===n);if(h===-1)return;const v=m.tasks[h];if(!v)return;if(spe(i,o)){const T=new Map,M=yH(o).toLowerCase();for(const D of s)for(const I of D.tasks){const H=I.frontmatter&&(I.frontmatter.archived===!0||I.frontmatter.archived==="true");T.set(I.id,{exists:!0,resolved:H||I.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of v.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const I=T.get(D);(!I||!I.resolved)&&j.push(D)}if(j.length>0){const D=j.length===1?j[0]:`${j.slice(0,-1).join(", ")} and ${j[j.length-1]}`;e().toast(`Cannot move ${n} to ${i}: blocked by ${D}`,"error");return}}const k=s.map(T=>({...T,tasks:[...T.tasks]})),w=k.find(T=>T.name===a),A=k.find(T=>T.name===i),[$]=w.tasks.splice(h,1);/done|termin|closed|complet/i.test(i)?$.checked=!0:$.checked=!1,r!==void 0?A.tasks.splice(r,0,$):A.tasks.push($),t({columns:k});try{const{tasksDirHandle:T}=e();if(!T&&!d)return;const M=a===i?k.filter(D=>D.name===i):k.filter(D=>D.name===a||D.name===i),{failedIds:j}=await Lf(()=>VL(T??null,M,o.board.columns),{maxAttempts:3});if(j.length>0){const D=j.length===1?`Could not save move for ${j[0]}`:`${j.length} tasks could not be moved`;e().toast(D,"warning",8e3),await e().reloadBoard()}}catch(T){const M=T;M instanceof To?e().toast("Disk is full — move was not saved. Free up space and try again.","error",8e3):e().toast("Failed to save: "+M.message,"error"),t({columns:s,taskContents:c,searchMatches:p})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o,taskContents:c,searchMatches:p}=e();if(!s&&!Lt())return;const d=r.map(h=>({...h,tasks:[...h.tasks]})),m=d.find(h=>h.name===n);if(!m)return;const[f]=m.tasks.splice(a,1);m.tasks.splice(i,0,f),t({columns:d});try{const{tasksDirHandle:h}=e(),v=Lt();if(!h&&!v)return;const{failedIds:k}=await Lf(()=>VL(h??null,[m],o.board.columns),{maxAttempts:3});k.length>0&&(e().toast(`Could not save reorder for ${k.length} task(s)`,"warning",8e3),await e().reloadBoard())}catch(h){const v=h;v instanceof To?e().toast("Disk is full — reorder was not saved.","error",8e3):e().toast("Failed to save: "+v.message,"error"),t({columns:r,taskContents:c,searchMatches:p})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=e();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await e().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await e().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=e();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){e().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);t({columns:p});try{const d=c.find(m=>m.name===n);if(d){const f=(await Promise.allSettled(d.tasks.map(async(h,v)=>{const{frontmatter:k,body:w}=await Ps(s,h.id);await cu(s,h.id,{...k,id:h.id,status:i,order:v},w)}))).filter(h=>h.status==="rejected").length;f>0&&e().toast(`${f} task(s) could not be renamed`,"warning",8e3)}await e().updateConfig(m=>{const f={...m.board.columnColors??{}},h=f[n.toLowerCase()];h&&(f[i.toLowerCase()]=h,delete f[n.toLowerCase()]);const v=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:v.map(k=>k.toLowerCase()===n.toLowerCase()?i:k),columnColors:f}}}),await e().reloadBoard()}catch(d){e().toast("Failed to rename column: "+d.message,"error"),t({columns:c})}},reorderColumns:async(n,a)=>{const{config:i,columns:r,tasksDirHandle:s}=e();if(n<0||a<0||n>=r.length||a>=r.length||n===a||!s&&!Lt())return;const o=Array.from(r),[c]=o.splice(n,1);o.splice(a,0,c),t({columns:o});const p=o.map(d=>d.name);try{await e().updateConfig(d=>({...d,board:{...d.board,columns:p}})),await e().reloadBoard()}catch(d){e().toast("Failed to reorder columns: "+d.message,"error"),t({columns:r})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=e();if(!i&&!Lt())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;t({columns:a.filter(o=>o.name!==n)});try{const c=(await Promise.allSettled(r.tasks.map(p=>zL(i,p.id)))).filter(p=>p.status==="rejected").length;c>0&&e().toast(`${c} task(s) could not be deleted`,"warning",8e3),await e().updateConfig(p=>{const d={...p.board.columnColors??{}};return delete d[n.toLowerCase()],{...p,board:{...p.board,columns:p.board.columns.filter(m=>m.toLowerCase()!==n.toLowerCase()),columnColors:d}}}),await e().reloadBoard(),e().toast("Column deleted")}catch(o){e().toast("Failed to delete column: "+o.message,"error"),t({columns:s})}},createTask:async(n,a)=>{const{columns:i,tasksDirHandle:r,config:s,taskContents:o,searchMatches:c}=e();if(!r&&!Lt()||!i.length)return null;const p=n||s.board.columns[0]||i[0].name,d=gpe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?upe(a):null,h={id:d,title:f?.title||"",checked:!1,dependsOn:f?.depends_on||[],tags:f?.tags||[],assignee:f?.assignee||null,priority:f?.priority||(s.fields.priority?s.board.defaultPriority:null),ownerType:s.fields.ownerType?s.board.defaultOwnerType:"",progress:null,frontmatter:{}},v=i.map($=>$.name===p?{...$,tasks:[...$.tasks,h]}:$),k=new Map(o),w={id:d,title:f?.title||"",status:p,order:m,priority:f?.priority||(s.fields.priority?s.board.defaultPriority:""),tags:f?.tags||[],assignee:f?.assignee||"",due:f?.due||"",depends_on:f?.depends_on||[],created:new Date().toISOString().slice(0,10),ownerType:s.fields.ownerType?s.board.defaultOwnerType:"",tools:""},A="";k.set(d,{frontmatter:w,subtasks:[],body:A}),t({columns:v,taskContents:k});try{const $=r||null;return await Lf(()=>cu($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const T=$;return T instanceof To?e().toast("Disk is full — task was not created.","error",8e3):e().toast("Failed to create: "+T.message,"error"),t({columns:i,taskContents:o,searchMatches:c}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r,searchMatches:s}=e();if(!i&&!Lt())return;const o=a.map(d=>({...d,tasks:d.tasks.filter(m=>m.id!==n)}));t({columns:o});const c=new Map(r);c.delete(n);const p=new Map(s);p.delete(n),t({taskContents:c,searchMatches:p});try{await zL(i||null,n),e().toast("Deleted")}catch(d){const m=d;e().toast("Failed to delete: "+m.message,"error"),t({columns:a,taskContents:r,searchMatches:s})}},archiveTask:async n=>{const{tasksDirHandle:a}=e();if(!(!a&&!Lt()))try{const{frontmatter:i,body:r}=await Ps(a||null,n);await tpe(a||null,n,{...i,archived:!0},r),e().drawerTaskId===n&&e().closeDrawer(),await e().reloadBoard(),e().toast("Archived")}catch(i){const r=i;r instanceof To?e().toast("Disk is full — task was not archived.","error",8e3):e().toast("Failed to archive: "+r.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=e();if(!(!a&&!Lt()))try{const{frontmatter:i,body:r}=await Ps(a||null,n),s={...i};delete s.archived,await npe(a||null,n,s,r),await e().reloadBoard(),e().toast("Restored")}catch(i){const r=i;r instanceof To?e().toast("Disk is full — task was not restored.","error",8e3):e().toast("Failed to restore: "+r.message,"error")}},setShowArchives:n=>t({showArchives:n}),setShowMetadata:n=>t({showMetadata:n}),openDrawer:async(n,a={})=>{const{tasksDirHandle:i,projectName:r}=e();if(!(!i&&!Lt()))try{const{frontmatter:s,body:o}=await Ps(i,n),{subtasks:c,bodyWithoutSubtasks:p}=Rs(o),d={frontmatter:s,subtasks:c,body:p,savedAt:Date.now()},m=e().drawerRecoveryData.get(n),f=m?{frontmatter:m.frontmatter,subtasks:m.subtasks,body:m.body}:{frontmatter:s,subtasks:c,body:p},h=new Map(e().drawerRecoveryData);h.delete(n),a.syncUrl!==!1&&zy(bS(n,r),a.replace),t({drawerTaskId:n,drawerData:f,drawerBaseVersion:d,conflictState:null,showConflictModal:!1,hasUnsavedDrawerEdits:!!m,lastSaveError:null,drawerRecoveryData:h}),m&&e().toast("Restored your unsaved edits for this task","info")}catch(s){e().toast("Failed to open: "+s.message,"error")}},closeDrawer:(n={})=>{n.syncUrl!==!1&&zy(E3(e().projectName),n.replace),t({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1,hasUnsavedDrawerEdits:!1,lastSaveError:null})},markDrawerDirty:()=>t({hasUnsavedDrawerEdits:!0}),forceCloseDrawer:()=>{const{drawerTaskId:n,drawerData:a}=e();if(n&&a){const i=new Map(e().drawerRecoveryData);i.set(n,{frontmatter:a.frontmatter,subtasks:a.subtasks,body:a.body}),t({drawerRecoveryData:i})}e().closeDrawer()},updateDrawerData:n=>{const{drawerData:a}=e();a&&t({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=e();if(!n||!a)return;const s=Gd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>cu(i||null,n,o,s),{maxAttempts:3}),e().toast("Saved");const c=new Map(e().drawerRecoveryData);c.delete(n),zy(E3(e().projectName)),t({drawerTaskId:null,drawerData:null,hasUnsavedDrawerEdits:!1,lastSaveError:null,drawerRecoveryData:c});const p=new Map(r);p.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),t({taskContents:p}),await e().reloadBoard()}catch(c){const p=c,d=p instanceof To?"Disk is full — your edits are kept. Free up space and retry.":"Failed to save: "+p.message;e().toast(d,"error",8e3),t({lastSaveError:d})}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=e();if(!(!n||!a))try{const s=Gd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>cu(i||null,n,o,s),{maxAttempts:3});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),t({taskContents:c,hasUnsavedDrawerEdits:!1,lastSaveError:null}),await e().reloadBoard()}catch(s){const o=s,c=o instanceof To?"Disk is full — your edits are kept.":"Failed to save: "+o.message;t({hasUnsavedDrawerEdits:!0,lastSaveError:c})}},setViewMode:n=>{localStorage.setItem("kandown:view",n),t({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),t({density:n})},setFilter:(n,a)=>{if(t(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=e(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?e().loadTaskContents(p).then(()=>{e().computeSearchMatches(o)}):e().computeSearchMatches(o)}}},clearFilters:()=>t({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>t({commandOpen:n}),setCheatsheetOpen:n=>t({cheatsheetOpen:n}),refreshAgentHook:async()=>{if(!Lt()){t({agentHook:null});return}const n=await Bce();t({agentHook:n?.agentHook??null})},sendTaskToAgent:async n=>{const a=e().agentHook;if(!a){e().toast("Agent hook not configured","error");return}e().toast(`Sending to ${a.label}…`);const i=await Uce(n);if(i===null){e().toast("Could not reach the daemon","error");return}i.ok?e().toast(`Sent to ${a.label}`):e().toast(i.error||"Agent hook failed","error")},setCurrentPage:n=>t({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=e();if(!a)return;const i=new Map(e().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Ps(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Rs(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),t({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){t({searchMatches:new Map});return}const{taskContents:a}=e(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=lH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++kpe,s=i??(a==="error"||a==="warning"?6e3:2500);t(o=>({toasts:[...o.toasts,{id:r,message:n,type:a}]})),setTimeout(()=>{t(o=>({toasts:o.toasts.filter(c=>c.id!==r)}))},s)},dismissToast:n=>t(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=e();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Ps(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Rs(p);t({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),e().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=Gd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await cu(r,s,p,c),t({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),e().toast("Overwritten remote changes")}catch(d){e().toast("Failed to overwrite: "+d.message,"error")}}}else t({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(Lt()){q2&&clearInterval(q2),q2=setInterval(()=>{e().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=e();if(!n||!a)return;Lo.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),Lo.start(n,a);const i=(s,o)=>{const c=jf.get(s);c&&clearTimeout(c);const p=Math.max(2e3,e().config.notifications.editDebounceMs),d=setTimeout(()=>{jf.delete(s);const m=e().config;m.notifications.taskEdits&&H2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);jf.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=e();if(!o)return;let p,d;try{({frontmatter:p,body:d}=await Ps(o,s))}catch(A){console.warn(`[Watcher] notifyTaskChange: failed to read ${s}:`,A);return}const{subtasks:m,bodyWithoutSubtasks:f}=Rs(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},v=T3(h),k=vu.get(s);if(!k){vu.set(s,v);return}c.notifications.statusChanges&&k.status!==v.status&&H2({title:"Task status changed",body:`${v.title}: ${k.status} → ${v.status}`,config:c});const w=ype(k.subtasks,v.subtasks);c.notifications.subtaskCompletions&&w>0&&H2({title:"Subtask completed",body:w===1?`${v.title}: 1 subtask completed.`:`${v.title}: ${w} subtasks completed.`,config:c}),_pe(k,v)&&i(s,v.title),vu.set(s,v)};Lo.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),Lo.on("taskChanged",async s=>{try{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=e();if(await r(s),o===s&&c&&p){let d,m;try{({frontmatter:d,body:m}=await Ps(p,s))}catch(T){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,T);return}const{subtasks:f,bodyWithoutSubtasks:h}=Rs(m),v=c,k=JSON.stringify(v.frontmatter)!==JSON.stringify(d),w=v.body!==h,A=JSON.stringify(v.subtasks)!==JSON.stringify(f);if(!k&&!w&&!A)return;let $="none";k&&(w||A)?$="full":k?$="metadata-only":(w||A)&&($="body-only"),t({conflictState:{taskId:s,type:$,local:v,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:$==="full"})}else await e().reloadBoard()}catch(o){console.error(`[Watcher] taskChanged handler error for ${s}:`,o)}}),Lo.on("newTaskDetected",async s=>{try{const{tasksDirHandle:o}=e();if(o){let c,p;try{({frontmatter:c,body:p}=await Ps(o,s))}catch(f){console.warn(`[Watcher] newTaskDetected: failed to read ${s}:`,f),e().toast(`New task ${s} detected but could not be loaded`,"warning");return}const{subtasks:d,bodyWithoutSubtasks:m}=Rs(p);vu.set(s,T3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}await e().reloadBoard()}catch(o){console.error(`[Watcher] newTaskDetected handler error for ${s}:`,o)}}),Lo.on("watcherError",s=>{t({watcherError:s}),e().toast(s,"warning",1e4)})},restartWatcher:()=>{const{dirHandle:n,tasksDirHandle:a}=e();!n||!a||(t({watcherError:null}),e().setupWatcher(),e().toast("File watcher restarted"))}}));Oy().then(t=>{se.setState({recentProjects:t})}).catch(t=>{console.warn("[Store] Failed to hydrate recent projects:",t)});cl($i);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{cl(se.getState().config)});const wpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:se},Symbol.toStringTag,{value:"Module"}));function xpe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return b.jsxs("button",{type:"button",className:E1("relative flex size-8 cursor-default items-center justify-center rounded-full transition-all [&_svg]:size-4",a?"text-zinc-950 dark:text-zinc-50":"text-zinc-400 hover:text-zinc-950 dark:text-zinc-500 dark:hover:text-zinc-50"),role:"radio","aria-checked":a,"aria-label":`Switch to ${n} theme`,onClick:()=>i(e),children:[t,a&&b.jsx(nt.div,{layoutId:r,transition:{type:"spring",bounce:.3,duration:.6},className:"absolute inset-0 rounded-full border border-zinc-200 dark:border-zinc-700"})]})}const Ape=[{icon:b.jsx(fce,{}),value:"auto",label:"system"},{icon:b.jsx(vce,{}),value:"light",label:"light"},{icon:b.jsx(hce,{}),value:"dark",label:"dark"}];function Cpe({value:t,onChange:e,className:n}){const a=se(d=>d.config.ui.theme),i=se(d=>d.updateConfig),r=`theme-option-${S.useId()}`,[s,o]=S.useState(!1);S.useEffect(()=>{o(!0)},[]);const c=t??a,p=e??(d=>{i(m=>({...m,ui:{...m.ui,theme:d}}))});return s?b.jsx(nt.div,{initial:{opacity:0},animate:{opacity:1},transition:{duration:.3},className:E1("inline-flex items-center overflow-hidden rounded-full bg-white ring-1 ring-zinc-200 ring-inset dark:bg-zinc-950 dark:ring-zinc-700",n),role:"radiogroup","aria-label":"Theme mode",children:Ape.map(d=>b.jsx(xpe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):b.jsx("div",{className:E1("flex h-8 w-24",n)})}function Npe(t){const e=roe(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Uz(e,a=>Math.round(a).toString())}const P3="0.32.1";function kH({className:t="w-6 h-6"}){return b.jsxs("svg",{viewBox:"0 0 150 150",className:t,version:"1.1",xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink",style:{fillRule:"evenodd",clipRule:"evenodd",strokeLinejoin:"round",strokeMiterlimit:2},children:[b.jsx("path",{d:"M148,21.594L148,128.406C148,139.22 139.22,148 128.406,148L21.594,148C10.78,148 2,139.22 2,128.406L2,21.594C2,10.78 10.78,2 21.594,2C139.22,2 148,10.78 148,21.594Z",style:{fill:"url(#_KandownLinear1)"}}),b.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[b.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:b.jsx("path",{d:"M56.491,62.601C57.2,61.897 57.602,60.925 57.606,59.908C57.627,53.936 57.7,33.2 57.7,33.2C57.6,29.7 55,27.6 52,27.6L42.075,27.6C38.544,27.6 35.673,30.547 35.644,34.201C35.528,48.996 35.327,83.484 35.956,83.013C35.956,83.013 52.072,66.994 56.491,62.601Z",style:{fill:"rgb(252,255,239)",fillRule:"nonzero"}})}),b.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:b.jsx("path",{d:"M87.6,43.8C84.2,43.9 80.5,45.1 77.7,47.5L39.2,86.2C37.1,88.3 35.8,91 35.7,93.7L35.7,116.574C35.7,117.219 36.088,117.802 36.683,118.051C37.278,118.3 37.965,118.167 38.425,117.714C50.467,105.842 98.836,58.16 110.608,46.555C111.047,46.122 111.185,45.468 110.956,44.894C110.728,44.321 110.178,43.94 109.561,43.928C106.663,43.871 103.089,43.8 103.089,43.8L87.6,43.8Z",style:{fill:"rgb(241,255,184)",fillRule:"nonzero"}})}),b.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:b.jsx("path",{d:"M38.038,101.45C35.808,101.45 34,100.387 34,99.075C34,95.326 34,88.102 34,84.58C34,84.042 34.364,83.525 35.011,83.145C35.658,82.764 36.536,82.55 37.452,82.55C46.372,82.55 69.5,82.55 69.5,82.55C69.5,82.55 69.5,77.52 69.5,74.389C69.5,73.947 69.936,73.545 70.617,73.359C71.298,73.173 72.101,73.236 72.675,73.521C80.028,77.17 98.075,86.125 104.279,89.204C104.832,89.479 105.168,89.876 105.206,90.301C105.245,90.727 104.982,91.143 104.481,91.45C98.972,94.832 83.072,104.591 75.013,109.539C74.195,110.041 72.946,110.206 71.841,109.959C70.737,109.712 69.99,109.101 69.946,108.406C69.744,105.262 69.5,101.45 69.5,101.45L38.038,101.45Z",style:{fill:"rgb(136,225,56)"}})})]}),b.jsx("defs",{children:b.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[b.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),b.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function Spe(){const{t}=Tn(),e=se(B=>B.dirHandle),n=se(B=>B.isOpen),a=se(B=>B.projectName),i=se(B=>B.closeDrawer),r=se(B=>B.drawerTaskId),s=se(B=>B.saveDrawer),o=se(B=>B.archivedTasks.length),c=se(B=>B.showArchives),p=se(B=>B.setShowArchives),d=se(B=>B.columns),m=se(B=>B.openFolder),f=se(B=>B.reloadBoard),h=se(B=>B.createTask),v=se(B=>B.setCommandOpen),k=se(B=>B.viewMode),w=se(B=>B.setViewMode),A=se(B=>B.density),$=se(B=>B.setDensity),T=se(B=>B.setCurrentPage),M=se(B=>B.recentProjects),j=se(B=>B.openRecentProject),D=se(B=>B.filters),I=se(B=>B.setFilter),H=se(B=>B.clearFilters),R=se(B=>B.config.fields),L=se(B=>B.lastReloadError),G=se(B=>B.watcherError),Y=se(B=>B.restartWatcher),[ne,U]=S.useState(!1),[F,V]=S.useState(!0),Q=S.useRef(null),J=S.useRef(null),oe=d.reduce((B,ae)=>B+ae.tasks.length,0),O=Npe(oe),Z=[];R.priority&&D.priority&&Z.push({type:"priority",label:D.priority,value:D.priority}),R.tags&&D.tag&&Z.push({type:"tag",label:"#"+D.tag,value:D.tag}),R.assignee&&D.assignee&&Z.push({type:"assignee",label:"@"+D.assignee,value:D.assignee});const K=[{label:t("filterBar.ownerAll"),value:""},{label:t("filterBar.ownerHuman"),value:"human"},{label:t("filterBar.ownerAI"),value:"ai"}],E=Z.length>0||D.search||R.ownerType&&D.ownerType;return S.useEffect(()=>{if(!ne)return;const B=ae=>{Q.current&&!Q.current.contains(ae.target)&&U(!1)};return document.addEventListener("mousedown",B),()=>document.removeEventListener("mousedown",B)},[ne]),S.useEffect(()=>{const B=setTimeout(()=>V(!1),5e3);return()=>clearTimeout(B)},[]),S.useEffect(()=>{document.title=a?`${a} · Kandown`:"Kandown"},[a]),b.jsxs(b.Fragment,{children:[(L||G)&&b.jsxs("div",{className:"flex items-center gap-2 px-5 py-1.5 bg-amber-500/10 border-b border-amber-500/30 text-[12px] text-amber-700 dark:text-amber-300",children:[b.jsx("span",{className:"flex-1 truncate",children:G?`⚠ ${G}`:`⚠ ${L}`}),G&&b.jsx("button",{type:"button",onClick:Y,className:"px-2 py-0.5 rounded bg-amber-500/20 hover:bg-amber-500/30 text-[11.5px] font-medium",children:"Restart watcher"}),b.jsx("button",{type:"button",onClick:f,className:"px-2 py-0.5 rounded bg-amber-500/20 hover:bg-amber-500/30 text-[11.5px] font-medium",children:"Reload"})]}),b.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:[b.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[b.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[b.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[b.jsx(kH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),b.jsx(Wa,{mode:"wait",initial:!1,children:F?b.jsxs(nt.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[b.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),b.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",P3]})]},"boot"):a?b.jsx(nt.span,{className:"text-[15px] font-semibold tracking-tight text-fg truncate max-w-[240px]",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:a},"project"):null})]}),r&&b.jsxs("button",{type:"button",onClick:()=>{s()},className:"hidden md:inline-flex items-center gap-2 rounded-xl border border-border bg-bg px-3 py-2 text-[13px] font-semibold text-fg shadow-sm transition-colors hover:border-border-strong hover:bg-bg-2",title:t("taskWorkspace.backToCards"),children:[b.jsx(Wn.ArrowLeft,{size:15}),b.jsx("span",{children:t("common.back")})]})]}),e&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),b.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:[b.jsx(Wn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),b.jsx("input",{ref:J,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:B=>I("search",B.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?b.jsx("button",{onClick:()=>I("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:b.jsx(Wn.X,{size:14})}):b.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"})]}),b.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[b.jsx(Wa,{children:Z.map(B=>b.jsxs(nt.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>I(B.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:[B.label,b.jsx(Wn.X,{size:10,className:"text-fg-muted/60"})]},B.type+B.value))}),R.ownerType&&b.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:K.map(B=>b.jsx("button",{onClick:()=>I("ownerType",B.value),className:`h-full px-2.5 text-[12px] transition-colors ${D.ownerType===B.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:B.label},B.value))}),E&&b.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),b.jsxs("div",{className:"relative flex-shrink-0",ref:Q,children:[b.jsxs("button",{onClick:()=>U(B=>!B),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:[b.jsx(Wn.Folder,{size:13,className:"text-fg-muted/70"}),b.jsxs("span",{className:"font-medium",children:[".",e.name]}),b.jsx(Wn.ChevronDown,{size:11,className:"opacity-50"})]}),b.jsx(Wa,{children:ne&&b.jsx(nt.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:b.jsxs("div",{className:"py-1.5",children:[M.length>0&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:t("header.recentProjects")}),M.map(B=>b.jsxs("button",{onClick:()=>{U(!1),j(B)},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:[b.jsx(Wn.Folder,{size:12,className:"text-fg-muted/60"}),b.jsx("span",{className:"truncate",children:B.name}),B.id===e.name&&b.jsx(Wn.Check,{size:12,className:"ml-auto text-emerald-500"})]},B.id)),b.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),b.jsxs("button",{onClick:()=>{U(!1),m()},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:[b.jsx(Wn.Plus,{size:12,className:"text-fg-muted/60"}),b.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),b.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[b.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),b.jsx(nt.span,{className:"tabular-nums font-medium",children:O}),b.jsx("span",{children:t("header.tasks")})]}),b.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:[b.jsx("button",{onClick:()=>w("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${k==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:t("common.board"),children:b.jsx(Wn.LayoutBoard,{size:18})}),b.jsx("button",{onClick:()=>w("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${k==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:t("common.list"),children:b.jsx(Wn.LayoutList,{size:18})})]}),b.jsx(an,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),b.jsx(an,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),b.jsx(an,{variant:"icon",icon:"Settings",onClick:()=>T("settings"),title:t("common.settings")}),b.jsx(Cpe,{}),b.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),b.jsx(an,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>v(!0),title:"Command palette (⌘K)"}),b.jsx(an,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),b.jsx(an,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):b.jsx(an,{variant:"primary",label:t("common.openFolder"),onClick:m})})]})]})}/**
179
+ `}}async function bH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Yce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function vH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function Xce(t){if(Lt())return fS();const e=new Set;for await(const n of t.values())n.kind==="file"&&n.name.endsWith(".md")&&e.add(n.name.slice(0,-3));try{const n=await t.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&e.add(a.name.slice(0,-3))}catch{}return[...e].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Ps(t,e){if(Lt())try{const i=await gS(e);return Og(i)}catch{return FL(e)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}})(t)??await bH(t,e);return a!==null?Og(a):FL(e)}async function Qce(t,e){if(Lt())try{const a=await gS(e);return{ok:!0,task:Og(a)}}catch(a){return a.name==="NotFoundError"?{ok:!1,reason:"not-found"}:{ok:!1,reason:"unknown",error:a}}let n=null;try{n=await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{}if(n===null)try{n=await bH(t,e)}catch(a){return{ok:!1,reason:"unknown",error:a}}if(n===null)return{ok:!1,reason:"not-found"};if(n.trim()==="")return{ok:!1,reason:"corrupted",error:new Error(`Task file ${e}.md is empty`)};try{return{ok:!0,task:Og(n)}}catch(a){return{ok:!1,reason:"corrupted",error:a}}}async function cu(t,e,n,a){const i=dS(n,a);if(Lt())return zce(e,i);try{const o=await(await(await Yce(t,e)?await vH(t,!0):t).getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await o.write(i)}finally{await o.close()}}catch(r){throw Mh(r,`${e}.md`)}}async function zL(t,e){if(Lt())return Hce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Jce(t,e){await sr(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function epe(t,e){await sr(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function tpe(t,e,n,a){const i=dS(n,a);if(Lt())return Jce(e,i);try{const o=await(await(await vH(t,!0)).getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await o.write(i)}finally{await o.close()}try{await t.removeEntry(`${e}.md`)}catch{}}catch(r){throw Mh(r,`archive/${e}.md`)}}async function npe(t,e,n,a){const i=dS(n,a);if(Lt())return epe(e,i);try{const s=await(await t.getFileHandle(`${e}.md`,{create:!0})).createWritable();try{await s.write(i)}finally{await s.close()}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}catch(r){throw Mh(r,`${e}.md`)}}const ape="kanban-md",ipe=1,sp="recentProjects";function hS(){return new Promise((t,e)=>{const n=indexedDB.open(ape,ipe);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(sp)||a.createObjectStore(sp,{keyPath:"id"})}})}async function z2(t){const e=await hS();return new Promise((n,a)=>{const i=e.transaction(sp,"readwrite");i.objectStore(sp).put(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Oy(){const t=await hS();return new Promise((e,n)=>{const i=t.transaction(sp,"readonly").objectStore(sp).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),e(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function rpe(t){const e=await hS();return new Promise((n,a)=>{const i=e.transaction(sp,"readwrite");i.objectStore(sp).delete(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function HL(t,e=!0){const n={mode:e?"readwrite":"read"};try{return await t.queryPermission(n)==="granted"||await t.requestPermission(n)==="granted"}catch(a){return console.warn("[FS] verifyPermission: handle is no longer valid:",a),!1}}function yH(t=$i){const e=t.board.columns;return e[e.length-1]??"Done"}function spe(t,e=$i){return t===yH(e)||t.toLowerCase()==="archived"}class ope{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;consecutiveErrors=0;maxConsecutiveErrors=5;disabled=!1;eventSource=null;start(e,n){this.dirHandle=e,this.tasksDirHandle=n,this.consecutiveErrors=0,this.disabled=!1,Lt()&&this.startServerSse(),e&&n&&(this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300))}startServerSse(){if(typeof window>"u"||!Lt()||this.eventSource)return;const e=window.__KANDOWN_TOKEN__,n=e?`/api/events?token=${encodeURIComponent(e)}`:"/api/events";try{this.eventSource=new EventSource(n),this.eventSource.onmessage=a=>{try{const i=JSON.parse(a.data);i.type==="change"&&(i.taskId?this.emit("taskChanged",i.taskId):(this.emit("configChanged"),this.emit("taskChanged","")))}catch{}}}catch(a){console.warn("[Watcher] EventSource init failed:",a)}}stop(){this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(e=>clearTimeout(e)),this.debounceTimers.clear(),this.consecutiveErrors=0,this.disabled=!1}isDisabled(){return this.disabled}on(e,n){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n),()=>{this.listeners.get(e)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!(!this.dirHandle||!this.tasksDirHandle))try{const e=await BL(this.dirHandle);e!==null&&(this.configHash=await this.hash(e)),await this.syncTaskDir(!1)}catch(e){console.warn("[Watcher] initHashes error:",e)}}async tick(){if(!(!this.dirHandle||!this.tasksDirHandle)&&!this.disabled)try{const e=await BL(this.dirHandle);if(e!==null){const n=await this.hash(e);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of[...this.knownTaskIds])try{const a=await UL(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))}}catch(a){console.warn(`[Watcher] Error reading task ${n}:`,a)}await this.syncTaskDir(!0),this.consecutiveErrors=0}catch(e){this.consecutiveErrors++,console.error(`[Watcher] Tick failed (${this.consecutiveErrors}/${this.maxConsecutiveErrors}):`,e),this.consecutiveErrors>=this.maxConsecutiveErrors&&this.disable(`File watcher stopped after ${this.maxConsecutiveErrors} consecutive errors: ${e.message}`)}}disable(e){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.disabled=!0,this.emit("watcherError",e)}debouncedEmit(e,...n){const a=e+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(e,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(e){if(!this.tasksDirHandle)return;let n;try{n=this.tasksDirHandle.values()}catch(a){throw a}for await(const a of n){if(a.kind!=="file"||!a.name.endsWith(".md"))continue;const i=a.name.replace(".md","");if(!this.knownTaskIds.has(i))try{this.knownTaskIds.add(i);const r=await UL(this.tasksDirHandle,i);r!==null&&(this.taskHashes.set(i,await this.hash(r)),e&&this.debouncedEmit("newTaskDetected",i))}catch(r){console.warn(`[Watcher] syncTaskDir: failed to read ${i}:`,r)}}}async hash(e){const a=new TextEncoder().encode(e),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(e,...n){this.listeners.get(e)?.forEach(i=>{if(e==="configChanged"){i();return}if(e==="taskChanged"){const[s]=n;i(s);return}if(e==="newTaskDetected"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function BL(t){try{return await(await(await t.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function UL(t,e){try{return await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}const Lo=new ope,cpe={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 qL=null;function _H(){return"Notification"in window?Notification.permission:"unsupported"}async function ppe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function H2({title:t,body:e,config:n}){if(n.notifications.webhookUrl&&fetch(n.notifications.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({event:"kandown_notification",title:t,body:e,timestamp:new Date().toISOString()})}).catch(()=>{}),n.notifications.sound&&lpe(n.notifications.soundId),!(!n.notifications.browser||_H()!=="granted"))try{new Notification(t,{body:e})}catch{}}function lpe(t){const e=cpe[t],n=window.AudioContext??window.webkitAudioContext;if(!(!n||e.length===0))try{qL??=new n;const a=qL;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;e.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{}}async function Lf(t,e={}){const{maxAttempts:n=3,baseDelayMs:a=500,retryableCheck:i=Dce}=e;let r;for(let s=1;s<=n;s++)try{return await t()}catch(o){if(r=o,!i(o))throw o;if(s<n){const c=a*Math.pow(2,s-1);await new Promise(p=>setTimeout(p,c))}}throw r}function dpe(t){const e=t.toLowerCase(),n=new Date;if(e==="today")return n.toISOString().split("T")[0];if(e==="tomorrow"){const i=new Date(n);return i.setDate(i.getDate()+1),i.toISOString().split("T")[0]}const a=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];if(a.includes(e)){const i=a.indexOf(e),r=new Date(n);let s=i-r.getDay();return s<=0&&(s+=7),r.setDate(r.getDate()+s),r.toISOString().split("T")[0]}return t}function upe(t){let e=t.trim(),n;const a=[];let i,r;const s=[];return e=e.replace(/(?:^|\s)p([1-4])(?:\s|$)/i,(c,p)=>(n=`P${p}`," ")),e=e.replace(/(?:^|\s)#([a-zA-Z0-9_-]+)/g,(c,p)=>(a.push(p.toLowerCase())," ")),e=e.replace(/(?:^|\s)@([a-zA-Z0-9_-]+)/g,(c,p)=>(i=p," ")),e=e.replace(/(?:^|\s)due:([^\s]+)/i,(c,p)=>(r=dpe(p)," ")),e=e.replace(/(?:^|\s)\+([a-zA-Z0-9_-]+)/g,(c,p)=>(s.push(p)," ")),{title:e.replace(/\s+/g," ").trim()||t,...n?{priority:n}:{},...a.length>0?{tags:a}:{},...i?{assignee:i}:{},...r?{due:r}:{},...s.length>0?{depends_on:s}:{}}}const mpe=/^[A-Za-z0-9_-]+$/;function B2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!mpe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function fpe(t){const e=new URLSearchParams(t.search).get("p");if(!e)return null;const[n]=e.split("/").filter(Boolean);return n||null}function Fy(t){const e=new URLSearchParams(t.search),n=B2(e.get("task"));if(n)return n;const a=e.get("p");if(a){const[,...r]=a.split("/").filter(Boolean),s=B2(r.join("/"));if(s)return s}return B2(decodeURIComponent(t.pathname))}function E3(t){return t?`/?p=${encodeURIComponent(t)}`:"/"}function bS(t,e){const n=t.replace(/^t(?=\d+$)/,""),a=`/${encodeURIComponent(n)}`;return e?`${a}?p=${encodeURIComponent(e)}`:a}function zy(t,e=!1){typeof window>"u"||`${window.location.pathname}${window.location.search}`===t||(e?window.history.replaceState({},"",t):window.history.pushState({},"",t),window.dispatchEvent(new Event("kandown:urlchange")))}function Dv(t){typeof window<"u"&&Fy(window.location)||zy(E3(t))}function gpe(t){let e=-1;for(const n of t)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>e&&(e=r)}}return"t"+(e+1)}async function hpe(){const t=await fS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await hH(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Rs(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function bpe(t){const e=await Xce(t),n=await Promise.all(e.map(async r=>{const s=await Qce(t,r);return{id:r,result:s}})),a=[],i=[];for(const{id:r,result:s}of n)if(s.ok){const o={...s.task.frontmatter,id:s.task.frontmatter.id||r,status:s.task.frontmatter.status||"Backlog"},{subtasks:c,bodyWithoutSubtasks:p}=Rs(s.task.body);a.push({id:r,frontmatter:o,body:p,subtasks:c})}else{if(s.reason==="not-found")continue;i.push(r)}return{tasks:a,failedIds:i}}async function VL(t,e,n){const a=[];for(const s of e){const o=s.name;s.tasks.forEach((c,p)=>{const d=(async()=>{const{frontmatter:m,body:f}=await Ps(t,c.id);await cu(t,c.id,{...m,id:c.id,status:o,order:p},f)})();a.push({id:c.id,promise:d})})}const i=await Promise.allSettled(a.map(s=>s.promise)),r=[];return i.forEach((s,o)=>{s.status==="rejected"&&r.push(a[o].id)}),{failedIds:r}}function cl(t){mH(t.ui.theme,t.ui.skin,t.ui.font,t.ui.background)}function T3(t){return{title:t.frontmatter.title||t.id,status:t.frontmatter.status||"Backlog",body:t.body,subtasks:t.subtasks}}function U2(t){vu.clear(),t.forEach(e=>{vu.set(e.id,T3(e))})}function vpe(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function ype(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function _pe(t,e){const n=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return t.title!==e.title||t.body!==e.body||JSON.stringify(n)!==JSON.stringify(a)}let kpe=0;const vu=new Map,jf=new Map;let q2=null;const se=wce((t,e)=>({isOpen:!1,loading:!1,dirHandle:null,projectName:null,tasksDirHandle:null,boardTitle:"Project Kanban",columns:[],archivedTasks:[],showArchives:!1,showMetadata:!0,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,cheatsheetOpen:!1,drawerTaskId:null,drawerData:null,currentPage:"board",config:$i,recentProjects:[],toasts:[],agentHook:null,isReloading:!1,lastReloadError:null,failedTaskIds:[],watcherError:null,hasUnsavedDrawerEdits:!1,lastSaveError:null,drawerRecoveryData:new Map,selectedTaskIds:[],toggleTaskSelection:n=>{t(a=>({selectedTaskIds:a.selectedTaskIds.includes(n)?a.selectedTaskIds.filter(s=>s!==n):[...a.selectedTaskIds,n]}))},clearTaskSelection:()=>t({selectedTaskIds:[]}),bulkMoveTasks:async n=>{const{selectedTaskIds:a,columns:i,moveTask:r}=e();for(const s of a){let o="Backlog";for(const c of i)if(c.tasks.some(p=>p.id===s)){o=c.name;break}await r(s,o,n)}t({selectedTaskIds:[]})},bulkDeleteTasks:async()=>{const{selectedTaskIds:n,deleteTask:a}=e();for(const i of n)await a(i);t({selectedTaskIds:[]})},drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{if(!fH()){const c=navigator.userAgent||"this browser";e().toast(new jce(c).message,"error",8e3);return}let n;try{n=await Vce()}catch(c){c instanceof mS?e().toast("Permission denied — please grant access to the project folder","error"):e().toast("Failed to open folder: "+c.message,"error");return}if(!n)return;const{projectHandle:a,kandownHandle:i,tasksHandle:r}=n,s=a.name;t({dirHandle:i,tasksDirHandle:r,projectName:s}),Dv(s);const o=Lt()?F2():null;try{await z2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}})}catch(c){console.warn("[Store] Failed to save recent project:",c)}await e().loadConfig(),await e().reloadBoard();try{const c=await Oy();t({recentProjects:c})}catch(c){console.warn("[Store] Failed to load recent projects:",c)}e().setupWatcher()},openRecentProject:async n=>{const a={dirHandle:e().dirHandle,tasksDirHandle:e().tasksDirHandle,projectName:e().projectName};if(!await HL(n.handle,!0)){let r=!1;try{await Ry(n.handle),r=!0}catch{r=!1}if(!r){e().toast(`"${n.name}" is no longer accessible. Removed from recent projects.`,"warning",8e3);try{await rpe(n.id);const s=await Oy();t({recentProjects:s})}catch{}return}e().toast("Permission denied — please grant access to the folder","error");return}try{const r=await Ry(n.handle),s=await $3(n.handle),o=n.handle.name;t({dirHandle:r,tasksDirHandle:s,projectName:o}),Dv(o);try{await z2({...n,lastOpened:Date.now()})}catch(c){console.warn("[Store] Failed to update recent project:",c)}await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()}catch(r){t(a),e().toast(`Failed to open project: ${r.message}`,"error")}},openServerProject:async()=>{t({loading:!0});try{const n=F2();if(!n)throw new Error("No server root");await OL();const a=vpe(n),i=await gH();cl(i);const r=await fS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:v}=await hH(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Rs(v);return{id:f,frontmatter:k,body:A,subtasks:w}}));U2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:Gd(f.body,f.subtasks)})),c=I2(o,i.board.columns),p=R2(o),d=c.reduce((f,h)=>f+h.tasks.length,0),m=new Map;if(d<=10)for(const f of s)m.set(f.frontmatter.id,{frontmatter:f.frontmatter,subtasks:f.subtasks,body:f.body});t({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),Dv(a),e().setupWatcher(),e().refreshAgentHook()}catch{t({loading:!1,isOpen:!1}),e().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!Lt())return;const n=F2();if(!n)return;await OL();const a=await Oy(),i=a.find(p=>p.kandownDir===n);if(!i){await e().openServerProject();return}if(!await HL(i.handle,!0)){await e().openServerProject();return}const s=await Ry(i.handle),o=await $3(i.handle),c=i.handle.name;t({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),Dv(c),await z2({...i,lastOpened:Date.now()}),await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=e();if(!(!n&&!Lt()))try{const a=await Zce(n);if(a.ok){t({config:a.config}),cl(a.config);return}if(a.reason==="corrupted"){if(a.rawContent&&n)try{const r=await(await n.getFileHandle("kandown.json.backup",{create:!0})).createWritable();try{await r.write(a.rawContent)}finally{await r.close()}}catch{}e().toast("kandown.json is corrupted — using default settings. A backup was saved as kandown.json.backup.","warning",1e4)}t({config:$i}),cl($i)}catch{t({config:$i}),cl($i)}},updateConfig:async n=>{const{dirHandle:a,config:i}=e();if(!a&&!Lt())return;const r=n(i);t({config:r}),cl(r);try{await Gce(a,r)}catch(s){const o=s;o instanceof To?e().toast("Disk is full — settings were not saved.","error",8e3):e().toast("Failed to save config: "+o.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=e();t({isReloading:!0,lastReloadError:null});try{if(Lt()){const i=await hpe();U2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:Gd(d.body,d.subtasks)})),s=I2(r,a.board.columns),o=R2(r);t({boardTitle:"Project Kanban",columns:s,archivedTasks:o});const c=s.reduce((d,m)=>d+m.tasks.length,0),p=new Map;if(c<=10)for(const d of i)p.set(d.frontmatter.id,{frontmatter:d.frontmatter,subtasks:d.subtasks,body:d.body});t({taskContents:p,searchMatches:new Map,failedTaskIds:[],isReloading:!1})}else if(n){const{tasks:i,failedIds:r}=await bpe(n);U2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:Gd(m.body,m.subtasks)})),o=I2(s,a.board.columns),c=R2(s);t({boardTitle:"Project Kanban",columns:o,archivedTasks:c});const p=o.reduce((m,f)=>m+f.tasks.length,0),d=new Map;if(p<=10)for(const m of i)d.set(m.frontmatter.id,{frontmatter:m.frontmatter,subtasks:m.subtasks,body:m.body});if(r.length>0){const m=r.length===1?`Task ${r[0]} could not be loaded`:`${r.length} tasks could not be loaded`;e().toast(m,"warning",8e3)}t({taskContents:d,searchMatches:new Map,failedTaskIds:r,isReloading:!1})}else t({isReloading:!1})}catch(i){const r=i.message||String(i);t({isReloading:!1,lastReloadError:`Failed to reload board: ${r}`}),e().toast(`Board reload failed — showing last loaded state (${r})`,"warning",8e3)}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o,taskContents:c,searchMatches:p}=e(),d=Lt();if(!d&&!e().tasksDirHandle)return;const m=s.find(T=>T.name===a),f=s.find(T=>T.name===i);if(!m||!f)return;const h=m.tasks.findIndex(T=>T.id===n);if(h===-1)return;const v=m.tasks[h];if(!v)return;if(spe(i,o)){const T=new Map,M=yH(o).toLowerCase();for(const D of s)for(const I of D.tasks){const H=I.frontmatter&&(I.frontmatter.archived===!0||I.frontmatter.archived==="true");T.set(I.id,{exists:!0,resolved:H||I.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of v.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const I=T.get(D);(!I||!I.resolved)&&j.push(D)}if(j.length>0){const D=j.length===1?j[0]:`${j.slice(0,-1).join(", ")} and ${j[j.length-1]}`;e().toast(`Cannot move ${n} to ${i}: blocked by ${D}`,"error");return}}const k=s.map(T=>({...T,tasks:[...T.tasks]})),w=k.find(T=>T.name===a),A=k.find(T=>T.name===i),[$]=w.tasks.splice(h,1);/done|termin|closed|complet/i.test(i)?$.checked=!0:$.checked=!1,r!==void 0?A.tasks.splice(r,0,$):A.tasks.push($),t({columns:k});try{const{tasksDirHandle:T}=e();if(!T&&!d)return;const M=a===i?k.filter(D=>D.name===i):k.filter(D=>D.name===a||D.name===i),{failedIds:j}=await Lf(()=>VL(T??null,M,o.board.columns),{maxAttempts:3});if(j.length>0){const D=j.length===1?`Could not save move for ${j[0]}`:`${j.length} tasks could not be moved`;e().toast(D,"warning",8e3),await e().reloadBoard()}}catch(T){const M=T;M instanceof To?e().toast("Disk is full — move was not saved. Free up space and try again.","error",8e3):e().toast("Failed to save: "+M.message,"error"),t({columns:s,taskContents:c,searchMatches:p})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o,taskContents:c,searchMatches:p}=e();if(!s&&!Lt())return;const d=r.map(h=>({...h,tasks:[...h.tasks]})),m=d.find(h=>h.name===n);if(!m)return;const[f]=m.tasks.splice(a,1);m.tasks.splice(i,0,f),t({columns:d});try{const{tasksDirHandle:h}=e(),v=Lt();if(!h&&!v)return;const{failedIds:k}=await Lf(()=>VL(h??null,[m],o.board.columns),{maxAttempts:3});k.length>0&&(e().toast(`Could not save reorder for ${k.length} task(s)`,"warning",8e3),await e().reloadBoard())}catch(h){const v=h;v instanceof To?e().toast("Disk is full — reorder was not saved.","error",8e3):e().toast("Failed to save: "+v.message,"error"),t({columns:r,taskContents:c,searchMatches:p})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=e();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await e().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await e().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=e();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){e().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);t({columns:p});try{const d=c.find(m=>m.name===n);if(d){const f=(await Promise.allSettled(d.tasks.map(async(h,v)=>{const{frontmatter:k,body:w}=await Ps(s,h.id);await cu(s,h.id,{...k,id:h.id,status:i,order:v},w)}))).filter(h=>h.status==="rejected").length;f>0&&e().toast(`${f} task(s) could not be renamed`,"warning",8e3)}await e().updateConfig(m=>{const f={...m.board.columnColors??{}},h=f[n.toLowerCase()];h&&(f[i.toLowerCase()]=h,delete f[n.toLowerCase()]);const v=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:v.map(k=>k.toLowerCase()===n.toLowerCase()?i:k),columnColors:f}}}),await e().reloadBoard()}catch(d){e().toast("Failed to rename column: "+d.message,"error"),t({columns:c})}},reorderColumns:async(n,a)=>{const{config:i,columns:r,tasksDirHandle:s}=e();if(n<0||a<0||n>=r.length||a>=r.length||n===a||!s&&!Lt())return;const o=Array.from(r),[c]=o.splice(n,1);o.splice(a,0,c),t({columns:o});const p=o.map(d=>d.name);try{await e().updateConfig(d=>({...d,board:{...d.board,columns:p}})),await e().reloadBoard()}catch(d){e().toast("Failed to reorder columns: "+d.message,"error"),t({columns:r})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=e();if(!i&&!Lt())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;t({columns:a.filter(o=>o.name!==n)});try{const c=(await Promise.allSettled(r.tasks.map(p=>zL(i,p.id)))).filter(p=>p.status==="rejected").length;c>0&&e().toast(`${c} task(s) could not be deleted`,"warning",8e3),await e().updateConfig(p=>{const d={...p.board.columnColors??{}};return delete d[n.toLowerCase()],{...p,board:{...p.board,columns:p.board.columns.filter(m=>m.toLowerCase()!==n.toLowerCase()),columnColors:d}}}),await e().reloadBoard(),e().toast("Column deleted")}catch(o){e().toast("Failed to delete column: "+o.message,"error"),t({columns:s})}},createTask:async(n,a)=>{const{columns:i,tasksDirHandle:r,config:s,taskContents:o,searchMatches:c}=e();if(!r&&!Lt()||!i.length)return null;const p=n||s.board.columns[0]||i[0].name,d=gpe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?upe(a):null,h={id:d,title:f?.title||"",checked:!1,dependsOn:f?.depends_on||[],tags:f?.tags||[],assignee:f?.assignee||null,priority:f?.priority||(s.fields.priority?s.board.defaultPriority:null),ownerType:s.fields.ownerType?s.board.defaultOwnerType:"",progress:null,frontmatter:{}},v=i.map($=>$.name===p?{...$,tasks:[...$.tasks,h]}:$),k=new Map(o),w={id:d,title:f?.title||"",status:p,order:m,priority:f?.priority||(s.fields.priority?s.board.defaultPriority:""),tags:f?.tags||[],assignee:f?.assignee||"",due:f?.due||"",depends_on:f?.depends_on||[],created:new Date().toISOString().slice(0,10),ownerType:s.fields.ownerType?s.board.defaultOwnerType:"",tools:""},A="";k.set(d,{frontmatter:w,subtasks:[],body:A}),t({columns:v,taskContents:k});try{const $=r||null;return await Lf(()=>cu($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const T=$;return T instanceof To?e().toast("Disk is full — task was not created.","error",8e3):e().toast("Failed to create: "+T.message,"error"),t({columns:i,taskContents:o,searchMatches:c}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r,searchMatches:s}=e();if(!i&&!Lt())return;const o=a.map(d=>({...d,tasks:d.tasks.filter(m=>m.id!==n)}));t({columns:o});const c=new Map(r);c.delete(n);const p=new Map(s);p.delete(n),t({taskContents:c,searchMatches:p});try{await zL(i||null,n),e().toast("Deleted")}catch(d){const m=d;e().toast("Failed to delete: "+m.message,"error"),t({columns:a,taskContents:r,searchMatches:s})}},archiveTask:async n=>{const{tasksDirHandle:a}=e();if(!(!a&&!Lt()))try{const{frontmatter:i,body:r}=await Ps(a||null,n);await tpe(a||null,n,{...i,archived:!0},r),e().drawerTaskId===n&&e().closeDrawer(),await e().reloadBoard(),e().toast("Archived")}catch(i){const r=i;r instanceof To?e().toast("Disk is full — task was not archived.","error",8e3):e().toast("Failed to archive: "+r.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=e();if(!(!a&&!Lt()))try{const{frontmatter:i,body:r}=await Ps(a||null,n),s={...i};delete s.archived,await npe(a||null,n,s,r),await e().reloadBoard(),e().toast("Restored")}catch(i){const r=i;r instanceof To?e().toast("Disk is full — task was not restored.","error",8e3):e().toast("Failed to restore: "+r.message,"error")}},setShowArchives:n=>t({showArchives:n}),setShowMetadata:n=>t({showMetadata:n}),openDrawer:async(n,a={})=>{const{tasksDirHandle:i,projectName:r}=e();if(!(!i&&!Lt()))try{const{frontmatter:s,body:o}=await Ps(i,n),{subtasks:c,bodyWithoutSubtasks:p}=Rs(o),d={frontmatter:s,subtasks:c,body:p,savedAt:Date.now()},m=e().drawerRecoveryData.get(n),f=m?{frontmatter:m.frontmatter,subtasks:m.subtasks,body:m.body}:{frontmatter:s,subtasks:c,body:p},h=new Map(e().drawerRecoveryData);h.delete(n),a.syncUrl!==!1&&zy(bS(n,r),a.replace),t({drawerTaskId:n,drawerData:f,drawerBaseVersion:d,conflictState:null,showConflictModal:!1,hasUnsavedDrawerEdits:!!m,lastSaveError:null,drawerRecoveryData:h}),m&&e().toast("Restored your unsaved edits for this task","info")}catch(s){e().toast("Failed to open: "+s.message,"error")}},closeDrawer:(n={})=>{n.syncUrl!==!1&&zy(E3(e().projectName),n.replace),t({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1,hasUnsavedDrawerEdits:!1,lastSaveError:null})},markDrawerDirty:()=>t({hasUnsavedDrawerEdits:!0}),forceCloseDrawer:()=>{const{drawerTaskId:n,drawerData:a}=e();if(n&&a){const i=new Map(e().drawerRecoveryData);i.set(n,{frontmatter:a.frontmatter,subtasks:a.subtasks,body:a.body}),t({drawerRecoveryData:i})}e().closeDrawer()},updateDrawerData:n=>{const{drawerData:a}=e();a&&t({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=e();if(!n||!a)return;const s=Gd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>cu(i||null,n,o,s),{maxAttempts:3}),e().toast("Saved");const c=new Map(e().drawerRecoveryData);c.delete(n),zy(E3(e().projectName)),t({drawerTaskId:null,drawerData:null,hasUnsavedDrawerEdits:!1,lastSaveError:null,drawerRecoveryData:c});const p=new Map(r);p.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),t({taskContents:p}),await e().reloadBoard()}catch(c){const p=c,d=p instanceof To?"Disk is full — your edits are kept. Free up space and retry.":"Failed to save: "+p.message;e().toast(d,"error",8e3),t({lastSaveError:d})}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=e();if(!(!n||!a))try{const s=Gd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>cu(i||null,n,o,s),{maxAttempts:3});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),t({taskContents:c,hasUnsavedDrawerEdits:!1,lastSaveError:null}),await e().reloadBoard()}catch(s){const o=s,c=o instanceof To?"Disk is full — your edits are kept.":"Failed to save: "+o.message;t({hasUnsavedDrawerEdits:!0,lastSaveError:c})}},setViewMode:n=>{localStorage.setItem("kandown:view",n),t({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),t({density:n})},setFilter:(n,a)=>{if(t(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=e(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?e().loadTaskContents(p).then(()=>{e().computeSearchMatches(o)}):e().computeSearchMatches(o)}}},clearFilters:()=>t({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>t({commandOpen:n}),setCheatsheetOpen:n=>t({cheatsheetOpen:n}),refreshAgentHook:async()=>{if(!Lt()){t({agentHook:null});return}const n=await Bce();t({agentHook:n?.agentHook??null})},sendTaskToAgent:async n=>{const a=e().agentHook;if(!a){e().toast("Agent hook not configured","error");return}e().toast(`Sending to ${a.label}…`);const i=await Uce(n);if(i===null){e().toast("Could not reach the daemon","error");return}i.ok?e().toast(`Sent to ${a.label}`):e().toast(i.error||"Agent hook failed","error")},setCurrentPage:n=>t({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=e();if(!a)return;const i=new Map(e().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Ps(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Rs(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),t({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){t({searchMatches:new Map});return}const{taskContents:a}=e(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=lH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++kpe,s=i??(a==="error"||a==="warning"?6e3:2500);t(o=>({toasts:[...o.toasts,{id:r,message:n,type:a}]})),setTimeout(()=>{t(o=>({toasts:o.toasts.filter(c=>c.id!==r)}))},s)},dismissToast:n=>t(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=e();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Ps(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Rs(p);t({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),e().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=Gd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await cu(r,s,p,c),t({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),e().toast("Overwritten remote changes")}catch(d){e().toast("Failed to overwrite: "+d.message,"error")}}}else t({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(Lt()){q2&&clearInterval(q2),q2=setInterval(()=>{e().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=e();if(!n||!a)return;Lo.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),Lo.start(n,a);const i=(s,o)=>{const c=jf.get(s);c&&clearTimeout(c);const p=Math.max(2e3,e().config.notifications.editDebounceMs),d=setTimeout(()=>{jf.delete(s);const m=e().config;m.notifications.taskEdits&&H2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);jf.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=e();if(!o)return;let p,d;try{({frontmatter:p,body:d}=await Ps(o,s))}catch(A){console.warn(`[Watcher] notifyTaskChange: failed to read ${s}:`,A);return}const{subtasks:m,bodyWithoutSubtasks:f}=Rs(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},v=T3(h),k=vu.get(s);if(!k){vu.set(s,v);return}c.notifications.statusChanges&&k.status!==v.status&&H2({title:"Task status changed",body:`${v.title}: ${k.status} → ${v.status}`,config:c});const w=ype(k.subtasks,v.subtasks);c.notifications.subtaskCompletions&&w>0&&H2({title:"Subtask completed",body:w===1?`${v.title}: 1 subtask completed.`:`${v.title}: ${w} subtasks completed.`,config:c}),_pe(k,v)&&i(s,v.title),vu.set(s,v)};Lo.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),Lo.on("taskChanged",async s=>{try{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=e();if(await r(s),o===s&&c&&p){let d,m;try{({frontmatter:d,body:m}=await Ps(p,s))}catch(T){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,T);return}const{subtasks:f,bodyWithoutSubtasks:h}=Rs(m),v=c,k=JSON.stringify(v.frontmatter)!==JSON.stringify(d),w=v.body!==h,A=JSON.stringify(v.subtasks)!==JSON.stringify(f);if(!k&&!w&&!A)return;let $="none";k&&(w||A)?$="full":k?$="metadata-only":(w||A)&&($="body-only"),t({conflictState:{taskId:s,type:$,local:v,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:$==="full"})}else await e().reloadBoard()}catch(o){console.error(`[Watcher] taskChanged handler error for ${s}:`,o)}}),Lo.on("newTaskDetected",async s=>{try{const{tasksDirHandle:o}=e();if(o){let c,p;try{({frontmatter:c,body:p}=await Ps(o,s))}catch(f){console.warn(`[Watcher] newTaskDetected: failed to read ${s}:`,f),e().toast(`New task ${s} detected but could not be loaded`,"warning");return}const{subtasks:d,bodyWithoutSubtasks:m}=Rs(p);vu.set(s,T3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}await e().reloadBoard()}catch(o){console.error(`[Watcher] newTaskDetected handler error for ${s}:`,o)}}),Lo.on("watcherError",s=>{t({watcherError:s}),e().toast(s,"warning",1e4)})},restartWatcher:()=>{const{dirHandle:n,tasksDirHandle:a}=e();!n||!a||(t({watcherError:null}),e().setupWatcher(),e().toast("File watcher restarted"))}}));Oy().then(t=>{se.setState({recentProjects:t})}).catch(t=>{console.warn("[Store] Failed to hydrate recent projects:",t)});cl($i);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{cl(se.getState().config)});const wpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:se},Symbol.toStringTag,{value:"Module"}));function xpe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return b.jsxs("button",{type:"button",className:E1("relative flex size-8 cursor-default items-center justify-center rounded-full transition-all [&_svg]:size-4",a?"text-zinc-950 dark:text-zinc-50":"text-zinc-400 hover:text-zinc-950 dark:text-zinc-500 dark:hover:text-zinc-50"),role:"radio","aria-checked":a,"aria-label":`Switch to ${n} theme`,onClick:()=>i(e),children:[t,a&&b.jsx(nt.div,{layoutId:r,transition:{type:"spring",bounce:.3,duration:.6},className:"absolute inset-0 rounded-full border border-zinc-200 dark:border-zinc-700"})]})}const Ape=[{icon:b.jsx(fce,{}),value:"auto",label:"system"},{icon:b.jsx(vce,{}),value:"light",label:"light"},{icon:b.jsx(hce,{}),value:"dark",label:"dark"}];function Cpe({value:t,onChange:e,className:n}){const a=se(d=>d.config.ui.theme),i=se(d=>d.updateConfig),r=`theme-option-${S.useId()}`,[s,o]=S.useState(!1);S.useEffect(()=>{o(!0)},[]);const c=t??a,p=e??(d=>{i(m=>({...m,ui:{...m.ui,theme:d}}))});return s?b.jsx(nt.div,{initial:{opacity:0},animate:{opacity:1},transition:{duration:.3},className:E1("inline-flex items-center overflow-hidden rounded-full bg-white ring-1 ring-zinc-200 ring-inset dark:bg-zinc-950 dark:ring-zinc-700",n),role:"radiogroup","aria-label":"Theme mode",children:Ape.map(d=>b.jsx(xpe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):b.jsx("div",{className:E1("flex h-8 w-24",n)})}function Npe(t){const e=roe(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Uz(e,a=>Math.round(a).toString())}const P3="0.32.2";function kH({className:t="w-6 h-6"}){return b.jsxs("svg",{viewBox:"0 0 150 150",className:t,version:"1.1",xmlns:"http://www.w3.org/2000/svg",xmlnsXlink:"http://www.w3.org/1999/xlink",style:{fillRule:"evenodd",clipRule:"evenodd",strokeLinejoin:"round",strokeMiterlimit:2},children:[b.jsx("path",{d:"M148,21.594L148,128.406C148,139.22 139.22,148 128.406,148L21.594,148C10.78,148 2,139.22 2,128.406L2,21.594C2,10.78 10.78,2 21.594,2C139.22,2 148,10.78 148,21.594Z",style:{fill:"url(#_KandownLinear1)"}}),b.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[b.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:b.jsx("path",{d:"M56.491,62.601C57.2,61.897 57.602,60.925 57.606,59.908C57.627,53.936 57.7,33.2 57.7,33.2C57.6,29.7 55,27.6 52,27.6L42.075,27.6C38.544,27.6 35.673,30.547 35.644,34.201C35.528,48.996 35.327,83.484 35.956,83.013C35.956,83.013 52.072,66.994 56.491,62.601Z",style:{fill:"rgb(252,255,239)",fillRule:"nonzero"}})}),b.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:b.jsx("path",{d:"M87.6,43.8C84.2,43.9 80.5,45.1 77.7,47.5L39.2,86.2C37.1,88.3 35.8,91 35.7,93.7L35.7,116.574C35.7,117.219 36.088,117.802 36.683,118.051C37.278,118.3 37.965,118.167 38.425,117.714C50.467,105.842 98.836,58.16 110.608,46.555C111.047,46.122 111.185,45.468 110.956,44.894C110.728,44.321 110.178,43.94 109.561,43.928C106.663,43.871 103.089,43.8 103.089,43.8L87.6,43.8Z",style:{fill:"rgb(241,255,184)",fillRule:"nonzero"}})}),b.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:b.jsx("path",{d:"M38.038,101.45C35.808,101.45 34,100.387 34,99.075C34,95.326 34,88.102 34,84.58C34,84.042 34.364,83.525 35.011,83.145C35.658,82.764 36.536,82.55 37.452,82.55C46.372,82.55 69.5,82.55 69.5,82.55C69.5,82.55 69.5,77.52 69.5,74.389C69.5,73.947 69.936,73.545 70.617,73.359C71.298,73.173 72.101,73.236 72.675,73.521C80.028,77.17 98.075,86.125 104.279,89.204C104.832,89.479 105.168,89.876 105.206,90.301C105.245,90.727 104.982,91.143 104.481,91.45C98.972,94.832 83.072,104.591 75.013,109.539C74.195,110.041 72.946,110.206 71.841,109.959C70.737,109.712 69.99,109.101 69.946,108.406C69.744,105.262 69.5,101.45 69.5,101.45L38.038,101.45Z",style:{fill:"rgb(136,225,56)"}})})]}),b.jsx("defs",{children:b.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[b.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),b.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function Spe(){const{t}=Tn(),e=se(B=>B.dirHandle),n=se(B=>B.isOpen),a=se(B=>B.projectName),i=se(B=>B.closeDrawer),r=se(B=>B.drawerTaskId),s=se(B=>B.saveDrawer),o=se(B=>B.archivedTasks.length),c=se(B=>B.showArchives),p=se(B=>B.setShowArchives),d=se(B=>B.columns),m=se(B=>B.openFolder),f=se(B=>B.reloadBoard),h=se(B=>B.createTask),v=se(B=>B.setCommandOpen),k=se(B=>B.viewMode),w=se(B=>B.setViewMode),A=se(B=>B.density),$=se(B=>B.setDensity),T=se(B=>B.setCurrentPage),M=se(B=>B.recentProjects),j=se(B=>B.openRecentProject),D=se(B=>B.filters),I=se(B=>B.setFilter),H=se(B=>B.clearFilters),R=se(B=>B.config.fields),L=se(B=>B.lastReloadError),G=se(B=>B.watcherError),Y=se(B=>B.restartWatcher),[ne,U]=S.useState(!1),[F,V]=S.useState(!0),Q=S.useRef(null),J=S.useRef(null),oe=d.reduce((B,ae)=>B+ae.tasks.length,0),O=Npe(oe),Z=[];R.priority&&D.priority&&Z.push({type:"priority",label:D.priority,value:D.priority}),R.tags&&D.tag&&Z.push({type:"tag",label:"#"+D.tag,value:D.tag}),R.assignee&&D.assignee&&Z.push({type:"assignee",label:"@"+D.assignee,value:D.assignee});const K=[{label:t("filterBar.ownerAll"),value:""},{label:t("filterBar.ownerHuman"),value:"human"},{label:t("filterBar.ownerAI"),value:"ai"}],E=Z.length>0||D.search||R.ownerType&&D.ownerType;return S.useEffect(()=>{if(!ne)return;const B=ae=>{Q.current&&!Q.current.contains(ae.target)&&U(!1)};return document.addEventListener("mousedown",B),()=>document.removeEventListener("mousedown",B)},[ne]),S.useEffect(()=>{const B=setTimeout(()=>V(!1),5e3);return()=>clearTimeout(B)},[]),S.useEffect(()=>{document.title=a?`${a} · Kandown`:"Kandown"},[a]),b.jsxs(b.Fragment,{children:[(L||G)&&b.jsxs("div",{className:"flex items-center gap-2 px-5 py-1.5 bg-amber-500/10 border-b border-amber-500/30 text-[12px] text-amber-700 dark:text-amber-300",children:[b.jsx("span",{className:"flex-1 truncate",children:G?`⚠ ${G}`:`⚠ ${L}`}),G&&b.jsx("button",{type:"button",onClick:Y,className:"px-2 py-0.5 rounded bg-amber-500/20 hover:bg-amber-500/30 text-[11.5px] font-medium",children:"Restart watcher"}),b.jsx("button",{type:"button",onClick:f,className:"px-2 py-0.5 rounded bg-amber-500/20 hover:bg-amber-500/30 text-[11.5px] font-medium",children:"Reload"})]}),b.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:[b.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[b.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[b.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[b.jsx(kH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),b.jsx(Wa,{mode:"wait",initial:!1,children:F?b.jsxs(nt.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[b.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),b.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",P3]})]},"boot"):a?b.jsx(nt.span,{className:"text-[15px] font-semibold tracking-tight text-fg truncate max-w-[240px]",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:a},"project"):null})]}),r&&b.jsxs("button",{type:"button",onClick:()=>{s()},className:"hidden md:inline-flex items-center gap-2 rounded-xl border border-border bg-bg px-3 py-2 text-[13px] font-semibold text-fg shadow-sm transition-colors hover:border-border-strong hover:bg-bg-2",title:t("taskWorkspace.backToCards"),children:[b.jsx(Wn.ArrowLeft,{size:15}),b.jsx("span",{children:t("common.back")})]})]}),e&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),b.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:[b.jsx(Wn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),b.jsx("input",{ref:J,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:B=>I("search",B.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?b.jsx("button",{onClick:()=>I("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:b.jsx(Wn.X,{size:14})}):b.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"})]}),b.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[b.jsx(Wa,{children:Z.map(B=>b.jsxs(nt.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>I(B.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:[B.label,b.jsx(Wn.X,{size:10,className:"text-fg-muted/60"})]},B.type+B.value))}),R.ownerType&&b.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:K.map(B=>b.jsx("button",{onClick:()=>I("ownerType",B.value),className:`h-full px-2.5 text-[12px] transition-colors ${D.ownerType===B.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:B.label},B.value))}),E&&b.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),b.jsxs("div",{className:"relative flex-shrink-0",ref:Q,children:[b.jsxs("button",{onClick:()=>U(B=>!B),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:[b.jsx(Wn.Folder,{size:13,className:"text-fg-muted/70"}),b.jsxs("span",{className:"font-medium",children:[".",e.name]}),b.jsx(Wn.ChevronDown,{size:11,className:"opacity-50"})]}),b.jsx(Wa,{children:ne&&b.jsx(nt.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:b.jsxs("div",{className:"py-1.5",children:[M.length>0&&b.jsxs(b.Fragment,{children:[b.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:t("header.recentProjects")}),M.map(B=>b.jsxs("button",{onClick:()=>{U(!1),j(B)},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:[b.jsx(Wn.Folder,{size:12,className:"text-fg-muted/60"}),b.jsx("span",{className:"truncate",children:B.name}),B.id===e.name&&b.jsx(Wn.Check,{size:12,className:"ml-auto text-emerald-500"})]},B.id)),b.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),b.jsxs("button",{onClick:()=>{U(!1),m()},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:[b.jsx(Wn.Plus,{size:12,className:"text-fg-muted/60"}),b.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),b.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?b.jsxs(b.Fragment,{children:[b.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[b.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),b.jsx(nt.span,{className:"tabular-nums font-medium",children:O}),b.jsx("span",{children:t("header.tasks")})]}),b.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:[b.jsx("button",{onClick:()=>w("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${k==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:t("common.board"),children:b.jsx(Wn.LayoutBoard,{size:18})}),b.jsx("button",{onClick:()=>w("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${k==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:t("common.list"),children:b.jsx(Wn.LayoutList,{size:18})})]}),b.jsx(an,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),b.jsx(an,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),b.jsx(an,{variant:"icon",icon:"Settings",onClick:()=>T("settings"),title:t("common.settings")}),b.jsx(Cpe,{}),b.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),b.jsx(an,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>v(!0),title:"Command palette (⌘K)"}),b.jsx(an,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),b.jsx(an,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):b.jsx(an,{variant:"primary",label:t("common.openFolder"),onClick:m})})]})]})}/**
180
180
  * @license @tabler/icons-react v3.41.1 - MIT
181
181
  *
182
182
  * 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.32.1",
3
+ "version": "0.32.2",
4
4
  "description": "File-based Kanban engine backed by Markdown. Zero backend, AI-agent friendly.",
5
5
  "type": "module",
6
6
  "bin": {