kandown 0.27.0 → 0.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/bin/kandown.js +27 -2
- package/dist/index.html +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.27.1 — 2026-07-20 — "Auto-Updater & Daemon Process Upgrade"
|
|
4
|
+
|
|
5
|
+
- **Fixed**: **Environment inheritance during background auto-update** — cleaned inherited `npm_config_*` and `npm_*` environment variables when spawning `npm install -g kandown` in `checkForUpdate()`. Previously, if Kandown was run via `npx` or `npm run`, inherited local prefix overrides could cause global background updates to fail or target a temporary cache.
|
|
6
|
+
- **Fixed**: **Binary path resolution for system PATH and `~/.local/bin`** — updated `resolveKandownBin()` to check `which kandown`, `command -v kandown`, and `~/.local/bin/kandown` before checking package manager prefixes, ensuring custom global PATH installations are detected accurately.
|
|
7
|
+
- **Fixed**: **Live daemon process upgrade on CLI update** — updated `refreshRunningProjectHtml()` so that background project daemon processes in RAM running older code are automatically stopped and relaunched with the newly updated CLI binary.
|
|
8
|
+
|
|
3
9
|
## 0.27.0 — 2026-07-20 — "Unified Views"
|
|
4
10
|
|
|
5
11
|
- **Added**: **Full Component Parity between Board and List Views** — unified the List view (`ListView.tsx`) and Board view (`Column.tsx`) so that List view sections and task items share the exact same components, column background color tints, icons, and action options as Board columns.
|
package/bin/kandown.js
CHANGED
|
@@ -130,17 +130,27 @@ function getCurrentVersion() {
|
|
|
130
130
|
* @returns {string|null} Absolute path to the kandown binary, or null.
|
|
131
131
|
*/
|
|
132
132
|
function resolveKandownBin() {
|
|
133
|
+
try {
|
|
134
|
+
const whichBin = String(execSync('which kandown 2>/dev/null || command -v kandown 2>/dev/null', {
|
|
135
|
+
timeout: 3000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
136
|
+
})).trim();
|
|
137
|
+
if (whichBin && existsSync(whichBin)) return whichBin;
|
|
138
|
+
} catch { /* ignore */ }
|
|
139
|
+
const localBin = join(homedir(), '.local', 'bin', 'kandown');
|
|
140
|
+
if (existsSync(localBin)) return localBin;
|
|
133
141
|
try {
|
|
134
142
|
const npmBin = String(execSync('npm config get prefix 2>/dev/null', {
|
|
135
143
|
timeout: 3000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
136
144
|
})).trim();
|
|
137
145
|
if (existsSync(join(npmBin, 'bin', 'kandown'))) return join(npmBin, 'bin', 'kandown');
|
|
146
|
+
if (existsSync(join(npmBin, 'kandown'))) return join(npmBin, 'kandown');
|
|
138
147
|
} catch { /* npm not available */ }
|
|
139
148
|
try {
|
|
140
149
|
const pnpmBin = String(execSync('pnpm config get prefix 2>/dev/null', {
|
|
141
150
|
timeout: 3000, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
142
151
|
})).trim();
|
|
143
152
|
if (existsSync(join(pnpmBin, 'bin', 'kandown'))) return join(pnpmBin, 'bin', 'kandown');
|
|
153
|
+
if (existsSync(join(pnpmBin, 'kandown'))) return join(pnpmBin, 'kandown');
|
|
144
154
|
} catch { /* pnpm not available */ }
|
|
145
155
|
return null;
|
|
146
156
|
}
|
|
@@ -307,10 +317,16 @@ async function checkForUpdate(argv = process.argv) {
|
|
|
307
317
|
const updateOk = await new Promise((resolve) => {
|
|
308
318
|
const tryInstall = (cmd) => {
|
|
309
319
|
return new Promise((res) => {
|
|
320
|
+
const cleanEnv = { ...process.env };
|
|
321
|
+
for (const k of Object.keys(cleanEnv)) {
|
|
322
|
+
if (k.startsWith('npm_config_') || k.startsWith('npm_') || k === 'INIT_CWD') {
|
|
323
|
+
delete cleanEnv[k];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
310
326
|
const child = spawn(cmd, ['install', '-g', 'kandown'], {
|
|
311
|
-
timeout:
|
|
327
|
+
timeout: 60000,
|
|
312
328
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
313
|
-
env:
|
|
329
|
+
env: cleanEnv,
|
|
314
330
|
detached: false,
|
|
315
331
|
});
|
|
316
332
|
child.stderr.on('data', () => {}); // silence npm noise
|
|
@@ -1926,10 +1942,19 @@ async function refreshRunningProjectHtml() {
|
|
|
1926
1942
|
.filter(dir => typeof dir === 'string' && dir.length > 0)
|
|
1927
1943
|
);
|
|
1928
1944
|
|
|
1945
|
+
const currentVersion = getCurrentVersion();
|
|
1929
1946
|
let refreshed = 0;
|
|
1930
1947
|
for (const kandownDir of kandownDirs) {
|
|
1931
1948
|
try {
|
|
1932
1949
|
if (refreshKandownHtml(kandownDir)) refreshed++;
|
|
1950
|
+
const current = await getDaemonStatus(kandownDir);
|
|
1951
|
+
if (current.running) {
|
|
1952
|
+
const daemonVersion = current.metadata?.version || null;
|
|
1953
|
+
if (!daemonVersion || (currentVersion && semverGt(currentVersion, daemonVersion) > 0)) {
|
|
1954
|
+
await stopDaemon(kandownDir);
|
|
1955
|
+
await startDaemon(kandownDir, current.metadata?.port || null);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1933
1958
|
} catch { /* best-effort: one locked project must not block restart */ }
|
|
1934
1959
|
}
|
|
1935
1960
|
return refreshed;
|
package/dist/index.html
CHANGED
|
@@ -176,7 +176,7 @@ Error generating stack: `+y.message+`
|
|
|
176
176
|
|
|
177
177
|
## Subtasks
|
|
178
178
|
|
|
179
|
-
`}}async function sH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Fce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function oH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function zce(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 sH(t,e);return a!==null?Og(a):jj(e)}async function Hce(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 sH(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 su(t,e,n,a){const i=lS(n,a);if(Pt())return Ece(e,i);try{const o=await(await(await Fce(t,e)?await oH(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 Tce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Bce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Uce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function qce(t,e,n,a){const i=lS(n,a);if(Pt())return Bce(e,i);try{const o=await(await(await oH(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 Vce(t,e,n,a){const i=lS(n,a);if(Pt())return Uce(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 Zce="kanban-md",Gce=1,ap="recentProjects";function fS(){return new Promise((t,e)=>{const n=indexedDB.open(Zce,Gce);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(ap)||a.createObjectStore(ap,{keyPath:"id"})}})}async function F2(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ap,"readwrite");i.objectStore(ap).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(ap,"readonly").objectStore(ap).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 Wce(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ap,"readwrite");i.objectStore(ap).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 cH(t=Ni){const e=t.board.columns;return e[e.length-1]??"Done"}function Kce(t,e=Ni){return t===cH(e)||t.toLowerCase()==="archived"}class Yce{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 il=new Yce,Xce={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 pH(){return"Notification"in window?Notification.permission:"unsupported"}async function Qce(){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&&Jce(n.notifications.soundId),!(!n.notifications.browser||pH()!=="granted"))try{new Notification(t,{body:e})}catch{}}function Jce(t){const e=Xce[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=Ace}=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 epe(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 tpe(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=epe(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 npe=/^[A-Za-z0-9_-]+$/;function H2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!npe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function ape(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 ipe(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 rpe(){const t=await uS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await rH(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 spe(t){const e=await zce(t),n=await Promise.all(e.map(async r=>{const s=await Hce(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 su(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){wce(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){hu.clear(),t.forEach(e=>{hu.set(e.id,E3(e))})}function ope(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function cpe(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function ppe(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 lpe=0;const hu=new Map,jf=new Map;let U2=null;const oe=mce((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(!aH()){const c=navigator.userAgent||"this browser";e().toast(new xce(c).message,"error",8e3);return}let n;try{n=await jce()}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 Wce(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=ope(n),i=await iH();sl(i);const r=await uS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await rH(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:Vd(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 Dce(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 Ice(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 rpe();B2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:Vd(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 spe(n);B2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:Vd(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(E=>E.name===a),f=s.find(E=>E.name===i);if(!m||!f)return;const h=m.tasks.findIndex(E=>E.id===n);if(h===-1)return;const b=m.tasks[h];if(!b)return;if(Kce(i,o)){const E=new Map,M=cH(o).toLowerCase();for(const D of s)for(const O of D.tasks){const H=O.frontmatter&&(O.frontmatter.archived===!0||O.frontmatter.archived==="true");E.set(O.id,{exists:!0,resolved:H||O.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of b.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const O=E.get(D);(!O||!O.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(E=>({...E,tasks:[...E.tasks]})),w=k.find(E=>E.name===a),A=k.find(E=>E.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:E}=e();if(!E&&!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(E??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(E){const M=E;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 su(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=ipe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?tpe(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(()=>su($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const E=$;return E instanceof $o?e().toast("Disk is full — task was not created.","error",8e3):e().toast("Failed to create: "+E.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 qce(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 Vce(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=Vd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>su(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=Vd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>su(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 Pce();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 Mce(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=nH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++lpe,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=Vd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await su(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;il.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),il.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=hu.get(s);if(!k){hu.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=cpe(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}),ppe(k,b)&&i(s,b.title),hu.set(s,b)};il.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),il.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(E){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,E);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)}}),il.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);hu.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)}}),il.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=>{oe.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(oe.getState().config)});const dpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:oe},Symbol.toStringTag,{value:"Module"}));function upe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return _.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&&_.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 mpe=[{icon:_.jsx(rce,{}),value:"auto",label:"system"},{icon:_.jsx(pce,{}),value:"light",label:"light"},{icon:_.jsx(oce,{}),value:"dark",label:"dark"}];function lH({value:t,onChange:e,className:n}){const a=oe(d=>d.config.ui.theme),i=oe(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?_.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:mpe.map(d=>_.jsx(upe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):_.jsx("div",{className:E_("flex h-8 w-24",n)})}function fpe(t){const e=Yse(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Dz(e,a=>Math.round(a).toString())}const T3="0.27.0";function dH({className:t="w-6 h-6"}){return _.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:[_.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)"}}),_.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[_.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:_.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"}})}),_.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:_.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"}})}),_.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:_.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)"}})})]}),_.jsx("defs",{children:_.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[_.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),_.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function gpe(){const{t}=kn(),e=oe(z=>z.dirHandle),n=oe(z=>z.isOpen),a=oe(z=>z.projectName),i=oe(z=>z.closeDrawer),r=oe(z=>z.drawerTaskId),s=oe(z=>z.saveDrawer),o=oe(z=>z.archivedTasks.length),c=oe(z=>z.showArchives),p=oe(z=>z.setShowArchives),d=oe(z=>z.columns),m=oe(z=>z.openFolder),f=oe(z=>z.reloadBoard),h=oe(z=>z.createTask),b=oe(z=>z.setCommandOpen),k=oe(z=>z.viewMode),w=oe(z=>z.setViewMode),A=oe(z=>z.density),$=oe(z=>z.setDensity),E=oe(z=>z.setCurrentPage),M=oe(z=>z.recentProjects),j=oe(z=>z.openRecentProject),D=oe(z=>z.filters),O=oe(z=>z.setFilter),H=oe(z=>z.clearFilters),I=oe(z=>z.config.fields),L=oe(z=>z.lastReloadError),K=oe(z=>z.watcherError),Y=oe(z=>z.restartWatcher),[ne,U]=S.useState(!1),[ee,Z]=S.useState(!0),te=S.useRef(null),B=S.useRef(null),se=d.reduce((z,ae)=>z+ae.tasks.length,0),R=fpe(se),V=[];I.priority&&D.priority&&V.push({type:"priority",label:D.priority,value:D.priority}),I.tags&&D.tag&&V.push({type:"tag",label:"#"+D.tag,value:D.tag}),I.assignee&&D.assignee&&V.push({type:"assignee",label:"@"+D.assignee,value:D.assignee});const W=[{label:t("filterBar.ownerAll"),value:""},{label:t("filterBar.ownerHuman"),value:"human"},{label:t("filterBar.ownerAI"),value:"ai"}],T=V.length>0||D.search||I.ownerType&&D.ownerType;return S.useEffect(()=>{if(!ne)return;const z=ae=>{te.current&&!te.current.contains(ae.target)&&U(!1)};return document.addEventListener("mousedown",z),()=>document.removeEventListener("mousedown",z)},[ne]),S.useEffect(()=>{const z=setTimeout(()=>Z(!1),5e3);return()=>clearTimeout(z)},[]),S.useEffect(()=>{document.title=a?`${a} · Kandown`:"Kandown"},[a]),_.jsxs(_.Fragment,{children:[(L||K)&&_.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:[_.jsx("span",{className:"flex-1 truncate",children:K?`⚠ ${K}`:`⚠ ${L}`}),K&&_.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"}),_.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"})]}),_.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:[_.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[_.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[_.jsx(dH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),_.jsx(oi,{mode:"wait",initial:!1,children:ee?_.jsxs(at.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[_.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),_.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?_.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&&_.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:[_.jsx(Zn.ArrowLeft,{size:15}),_.jsx("span",{children:t("common.back")})]})]}),e&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.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:[_.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),_.jsx("input",{ref:B,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:z=>O("search",z.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?_.jsx("button",{onClick:()=>O("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:_.jsx(Zn.X,{size:14})}):_.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"})]}),_.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[_.jsx(oi,{children:V.map(z=>_.jsxs(at.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>O(z.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:[z.label,_.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},z.type+z.value))}),I.ownerType&&_.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:W.map(z=>_.jsx("button",{onClick:()=>O("ownerType",z.value),className:`h-full px-2.5 text-[12px] transition-colors ${D.ownerType===z.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:z.label},z.value))}),T&&_.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"relative flex-shrink-0",ref:te,children:[_.jsxs("button",{onClick:()=>U(z=>!z),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:[_.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),_.jsxs("span",{className:"font-medium",children:[".",e.name]}),_.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),_.jsx(oi,{children:ne&&_.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:_.jsxs("div",{className:"py-1.5",children:[M.length>0&&_.jsxs(_.Fragment,{children:[_.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(z=>_.jsxs("button",{onClick:()=>{U(!1),j(z)},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:[_.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{className:"truncate",children:z.name}),z.id===e.name&&_.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},z.id)),_.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),_.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:[_.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),_.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[_.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),_.jsx(at.span,{className:"tabular-nums font-medium",children:R}),_.jsx("span",{children:t("header.tasks")})]}),_.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:[_.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:_.jsx(Zn.LayoutBoard,{size:18})}),_.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:_.jsx(Zn.LayoutList,{size:18})})]}),_.jsx(en,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),_.jsx(en,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),_.jsx(en,{variant:"icon",icon:"Settings",onClick:()=>E("settings"),title:t("common.settings")}),_.jsx(lH,{}),_.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),_.jsx(en,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>b(!0),title:"Command palette (⌘K)"}),_.jsx(en,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),_.jsx(en,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):_.jsx(en,{variant:"primary",label:t("common.openFolder"),onClick:m})})]})]})}/**
|
|
179
|
+
`}}async function sH(t,e){try{return await(await(await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`)).getFile()).text()}catch{return null}}async function Fce(t,e){try{return await(await t.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${e}.md`),!0}catch{return!1}}async function oH(t,e){try{return await t.getDirectoryHandle("archive",{create:e})}catch{return null}}async function zce(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 sH(t,e);return a!==null?Og(a):jj(e)}async function Hce(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 sH(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 su(t,e,n,a){const i=lS(n,a);if(Pt())return Ece(e,i);try{const o=await(await(await Fce(t,e)?await oH(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 Tce(e);try{await t.removeEntry(`${e}.md`)}catch{}try{await(await t.getDirectoryHandle("archive",{create:!1})).removeEntry(`${e}.md`)}catch{}}async function Bce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/archive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function Uce(t,e){await ir(`/api/tasks/${encodeURIComponent(t)}/unarchive`,{method:"POST",body:e,headers:{"Content-Type":"text/plain"}})}async function qce(t,e,n,a){const i=lS(n,a);if(Pt())return Bce(e,i);try{const o=await(await(await oH(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 Vce(t,e,n,a){const i=lS(n,a);if(Pt())return Uce(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 Zce="kanban-md",Gce=1,ap="recentProjects";function fS(){return new Promise((t,e)=>{const n=indexedDB.open(Zce,Gce);n.onerror=()=>e(n.error),n.onsuccess=()=>t(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(ap)||a.createObjectStore(ap,{keyPath:"id"})}})}async function F2(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ap,"readwrite");i.objectStore(ap).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(ap,"readonly").objectStore(ap).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 Wce(t){const e=await fS();return new Promise((n,a)=>{const i=e.transaction(ap,"readwrite");i.objectStore(ap).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 cH(t=Ni){const e=t.board.columns;return e[e.length-1]??"Done"}function Kce(t,e=Ni){return t===cH(e)||t.toLowerCase()==="archived"}class Yce{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 il=new Yce,Xce={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 pH(){return"Notification"in window?Notification.permission:"unsupported"}async function Qce(){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&&Jce(n.notifications.soundId),!(!n.notifications.browser||pH()!=="granted"))try{new Notification(t,{body:e})}catch{}}function Jce(t){const e=Xce[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=Ace}=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 epe(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 tpe(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=epe(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 npe=/^[A-Za-z0-9_-]+$/;function H2(t){if(!t)return null;const e=t.trim().replace(/^\/+|\/+$/g,"").replace(/^#/,"");return!e||e.includes("/")||e.includes(".")||!npe.test(e)?null:/^\d+$/.test(e)?`t${e}`:e}function ape(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 ipe(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 rpe(){const t=await uS();return await Promise.all(t.map(async n=>{const{frontmatter:a,body:i}=await rH(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 spe(t){const e=await zce(t),n=await Promise.all(e.map(async r=>{const s=await Hce(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 su(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){wce(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){hu.clear(),t.forEach(e=>{hu.set(e.id,E3(e))})}function ope(t){const e=t.split(/[\\/]+/).filter(Boolean),n=e[e.length-1];return n===".kandown"?e[e.length-2]??"Project":n??"Project"}function cpe(t,e){return e.reduce((n,a,i)=>{const r=t[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function ppe(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 lpe=0;const hu=new Map,jf=new Map;let U2=null;const oe=mce((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(!aH()){const c=navigator.userAgent||"this browser";e().toast(new xce(c).message,"error",8e3);return}let n;try{n=await jce()}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 Wce(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=ope(n),i=await iH();sl(i);const r=await uS(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await rH(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:Vd(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 Dce(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 Ice(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 rpe();B2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:Vd(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 spe(n);B2(i);const s=i.map(m=>({frontmatter:m.frontmatter,body:Vd(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(E=>E.name===a),f=s.find(E=>E.name===i);if(!m||!f)return;const h=m.tasks.findIndex(E=>E.id===n);if(h===-1)return;const b=m.tasks[h];if(!b)return;if(Kce(i,o)){const E=new Map,M=cH(o).toLowerCase();for(const D of s)for(const O of D.tasks){const H=O.frontmatter&&(O.frontmatter.archived===!0||O.frontmatter.archived==="true");E.set(O.id,{exists:!0,resolved:H||O.id===n||D.name.toLowerCase()===M})}const j=[];for(const D of b.dependsOn){if(typeof D!="string"||!D.trim()||D===n)continue;const O=E.get(D);(!O||!O.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(E=>({...E,tasks:[...E.tasks]})),w=k.find(E=>E.name===a),A=k.find(E=>E.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:E}=e();if(!E&&!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(E??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(E){const M=E;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 su(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=ipe(i),m=i.find($=>$.name===p)?.tasks.length??0,f=a?tpe(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(()=>su($,d,w,A),{maxAttempts:3}),e().toast(`Created ${d.replace(/^t/,"")}`),await e().openDrawer(d),d}catch($){const E=$;return E instanceof $o?e().toast("Disk is full — task was not created.","error",8e3):e().toast("Failed to create: "+E.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 qce(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 Vce(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=Vd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await Lf(()=>su(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=Vd(a.body,a.subtasks),o={...a.frontmatter,id:n};await Lf(()=>su(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 Pce();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 Mce(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=nH(o,r);c.length>0&&i.set(s,c)}t({searchMatches:i})},toast:(n,a="success",i)=>{const r=++lpe,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=Vd(i.body,i.subtasks),p={...i.frontmatter,id:s};try{await su(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;il.stop(),jf.forEach(s=>clearTimeout(s)),jf.clear(),il.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=hu.get(s);if(!k){hu.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=cpe(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}),ppe(k,b)&&i(s,b.title),hu.set(s,b)};il.on("configChanged",()=>{try{e().loadConfig(),e().toast("Settings updated externally","info")}catch(s){console.error("[Watcher] configChanged handler error:",s)}}),il.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(E){console.warn(`[Watcher] taskChanged: failed to re-read ${s}:`,E);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)}}),il.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);hu.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)}}),il.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=>{oe.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(oe.getState().config)});const dpe=Object.freeze(Object.defineProperty({__proto__:null,useStore:oe},Symbol.toStringTag,{value:"Module"}));function upe({icon:t,value:e,label:n,isActive:a,onClick:i,layoutId:r}){return _.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&&_.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 mpe=[{icon:_.jsx(rce,{}),value:"auto",label:"system"},{icon:_.jsx(pce,{}),value:"light",label:"light"},{icon:_.jsx(oce,{}),value:"dark",label:"dark"}];function lH({value:t,onChange:e,className:n}){const a=oe(d=>d.config.ui.theme),i=oe(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?_.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:mpe.map(d=>_.jsx(upe,{icon:d.icon,value:d.value,label:d.label,isActive:c===d.value,onClick:p,layoutId:r},d.value))},String(s)):_.jsx("div",{className:E_("flex h-8 w-24",n)})}function fpe(t){const e=Yse(t,{stiffness:180,damping:22,mass:.8});return S.useEffect(()=>{e.set(t)},[t,e]),Dz(e,a=>Math.round(a).toString())}const T3="0.27.1";function dH({className:t="w-6 h-6"}){return _.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:[_.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)"}}),_.jsxs("g",{transform:"matrix(0.823551,0,0,0.823551,12.830775,11.282318)",children:[_.jsx("g",{transform:"matrix(1.620982,0,0,1.566239,-41.718998,-41.628205)",children:_.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"}})}),_.jsx("g",{transform:"matrix(1.566239,0,0,1.566239,-39.774573,-41.628205)",children:_.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"}})}),_.jsx("g",{transform:"matrix(0.766318,0.766329,-1.302872,1.302853,170.442469,-47.541879)",children:_.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)"}})})]}),_.jsx("defs",{children:_.jsxs("linearGradient",{id:"_KandownLinear1",x1:"0",y1:"0",x2:"1",y2:"0",gradientUnits:"userSpaceOnUse",gradientTransform:"matrix(62,131,-131,62,9,8)",children:[_.jsx("stop",{offset:"0",style:{stopColor:"rgb(12,29,23)",stopOpacity:1}}),_.jsx("stop",{offset:"1",style:{stopColor:"rgb(24,41,35)",stopOpacity:1}})]})})]})}function gpe(){const{t}=kn(),e=oe(z=>z.dirHandle),n=oe(z=>z.isOpen),a=oe(z=>z.projectName),i=oe(z=>z.closeDrawer),r=oe(z=>z.drawerTaskId),s=oe(z=>z.saveDrawer),o=oe(z=>z.archivedTasks.length),c=oe(z=>z.showArchives),p=oe(z=>z.setShowArchives),d=oe(z=>z.columns),m=oe(z=>z.openFolder),f=oe(z=>z.reloadBoard),h=oe(z=>z.createTask),b=oe(z=>z.setCommandOpen),k=oe(z=>z.viewMode),w=oe(z=>z.setViewMode),A=oe(z=>z.density),$=oe(z=>z.setDensity),E=oe(z=>z.setCurrentPage),M=oe(z=>z.recentProjects),j=oe(z=>z.openRecentProject),D=oe(z=>z.filters),O=oe(z=>z.setFilter),H=oe(z=>z.clearFilters),I=oe(z=>z.config.fields),L=oe(z=>z.lastReloadError),K=oe(z=>z.watcherError),Y=oe(z=>z.restartWatcher),[ne,U]=S.useState(!1),[ee,Z]=S.useState(!0),te=S.useRef(null),B=S.useRef(null),se=d.reduce((z,ae)=>z+ae.tasks.length,0),R=fpe(se),V=[];I.priority&&D.priority&&V.push({type:"priority",label:D.priority,value:D.priority}),I.tags&&D.tag&&V.push({type:"tag",label:"#"+D.tag,value:D.tag}),I.assignee&&D.assignee&&V.push({type:"assignee",label:"@"+D.assignee,value:D.assignee});const W=[{label:t("filterBar.ownerAll"),value:""},{label:t("filterBar.ownerHuman"),value:"human"},{label:t("filterBar.ownerAI"),value:"ai"}],T=V.length>0||D.search||I.ownerType&&D.ownerType;return S.useEffect(()=>{if(!ne)return;const z=ae=>{te.current&&!te.current.contains(ae.target)&&U(!1)};return document.addEventListener("mousedown",z),()=>document.removeEventListener("mousedown",z)},[ne]),S.useEffect(()=>{const z=setTimeout(()=>Z(!1),5e3);return()=>clearTimeout(z)},[]),S.useEffect(()=>{document.title=a?`${a} · Kandown`:"Kandown"},[a]),_.jsxs(_.Fragment,{children:[(L||K)&&_.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:[_.jsx("span",{className:"flex-1 truncate",children:K?`⚠ ${K}`:`⚠ ${L}`}),K&&_.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"}),_.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"})]}),_.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:[_.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[_.jsxs("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:[_.jsxs("button",{onClick:()=>{r?s():i({replace:!0})},className:"flex items-center gap-2 cursor-pointer",children:[_.jsx(dH,{className:"w-[34px] h-[34px] dark:text-white text-black"}),_.jsx(oi,{mode:"wait",initial:!1,children:ee?_.jsxs(at.span,{className:"flex items-center gap-2",initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},transition:{duration:.45},children:[_.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),_.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?_.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&&_.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:[_.jsx(Zn.ArrowLeft,{size:15}),_.jsx("span",{children:t("common.back")})]})]}),e&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.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:[_.jsx(Zn.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),_.jsx("input",{ref:B,type:"text",placeholder:t("filterBar.searchPlaceholder"),value:D.search,onChange:z=>O("search",z.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),D.search?_.jsx("button",{onClick:()=>O("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:_.jsx(Zn.X,{size:14})}):_.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"})]}),_.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[_.jsx(oi,{children:V.map(z=>_.jsxs(at.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>O(z.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:[z.label,_.jsx(Zn.X,{size:10,className:"text-fg-muted/60"})]},z.type+z.value))}),I.ownerType&&_.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:W.map(z=>_.jsx("button",{onClick:()=>O("ownerType",z.value),className:`h-full px-2.5 text-[12px] transition-colors ${D.ownerType===z.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:z.label},z.value))}),T&&_.jsx("button",{onClick:H,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:t("filterBar.clearAll")})]})]}),e&&_.jsxs(_.Fragment,{children:[_.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),_.jsxs("div",{className:"relative flex-shrink-0",ref:te,children:[_.jsxs("button",{onClick:()=>U(z=>!z),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:[_.jsx(Zn.Folder,{size:13,className:"text-fg-muted/70"}),_.jsxs("span",{className:"font-medium",children:[".",e.name]}),_.jsx(Zn.ChevronDown,{size:11,className:"opacity-50"})]}),_.jsx(oi,{children:ne&&_.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:_.jsxs("div",{className:"py-1.5",children:[M.length>0&&_.jsxs(_.Fragment,{children:[_.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(z=>_.jsxs("button",{onClick:()=>{U(!1),j(z)},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:[_.jsx(Zn.Folder,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{className:"truncate",children:z.name}),z.id===e.name&&_.jsx(Zn.Check,{size:12,className:"ml-auto text-emerald-500"})]},z.id)),_.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),_.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:[_.jsx(Zn.Plus,{size:12,className:"text-fg-muted/60"}),_.jsx("span",{children:t("header.openFolder...")})]})]})})})]})]})]}),_.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||e?_.jsxs(_.Fragment,{children:[_.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[_.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),_.jsx(at.span,{className:"tabular-nums font-medium",children:R}),_.jsx("span",{children:t("header.tasks")})]}),_.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:[_.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:_.jsx(Zn.LayoutBoard,{size:18})}),_.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:_.jsx(Zn.LayoutList,{size:18})})]}),_.jsx(en,{variant:"icon",icon:"Archive",onClick:()=>p(!c),title:c?t("header.backToBoard"):`${t("header.archives")} (${o})`,className:c?"text-accent":""}),_.jsx(en,{variant:"icon",icon:"Density",onClick:()=>$(A==="compact"?"comfortable":"compact"),title:`Density: ${A}`}),_.jsx(en,{variant:"icon",icon:"Settings",onClick:()=>E("settings"),title:t("common.settings")}),_.jsx(lH,{}),_.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),_.jsx(en,{variant:"secondary",icon:"Search",label:t("common.search"),shortcut:"⌘K",onClick:()=>b(!0),title:"Command palette (⌘K)"}),_.jsx(en,{variant:"icon",icon:"Refresh",onClick:f,title:`${t("common.reload")} (R)`}),_.jsx(en,{variant:"primary",icon:"Plus",label:t("common.newTask"),shortcut:"N",onClick:()=>h()})]}):_.jsx(en,{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.
|