kandown 0.28.1 → 0.29.0

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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.29.0 — 2026-07-20 — "Global Daemon Refresh"
4
+
5
+ - **Added**: **Global daemon refresh command** — added `kandown daemon refresh-all`, which scans every active local Kandown daemon across the configured port range, refreshes each open project's `.kandown/kandown.html`, and restarts any daemon running an older CLI version.
6
+ - **Added**: **Forced daemon reboot aliases** — added `kandown daemon restart-all` and `kandown daemon refresh-all --force` for cases where every open Kandown daemon should be restarted, even when versions already match.
7
+ - **Changed**: **Safer post-update refresh flow** — refactored the existing auto-update refresh path to reuse the same daemon scan/restart helper, keeping automatic package-update behavior aligned with the new explicit global command.
8
+ - **Changed**: **Daemon CLI documentation** — updated CLI help and the README command table to surface `daemon refresh-all` as the recommended fix when old project sessions remain open after a global update.
9
+ - **Fixed**: **Outdated open project daemons after global updates** — users no longer need to manually find and restart each project when a global Kandown update succeeds but already-running daemons continue serving older code in memory.
10
+
3
11
  ## 0.28.1 — 2026-07-20 — "Robust Global Auto-Updater"
4
12
 
5
13
  - **Fixed**: **Global package manager auto-update commands** — updated `checkForUpdate()` and `cmdUpdate()` to run `pnpm add -g kandown@latest` (instead of `pnpm install -g kandown`), with fallback chain across `pnpm`, `npm`, `yarn`, and `bun`.
package/README.md CHANGED
@@ -91,6 +91,7 @@ The web daemon stays alive after you quit the TUI so the browser keeps working.
91
91
  | `kandown daemon status` | Show this project's web daemon status |
92
92
  | `kandown daemon start` | Start/reconnect this project's web daemon |
93
93
  | `kandown daemon stop` | Stop this project's web daemon |
94
+ | `kandown daemon refresh-all` | Refresh open projects and restart outdated daemons with the current CLI version |
94
95
  | `kandown update` | Update `kandown.html` to latest |
95
96
  | `kandown list` \| `show` \| `create` \| `move` \| `assign` \| `commit` | One-shot task commands (see below) |
96
97
  | `kandown tasks` | Full help for the one-shot task commands |
package/bin/kandown.js CHANGED
@@ -574,7 +574,7 @@ ${c.bold}Commands:${c.reset}
574
574
  ${c.cyan}board${c.reset} Open the interactive kanban board in the terminal
575
575
  ${c.cyan}init${c.reset} Initialize .kandown/ in the current directory
576
576
  ${c.cyan}settings${c.reset} Open the settings TUI
577
- ${c.cyan}daemon${c.reset} Manage the per-project web daemon (start|stop|status)
577
+ ${c.cyan}daemon${c.reset} Manage web daemons (start|stop|status|refresh-all)
578
578
  ${c.cyan}update${c.reset} Update kandown.html to the latest version
579
579
  ${c.cyan}list${c.reset} List tasks ${c.dim}(-s status, -a assignee, -t tag, -p priority, --json)${c.reset}
580
580
  ${c.cyan}show${c.reset} Print a task's raw file content
@@ -594,6 +594,7 @@ ${c.bold}Examples:${c.reset}
594
594
  ${c.dim}$${c.reset} npx kandown --port 3000 ${c.dim}# use a specific web UI port${c.reset}
595
595
  ${c.dim}$${c.reset} npx kandown board ${c.dim}# board TUI only${c.reset}
596
596
  ${c.dim}$${c.reset} npx kandown daemon stop ${c.dim}# stop this project's web daemon${c.reset}
597
+ ${c.dim}$${c.reset} npx kandown daemon refresh-all ${c.dim}# refresh/restart outdated open daemons${c.reset}
597
598
  ${c.dim}$${c.reset} npx kandown init
598
599
  ${c.dim}$${c.reset} npx kandown init --path docs/kanban
599
600
  ${c.dim}$${c.reset} npx kandown init --no-agents
@@ -1946,35 +1947,62 @@ function refreshKandownHtml(kandownDir) {
1946
1947
  return false;
1947
1948
  }
1948
1949
 
1949
- async function refreshRunningProjectHtml() {
1950
+ function daemonScanPorts() {
1950
1951
  const ports = [];
1951
1952
  for (let port = START_PORT_RANGE; port <= END_PORT_RANGE; port++) {
1952
1953
  if (!isBrowserUnsafePort(port)) ports.push(port);
1953
1954
  }
1955
+ return ports;
1956
+ }
1954
1957
 
1955
- const daemons = await Promise.all(ports.map(port => fetchDaemonInfo(port)));
1956
- const kandownDirs = new Set(
1957
- daemons
1958
- .map(daemon => daemon?.kandownDir)
1959
- .filter(dir => typeof dir === 'string' && dir.length > 0)
1960
- );
1958
+ async function listRunningDaemons() {
1959
+ const daemons = await Promise.all(daemonScanPorts().map(port => fetchDaemonInfo(port)));
1960
+ const byDir = new Map();
1961
+ for (const daemon of daemons) {
1962
+ if (!daemon || typeof daemon.kandownDir !== 'string' || daemon.kandownDir.length === 0) continue;
1963
+ if (!byDir.has(daemon.kandownDir)) byDir.set(daemon.kandownDir, daemon);
1964
+ }
1965
+ return [...byDir.values()];
1966
+ }
1961
1967
 
1968
+ async function refreshRunningDaemons({ forceRestart = false } = {}) {
1969
+ const daemons = await listRunningDaemons();
1962
1970
  const currentVersion = getCurrentVersion();
1963
- let refreshed = 0;
1964
- for (const kandownDir of kandownDirs) {
1971
+ const results = {
1972
+ found: daemons.length,
1973
+ refreshed: 0,
1974
+ restarted: 0,
1975
+ skipped: 0,
1976
+ failed: [],
1977
+ };
1978
+
1979
+ for (const daemon of daemons) {
1980
+ const kandownDir = daemon.kandownDir;
1965
1981
  try {
1966
- if (refreshKandownHtml(kandownDir)) refreshed++;
1982
+ if (refreshKandownHtml(kandownDir)) results.refreshed++;
1967
1983
  const current = await getDaemonStatus(kandownDir);
1968
- if (current.running) {
1969
- const daemonVersion = current.metadata?.version || null;
1970
- if (!daemonVersion || (currentVersion && semverGt(currentVersion, daemonVersion) > 0)) {
1971
- await stopDaemon(kandownDir);
1972
- await startDaemon(kandownDir, current.metadata?.port || null);
1973
- }
1984
+ const daemonVersion = daemon.version || current.metadata?.version || null;
1985
+ const shouldRestart = forceRestart || !daemonVersion || (currentVersion && semverGt(currentVersion, daemonVersion) > 0);
1986
+ if (!shouldRestart) {
1987
+ results.skipped++;
1988
+ continue;
1974
1989
  }
1975
- } catch { /* best-effort: one locked project must not block restart */ }
1990
+ const preferredPort = daemon.port || current.metadata?.port || null;
1991
+ await stopDaemon(kandownDir);
1992
+ const restarted = await startDaemon(kandownDir, preferredPort);
1993
+ if (!restarted.running) throw new Error('daemon failed to restart');
1994
+ results.restarted++;
1995
+ } catch (e) {
1996
+ results.failed.push({ kandownDir, error: e.message });
1997
+ }
1976
1998
  }
1977
- return refreshed;
1999
+
2000
+ return results;
2001
+ }
2002
+
2003
+ async function refreshRunningProjectHtml() {
2004
+ const results = await refreshRunningDaemons({ forceRestart: false });
2005
+ return results.refreshed;
1978
2006
  }
1979
2007
 
1980
2008
  async function waitForDaemon(kandownDir, timeoutMs = 8000) {
@@ -2978,6 +3006,21 @@ function detectStaleKandown(port, currentKandownDir) {
2978
3006
 
2979
3007
  async function cmdDaemon(rawArgs) {
2980
3008
  const [subcommand = 'status', ...rest] = rawArgs;
3009
+
3010
+ if (subcommand === 'refresh-all' || subcommand === 'restart-all') {
3011
+ const forceRestart = rest.includes('--force') || subcommand === 'restart-all';
3012
+ const results = await refreshRunningDaemons({ forceRestart });
3013
+ success(`Scanned ${results.found} running daemon${results.found === 1 ? '' : 's'}`);
3014
+ info(`Refreshed ${results.refreshed} kandown.html bundle${results.refreshed === 1 ? '' : 's'}`);
3015
+ info(`${forceRestart ? 'Restarted' : 'Restarted outdated'} ${results.restarted} daemon${results.restarted === 1 ? '' : 's'}`);
3016
+ if (!forceRestart && results.skipped > 0) info(`Skipped ${results.skipped} already-current daemon${results.skipped === 1 ? '' : 's'}`);
3017
+ if (results.failed.length > 0) {
3018
+ for (const failure of results.failed) warn(`${failure.kandownDir}: ${failure.error}`);
3019
+ process.exit(1);
3020
+ }
3021
+ return;
3022
+ }
3023
+
2981
3024
  const { kandownDir } = ensureKandownDir(rest);
2982
3025
  const preferredPort = parsePort(parseArgs(rest).port);
2983
3026
 
@@ -3039,7 +3082,7 @@ async function cmdDaemon(rawArgs) {
3039
3082
  }
3040
3083
 
3041
3084
  err(`Unknown daemon command: ${subcommand}`);
3042
- log(` Use ${c.cyan}kandown daemon start|stop|status${c.reset}`);
3085
+ log(` Use ${c.cyan}kandown daemon start|stop|status|refresh-all${c.reset}`);
3043
3086
  process.exit(1);
3044
3087
  }
3045
3088
 
package/dist/index.html CHANGED
@@ -176,7 +176,7 @@ Error generating stack: `+_.message+`
176
176
 
177
177
  ## Subtasks
178
178
 
179
- `}}async function cH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Uce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function pH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function qce(t){if(Pt())return uS();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 Es(t,e){if(Pt())try{const i=await mS(e);return Og(i)}catch{return jj(e)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}})(t)??await cH(t,e);return a!==null?Og(a):jj(e)}async function Vce(t,e){if(Pt())try{const a=await mS(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 cH(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 ru(t,e,n,a){const i=lS(n,a);if(Pt())return Lce(e,i);try{const o=await(await(await Uce(t,e)?await pH(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 Dj(t,e){if(Pt())return jce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Zce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Gce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Wce(t,e,n,a){const i=lS(n,a);if(Pt())return Zce(e,i);try{const o=await(await(await pH(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 Kce(t,e,n,a){const i=lS(n,a);if(Pt())return Gce(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 Yce="kanban-md",Xce=1,ip="recentProjects";function fS(){return new Promise((t,e)=>{const n=indexedDB.open(Yce,Xce);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(ip)||a.createObjectStore(ip,{keyPath:"id"})}})}async function F2(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ip,"readwrite");i.objectStore(ip).put(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Oy(){const t=await fS();return new Promise((e,n)=>{const i=t.transaction(ip,"readonly").objectStore(ip).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 Qce(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ip,"readwrite");i.objectStore(ip).delete(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Ij(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 lH(t=Ni){const e=t.board.columns;return e[e.length-1]??"Done"}function Jce(t,e=Ni){return t===lH(e)||t.toLowerCase()==="archived"}class epe{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,Pt()&&this.startServerSse(),e&&n&&(this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300))}startServerSse(){if(typeof window>"u"||!Pt()||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 Rj(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 Rj(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 Oj(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 Oj(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 Rj(t){try{return await(await(await t.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function Oj(t,e){try{return await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}const Po=new epe,tpe={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 Fj=null;function dH(){return"Notification"in window?Notification.permission:"unsupported"}async function npe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function z2({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&&ape(n.notifications.soundId),!(!n.notifications.browser||dH()!=="granted"))try{new Notification(t,{body:e})}catch{}}function ape(t){const e=tpe[t],n=window.AudioContext??window.webkitAudioContext;if(!(!n||e.length===0))try{Fj??=new n;const a=Fj;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=$ce}=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 ipe(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 rpe(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=ipe(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 spe=/^[A-Za-z0-9_-]+$/;function H2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!spe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function ope(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=H2(e.get("task"));if(n)return n;const a=e.get("p");if(a){const[,...r]=a.split("/").filter(Boolean),s=H2(r.join("/"));if(s)return s}return H2(decodeURIComponent(t.pathname))}function $3(t){return t?`/?p=${encodeURIComponent(t)}`:"/"}function gS(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($3(t))}function cpe(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 ppe(){const t=await uS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await oH(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ds(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function lpe(t){const e=await qce(t),n=await Promise.all(e.map(async r=>{const s=await Vce(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}=Ds(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 zj(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 Es(t,c.id);await ru(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 sl(t){Nce(t.ui.theme,t.ui.skin,t.ui.font,t.ui.background)}function E3(t){return{title:t.frontmatter.title||t.id,status:t.frontmatter.status||"Backlog",body:t.body,subtasks:t.subtasks}}function B2(t){gu.clear(),t.forEach(e=>{gu.set(e.id,E3(e))})}function dpe(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function upe(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function mpe(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 fpe=0;const gu=new Map,jf=new Map;let U2=null;const se=bce((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:Ni,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(!rH()){const c=navigator.userAgent||"this browser";e().toast(new Sce(c).message,"error",8e3);return}let n;try{n=await Oce()}catch(c){c instanceof dS?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=Pt()?O2():null;try{await F2({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 Ij(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 Qce(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 S3(n.handle),o=n.handle.name;t({dirHandle:r,tasksDirHandle:s,projectName:o}),Dv(o);try{await F2({...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=O2();if(!n)throw new Error("No server root");await Lj();const a=dpe(n),i=await sH();sl(i);const r=await uS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await oH(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Ds(b);return{id:f,frontmatter:k,body:A,subtasks:w}}));B2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:qd(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(!Pt())return;const n=O2();if(!n)return;await Lj();const a=await Oy(),i=a.find(p=>p.kandownDir===n);if(!i){await e().openServerProject();return}if(!await Ij(i.handle,!0)){await e().openServerProject();return}const s=await Ry(i.handle),o=await S3(i.handle),c=i.handle.name;t({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),Dv(c),await F2({...i,lastOpened:Date.now()}),await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=e();if(!(!n&&!Pt()))try{const a=await Fce(n);if(a.ok){t({config:a.config}),sl(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:Ni}),sl(Ni)}catch{t({config:Ni}),sl(Ni)}},updateConfig:async n=>{const{dirHandle:a,config:i}=e();if(!a&&!Pt())return;const r=n(i);t({config:r}),sl(r);try{await zce(a,r)}catch(s){const o=s;o instanceof $o?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(Pt()){const i=await ppe();B2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:qd(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 lpe(n);B2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:qd(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=Pt();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 b=m.tasks[h];if(!b)return;if(Jce(i,o)){const T=new Map,M=lH(o).toLowerCase();for(const D of s)for(const R of D.tasks){const H=R.frontmatter&&(R.frontmatter.archived===!0||R.frontmatter.archived==="true");T.set(R.id,{exists:!0,resolved:H||R.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of b.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const R=T.get(D);(!R||!R.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(()=>zj(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 $o?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&&!Pt())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(),b=Pt();if(!h&&!b)return;const{failedIds:k}=await Lf(()=>zj(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 b=h;b instanceof $o?e().toast("Disk is full — reorder was not saved.","error",8e3):e().toast("Failed to save: "+b.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,b)=>{const{frontmatter:k,body:w}=await Es(s,h.id);await ru(s,h.id,{...k,id:h.id,status:i,order:b},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 b=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.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&&!Pt())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&&!Pt())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=>Dj(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&&!Pt()||!i.length)return null;const p=n||s.board.columns[0]||i[0].name,d=cpe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?rpe(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:{}},b=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:b,taskContents:k});try{const $=r||null;return await Lf(()=>ru($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const T=$;return T instanceof $o?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&&!Pt())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 Dj(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&&!Pt()))try{const{frontmatter:i,body:r}=await Es(a||null,n);await Wce(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 $o?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&&!Pt()))try{const{frontmatter:i,body:r}=await Es(a||null,n),s={...i};delete s.archived,await Kce(a||null,n,s,r),await e().reloadBoard(),e().toast("Restored")}catch(i){const r=i;r instanceof $o?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&&!Pt()))try{const{frontmatter:s,body:o}=await Es(i,n),{subtasks:c,bodyWithoutSubtasks:p}=Ds(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(gS(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($3(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=qd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>ru(i||null,n,o,s),{maxAttempts:3}),e().toast("Saved");const c=new Map(e().drawerRecoveryData);c.delete(n),zy($3(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 $o?"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=qd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>ru(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 $o?"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(!Pt()){t({agentHook:null});return}const n=await Dce();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 Ice(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 Es(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ds(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=iH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++fpe,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 Es(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(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=qd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await ru(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(Pt()){U2&&clearInterval(U2),U2=setInterval(()=>{e().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=e();if(!n||!a)return;Po.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),Po.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&&z2({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 Es(o,s))}catch(A){console.warn(`[Watcher] notifyTaskChange: failed to read ${s}:`,A);return}const{subtasks:m,bodyWithoutSubtasks:f}=Ds(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=E3(h),k=gu.get(s);if(!k){gu.set(s,b);return}c.notifications.statusChanges&&k.status!==b.status&&z2({title:"Task status changed",body:`${b.title}: ${k.status} → ${b.status}`,config:c});const w=upe(k.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&z2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),mpe(k,b)&&i(s,b.title),gu.set(s,b)};Po.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),Po.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 Es(p,s))}catch(T){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,T);return}const{subtasks:f,bodyWithoutSubtasks:h}=Ds(m),b=c,k=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,A=JSON.stringify(b.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:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:$==="full"})}else await e().reloadBoard()}catch(o){console.error(`[Watcher] taskChanged handler error for ${s}:`,o)}}),Po.on("newTaskDetected",async s=>{try{const{tasksDirHandle:o}=e();if(o){let c,p;try{({frontmatter:c,body:p}=await Es(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}=Ds(p);gu.set(s,E3({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)}}),Po.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)});sl(Ni);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{sl(se.getState().config)});const gpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:se},Symbol.toStringTag,{value:"Module"}));function hpe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return v.jsxs("button",{type:"button",className:E_("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&&v.jsx(at.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 bpe=[{icon:v.jsx(pce,{}),value:"auto",label:"system"},{icon:v.jsx(mce,{}),value:"light",label:"light"},{icon:v.jsx(dce,{}),value:"dark",label:"dark"}];function uH({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?v.jsx(at.div,{initial:{opacity:0},animate:{opacity:1},transition:{duration:.3},className:E_("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:bpe.map(d=>v.jsx(hpe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):v.jsx("div",{className:E_("flex h-8 w-24",n)})}function vpe(t){const e=eoe(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Rz(e,a=>Math.round(a).toString())}const T3="0.28.1";function mH({className:t="w-6 h-6"}){return v.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:[v.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)"}}),v.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[v.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:v.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"}})}),v.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:v.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"}})}),v.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:v.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)"}})})]}),v.jsx("defs",{children:v.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[v.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),v.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function ype(){const{t}=Cn(),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),b=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),R=se(B=>B.setFilter),H=se(B=>B.clearFilters),I=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=vpe(oe),Z=[];I.priority&&D.priority&&Z.push({type:"priority",label:D.priority,value:D.priority}),I.tags&&D.tag&&Z.push({type:"tag",label:"#"+D.tag,value:D.tag}),I.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||I.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]),v.jsxs(v.Fragment,{children:[(L||G)&&v.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:[v.jsx("span",{className:"flex-1 truncate",children:G?`⚠ ${G}`:`⚠ ${L}`}),G&&v.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"}),v.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"})]}),v.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:[v.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[v.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[v.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[v.jsx(mH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),v.jsx(oi,{mode:"wait",initial:!1,children:F?v.jsxs(at.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[v.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),v.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",T3]})]},"boot"):a?v.jsx(at.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&&v.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:[v.jsx(Zn.ArrowLeft,{size:15}),v.jsx("span",{children:t("common.back")})]})]}),e&&v.jsxs(v.Fragment,{children:[v.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),v.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:[v.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),v.jsx("input",{ref:J,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:B=>R("search",B.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?v.jsx("button",{onClick:()=>R("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:v.jsx(Zn.X,{size:14})}):v.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"})]}),v.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[v.jsx(oi,{children:Z.map(B=>v.jsxs(at.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>R(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,v.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},B.type+B.value))}),I.ownerType&&v.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=>v.jsx("button",{onClick:()=>R("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&&v.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&v.jsxs(v.Fragment,{children:[v.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),v.jsxs("div",{className:"relative flex-shrink-0",ref:Q,children:[v.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:[v.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),v.jsxs("span",{className:"font-medium",children:[".",e.name]}),v.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),v.jsx(oi,{children:ne&&v.jsx(at.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:v.jsxs("div",{className:"py-1.5",children:[M.length>0&&v.jsxs(v.Fragment,{children:[v.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=>v.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:[v.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),v.jsx("span",{className:"truncate",children:B.name}),B.id===e.name&&v.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},B.id)),v.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),v.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:[v.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),v.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),v.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?v.jsxs(v.Fragment,{children:[v.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[v.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),v.jsx(at.span,{className:"tabular-nums font-medium",children:O}),v.jsx("span",{children:t("header.tasks")})]}),v.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:[v.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:v.jsx(Zn.LayoutBoard,{size:18})}),v.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:v.jsx(Zn.LayoutList,{size:18})})]}),v.jsx(tn,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),v.jsx(tn,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),v.jsx(tn,{variant:"icon",icon:"Settings",onClick:()=>T("settings"),title:t("common.settings")}),v.jsx(uH,{}),v.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),v.jsx(tn,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>b(!0),title:"Command palette (⌘K)"}),v.jsx(tn,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),v.jsx(tn,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):v.jsx(tn,{variant:"primary",label:t("common.openFolder"),onClick:m})})]})]})}/**
179
+ `}}async function cH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Uce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function pH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function qce(t){if(Pt())return uS();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 Es(t,e){if(Pt())try{const i=await mS(e);return Og(i)}catch{return jj(e)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}})(t)??await cH(t,e);return a!==null?Og(a):jj(e)}async function Vce(t,e){if(Pt())try{const a=await mS(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 cH(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 ru(t,e,n,a){const i=lS(n,a);if(Pt())return Lce(e,i);try{const o=await(await(await Uce(t,e)?await pH(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 Dj(t,e){if(Pt())return jce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Zce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Gce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Wce(t,e,n,a){const i=lS(n,a);if(Pt())return Zce(e,i);try{const o=await(await(await pH(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 Kce(t,e,n,a){const i=lS(n,a);if(Pt())return Gce(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 Yce="kanban-md",Xce=1,ip="recentProjects";function fS(){return new Promise((t,e)=>{const n=indexedDB.open(Yce,Xce);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(ip)||a.createObjectStore(ip,{keyPath:"id"})}})}async function F2(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ip,"readwrite");i.objectStore(ip).put(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Oy(){const t=await fS();return new Promise((e,n)=>{const i=t.transaction(ip,"readonly").objectStore(ip).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 Qce(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ip,"readwrite");i.objectStore(ip).delete(t),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function Ij(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 lH(t=Ni){const e=t.board.columns;return e[e.length-1]??"Done"}function Jce(t,e=Ni){return t===lH(e)||t.toLowerCase()==="archived"}class epe{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,Pt()&&this.startServerSse(),e&&n&&(this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300))}startServerSse(){if(typeof window>"u"||!Pt()||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 Rj(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 Rj(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 Oj(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 Oj(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 Rj(t){try{return await(await(await t.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function Oj(t,e){try{return await(await(await t.getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}const Po=new epe,tpe={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 Fj=null;function dH(){return"Notification"in window?Notification.permission:"unsupported"}async function npe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function z2({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&&ape(n.notifications.soundId),!(!n.notifications.browser||dH()!=="granted"))try{new Notification(t,{body:e})}catch{}}function ape(t){const e=tpe[t],n=window.AudioContext??window.webkitAudioContext;if(!(!n||e.length===0))try{Fj??=new n;const a=Fj;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=$ce}=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 ipe(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 rpe(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=ipe(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 spe=/^[A-Za-z0-9_-]+$/;function H2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!spe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function ope(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=H2(e.get("task"));if(n)return n;const a=e.get("p");if(a){const[,...r]=a.split("/").filter(Boolean),s=H2(r.join("/"));if(s)return s}return H2(decodeURIComponent(t.pathname))}function $3(t){return t?`/?p=${encodeURIComponent(t)}`:"/"}function gS(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($3(t))}function cpe(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 ppe(){const t=await uS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await oH(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ds(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function lpe(t){const e=await qce(t),n=await Promise.all(e.map(async r=>{const s=await Vce(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}=Ds(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 zj(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 Es(t,c.id);await ru(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 sl(t){Nce(t.ui.theme,t.ui.skin,t.ui.font,t.ui.background)}function E3(t){return{title:t.frontmatter.title||t.id,status:t.frontmatter.status||"Backlog",body:t.body,subtasks:t.subtasks}}function B2(t){gu.clear(),t.forEach(e=>{gu.set(e.id,E3(e))})}function dpe(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function upe(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function mpe(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 fpe=0;const gu=new Map,jf=new Map;let U2=null;const se=bce((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:Ni,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(!rH()){const c=navigator.userAgent||"this browser";e().toast(new Sce(c).message,"error",8e3);return}let n;try{n=await Oce()}catch(c){c instanceof dS?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=Pt()?O2():null;try{await F2({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 Ij(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 Qce(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 S3(n.handle),o=n.handle.name;t({dirHandle:r,tasksDirHandle:s,projectName:o}),Dv(o);try{await F2({...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=O2();if(!n)throw new Error("No server root");await Lj();const a=dpe(n),i=await sH();sl(i);const r=await uS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await oH(f),k={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Ds(b);return{id:f,frontmatter:k,body:A,subtasks:w}}));B2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:qd(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(!Pt())return;const n=O2();if(!n)return;await Lj();const a=await Oy(),i=a.find(p=>p.kandownDir===n);if(!i){await e().openServerProject();return}if(!await Ij(i.handle,!0)){await e().openServerProject();return}const s=await Ry(i.handle),o=await S3(i.handle),c=i.handle.name;t({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),Dv(c),await F2({...i,lastOpened:Date.now()}),await e().loadConfig(),await e().reloadBoard(),e().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=e();if(!(!n&&!Pt()))try{const a=await Fce(n);if(a.ok){t({config:a.config}),sl(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:Ni}),sl(Ni)}catch{t({config:Ni}),sl(Ni)}},updateConfig:async n=>{const{dirHandle:a,config:i}=e();if(!a&&!Pt())return;const r=n(i);t({config:r}),sl(r);try{await zce(a,r)}catch(s){const o=s;o instanceof $o?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(Pt()){const i=await ppe();B2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:qd(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 lpe(n);B2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:qd(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=Pt();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 b=m.tasks[h];if(!b)return;if(Jce(i,o)){const T=new Map,M=lH(o).toLowerCase();for(const D of s)for(const R of D.tasks){const H=R.frontmatter&&(R.frontmatter.archived===!0||R.frontmatter.archived==="true");T.set(R.id,{exists:!0,resolved:H||R.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of b.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const R=T.get(D);(!R||!R.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(()=>zj(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 $o?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&&!Pt())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(),b=Pt();if(!h&&!b)return;const{failedIds:k}=await Lf(()=>zj(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 b=h;b instanceof $o?e().toast("Disk is full — reorder was not saved.","error",8e3):e().toast("Failed to save: "+b.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,b)=>{const{frontmatter:k,body:w}=await Es(s,h.id);await ru(s,h.id,{...k,id:h.id,status:i,order:b},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 b=m.board.columns.some(k=>k.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.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&&!Pt())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&&!Pt())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=>Dj(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&&!Pt()||!i.length)return null;const p=n||s.board.columns[0]||i[0].name,d=cpe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?rpe(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:{}},b=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:b,taskContents:k});try{const $=r||null;return await Lf(()=>ru($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const T=$;return T instanceof $o?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&&!Pt())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 Dj(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&&!Pt()))try{const{frontmatter:i,body:r}=await Es(a||null,n);await Wce(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 $o?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&&!Pt()))try{const{frontmatter:i,body:r}=await Es(a||null,n),s={...i};delete s.archived,await Kce(a||null,n,s,r),await e().reloadBoard(),e().toast("Restored")}catch(i){const r=i;r instanceof $o?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&&!Pt()))try{const{frontmatter:s,body:o}=await Es(i,n),{subtasks:c,bodyWithoutSubtasks:p}=Ds(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(gS(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($3(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=qd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>ru(i||null,n,o,s),{maxAttempts:3}),e().toast("Saved");const c=new Map(e().drawerRecoveryData);c.delete(n),zy($3(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 $o?"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=qd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>ru(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 $o?"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(!Pt()){t({agentHook:null});return}const n=await Dce();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 Ice(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 Es(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ds(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=iH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++fpe,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 Es(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ds(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=qd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await ru(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(Pt()){U2&&clearInterval(U2),U2=setInterval(()=>{e().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=e();if(!n||!a)return;Po.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),Po.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&&z2({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 Es(o,s))}catch(A){console.warn(`[Watcher] notifyTaskChange: failed to read ${s}:`,A);return}const{subtasks:m,bodyWithoutSubtasks:f}=Ds(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=E3(h),k=gu.get(s);if(!k){gu.set(s,b);return}c.notifications.statusChanges&&k.status!==b.status&&z2({title:"Task status changed",body:`${b.title}: ${k.status} → ${b.status}`,config:c});const w=upe(k.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&z2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),mpe(k,b)&&i(s,b.title),gu.set(s,b)};Po.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),Po.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 Es(p,s))}catch(T){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,T);return}const{subtasks:f,bodyWithoutSubtasks:h}=Ds(m),b=c,k=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,A=JSON.stringify(b.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:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:$==="full"})}else await e().reloadBoard()}catch(o){console.error(`[Watcher] taskChanged handler error for ${s}:`,o)}}),Po.on("newTaskDetected",async s=>{try{const{tasksDirHandle:o}=e();if(o){let c,p;try{({frontmatter:c,body:p}=await Es(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}=Ds(p);gu.set(s,E3({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)}}),Po.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)});sl(Ni);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{sl(se.getState().config)});const gpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:se},Symbol.toStringTag,{value:"Module"}));function hpe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return v.jsxs("button",{type:"button",className:E_("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&&v.jsx(at.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 bpe=[{icon:v.jsx(pce,{}),value:"auto",label:"system"},{icon:v.jsx(mce,{}),value:"light",label:"light"},{icon:v.jsx(dce,{}),value:"dark",label:"dark"}];function uH({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?v.jsx(at.div,{initial:{opacity:0},animate:{opacity:1},transition:{duration:.3},className:E_("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:bpe.map(d=>v.jsx(hpe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):v.jsx("div",{className:E_("flex h-8 w-24",n)})}function vpe(t){const e=eoe(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Rz(e,a=>Math.round(a).toString())}const T3="0.29.0";function mH({className:t="w-6 h-6"}){return v.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:[v.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)"}}),v.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[v.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:v.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"}})}),v.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:v.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"}})}),v.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:v.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)"}})})]}),v.jsx("defs",{children:v.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[v.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),v.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function ype(){const{t}=Cn(),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),b=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),R=se(B=>B.setFilter),H=se(B=>B.clearFilters),I=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=vpe(oe),Z=[];I.priority&&D.priority&&Z.push({type:"priority",label:D.priority,value:D.priority}),I.tags&&D.tag&&Z.push({type:"tag",label:"#"+D.tag,value:D.tag}),I.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||I.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]),v.jsxs(v.Fragment,{children:[(L||G)&&v.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:[v.jsx("span",{className:"flex-1 truncate",children:G?`⚠ ${G}`:`⚠ ${L}`}),G&&v.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"}),v.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"})]}),v.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:[v.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[v.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[v.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[v.jsx(mH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),v.jsx(oi,{mode:"wait",initial:!1,children:F?v.jsxs(at.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[v.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),v.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",T3]})]},"boot"):a?v.jsx(at.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&&v.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:[v.jsx(Zn.ArrowLeft,{size:15}),v.jsx("span",{children:t("common.back")})]})]}),e&&v.jsxs(v.Fragment,{children:[v.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),v.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:[v.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),v.jsx("input",{ref:J,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:B=>R("search",B.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?v.jsx("button",{onClick:()=>R("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:v.jsx(Zn.X,{size:14})}):v.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"})]}),v.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[v.jsx(oi,{children:Z.map(B=>v.jsxs(at.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>R(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,v.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},B.type+B.value))}),I.ownerType&&v.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=>v.jsx("button",{onClick:()=>R("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&&v.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&v.jsxs(v.Fragment,{children:[v.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),v.jsxs("div",{className:"relative flex-shrink-0",ref:Q,children:[v.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:[v.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),v.jsxs("span",{className:"font-medium",children:[".",e.name]}),v.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),v.jsx(oi,{children:ne&&v.jsx(at.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:v.jsxs("div",{className:"py-1.5",children:[M.length>0&&v.jsxs(v.Fragment,{children:[v.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=>v.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:[v.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),v.jsx("span",{className:"truncate",children:B.name}),B.id===e.name&&v.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},B.id)),v.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),v.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:[v.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),v.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),v.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?v.jsxs(v.Fragment,{children:[v.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[v.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),v.jsx(at.span,{className:"tabular-nums font-medium",children:O}),v.jsx("span",{children:t("header.tasks")})]}),v.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:[v.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:v.jsx(Zn.LayoutBoard,{size:18})}),v.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:v.jsx(Zn.LayoutList,{size:18})})]}),v.jsx(tn,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),v.jsx(tn,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),v.jsx(tn,{variant:"icon",icon:"Settings",onClick:()=>T("settings"),title:t("common.settings")}),v.jsx(uH,{}),v.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),v.jsx(tn,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>b(!0),title:"Command palette (⌘K)"}),v.jsx(tn,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),v.jsx(tn,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):v.jsx(tn,{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.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "File-based Kanban engine backed by Markdown. Zero backend, AI-agent friendly.",
5
5
  "type": "module",
6
6
  "bin": {