kandown 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tui.js +44 -11
- package/dist/index.html +1 -1
- package/package.json +1 -1
package/bin/tui.js
CHANGED
|
@@ -57015,7 +57015,7 @@ function columnAccentColor(name) {
|
|
|
57015
57015
|
if (/done|archive|closed|complete/.test(normalized)) return "green";
|
|
57016
57016
|
return "cyan";
|
|
57017
57017
|
}
|
|
57018
|
-
function
|
|
57018
|
+
function SingleTaskRow({ task, focused, dragging, colWidth }) {
|
|
57019
57019
|
const cursor = dragging ? "\u2195" : focused ? "\u25B8" : " ";
|
|
57020
57020
|
const check2 = task.checked ? "\u2713" : "\u25CB";
|
|
57021
57021
|
const idStr = task.id;
|
|
@@ -57045,6 +57045,42 @@ function TaskRow({ task, focused, dragging, colWidth }) {
|
|
|
57045
57045
|
] })
|
|
57046
57046
|
] });
|
|
57047
57047
|
}
|
|
57048
|
+
function getTitleCategory(title) {
|
|
57049
|
+
const bracket = title.match(/^\[([^\]]+)\]\s*/);
|
|
57050
|
+
if (bracket) return { key: `[${bracket[1].toLowerCase()}]`, label: bracket[1] };
|
|
57051
|
+
const hash = title.match(/#(\w+)/);
|
|
57052
|
+
if (hash) return { key: `#${hash[1].toLowerCase()}`, label: `#${hash[1]}` };
|
|
57053
|
+
return null;
|
|
57054
|
+
}
|
|
57055
|
+
function CategoryTaskRow({ task, focused, dragging, colWidth }) {
|
|
57056
|
+
const cursor = dragging ? "\u2195" : focused ? "\u25B8" : " ";
|
|
57057
|
+
const check2 = task.checked ? "\u2713" : "\u25CB";
|
|
57058
|
+
const idStr = task.id;
|
|
57059
|
+
const category = getTitleCategory(task.title);
|
|
57060
|
+
const titleClean = category ? task.title.slice(task.title.indexOf(category.key) + category.key.length).trim() : task.title;
|
|
57061
|
+
const contentWidth = Math.max(4, colWidth - 2);
|
|
57062
|
+
const titleStr = truncate(titleClean, contentWidth);
|
|
57063
|
+
const bg = dragging ? "yellow" : focused ? "cyan" : "#222";
|
|
57064
|
+
const txtColor = dragging ? "black" : focused ? "black" : "white";
|
|
57065
|
+
const catColor = dragging ? "black" : focused ? "black" : "magenta";
|
|
57066
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", backgroundColor: bg, children: [
|
|
57067
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: txtColor, bold: focused || dragging, children: [
|
|
57068
|
+
cursor,
|
|
57069
|
+
" ",
|
|
57070
|
+
check2,
|
|
57071
|
+
" ",
|
|
57072
|
+
idStr
|
|
57073
|
+
] }),
|
|
57074
|
+
category && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: catColor, bold: true, children: [
|
|
57075
|
+
" ",
|
|
57076
|
+
category.label
|
|
57077
|
+
] }),
|
|
57078
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { color: txtColor, children: [
|
|
57079
|
+
" ",
|
|
57080
|
+
titleStr
|
|
57081
|
+
] })
|
|
57082
|
+
] });
|
|
57083
|
+
}
|
|
57048
57084
|
function MovePlaceholder({ name, focused, colWidth }) {
|
|
57049
57085
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
57050
57086
|
Text,
|
|
@@ -57077,17 +57113,9 @@ function KanbanColumn({
|
|
|
57077
57113
|
const countStr = tasks.length > 0 ? ` (${tasks.length})` : "";
|
|
57078
57114
|
const rows = [];
|
|
57079
57115
|
tasks.forEach((task, idx) => {
|
|
57116
|
+
const hasCategory = getTitleCategory(task.title) !== null;
|
|
57080
57117
|
rows.push(
|
|
57081
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
57082
|
-
TaskRow,
|
|
57083
|
-
{
|
|
57084
|
-
task,
|
|
57085
|
-
focused: isFocused && idx === focusedRow,
|
|
57086
|
-
dragging: task.id === draggedTaskId,
|
|
57087
|
-
colWidth
|
|
57088
|
-
},
|
|
57089
|
-
task.id
|
|
57090
|
-
)
|
|
57118
|
+
hasCategory ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CategoryTaskRow, { task, focused: !!(isFocused && idx === focusedRow), dragging: task.id === draggedTaskId, colWidth }, task.id) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SingleTaskRow, { task, focused: !!(isFocused && idx === focusedRow), dragging: task.id === draggedTaskId, colWidth }, task.id)
|
|
57091
57119
|
);
|
|
57092
57120
|
if (contextMenuRow === idx) {
|
|
57093
57121
|
rows.push(
|
|
@@ -57101,6 +57129,11 @@ function KanbanColumn({
|
|
|
57101
57129
|
)
|
|
57102
57130
|
);
|
|
57103
57131
|
}
|
|
57132
|
+
if (idx < tasks.length - 1) {
|
|
57133
|
+
rows.push(
|
|
57134
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: isFocused ? "cyan" : "gray", dimColor: true, children: "\u2500".repeat(colWidth) }, `sep-${task.id}`)
|
|
57135
|
+
);
|
|
57136
|
+
}
|
|
57104
57137
|
});
|
|
57105
57138
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", width: colWidth, marginRight: 1, children: [
|
|
57106
57139
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { backgroundColor: headerBg, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: headerColor, bold: true, children: pad(`${name}${countStr}`, colWidth) }) }),
|
package/dist/index.html
CHANGED
|
@@ -106,7 +106,7 @@ Error generating stack: `+y.message+`
|
|
|
106
106
|
|
|
107
107
|
## Subtasks
|
|
108
108
|
|
|
109
|
-
`}}async function Poe(e,t){try{return await(await(await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}async function Toe(e,t){try{return await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`),!0}catch{return!1}}async function Cz(e,t){try{return await e.getDirectoryHandle("archive",{create:t})}catch{return null}}async function Loe(e){if(on())return U4();const t=new Set;for await(const n of e.values())n.kind==="file"&&n.name.endsWith(".md")&&t.add(n.name.slice(0,-3));try{const n=await e.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&t.add(a.name.slice(0,-3))}catch{}return[...t].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Wr(e,t){if(on())try{const i=await Az(t);return c3(i)}catch{return sj(t)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}})(e)??await Poe(e,t);return a!==null?c3(a):sj(t)}async function eu(e,t,n,a){const i=q4(n,a);if(on())return Aoe(t,i);const o=await(await(await Toe(e,t)?await Cz(e,!0):e).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close()}async function oj(e,t){if(on())return Coe(t);try{await e.removeEntry(`${t}.md`)}catch{}try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}async function Moe(e,t){await gp(`/api/tasks/${encodeURIComponent(e)}/archive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function joe(e,t){await gp(`/api/tasks/${encodeURIComponent(e)}/unarchive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Doe(e,t,n,a){const i=q4(n,a);if(on())return Moe(t,i);const o=await(await(await Cz(e,!0)).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close();try{await e.removeEntry(`${t}.md`)}catch{}}async function Ioe(e,t,n,a){const i=q4(n,a);if(on())return joe(t,i);const s=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await s.write(i),await s.close();try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}const Roe="kanban-md",Ooe=1,Mu="recentProjects";function Nz(){return new Promise((e,t)=>{const n=indexedDB.open(Roe,Ooe);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Mu)||a.createObjectStore(Mu,{keyPath:"id"})}})}async function w2(e){const t=await Nz();return new Promise((n,a)=>{const i=t.transaction(Mu,"readwrite");i.objectStore(Mu).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function p3(){const e=await Nz();return new Promise((t,n)=>{const i=e.transaction(Mu,"readonly").objectStore(Mu).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function cj(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}class Foe{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await pj(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await pj(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await lj(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await lj(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function pj(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function lj(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const tu=new Foe,zoe={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 dj=null;function Sz(){return"Notification"in window?Notification.permission:"unsupported"}async function Hoe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function x2({title:e,body:t,config:n}){if(n.notifications.sound&&Boe(n.notifications.soundId),!(!n.notifications.browser||Sz()!=="granted"))try{new Notification(e,{body:t})}catch{}}function Boe(e){const t=zoe[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{dj??=new n;const a=dj;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function qoe(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function Uoe(){const e=await U4();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await xz(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ms(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function Voe(e){const t=await Loe(e);return await Promise.all(t.map(async a=>{const{frontmatter:i,body:r}=await Wr(e,a),s={...i,id:i.id||a,status:i.status||"Backlog"},{subtasks:o,bodyWithoutSubtasks:c}=Ms(r);return{id:a,frontmatter:s,body:c,subtasks:o}}))}async function uj(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await Wr(e,s.id);await eu(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function nl(e){_oe(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function l3(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function A2(e){du.clear(),e.forEach(t=>{du.set(t.id,l3(t))})}function Zoe(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function Goe(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function Woe(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let Koe=0;const du=new Map,Ef=new Map;let C2=null;const be=doe((e,t)=>({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,drawerTaskId:null,drawerData:null,currentPage:"board",config:br,recentProjects:[],toasts:[],drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await Soe();if(!n)return;const{projectHandle:a,kandownHandle:i}=n,r=await sg(i),s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=on()?k2():null;await w2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await p3();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await cj(n.handle,!0)){t().toast("Permission denied","error");return}const i=await rj(n.handle),r=await sg(i),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await w2({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=k2();if(!n)throw new Error("No server root");const a=Zoe(n),i=await wz();nl(i);const r=await U4(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await xz(f),_={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Ms(b);return{id:f,frontmatter:_,body:A,subtasks:w}}));A2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:zd(f.body,f.subtasks)})),c=y2(o,i.board.columns),p=_2(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});e({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`),t().setupWatcher()}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!on())return;const n=k2();if(!n)return;const a=await p3(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await cj(i.handle,!0)){await t().openServerProject();return}const s=await rj(i.handle),o=await sg(s),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await w2({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!on()))try{const a=await $oe(n);a?(e({config:a}),nl(a)):(e({config:br}),nl(br))}catch{e({config:br}),nl(br)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!on())return;const r=n(i);e({config:r}),nl(r);try{await Eoe(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(on())try{const i=await Uoe();A2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=y2(r,a.board.columns),o=_2(r);e({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});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}else if(n)try{const i=await Voe(n);A2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=y2(r,a.board.columns),o=_2(r);e({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});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t(),c=on();if(!c&&!t().tasksDirHandle)return;const p=s.find(w=>w.name===a),d=s.find(w=>w.name===i);if(!p||!d)return;const m=p.tasks.findIndex(w=>w.id===n);if(m===-1)return;const f=s.map(w=>({...w,tasks:[...w.tasks]})),h=f.find(w=>w.name===a),b=f.find(w=>w.name===i),[_]=h.tasks.splice(m,1);/done|termin|closed|complet/i.test(i)?_.checked=!0:_.checked=!1,r!==void 0?b.tasks.splice(r,0,_):b.tasks.push(_),e({columns:f});try{const{tasksDirHandle:w}=t();if(!w&&!c)return;const A=a===i?f.filter(S=>S.name===i):f.filter(S=>S.name===a||S.name===i);await uj(w??null,A,o.board.columns)}catch(w){t().toast("Failed to save: "+w.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!on())return;const c=r.map(m=>({...m,tasks:[...m.tasks]})),p=c.find(m=>m.name===n);if(!p)return;const[d]=p.tasks.splice(a,1);p.tasks.splice(i,0,d),e({columns:c});try{const{tasksDirHandle:m}=t(),f=on();if(!m&&!f)return;await uj(m??null,[p],o.board.columns)}catch(m){t().toast("Failed to save: "+m.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);e({columns:p});try{const d=c.find(m=>m.name===n);d&&await Promise.all(d.tasks.map(async(m,f)=>{const{frontmatter:h,body:b}=await Wr(s,m.id);await eu(s,m.id,{...h,id:m.id,status:i,order:f},b)})),await t().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(_=>_.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.map(_=>_.toLowerCase()===n.toLowerCase()?i:_),columnColors:f}}}),await t().reloadBoard()}catch(d){t().toast("Failed to rename column: "+d.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!on())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>oj(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!on()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=qoe(a),p=a.find(f=>f.name===o)?.tasks.length??0,d={id:c,title:"",checked:!1,tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null,frontmatter:{}},m=a.map(f=>f.name===o?{...f,tasks:[...f.tasks,d]}:f);e({columns:m});try{const f={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},h="";await eu(i||null,c,f,h);const _=new Map(s);return _.set(c,{frontmatter:f,subtasks:[],body:h}),e({taskContents:_}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(f){return t().toast("Failed to create: "+f.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!on())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(d=>d.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await oj(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},archiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a||null,n);await Doe(a||null,n,{...i,archived:!0},r),t().drawerTaskId===n&&t().closeDrawer(),await t().reloadBoard(),t().toast("Archived")}catch(i){t().toast("Failed to archive: "+i.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a||null,n),s={...i};delete s.archived,await Ioe(a||null,n,s,r),await t().reloadBoard(),t().toast("Restored")}catch(i){t().toast("Failed to restore: "+i.message,"error")}},setShowArchives:n=>e({showArchives:n}),setShowMetadata:n=>e({showMetadata:n}),openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Ms(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await eu(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};await eu(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Wr(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ms(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=kz(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++Koe;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Wr(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ms(p);e({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=zd(i.body,i.subtasks),p={...i.frontmatter,id:s};await eu(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(on()){C2&&clearInterval(C2),C2=setInterval(()=>{t().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;tu.stop(),Ef.forEach(s=>clearTimeout(s)),Ef.clear(),tu.start(n,a);const i=(s,o)=>{const c=Ef.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),d=setTimeout(()=>{Ef.delete(s);const m=t().config;m.notifications.taskEdits&&x2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);Ef.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:d}=await Wr(o,s),{subtasks:m,bodyWithoutSubtasks:f}=Ms(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=l3(h),_=du.get(s);if(!_){du.set(s,b);return}c.notifications.statusChanges&&_.status!==b.status&&x2({title:"Task status changed",body:`${b.title}: ${_.status} → ${b.status}`,config:c});const w=Goe(_.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&x2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),Woe(_,b)&&i(s,b.title),du.set(s,b)};tu.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),tu.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:d,body:m}=await Wr(p,s),{subtasks:f,bodyWithoutSubtasks:h}=Ms(m),b=c,_=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,A=JSON.stringify(b.subtasks)!==JSON.stringify(f);if(!_&&!w&&!A)return;let S="none";_&&(w||A)?S="full":_?S="metadata-only":(w||A)&&(S="body-only"),e({conflictState:{taskId:s,type:S,local:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:S==="full"})}else t().reloadBoard()}),tu.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await Wr(o,s),{subtasks:d,bodyWithoutSubtasks:m}=Ms(p);du.set(s,l3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}t().reloadBoard()})}}));p3().then(e=>{be.setState({recentProjects:e})});nl(br);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{nl(be.getState().config)});function Yoe(){const e=be(o=>o.config),t=be(o=>o.updateConfig),[n,a]=$.useState(!1);if($.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return k.jsx(Ct.button,{onClick:s,className:s3("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:k.jsx(Ct.div,{className:s3("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:k.jsx(Ct.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?k.jsx(la.Moon,{size:13,className:"text-sky-300"}):k.jsx(la.Sun,{size:13,className:"text-amber-400"})},i)})})}function Xoe(e){const t=sse(e,{stiffness:180,damping:22,mass:.8});return $.useEffect(()=>{t.set(e)},[e,t]),tz(t,a=>Math.round(a).toString())}const d3="0.11.0",Qoe=({className:e})=>k.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[k.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),k.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),k.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function Joe(){const{t:e}=Wn(),t=be(V=>V.dirHandle),n=be(V=>V.isOpen);be(V=>V.projectName);const a=be(V=>V.archivedTasks.length),i=be(V=>V.showArchives),r=be(V=>V.setShowArchives),s=be(V=>V.columns),o=be(V=>V.openFolder),c=be(V=>V.reloadBoard),p=be(V=>V.createTask),d=be(V=>V.setCommandOpen),m=be(V=>V.viewMode),f=be(V=>V.setViewMode),h=be(V=>V.density),b=be(V=>V.setDensity),_=be(V=>V.setCurrentPage),w=be(V=>V.recentProjects),A=be(V=>V.openRecentProject),S=be(V=>V.filters),E=be(V=>V.setFilter),T=be(V=>V.clearFilters),L=be(V=>V.config.fields),[D,z]=$.useState(!1),j=$.useRef(null),F=$.useRef(null),M=s.reduce((V,H)=>V+H.tasks.length,0),K=Xoe(M),Q=[];L.priority&&S.priority&&Q.push({type:"priority",label:S.priority,value:S.priority}),L.tags&&S.tag&&Q.push({type:"tag",label:"#"+S.tag,value:S.tag}),L.assignee&&S.assignee&&Q.push({type:"assignee",label:"@"+S.assignee,value:S.assignee});const ee=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],Z=Q.length>0||S.search||L.ownerType&&S.ownerType;return $.useEffect(()=>{if(!D)return;const V=H=>{j.current&&!j.current.contains(H.target)&&z(!1)};return document.addEventListener("mousedown",V),()=>document.removeEventListener("mousedown",V)},[D]),k.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:[k.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[k.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:k.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[k.jsx(Qoe,{className:"w-[34px] h-[34px] dark:text-white text-black"}),k.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),k.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",d3]})]})}),t&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),k.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:[k.jsx(la.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),k.jsx("input",{ref:F,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:S.search,onChange:V=>E("search",V.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),S.search?k.jsx("button",{onClick:()=>E("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:k.jsx(la.X,{size:14})}):k.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"})]}),k.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[k.jsx(Vs,{children:Q.map(V=>k.jsxs(Ct.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>E(V.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:[V.label,k.jsx(la.X,{size:10,className:"text-fg-muted/60"})]},V.type+V.value))}),L.ownerType&&k.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:ee.map(V=>k.jsx("button",{onClick:()=>E("ownerType",V.value),className:`h-full px-2.5 text-[12px] transition-colors ${S.ownerType===V.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:V.label},V.value))}),Z&&k.jsx("button",{onClick:T,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),k.jsxs("div",{className:"relative flex-shrink-0",ref:j,children:[k.jsxs("button",{onClick:()=>z(V=>!V),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:[k.jsx(la.Folder,{size:13,className:"text-fg-muted/70"}),k.jsxs("span",{className:"font-medium",children:[".",t.name]}),k.jsx(la.ChevronDown,{size:11,className:"opacity-50"})]}),k.jsx(Vs,{children:D&&k.jsx(Ct.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:k.jsxs("div",{className:"py-1.5",children:[w.length>0&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),w.map(V=>k.jsxs("button",{onClick:()=>{z(!1),A(V)},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:[k.jsx(la.Folder,{size:12,className:"text-fg-muted/60"}),k.jsx("span",{className:"truncate",children:V.name}),V.id===t.name&&k.jsx(la.Check,{size:12,className:"ml-auto text-emerald-500"})]},V.id)),k.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),k.jsxs("button",{onClick:()=>{z(!1),o()},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:[k.jsx(la.Plus,{size:12,className:"text-fg-muted/60"}),k.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),k.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?k.jsxs(k.Fragment,{children:[k.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[k.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),k.jsx(Ct.span,{className:"tabular-nums font-medium",children:K}),k.jsx("span",{children:e("header.tasks")})]}),k.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:[k.jsx("button",{onClick:()=>f("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:k.jsx(la.LayoutBoard,{size:18})}),k.jsx("button",{onClick:()=>f("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:k.jsx(la.LayoutList,{size:18})})]}),k.jsx(Sa,{variant:"icon",icon:"Archive",onClick:()=>r(!i),title:i?e("header.backToBoard"):`${e("header.archives")} (${a})`,className:i?"text-accent":""}),k.jsx(Sa,{variant:"icon",icon:"Density",onClick:()=>b(h==="compact"?"comfortable":"compact"),title:`Density: ${h}`}),k.jsx(Sa,{variant:"icon",icon:"Settings",onClick:()=>_("settings"),title:e("common.settings")}),k.jsx(Yoe,{}),k.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),k.jsx(Sa,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>d(!0),title:"Command palette (⌘K)"}),k.jsx(Sa,{variant:"icon",icon:"Refresh",onClick:c,title:`${e("common.reload")} (R)`}),k.jsx(Sa,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>p()})]}):k.jsx(Sa,{variant:"primary",label:e("common.openFolder"),onClick:o})})]})}/**
|
|
109
|
+
`}}async function Poe(e,t){try{return await(await(await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}async function Toe(e,t){try{return await(await e.getDirectoryHandle("archive",{create:!1})).getFileHandle(`${t}.md`),!0}catch{return!1}}async function Cz(e,t){try{return await e.getDirectoryHandle("archive",{create:t})}catch{return null}}async function Loe(e){if(on())return U4();const t=new Set;for await(const n of e.values())n.kind==="file"&&n.name.endsWith(".md")&&t.add(n.name.slice(0,-3));try{const n=await e.getDirectoryHandle("archive",{create:!1});for await(const a of n.values())a.kind==="file"&&a.name.endsWith(".md")&&t.add(a.name.slice(0,-3))}catch{}return[...t].sort((n,a)=>n.localeCompare(a,void 0,{numeric:!0}))}async function Wr(e,t){if(on())try{const i=await Az(t);return c3(i)}catch{return sj(t)}const a=await(async i=>{try{return await(await(await i.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}})(e)??await Poe(e,t);return a!==null?c3(a):sj(t)}async function eu(e,t,n,a){const i=q4(n,a);if(on())return Aoe(t,i);const o=await(await(await Toe(e,t)?await Cz(e,!0):e).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close()}async function oj(e,t){if(on())return Coe(t);try{await e.removeEntry(`${t}.md`)}catch{}try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}async function Moe(e,t){await gp(`/api/tasks/${encodeURIComponent(e)}/archive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function joe(e,t){await gp(`/api/tasks/${encodeURIComponent(e)}/unarchive`,{method:"POST",body:t,headers:{"Content-Type":"text/plain"}})}async function Doe(e,t,n,a){const i=q4(n,a);if(on())return Moe(t,i);const o=await(await(await Cz(e,!0)).getFileHandle(`${t}.md`,{create:!0})).createWritable();await o.write(i),await o.close();try{await e.removeEntry(`${t}.md`)}catch{}}async function Ioe(e,t,n,a){const i=q4(n,a);if(on())return joe(t,i);const s=await(await e.getFileHandle(`${t}.md`,{create:!0})).createWritable();await s.write(i),await s.close();try{await(await e.getDirectoryHandle("archive",{create:!1})).removeEntry(`${t}.md`)}catch{}}const Roe="kanban-md",Ooe=1,Mu="recentProjects";function Nz(){return new Promise((e,t)=>{const n=indexedDB.open(Roe,Ooe);n.onerror=()=>t(n.error),n.onsuccess=()=>e(n.result),n.onupgradeneeded=()=>{const a=n.result;a.objectStoreNames.contains(Mu)||a.createObjectStore(Mu,{keyPath:"id"})}})}async function w2(e){const t=await Nz();return new Promise((n,a)=>{const i=t.transaction(Mu,"readwrite");i.objectStore(Mu).put(e),i.oncomplete=()=>n(),i.onerror=()=>a(i.error)})}async function p3(){const e=await Nz();return new Promise((t,n)=>{const i=e.transaction(Mu,"readonly").objectStore(Mu).getAll();i.onsuccess=()=>{const r=i.result||[];r.sort((s,o)=>o.lastOpened-s.lastOpened),t(r.slice(0,10))},i.onerror=()=>n(i.error)})}async function cj(e,t=!0){const n={mode:t?"readwrite":"read"};return await e.queryPermission(n)==="granted"||await e.requestPermission(n)==="granted"}class Foe{dirHandle=null;tasksDirHandle=null;intervalId=null;configHash=null;taskHashes=new Map;knownTaskIds=new Set;listeners=new Map;debounceTimers=new Map;debounceDelay=150;start(t,n){this.dirHandle=t,this.tasksDirHandle=n,this.initHashes(),this.intervalId=setInterval(()=>void this.tick(),300)}stop(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.configHash=null,this.taskHashes.clear(),this.knownTaskIds.clear(),this.listeners.clear(),this.debounceTimers.forEach(t=>clearTimeout(t)),this.debounceTimers.clear()}on(t,n){return this.listeners.has(t)||this.listeners.set(t,new Set),this.listeners.get(t).add(n),()=>{this.listeners.get(t)?.delete(n)}}getKnownTaskIds(){return Array.from(this.knownTaskIds)}async initHashes(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await pj(this.dirHandle);t!==null&&(this.configHash=await this.hash(t)),await this.syncTaskDir(!1)}async tick(){if(!this.dirHandle||!this.tasksDirHandle)return;const t=await pj(this.dirHandle);if(t!==null){const n=await this.hash(t);this.configHash!==null&&n!==this.configHash&&(this.configHash=n,this.debouncedEmit("configChanged"))}for(const n of this.knownTaskIds){const a=await lj(this.tasksDirHandle,n);if(a!==null){const i=await this.hash(a),r=this.taskHashes.get(n);r!==void 0&&i!==r&&(this.taskHashes.set(n,i),this.debouncedEmit("taskChanged",n))}}await this.syncTaskDir(!0)}debouncedEmit(t,...n){const a=t+JSON.stringify(n),i=this.debounceTimers.get(a);i&&clearTimeout(i);const r=setTimeout(()=>{this.debounceTimers.delete(a),this.emit(t,...n)},this.debounceDelay);this.debounceTimers.set(a,r)}async syncTaskDir(t){if(this.tasksDirHandle){for await(const n of this.tasksDirHandle.values())if(n.kind==="file"&&n.name.endsWith(".md")){const a=n.name.replace(".md","");if(!this.knownTaskIds.has(a)){this.knownTaskIds.add(a);const i=await lj(this.tasksDirHandle,a);i!==null&&(this.taskHashes.set(a,await this.hash(i)),t&&this.debouncedEmit("newTaskDetected",a))}}}}async hash(t){const a=new TextEncoder().encode(t),i=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(i)).map(s=>s.toString(16).padStart(2,"0")).join("")}emit(t,...n){this.listeners.get(t)?.forEach(i=>{if(t==="configChanged"){i();return}if(t==="taskChanged"){const[s]=n;i(s);return}const[r]=n;i(r)})}}async function pj(e){try{return await(await(await e.getFileHandle("kandown.json")).getFile()).text()}catch{return null}}async function lj(e,t){try{return await(await(await e.getFileHandle(`${t}.md`)).getFile()).text()}catch{return null}}const tu=new Foe,zoe={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 dj=null;function Sz(){return"Notification"in window?Notification.permission:"unsupported"}async function Hoe(){return"Notification"in window?await Notification.requestPermission():"unsupported"}function x2({title:e,body:t,config:n}){if(n.notifications.sound&&Boe(n.notifications.soundId),!(!n.notifications.browser||Sz()!=="granted"))try{new Notification(e,{body:t})}catch{}}function Boe(e){const t=zoe[e],n=window.AudioContext??window.webkitAudioContext;if(!(!n||t.length===0))try{dj??=new n;const a=dj;a.state==="suspended"&&a.resume();const i=a.currentTime+.01;t.forEach(r=>{const s=a.createOscillator(),o=a.createGain(),c=i+r.delayMs/1e3,p=c+r.durationMs/1e3;s.type=r.type??"sine",s.frequency.setValueAtTime(r.frequency,c),o.gain.setValueAtTime(1e-4,c),o.gain.exponentialRampToValueAtTime(.08,c+.01),o.gain.exponentialRampToValueAtTime(1e-4,p),s.connect(o),o.connect(a.destination),s.start(c),s.stop(p+.02)})}catch{}}function qoe(e){let t=-1;for(const n of e)for(const a of n.tasks){const i=a.id.match(/^t(\d+)$/);if(i){const r=parseInt(i[1],10);r>t&&(t=r)}}return"t"+(t+1)}async function Uoe(){const e=await U4();return await Promise.all(e.map(async n=>{const{frontmatter:a,body:i}=await xz(n),r={...a,id:a.id||n,status:a.status||"Backlog"},{subtasks:s,bodyWithoutSubtasks:o}=Ms(i);return{id:n,frontmatter:r,body:o,subtasks:s}}))}async function Voe(e){const t=await Loe(e);return await Promise.all(t.map(async a=>{const{frontmatter:i,body:r}=await Wr(e,a),s={...i,id:i.id||a,status:i.status||"Backlog"},{subtasks:o,bodyWithoutSubtasks:c}=Ms(r);return{id:a,frontmatter:s,body:c,subtasks:o}}))}async function uj(e,t,n){const a=[];for(const i of t){const r=i.name;i.tasks.forEach((s,o)=>{a.push((async()=>{const{frontmatter:c,body:p}=await Wr(e,s.id);await eu(e,s.id,{...c,id:s.id,status:r,order:o},p)})())})}await Promise.all(a)}function nl(e){_oe(e.ui.theme,e.ui.skin,e.ui.font,e.ui.background)}function l3(e){return{title:e.frontmatter.title||e.id,status:e.frontmatter.status||"Backlog",body:e.body,subtasks:e.subtasks}}function A2(e){du.clear(),e.forEach(t=>{du.set(t.id,l3(t))})}function Zoe(e){const t=e.split(/[\\/]+/).filter(Boolean),n=t[t.length-1];return n===".kandown"?t[t.length-2]??"Project":n??"Project"}function Goe(e,t){return t.reduce((n,a,i)=>{const r=e[i]?.done??!1;return n+(a.done&&!r?1:0)},0)}function Woe(e,t){const n=e.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""})),a=t.subtasks.map(i=>({text:i.text,description:i.description??"",report:i.report??""}));return e.title!==t.title||e.body!==t.body||JSON.stringify(n)!==JSON.stringify(a)}let Koe=0;const du=new Map,Ef=new Map;let C2=null;const be=doe((e,t)=>({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,drawerTaskId:null,drawerData:null,currentPage:"board",config:br,recentProjects:[],toasts:[],drawerBaseVersion:null,conflictState:null,showConflictModal:!1,openFolder:async()=>{const n=await Soe();if(!n)return;const{projectHandle:a,kandownHandle:i}=n,r=await sg(i),s=a.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`);const o=on()?k2():null;await w2({id:a.name,name:a.name,handle:a,lastOpened:Date.now(),...o?{kandownDir:o}:{}}),await t().loadConfig(),await t().reloadBoard();const c=await p3();e({recentProjects:c}),t().setupWatcher()},openRecentProject:async n=>{if(!await cj(n.handle,!0)){t().toast("Permission denied","error");return}const i=await rj(n.handle),r=await sg(i),s=n.handle.name;e({dirHandle:i,tasksDirHandle:r,projectName:s}),window.history.pushState({},"",`?p=${encodeURIComponent(s)}`),await w2({...n,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},openServerProject:async()=>{e({loading:!0});try{const n=k2();if(!n)throw new Error("No server root");const a=Zoe(n),i=await wz();nl(i);const r=await U4(),s=await Promise.all(r.map(async f=>{const{frontmatter:h,body:b}=await xz(f),_={...h,id:h.id||f,status:h.status||"Backlog"},{subtasks:w,bodyWithoutSubtasks:A}=Ms(b);return{id:f,frontmatter:_,body:A,subtasks:w}}));A2(s);const o=s.map(f=>({frontmatter:f.frontmatter,body:zd(f.body,f.subtasks)})),c=y2(o,i.board.columns),p=_2(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});e({loading:!1,isOpen:!0,config:i,columns:c,archivedTasks:p,boardTitle:"Project Kanban",projectName:a,taskContents:m,searchMatches:new Map}),window.history.pushState({},"",`?p=${encodeURIComponent(a)}`),t().setupWatcher()}catch{e({loading:!1,isOpen:!1}),t().toast("Impossible de charger le projet. Relancez `kandown`.","error")}},tryAutoOpenServerProject:async()=>{if(!on())return;const n=k2();if(!n)return;const a=await p3(),i=a.find(p=>p.kandownDir===n);if(!i){await t().openServerProject();return}if(!await cj(i.handle,!0)){await t().openServerProject();return}const s=await rj(i.handle),o=await sg(s),c=i.handle.name;e({dirHandle:s,tasksDirHandle:o,projectName:c,recentProjects:a,isOpen:!0}),window.history.pushState({},"",`?p=${encodeURIComponent(c)}`),await w2({...i,lastOpened:Date.now()}),await t().loadConfig(),await t().reloadBoard(),t().setupWatcher()},loadConfig:async()=>{const{dirHandle:n}=t();if(!(!n&&!on()))try{const a=await $oe(n);a?(e({config:a}),nl(a)):(e({config:br}),nl(br))}catch{e({config:br}),nl(br)}},updateConfig:async n=>{const{dirHandle:a,config:i}=t();if(!a&&!on())return;const r=n(i);e({config:r}),nl(r);try{await Eoe(a,r)}catch(s){t().toast("Failed to save config: "+s.message,"error")}},reloadBoard:async()=>{const{tasksDirHandle:n,config:a}=t();if(on())try{const i=await Uoe();A2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=y2(r,a.board.columns),o=_2(r);e({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});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}else if(n)try{const i=await Voe(n);A2(i);const r=i.map(d=>({frontmatter:d.frontmatter,body:zd(d.body,d.subtasks)})),s=y2(r,a.board.columns),o=_2(r);e({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});e({taskContents:p,searchMatches:new Map})}catch(i){t().toast("Failed to load board: "+i.message,"error")}},moveTask:async(n,a,i,r)=>{const{columns:s,config:o}=t(),c=on();if(!c&&!t().tasksDirHandle)return;const p=s.find(w=>w.name===a),d=s.find(w=>w.name===i);if(!p||!d)return;const m=p.tasks.findIndex(w=>w.id===n);if(m===-1)return;const f=s.map(w=>({...w,tasks:[...w.tasks]})),h=f.find(w=>w.name===a),b=f.find(w=>w.name===i),[_]=h.tasks.splice(m,1);/done|termin|closed|complet/i.test(i)?_.checked=!0:_.checked=!1,r!==void 0?b.tasks.splice(r,0,_):b.tasks.push(_),e({columns:f});try{const{tasksDirHandle:w}=t();if(!w&&!c)return;const A=a===i?f.filter(S=>S.name===i):f.filter(S=>S.name===a||S.name===i);await uj(w??null,A,o.board.columns)}catch(w){t().toast("Failed to save: "+w.message,"error"),e({columns:s})}},reorderInColumn:async(n,a,i)=>{const{columns:r,tasksDirHandle:s,config:o}=t();if(!s&&!on())return;const c=r.map(m=>({...m,tasks:[...m.tasks]})),p=c.find(m=>m.name===n);if(!p)return;const[d]=p.tasks.splice(a,1);p.tasks.splice(i,0,d),e({columns:c});try{const{tasksDirHandle:m}=t(),f=on();if(!m&&!f)return;await uj(m??null,[p],o.board.columns)}catch(m){t().toast("Failed to save: "+m.message,"error"),e({columns:r})}},addColumn:async n=>{const a=n.trim();if(!a)return;const{config:i}=t();i.board.columns.some(r=>r.toLowerCase()===a.toLowerCase())||(await t().updateConfig(r=>({...r,board:{...r.board,columns:[...r.board.columns,a]}})),await t().reloadBoard())},renameColumn:async(n,a)=>{const i=a.trim(),{columns:r,tasksDirHandle:s,config:o}=t();if(!s||!i||i.toLowerCase()===n.toLowerCase())return;if(r.some(d=>d.name.toLowerCase()===i.toLowerCase())){t().toast("Column already exists","error");return}const c=r,p=r.map(d=>d.name===n?{...d,name:i}:d);e({columns:p});try{const d=c.find(m=>m.name===n);d&&await Promise.all(d.tasks.map(async(m,f)=>{const{frontmatter:h,body:b}=await Wr(s,m.id);await eu(s,m.id,{...h,id:m.id,status:i,order:f},b)})),await t().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(_=>_.toLowerCase()===n.toLowerCase())?m.board.columns:[...m.board.columns,n];return{...m,board:{...m.board,columns:b.map(_=>_.toLowerCase()===n.toLowerCase()?i:_),columnColors:f}}}),await t().reloadBoard()}catch(d){t().toast("Failed to rename column: "+d.message,"error"),e({columns:c})}},deleteColumn:async n=>{const{columns:a,tasksDirHandle:i}=t();if(!i&&!on())return;const r=a.find(o=>o.name===n);if(!r)return;const s=a;e({columns:a.filter(o=>o.name!==n)});try{await Promise.all(r.tasks.map(o=>oj(i,o.id))),await t().updateConfig(o=>{const c={...o.board.columnColors??{}};return delete c[n.toLowerCase()],{...o,board:{...o.board,columns:o.board.columns.filter(p=>p.toLowerCase()!==n.toLowerCase()),columnColors:c}}}),await t().reloadBoard(),t().toast("Column deleted")}catch(o){t().toast("Failed to delete column: "+o.message,"error"),e({columns:s})}},createTask:async n=>{const{columns:a,tasksDirHandle:i,config:r,taskContents:s}=t();if(!i&&!on()||!a.length)return null;const o=n||r.board.columns[0]||a[0].name,c=qoe(a),p=a.find(f=>f.name===o)?.tasks.length??0,d={id:c,title:"",checked:!1,tags:[],assignee:null,priority:r.fields.priority?r.board.defaultPriority:null,ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",progress:null,frontmatter:{}},m=a.map(f=>f.name===o?{...f,tasks:[...f.tasks,d]}:f);e({columns:m});try{const f={id:c,title:"",status:o,order:p,priority:r.fields.priority?r.board.defaultPriority:"",tags:[],assignee:"",created:new Date().toISOString().slice(0,10),ownerType:r.fields.ownerType?r.board.defaultOwnerType:"",tools:""},h="";await eu(i||null,c,f,h);const _=new Map(s);return _.set(c,{frontmatter:f,subtasks:[],body:h}),e({taskContents:_}),t().toast(`Created ${c.replace(/^t/,"")}`),await t().openDrawer(c),c}catch(f){return t().toast("Failed to create: "+f.message,"error"),e({columns:a}),null}},deleteTask:async n=>{const{columns:a,tasksDirHandle:i,taskContents:r}=t();if(!i&&!on())return;const s=a.map(p=>({...p,tasks:p.tasks.filter(d=>d.id!==n)}));e({columns:s});const o=new Map(r);o.delete(n);const c=new Map(t().searchMatches);c.delete(n),e({taskContents:o,searchMatches:c});try{await oj(i||null,n),t().toast("Deleted")}catch(p){t().toast("Failed to delete: "+p.message,"error"),e({columns:a})}},archiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a||null,n);await Doe(a||null,n,{...i,archived:!0},r),t().drawerTaskId===n&&t().closeDrawer(),await t().reloadBoard(),t().toast("Archived")}catch(i){t().toast("Failed to archive: "+i.message,"error")}},unarchiveTask:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a||null,n),s={...i};delete s.archived,await Ioe(a||null,n,s,r),await t().reloadBoard(),t().toast("Restored")}catch(i){t().toast("Failed to restore: "+i.message,"error")}},setShowArchives:n=>e({showArchives:n}),setShowMetadata:n=>e({showMetadata:n}),openDrawer:async n=>{const{tasksDirHandle:a}=t();if(!(!a&&!on()))try{const{frontmatter:i,body:r}=await Wr(a,n),{subtasks:s,bodyWithoutSubtasks:o}=Ms(r),c={frontmatter:i,subtasks:s,body:o,savedAt:Date.now()};e({drawerTaskId:n,drawerData:{frontmatter:i,subtasks:s,body:o},drawerBaseVersion:c,conflictState:null,showConflictModal:!1})}catch(i){t().toast("Failed to open: "+i.message,"error")}},closeDrawer:()=>e({drawerTaskId:null,drawerData:null,drawerBaseVersion:null,conflictState:null,showConflictModal:!1}),updateDrawerData:n=>{const{drawerData:a}=t();a&&e({drawerData:n(a)})},saveDrawer:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!n||!a)return;const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};try{await eu(i||null,n,o,s),t().toast("Saved"),e({drawerTaskId:null,drawerData:null});const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(c){t().toast("Failed to save: "+c.message,"error")}},saveDrawerMetadata:async()=>{const{drawerTaskId:n,drawerData:a,tasksDirHandle:i,taskContents:r}=t();if(!(!n||!a))try{const s=zd(a.body,a.subtasks),o={...a.frontmatter,id:n};await eu(i||null,n,o,s);const c=new Map(r);c.set(n,{frontmatter:o,subtasks:a.subtasks,body:a.body}),e({taskContents:c}),await t().reloadBoard()}catch(s){t().toast("Failed to save: "+s.message,"error")}},setViewMode:n=>{localStorage.setItem("kandown:view",n),e({viewMode:n})},setDensity:n=>{localStorage.setItem("kandown:density",n),e({density:n})},setFilter:(n,a)=>{if(e(i=>({filters:{...i.filters,[n]:a}})),n==="search"){const{columns:i,tasksDirHandle:r,taskContents:s}=t(),o=a,c=i.flatMap(p=>p.tasks.map(d=>d.id));if(r){const p=c.filter(d=>!s.has(d));p.length>0?t().loadTaskContents(p).then(()=>{t().computeSearchMatches(o)}):t().computeSearchMatches(o)}}},clearFilters:()=>e({filters:{search:"",priority:null,tag:null,assignee:null,ownerType:null},searchMatches:new Map}),setCommandOpen:n=>e({commandOpen:n}),setCurrentPage:n=>e({currentPage:n}),loadTaskContents:async n=>{const{tasksDirHandle:a}=t();if(!a)return;const i=new Map(t().taskContents);await Promise.all(n.map(async r=>{if(!i.has(r))try{const{frontmatter:s,body:o}=await Wr(a,r),{subtasks:c,bodyWithoutSubtasks:p}=Ms(o);i.set(r,{frontmatter:s,subtasks:c,body:p})}catch{}})),e({taskContents:i})},computeSearchMatches:n=>{if(!n.trim()){e({searchMatches:new Map});return}const{taskContents:a}=t(),i=new Map,r=n.toLowerCase();for(const[s,o]of a){const c=kz(o,r);c.length>0&&i.set(s,c)}e({searchMatches:i})},toast:(n,a="success")=>{const i=++Koe;e(r=>({toasts:[...r.toasts,{id:i,message:n,type:a}]})),setTimeout(()=>{e(r=>({toasts:r.toasts.filter(s=>s.id!==i)}))},2500)},dismissToast:n=>e(a=>({toasts:a.toasts.filter(i=>i.id!==n)})),resolveConflict:async n=>{const{conflictState:a,drawerData:i,tasksDirHandle:r,drawerTaskId:s,drawerBaseVersion:o}=t();if(!(!a||!r||!s))if(n==="reload"){const{frontmatter:c,body:p}=await Wr(r,s),{subtasks:d,bodyWithoutSubtasks:m}=Ms(p);e({drawerData:{frontmatter:c,subtasks:d,body:m},drawerBaseVersion:{frontmatter:c,subtasks:d,body:m,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Reloaded from disk")}else if(n==="overwrite"){if(i&&s&&o){const c=zd(i.body,i.subtasks),p={...i.frontmatter,id:s};await eu(r,s,p,c),e({drawerBaseVersion:{...i,savedAt:Date.now()},conflictState:null,showConflictModal:!1}),t().toast("Overwritten remote changes")}}else e({conflictState:null,showConflictModal:!1})},setupWatcher:()=>{if(on()){C2&&clearInterval(C2),C2=setInterval(()=>{t().reloadBoard()},2e3);return}const{dirHandle:n,tasksDirHandle:a}=t();if(!n||!a)return;tu.stop(),Ef.forEach(s=>clearTimeout(s)),Ef.clear(),tu.start(n,a);const i=(s,o)=>{const c=Ef.get(s);c&&clearTimeout(c);const p=Math.max(2e3,t().config.notifications.editDebounceMs),d=setTimeout(()=>{Ef.delete(s);const m=t().config;m.notifications.taskEdits&&x2({title:"Task edited",body:`${o} changed on disk.`,config:m})},p);Ef.set(s,d)},r=async s=>{const{tasksDirHandle:o,config:c}=t();if(!o)return;const{frontmatter:p,body:d}=await Wr(o,s),{subtasks:m,bodyWithoutSubtasks:f}=Ms(d),h={id:s,frontmatter:{...p,id:p.id||s,status:p.status||"Backlog"},body:f,subtasks:m},b=l3(h),_=du.get(s);if(!_){du.set(s,b);return}c.notifications.statusChanges&&_.status!==b.status&&x2({title:"Task status changed",body:`${b.title}: ${_.status} → ${b.status}`,config:c});const w=Goe(_.subtasks,b.subtasks);c.notifications.subtaskCompletions&&w>0&&x2({title:"Subtask completed",body:w===1?`${b.title}: 1 subtask completed.`:`${b.title}: ${w} subtasks completed.`,config:c}),Woe(_,b)&&i(s,b.title),du.set(s,b)};tu.on("configChanged",()=>{t().loadConfig(),t().toast("Settings updated externally","info")}),tu.on("taskChanged",async s=>{const{drawerTaskId:o,drawerBaseVersion:c,tasksDirHandle:p}=t();if(await r(s),o===s&&c&&p){const{frontmatter:d,body:m}=await Wr(p,s),{subtasks:f,bodyWithoutSubtasks:h}=Ms(m),b=c,_=JSON.stringify(b.frontmatter)!==JSON.stringify(d),w=b.body!==h,A=JSON.stringify(b.subtasks)!==JSON.stringify(f);if(!_&&!w&&!A)return;let S="none";_&&(w||A)?S="full":_?S="metadata-only":(w||A)&&(S="body-only"),e({conflictState:{taskId:s,type:S,local:b,remote:{frontmatter:d,body:h,subtasks:f}},showConflictModal:S==="full"})}else t().reloadBoard()}),tu.on("newTaskDetected",async s=>{const{tasksDirHandle:o}=t();if(o){const{frontmatter:c,body:p}=await Wr(o,s),{subtasks:d,bodyWithoutSubtasks:m}=Ms(p);du.set(s,l3({id:s,frontmatter:{...c,id:c.id||s,status:c.status||"Backlog"},body:m,subtasks:d}))}t().reloadBoard()})}}));p3().then(e=>{be.setState({recentProjects:e})});nl(br);window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{nl(be.getState().config)});function Yoe(){const e=be(o=>o.config),t=be(o=>o.updateConfig),[n,a]=$.useState(!1);if($.useEffect(()=>{a(!0)},[]),!n)return null;const i=e.ui.theme==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.ui.theme,r=i==="dark",s=()=>{const o=r?"light":"dark";t(c=>({...c,ui:{...c.ui,theme:o}}))};return k.jsx(Ct.button,{onClick:s,className:s3("relative inline-flex items-center rounded-full transition-all duration-300","border border-black/[0.08] dark:border-white/[0.12]","bg-black/[0.05] dark:bg-white/[0.08]","h-8 w-[52px] px-1"),title:r?"Switch to light mode":"Switch to dark mode",children:k.jsx(Ct.div,{className:s3("inline-flex items-center justify-center rounded-full shadow-md",r?"bg-[#1e293b] border border-white/[0.15]":"bg-black border border-black/20","h-[26px] w-[26px]"),animate:{x:r?22:0},transition:{type:"spring",stiffness:500,damping:30},children:k.jsx(Ct.div,{initial:{rotate:-90,scale:.5,opacity:0},animate:{rotate:0,scale:1,opacity:1},transition:{duration:.25,ease:"easeOut"},children:r?k.jsx(la.Moon,{size:13,className:"text-sky-300"}):k.jsx(la.Sun,{size:13,className:"text-amber-400"})},i)})})}function Xoe(e){const t=sse(e,{stiffness:180,damping:22,mass:.8});return $.useEffect(()=>{t.set(e)},[e,t]),tz(t,a=>Math.round(a).toString())}const d3="0.11.2",Qoe=({className:e})=>k.jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 150 150",className:e,fill:"currentColor",children:[k.jsx("path",{d:"m57.6 64.6 0.1-0.1v-31.3c-0.1-3.5-2.7-5.6-5.7-5.6h-16.3v59.6c0.2-1.3 0.9-2.6 1.7-3.2l20.2-19.4z"}),k.jsx("path",{d:"m87.6 43.8c-3.4 0.1-7.1 1.3-9.9 3.7l-38.5 38.7c-2.1 2.1-3.4 4.8-3.5 7.5v26.7l77.5-76.4v-0.2h-25.6z"}),k.jsx("path",{d:"m108.1 96.4-6 5-22.4-20.8-14.6 14.2 21.1 21.4-5 4.1c-0.6 0.5-0.3 0.9 0.2 0.9h27.6c0.4 0 0.7-0.4 0.7-0.6v-24.8c0-0.7-1.1-0.3-1.6 0.3v0.3z"})]});function Joe(){const{t:e}=Wn(),t=be(V=>V.dirHandle),n=be(V=>V.isOpen);be(V=>V.projectName);const a=be(V=>V.archivedTasks.length),i=be(V=>V.showArchives),r=be(V=>V.setShowArchives),s=be(V=>V.columns),o=be(V=>V.openFolder),c=be(V=>V.reloadBoard),p=be(V=>V.createTask),d=be(V=>V.setCommandOpen),m=be(V=>V.viewMode),f=be(V=>V.setViewMode),h=be(V=>V.density),b=be(V=>V.setDensity),_=be(V=>V.setCurrentPage),w=be(V=>V.recentProjects),A=be(V=>V.openRecentProject),S=be(V=>V.filters),E=be(V=>V.setFilter),T=be(V=>V.clearFilters),L=be(V=>V.config.fields),[D,z]=$.useState(!1),j=$.useRef(null),F=$.useRef(null),M=s.reduce((V,H)=>V+H.tasks.length,0),K=Xoe(M),Q=[];L.priority&&S.priority&&Q.push({type:"priority",label:S.priority,value:S.priority}),L.tags&&S.tag&&Q.push({type:"tag",label:"#"+S.tag,value:S.tag}),L.assignee&&S.assignee&&Q.push({type:"assignee",label:"@"+S.assignee,value:S.assignee});const ee=[{label:e("filterBar.ownerAll"),value:""},{label:e("filterBar.ownerHuman"),value:"human"},{label:e("filterBar.ownerAI"),value:"ai"}],Z=Q.length>0||S.search||L.ownerType&&S.ownerType;return $.useEffect(()=>{if(!D)return;const V=H=>{j.current&&!j.current.contains(H.target)&&z(!1)};return document.addEventListener("mousedown",V),()=>document.removeEventListener("mousedown",V)},[D]),k.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:[k.jsxs("div",{className:"flex items-center gap-4 min-w-0",children:[k.jsx("div",{className:"flex items-center gap-2.5 flex-shrink-0",children:k.jsxs("button",{onClick:()=>window.history.pushState({},"",window.location.pathname),className:"flex items-center gap-2 cursor-pointer",children:[k.jsx(Qoe,{className:"w-[34px] h-[34px] dark:text-white text-black"}),k.jsx("span",{className:"text-[15px] font-semibold tracking-tight text-fg",children:"kandown"}),k.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",d3]})]})}),t&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),k.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:[k.jsx(la.Search,{size:14,className:"text-fg-muted/60 flex-shrink-0"}),k.jsx("input",{ref:F,type:"text",placeholder:e("filterBar.searchPlaceholder"),value:S.search,onChange:V=>E("search",V.target.value),className:"bg-transparent border-none outline-none text-fg text-[13px] w-full placeholder:text-fg-muted/60"}),S.search?k.jsx("button",{onClick:()=>E("search",""),className:"text-fg-muted/60 hover:text-fg flex-shrink-0",children:k.jsx(la.X,{size:14})}):k.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"})]}),k.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap overflow-hidden",children:[k.jsx(Vs,{children:Q.map(V=>k.jsxs(Ct.button,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},exit:{opacity:0,scale:.9},transition:{duration:.15},onClick:()=>E(V.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:[V.label,k.jsx(la.X,{size:10,className:"text-fg-muted/60"})]},V.type+V.value))}),L.ownerType&&k.jsx("div",{className:"flex items-center h-6 border border-black/[0.06] dark:border-white/[0.1] rounded-lg overflow-hidden",children:ee.map(V=>k.jsx("button",{onClick:()=>E("ownerType",V.value),className:`h-full px-2.5 text-[12px] transition-colors ${S.ownerType===V.value?"bg-black/[0.06] dark:bg-white/[0.12] text-fg":"text-fg-muted/70 hover:text-fg"}`,children:V.label},V.value))}),Z&&k.jsx("button",{onClick:T,className:"text-[12px] text-fg-muted/60 hover:text-fg transition-colors",children:e("filterBar.clearAll")})]})]}),t&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"w-px h-[20px] bg-black/[0.08] dark:bg-white/[0.08] flex-shrink-0"}),k.jsxs("div",{className:"relative flex-shrink-0",ref:j,children:[k.jsxs("button",{onClick:()=>z(V=>!V),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:[k.jsx(la.Folder,{size:13,className:"text-fg-muted/70"}),k.jsxs("span",{className:"font-medium",children:[".",t.name]}),k.jsx(la.ChevronDown,{size:11,className:"opacity-50"})]}),k.jsx(Vs,{children:D&&k.jsx(Ct.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:k.jsxs("div",{className:"py-1.5",children:[w.length>0&&k.jsxs(k.Fragment,{children:[k.jsx("div",{className:"px-3 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-fg-muted/60",children:e("header.recentProjects")}),w.map(V=>k.jsxs("button",{onClick:()=>{z(!1),A(V)},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:[k.jsx(la.Folder,{size:12,className:"text-fg-muted/60"}),k.jsx("span",{className:"truncate",children:V.name}),V.id===t.name&&k.jsx(la.Check,{size:12,className:"ml-auto text-emerald-500"})]},V.id)),k.jsx("div",{className:"h-px bg-black/[0.06] dark:bg-white/[0.08] my-1.5 mx-2"})]}),k.jsxs("button",{onClick:()=>{z(!1),o()},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:[k.jsx(la.Plus,{size:12,className:"text-fg-muted/60"}),k.jsx("span",{children:e("header.openFolder...")})]})]})})})]})]})]}),k.jsx("div",{className:"flex items-center gap-2 flex-shrink-0",children:n||t?k.jsxs(k.Fragment,{children:[k.jsxs("div",{className:"flex items-center gap-2 mr-2 text-[12.5px] text-fg-muted/70",children:[k.jsx("span",{className:"inline-block w-1.5 h-1.5 rounded-full bg-emerald-500"}),k.jsx(Ct.span,{className:"tabular-nums font-medium",children:K}),k.jsx("span",{children:e("header.tasks")})]}),k.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:[k.jsx("button",{onClick:()=>f("board"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="board"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.board"),children:k.jsx(la.LayoutBoard,{size:18})}),k.jsx("button",{onClick:()=>f("list"),className:`w-9 h-9 inline-flex items-center justify-center rounded-lg transition-all ${m==="list"?"bg-card text-fg shadow-sm":"text-fg-muted/70 hover:text-fg"}`,title:e("common.list"),children:k.jsx(la.LayoutList,{size:18})})]}),k.jsx(Sa,{variant:"icon",icon:"Archive",onClick:()=>r(!i),title:i?e("header.backToBoard"):`${e("header.archives")} (${a})`,className:i?"text-accent":""}),k.jsx(Sa,{variant:"icon",icon:"Density",onClick:()=>b(h==="compact"?"comfortable":"compact"),title:`Density: ${h}`}),k.jsx(Sa,{variant:"icon",icon:"Settings",onClick:()=>_("settings"),title:e("common.settings")}),k.jsx(Yoe,{}),k.jsx("div",{className:"w-px h-5 bg-black/[0.08] dark:bg-white/[0.08] mx-1"}),k.jsx(Sa,{variant:"secondary",icon:"Search",label:e("common.search"),shortcut:"⌘K",onClick:()=>d(!0),title:"Command palette (⌘K)"}),k.jsx(Sa,{variant:"icon",icon:"Refresh",onClick:c,title:`${e("common.reload")} (R)`}),k.jsx(Sa,{variant:"primary",icon:"Plus",label:e("common.newTask"),shortcut:"N",onClick:()=>p()})]}):k.jsx(Sa,{variant:"primary",label:e("common.openFolder"),onClick:o})})]})}/**
|
|
110
110
|
* @license @tabler/icons-react v3.41.1 - MIT
|
|
111
111
|
*
|
|
112
112
|
* This source code is licensed under the MIT license.
|