feat: edit project description + folder selector for new docs

This commit is contained in:
Hiro
2026-03-31 00:17:47 +00:00
parent ae324352a4
commit ce938747ab
28 changed files with 316 additions and 208 deletions

View File

@@ -1 +1 @@
import{d as l,c as n,q as c,b as i,O as r,o}from"./index-BuR1WXAD.js";const u=["type","disabled"],f={key:0,class:"btn__spinner"},b=l({__name:"Button",props:{variant:{default:"primary"},size:{default:"md"},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},type:{default:"button"}},setup(e){return(t,a)=>(o(),n("button",{type:e.type,disabled:e.disabled||e.loading,class:c(["btn",`btn--${e.variant}`,`btn--${e.size}`])},[e.loading?(o(),n("span",f)):i("",!0),r(t.$slots,"default",{},void 0)],10,u))}}),m=(e,t)=>{const a=e.__vccOpts||e;for(const[s,d]of t)a[s]=d;return a},y=m(b,[["__scopeId","data-v-a1ef9cca"]]);export{y as B,m as _}; import{d as l,c as n,q as c,b as i,Q as r,o}from"./index-Bwmlaq-e.js";const u=["type","disabled"],f={key:0,class:"btn__spinner"},b=l({__name:"Button",props:{variant:{default:"primary"},size:{default:"md"},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},type:{default:"button"}},setup(e){return(t,a)=>(o(),n("button",{type:e.type,disabled:e.disabled||e.loading,class:c(["btn",`btn--${e.variant}`,`btn--${e.size}`])},[e.loading?(o(),n("span",f)):i("",!0),r(t.$slots,"default",{},void 0)],10,u))}}),m=(e,t)=>{const a=e.__vccOpts||e;for(const[s,d]of t)a[s]=d;return a},y=m(b,[["__scopeId","data-v-a1ef9cca"]]);export{y as B,m as _};

View File

@@ -1 +0,0 @@
import{d as l,c as n,q as c,b as i,Q as r,o}from"./index-D2fAAQJ2.js";const u=["type","disabled"],f={key:0,class:"btn__spinner"},b=l({__name:"Button",props:{variant:{default:"primary"},size:{default:"md"},disabled:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},type:{default:"button"}},setup(e){return(t,a)=>(o(),n("button",{type:e.type,disabled:e.disabled||e.loading,class:c(["btn",`btn--${e.variant}`,`btn--${e.size}`])},[e.loading?(o(),n("span",f)):i("",!0),r(t.$slots,"default",{},void 0)],10,u))}}),m=(e,t)=>{const a=e.__vccOpts||e;for(const[s,d]of t)a[s]=d;return a},y=m(b,[["__scopeId","data-v-a1ef9cca"]]);export{y as B,m as _};

View File

@@ -1 +0,0 @@
import{d as b,k as x,c as a,a as t,t as u,l as c,f as p,g as i,F as P,m as B,r as f,o as n,h as m,e as y,v as g,w as N,j as D,b as M}from"./index-D2fAAQJ2.js";import{u as V}from"./projects-CWhQXAJL.js";import{B as _,_ as S}from"./Button-D-QrZfPJ.js";import{M as $}from"./Modal-C7dh6Uqe.js";const F={class:"dashboard"},U={class:"dashboard__header"},z={class:"dashboard__subtitle"},L={key:0,class:"dashboard__loading"},H={key:1,class:"dashboard__empty"},R={key:2,class:"dashboard__grid"},T=["onClick"],E={class:"project-card__content"},I={class:"project-card__name"},Y={key:0,class:"project-card__desc"},q={class:"project-card__date"},A={class:"form__field"},G={class:"form__field"},J=b({__name:"Dashboard",setup(K){const k=D(),s=V(),l=f(!1),d=f(""),v=f(""),j=f(!1);x(async()=>{await s.fetchProjects()});function h(r){k.push(`/projects/${r}`)}async function w(){if(d.value.trim()){j.value=!0;try{const r=await s.createProject(d.value.trim(),v.value.trim());l.value=!1,d.value="",v.value="",h(r.id)}catch(r){console.error("Failed to create project:",r)}finally{j.value=!1}}}function C(r){return new Date(r).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}return(r,e)=>(n(),a("div",F,[t("div",U,[t("div",null,[e[6]||(e[6]=t("h1",{class:"dashboard__title"},"Your Projects",-1)),t("p",z,u(c(s).projects.length)+" project"+u(c(s).projects.length!==1?"s":""),1)]),p(_,{variant:"primary",onClick:e[0]||(e[0]=o=>l.value=!0)},{default:i(()=>[...e[7]||(e[7]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),t("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1),m(" New Project ",-1)])]),_:1})]),c(s).loading?(n(),a("div",L,[...e[8]||(e[8]=[t("div",{class:"dashboard__spinner"},null,-1),t("p",null,"Loading projects...",-1)])])):c(s).projects.length===0?(n(),a("div",H,[e[10]||(e[10]=t("svg",{width:"64",height:"64",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.5"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})],-1)),e[11]||(e[11]=t("h2",null,"No projects yet",-1)),e[12]||(e[12]=t("p",null,"Create your first project to start organizing your documents",-1)),p(_,{variant:"primary",onClick:e[1]||(e[1]=o=>l.value=!0)},{default:i(()=>[...e[9]||(e[9]=[m(" Create First Project ",-1)])]),_:1})])):(n(),a("div",R,[(n(!0),a(P,null,B(c(s).projects,o=>(n(),a("article",{key:o.id,class:"project-card",onClick:O=>h(o.id)},[e[13]||(e[13]=t("div",{class:"project-card__icon"},[t("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})])],-1)),t("div",E,[t("h3",I,u(o.name),1),o.description?(n(),a("p",Y,u(o.description),1)):M("",!0),t("p",q," Updated "+u(C(o.updated_at)),1)]),e[14]||(e[14]=t("svg",{class:"project-card__arrow",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"m9 18 6-6-6-6"})],-1))],8,T))),128))])),p($,{show:l.value,title:"Create New Project",onClose:e[5]||(e[5]=o=>l.value=!1)},{footer:i(()=>[p(_,{variant:"secondary",onClick:e[4]||(e[4]=o=>l.value=!1)},{default:i(()=>[...e[17]||(e[17]=[m("Cancel",-1)])]),_:1}),p(_,{variant:"primary",loading:j.value,onClick:w},{default:i(()=>[...e[18]||(e[18]=[m("Create Project",-1)])]),_:1},8,["loading"])]),default:i(()=>[t("form",{onSubmit:N(w,["prevent"])},[t("div",A,[e[15]||(e[15]=t("label",{for:"project-name",class:"form__label"},"Project Name",-1)),y(t("input",{id:"project-name","onUpdate:modelValue":e[2]||(e[2]=o=>d.value=o),type:"text",class:"form__input",placeholder:"My Research Project",autofocus:""},null,512),[[g,d.value]])]),t("div",G,[e[16]||(e[16]=t("label",{for:"project-desc",class:"form__label"},"Description (optional)",-1)),y(t("textarea",{id:"project-desc","onUpdate:modelValue":e[3]||(e[3]=o=>v.value=o),class:"form__textarea",placeholder:"Brief description of your project...",rows:"3"},null,512),[[g,v.value]])])],32)]),_:1},8,["show"])]))}}),ee=S(J,[["__scopeId","data-v-a5d62dba"]]);export{ee as default};

View File

@@ -1 +1 @@
import{d as b,k as x,c as a,a as t,t as u,l as c,f as p,g as i,F as P,m as B,r as f,o as n,h as m,e as y,v as g,w as N,j as D,b as M}from"./index-BuR1WXAD.js";import{u as V}from"./projects-BPkECm1G.js";import{B as _,_ as S}from"./Button-BgP1U8Po.js";import{M as $}from"./Modal-WC6XeOBi.js";const F={class:"dashboard"},U={class:"dashboard__header"},z={class:"dashboard__subtitle"},L={key:0,class:"dashboard__loading"},H={key:1,class:"dashboard__empty"},R={key:2,class:"dashboard__grid"},T=["onClick"],E={class:"project-card__content"},I={class:"project-card__name"},Y={key:0,class:"project-card__desc"},q={class:"project-card__date"},A={class:"form__field"},G={class:"form__field"},J=b({__name:"Dashboard",setup(K){const k=D(),s=V(),l=f(!1),d=f(""),v=f(""),j=f(!1);x(async()=>{await s.fetchProjects()});function h(r){k.push(`/projects/${r}`)}async function w(){if(d.value.trim()){j.value=!0;try{const r=await s.createProject(d.value.trim(),v.value.trim());l.value=!1,d.value="",v.value="",h(r.id)}catch(r){console.error("Failed to create project:",r)}finally{j.value=!1}}}function C(r){return new Date(r).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}return(r,e)=>(n(),a("div",F,[t("div",U,[t("div",null,[e[6]||(e[6]=t("h1",{class:"dashboard__title"},"Your Projects",-1)),t("p",z,u(c(s).projects.length)+" project"+u(c(s).projects.length!==1?"s":""),1)]),p(_,{variant:"primary",onClick:e[0]||(e[0]=o=>l.value=!0)},{default:i(()=>[...e[7]||(e[7]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),t("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1),m(" New Project ",-1)])]),_:1})]),c(s).loading?(n(),a("div",L,[...e[8]||(e[8]=[t("div",{class:"dashboard__spinner"},null,-1),t("p",null,"Loading projects...",-1)])])):c(s).projects.length===0?(n(),a("div",H,[e[10]||(e[10]=t("svg",{width:"64",height:"64",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.5"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})],-1)),e[11]||(e[11]=t("h2",null,"No projects yet",-1)),e[12]||(e[12]=t("p",null,"Create your first project to start organizing your documents",-1)),p(_,{variant:"primary",onClick:e[1]||(e[1]=o=>l.value=!0)},{default:i(()=>[...e[9]||(e[9]=[m(" Create First Project ",-1)])]),_:1})])):(n(),a("div",R,[(n(!0),a(P,null,B(c(s).projects,o=>(n(),a("article",{key:o.id,class:"project-card",onClick:O=>h(o.id)},[e[13]||(e[13]=t("div",{class:"project-card__icon"},[t("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})])],-1)),t("div",E,[t("h3",I,u(o.name),1),o.description?(n(),a("p",Y,u(o.description),1)):M("",!0),t("p",q," Updated "+u(C(o.updated_at)),1)]),e[14]||(e[14]=t("svg",{class:"project-card__arrow",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"m9 18 6-6-6-6"})],-1))],8,T))),128))])),p($,{show:l.value,title:"Create New Project",onClose:e[5]||(e[5]=o=>l.value=!1)},{footer:i(()=>[p(_,{variant:"secondary",onClick:e[4]||(e[4]=o=>l.value=!1)},{default:i(()=>[...e[17]||(e[17]=[m("Cancel",-1)])]),_:1}),p(_,{variant:"primary",loading:j.value,onClick:w},{default:i(()=>[...e[18]||(e[18]=[m("Create Project",-1)])]),_:1},8,["loading"])]),default:i(()=>[t("form",{onSubmit:N(w,["prevent"])},[t("div",A,[e[15]||(e[15]=t("label",{for:"project-name",class:"form__label"},"Project Name",-1)),y(t("input",{id:"project-name","onUpdate:modelValue":e[2]||(e[2]=o=>d.value=o),type:"text",class:"form__input",placeholder:"My Research Project",autofocus:""},null,512),[[g,d.value]])]),t("div",G,[e[16]||(e[16]=t("label",{for:"project-desc",class:"form__label"},"Description (optional)",-1)),y(t("textarea",{id:"project-desc","onUpdate:modelValue":e[3]||(e[3]=o=>v.value=o),class:"form__textarea",placeholder:"Brief description of your project...",rows:"3"},null,512),[[g,v.value]])])],32)]),_:1},8,["show"])]))}}),ee=S(J,[["__scopeId","data-v-a5d62dba"]]);export{ee as default}; import{d as b,k as x,c as a,a as t,t as u,l as c,f as p,g as i,F as P,m as B,r as f,o as n,h as m,e as y,v as g,w as N,j as D,b as M}from"./index-Bwmlaq-e.js";import{u as V}from"./projects-DaKKWK6n.js";import{B as _,_ as S}from"./Button-BCQM6xXJ.js";import{M as $}from"./Modal-DyPCz1pc.js";const F={class:"dashboard"},U={class:"dashboard__header"},z={class:"dashboard__subtitle"},L={key:0,class:"dashboard__loading"},H={key:1,class:"dashboard__empty"},R={key:2,class:"dashboard__grid"},T=["onClick"],E={class:"project-card__content"},I={class:"project-card__name"},Y={key:0,class:"project-card__desc"},q={class:"project-card__date"},A={class:"form__field"},G={class:"form__field"},J=b({__name:"Dashboard",setup(K){const k=D(),s=V(),l=f(!1),d=f(""),v=f(""),j=f(!1);x(async()=>{await s.fetchProjects()});function h(r){k.push(`/projects/${r}`)}async function w(){if(d.value.trim()){j.value=!0;try{const r=await s.createProject(d.value.trim(),v.value.trim());l.value=!1,d.value="",v.value="",h(r.id)}catch(r){console.error("Failed to create project:",r)}finally{j.value=!1}}}function C(r){return new Date(r).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}return(r,e)=>(n(),a("div",F,[t("div",U,[t("div",null,[e[6]||(e[6]=t("h1",{class:"dashboard__title"},"Your Projects",-1)),t("p",z,u(c(s).projects.length)+" project"+u(c(s).projects.length!==1?"s":""),1)]),p(_,{variant:"primary",onClick:e[0]||(e[0]=o=>l.value=!0)},{default:i(()=>[...e[7]||(e[7]=[t("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),t("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1),m(" New Project ",-1)])]),_:1})]),c(s).loading?(n(),a("div",L,[...e[8]||(e[8]=[t("div",{class:"dashboard__spinner"},null,-1),t("p",null,"Loading projects...",-1)])])):c(s).projects.length===0?(n(),a("div",H,[e[10]||(e[10]=t("svg",{width:"64",height:"64",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"1.5"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})],-1)),e[11]||(e[11]=t("h2",null,"No projects yet",-1)),e[12]||(e[12]=t("p",null,"Create your first project to start organizing your documents",-1)),p(_,{variant:"primary",onClick:e[1]||(e[1]=o=>l.value=!0)},{default:i(()=>[...e[9]||(e[9]=[m(" Create First Project ",-1)])]),_:1})])):(n(),a("div",R,[(n(!0),a(P,null,B(c(s).projects,o=>(n(),a("article",{key:o.id,class:"project-card",onClick:O=>h(o.id)},[e[13]||(e[13]=t("div",{class:"project-card__icon"},[t("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"})])],-1)),t("div",E,[t("h3",I,u(o.name),1),o.description?(n(),a("p",Y,u(o.description),1)):M("",!0),t("p",q," Updated "+u(C(o.updated_at)),1)]),e[14]||(e[14]=t("svg",{class:"project-card__arrow",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2"},[t("path",{d:"m9 18 6-6-6-6"})],-1))],8,T))),128))])),p($,{show:l.value,title:"Create New Project",onClose:e[5]||(e[5]=o=>l.value=!1)},{footer:i(()=>[p(_,{variant:"secondary",onClick:e[4]||(e[4]=o=>l.value=!1)},{default:i(()=>[...e[17]||(e[17]=[m("Cancel",-1)])]),_:1}),p(_,{variant:"primary",loading:j.value,onClick:w},{default:i(()=>[...e[18]||(e[18]=[m("Create Project",-1)])]),_:1},8,["loading"])]),default:i(()=>[t("form",{onSubmit:N(w,["prevent"])},[t("div",A,[e[15]||(e[15]=t("label",{for:"project-name",class:"form__label"},"Project Name",-1)),y(t("input",{id:"project-name","onUpdate:modelValue":e[2]||(e[2]=o=>d.value=o),type:"text",class:"form__input",placeholder:"My Research Project",autofocus:""},null,512),[[g,d.value]])]),t("div",G,[e[16]||(e[16]=t("label",{for:"project-desc",class:"form__label"},"Description (optional)",-1)),y(t("textarea",{id:"project-desc","onUpdate:modelValue":e[3]||(e[3]=o=>v.value=o),class:"form__textarea",placeholder:"Brief description of your project...",rows:"3"},null,512),[[g,v.value]])])],32)]),_:1},8,["show"])]))}}),ee=S(J,[["__scopeId","data-v-a5d62dba"]]);export{ee as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.header[data-v-0d1f0999]{display:flex;align-items:center;justify-content:space-between;height:56px;padding:0 1.5rem;background:var(--bg-primary);border-bottom:1px solid var(--border)}.header__left[data-v-0d1f0999]{flex:1}.header__logo[data-v-0d1f0999]{display:flex;align-items:center;gap:.75rem;text-decoration:none}.header__title[data-v-0d1f0999]{font-size:1.125rem;font-weight:600;color:var(--text-primary)}.header__center[data-v-0d1f0999]{flex:2;max-width:480px}.header__search[data-v-0d1f0999]{position:relative;display:flex;align-items:center}.header__search-icon[data-v-0d1f0999]{position:absolute;left:.75rem;color:var(--text-secondary)}.header__search-input[data-v-0d1f0999]{width:100%;padding:.5rem 4rem .5rem 2.25rem;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;font-size:.875rem;color:var(--text-primary);outline:none;transition:border-color .15s,box-shadow .15s}.header__search-input[data-v-0d1f0999]::placeholder{color:var(--text-secondary)}.header__search-input[data-v-0d1f0999]:focus{border-color:var(--accent);box-shadow:0 0 0 3px #6366f11a}.header__kbd[data-v-0d1f0999]{position:absolute;right:.75rem;padding:.125rem .375rem;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:4px;font-size:.75rem;color:var(--text-secondary)}.header__right[data-v-0d1f0999]{flex:1;display:flex;justify-content:flex-end}.header__user[data-v-0d1f0999]{position:relative;display:flex;align-items:center;gap:.5rem;padding:.375rem .75rem .375rem .375rem;border-radius:8px;cursor:pointer;transition:background .15s}.header__user[data-v-0d1f0999]:hover{background:var(--bg-secondary)}.header__avatar[data-v-0d1f0999]{width:32px;height:32px;display:flex;align-items:center;justify-content:center;background:var(--accent);border-radius:6px;color:#fff;font-weight:600;font-size:.875rem}.header__username[data-v-0d1f0999]{font-size:.875rem;color:var(--text-primary)}.header__dropdown[data-v-0d1f0999]{position:absolute;top:100%;right:0;margin-top:.5rem;min-width:180px;background:var(--bg-primary);border:1px solid var(--border);border-radius:8px;box-shadow:0 10px 30px #00000026;overflow:hidden;z-index:100}.header__dropdown-item[data-v-0d1f0999]{display:flex;align-items:center;gap:.5rem;width:100%;padding:.75rem 1rem;background:none;border:none;font-size:.875rem;color:var(--text-primary);cursor:pointer;text-align:left;text-decoration:none}.header__dropdown-item[data-v-0d1f0999]:hover{background:var(--bg-secondary)}.header__dropdown-item--info[data-v-0d1f0999]{flex-direction:column;align-items:flex-start;gap:.125rem}.header__dropdown-item--info small[data-v-0d1f0999]{color:var(--text-secondary);font-size:.75rem}.header__dropdown-divider[data-v-0d1f0999]{margin:.25rem 0;border:none;border-top:1px solid var(--border)}.fade-enter-active[data-v-0d1f0999],.fade-leave-active[data-v-0d1f0999]{transition:opacity .15s,transform .15s}.fade-enter-from[data-v-0d1f0999],.fade-leave-to[data-v-0d1f0999]{opacity:0;transform:translateY(-4px)}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
import{d as C,u as k,c as _,a as e,t as n,w as S,b as A,e as v,v as g,f as B,g as M,h as p,r as i,i as m,j as V,o as f}from"./index-D2fAAQJ2.js";import{B as D,_ as E}from"./Button-D-QrZfPJ.js";const N={class:"login"},I={class:"login__card"},L={class:"login__header"},T={class:"login__title"},U={class:"login__subtitle"},P={key:0,class:"login__error"},R={class:"login__field"},j={class:"login__field"},z={class:"login__toggle"},W=C({__name:"Login",setup(q){const h=V(),r=k(),o=i(""),s=i(""),a=i(!1),u=i(!1),l=i(""),w=m(()=>a.value?"Create Account":"Welcome Back"),y=m(()=>a.value?"Create Account":"Sign In");async function b(){if(!o.value||!s.value){l.value="Please fill in all fields";return}u.value=!0,l.value="";try{a.value?(await r.register(o.value,s.value),await r.login(o.value,s.value)):await r.login(o.value,s.value),h.push("/dashboard")}catch(d){l.value=d instanceof Error?d.message:"Authentication failed"}finally{u.value=!1}}function x(){a.value=!a.value,l.value=""}return(d,t)=>(f(),_("div",N,[e("div",I,[e("div",L,[t[2]||(t[2]=e("svg",{width:"40",height:"40",viewBox:"0 0 28 28",fill:"none"},[e("rect",{width:"28",height:"28",rx:"6",fill:"var(--accent)"}),e("path",{d:"M8 10h12M8 14h8M8 18h10",stroke:"white","stroke-width":"2","stroke-linecap":"round"})],-1)),e("h1",T,n(w.value),1),e("p",U,n(a.value?"Create your account to get started":"Sign in to access your documents"),1)]),e("form",{class:"login__form",onSubmit:S(b,["prevent"])},[l.value?(f(),_("div",P,n(l.value),1)):A("",!0),e("div",R,[t[3]||(t[3]=e("label",{for:"username",class:"login__label"},"Username",-1)),v(e("input",{id:"username","onUpdate:modelValue":t[0]||(t[0]=c=>o.value=c),type:"text",class:"login__input",placeholder:"Enter your username",autocomplete:"username"},null,512),[[g,o.value]])]),e("div",j,[t[4]||(t[4]=e("label",{for:"password",class:"login__label"},"Password",-1)),v(e("input",{id:"password","onUpdate:modelValue":t[1]||(t[1]=c=>s.value=c),type:"password",class:"login__input",placeholder:"Enter your password",autocomplete:"current-password"},null,512),[[g,s.value]])]),B(D,{type:"submit",variant:"primary",size:"lg",loading:u.value,style:{width:"100%"}},{default:M(()=>[p(n(y.value),1)]),_:1},8,["loading"]),e("p",z,[p(n(a.value?"Already have an account?":"Don't have an account?")+" ",1),e("button",{type:"button",class:"login__toggle-btn",onClick:x},n(a.value?"Sign in":"Create one"),1)])],32)]),t[5]||(t[5]=e("p",{class:"login__footer"}," Claudia Docs — Document Management for AI Agents ",-1))]))}}),H=E(W,[["__scopeId","data-v-d1a9e623"]]);export{H as default};

View File

@@ -1 +1 @@
import{d as C,u as k,c as _,a as e,t as n,w as S,b as A,e as v,v as g,f as B,g as M,h as p,r as i,i as m,j as V,o as f}from"./index-BuR1WXAD.js";import{B as D,_ as E}from"./Button-BgP1U8Po.js";const N={class:"login"},I={class:"login__card"},L={class:"login__header"},T={class:"login__title"},U={class:"login__subtitle"},P={key:0,class:"login__error"},R={class:"login__field"},j={class:"login__field"},z={class:"login__toggle"},W=C({__name:"Login",setup(q){const h=V(),r=k(),o=i(""),s=i(""),a=i(!1),u=i(!1),l=i(""),w=m(()=>a.value?"Create Account":"Welcome Back"),y=m(()=>a.value?"Create Account":"Sign In");async function b(){if(!o.value||!s.value){l.value="Please fill in all fields";return}u.value=!0,l.value="";try{a.value?(await r.register(o.value,s.value),await r.login(o.value,s.value)):await r.login(o.value,s.value),h.push("/dashboard")}catch(d){l.value=d instanceof Error?d.message:"Authentication failed"}finally{u.value=!1}}function x(){a.value=!a.value,l.value=""}return(d,t)=>(f(),_("div",N,[e("div",I,[e("div",L,[t[2]||(t[2]=e("svg",{width:"40",height:"40",viewBox:"0 0 28 28",fill:"none"},[e("rect",{width:"28",height:"28",rx:"6",fill:"var(--accent)"}),e("path",{d:"M8 10h12M8 14h8M8 18h10",stroke:"white","stroke-width":"2","stroke-linecap":"round"})],-1)),e("h1",T,n(w.value),1),e("p",U,n(a.value?"Create your account to get started":"Sign in to access your documents"),1)]),e("form",{class:"login__form",onSubmit:S(b,["prevent"])},[l.value?(f(),_("div",P,n(l.value),1)):A("",!0),e("div",R,[t[3]||(t[3]=e("label",{for:"username",class:"login__label"},"Username",-1)),v(e("input",{id:"username","onUpdate:modelValue":t[0]||(t[0]=c=>o.value=c),type:"text",class:"login__input",placeholder:"Enter your username",autocomplete:"username"},null,512),[[g,o.value]])]),e("div",j,[t[4]||(t[4]=e("label",{for:"password",class:"login__label"},"Password",-1)),v(e("input",{id:"password","onUpdate:modelValue":t[1]||(t[1]=c=>s.value=c),type:"password",class:"login__input",placeholder:"Enter your password",autocomplete:"current-password"},null,512),[[g,s.value]])]),B(D,{type:"submit",variant:"primary",size:"lg",loading:u.value,style:{width:"100%"}},{default:M(()=>[p(n(y.value),1)]),_:1},8,["loading"]),e("p",z,[p(n(a.value?"Already have an account?":"Don't have an account?")+" ",1),e("button",{type:"button",class:"login__toggle-btn",onClick:x},n(a.value?"Sign in":"Create one"),1)])],32)]),t[5]||(t[5]=e("p",{class:"login__footer"}," Claudia Docs — Document Management for AI Agents ",-1))]))}}),H=E(W,[["__scopeId","data-v-d1a9e623"]]);export{H as default}; import{d as C,u as k,c as _,a as e,t as n,w as S,b as A,e as v,v as g,f as B,g as M,h as p,r as i,i as m,j as V,o as f}from"./index-Bwmlaq-e.js";import{B as D,_ as E}from"./Button-BCQM6xXJ.js";const N={class:"login"},I={class:"login__card"},L={class:"login__header"},T={class:"login__title"},U={class:"login__subtitle"},P={key:0,class:"login__error"},R={class:"login__field"},j={class:"login__field"},z={class:"login__toggle"},W=C({__name:"Login",setup(q){const h=V(),r=k(),o=i(""),s=i(""),a=i(!1),u=i(!1),l=i(""),w=m(()=>a.value?"Create Account":"Welcome Back"),y=m(()=>a.value?"Create Account":"Sign In");async function b(){if(!o.value||!s.value){l.value="Please fill in all fields";return}u.value=!0,l.value="";try{a.value?(await r.register(o.value,s.value),await r.login(o.value,s.value)):await r.login(o.value,s.value),h.push("/dashboard")}catch(d){l.value=d instanceof Error?d.message:"Authentication failed"}finally{u.value=!1}}function x(){a.value=!a.value,l.value=""}return(d,t)=>(f(),_("div",N,[e("div",I,[e("div",L,[t[2]||(t[2]=e("svg",{width:"40",height:"40",viewBox:"0 0 28 28",fill:"none"},[e("rect",{width:"28",height:"28",rx:"6",fill:"var(--accent)"}),e("path",{d:"M8 10h12M8 14h8M8 18h10",stroke:"white","stroke-width":"2","stroke-linecap":"round"})],-1)),e("h1",T,n(w.value),1),e("p",U,n(a.value?"Create your account to get started":"Sign in to access your documents"),1)]),e("form",{class:"login__form",onSubmit:S(b,["prevent"])},[l.value?(f(),_("div",P,n(l.value),1)):A("",!0),e("div",R,[t[3]||(t[3]=e("label",{for:"username",class:"login__label"},"Username",-1)),v(e("input",{id:"username","onUpdate:modelValue":t[0]||(t[0]=c=>o.value=c),type:"text",class:"login__input",placeholder:"Enter your username",autocomplete:"username"},null,512),[[g,o.value]])]),e("div",j,[t[4]||(t[4]=e("label",{for:"password",class:"login__label"},"Password",-1)),v(e("input",{id:"password","onUpdate:modelValue":t[1]||(t[1]=c=>s.value=c),type:"password",class:"login__input",placeholder:"Enter your password",autocomplete:"current-password"},null,512),[[g,s.value]])]),B(D,{type:"submit",variant:"primary",size:"lg",loading:u.value,style:{width:"100%"}},{default:M(()=>[p(n(y.value),1)]),_:1},8,["loading"]),e("p",z,[p(n(a.value?"Already have an account?":"Don't have an account?")+" ",1),e("button",{type:"button",class:"login__toggle-btn",onClick:x},n(a.value?"Sign in":"Create one"),1)])],32)]),t[5]||(t[5]=e("p",{class:"login__footer"}," Claudia Docs — Document Management for AI Agents ",-1))]))}}),H=E(W,[["__scopeId","data-v-d1a9e623"]]);export{H as default};

View File

@@ -1 +1 @@
import{d as _,n as f,x as u,s as k,f as h,T as v,g as p,O as y,o as s,c as l,a,t as C,b as n,Q as i,q as B}from"./index-D2fAAQJ2.js";import{_ as w}from"./Button-D-QrZfPJ.js";const $={key:0,class:"modal__header"},b={class:"modal__title"},E={class:"modal__body"},g={key:1,class:"modal__footer"},z=_({__name:"Modal",props:{show:{type:Boolean},title:{},size:{default:"md"}},emits:["close"],setup(o,{emit:c}){const t=c;function r(e){e.target.classList.contains("modal")&&t("close")}function m(e){e.key==="Escape"&&t("close")}return f(()=>t,()=>{},{immediate:!0}),u(()=>{document.removeEventListener("keydown",m)}),(e,d)=>(s(),k(y,{to:"body"},[h(v,{name:"modal"},{default:p(()=>[o.show?(s(),l("div",{key:0,class:"modal",onClick:r},[a("div",{class:B(["modal__content",`modal__content--${o.size}`])},[o.title?(s(),l("div",$,[a("h2",b,C(o.title),1),a("button",{class:"modal__close",onClick:d[0]||(d[0]=M=>t("close"))},"×")])):n("",!0),a("div",E,[i(e.$slots,"default",{},void 0,!0)]),e.$slots.footer?(s(),l("div",g,[i(e.$slots,"footer",{},void 0,!0)])):n("",!0)],2)])):n("",!0)]),_:3})]))}}),V=w(z,[["__scopeId","data-v-f5a1757e"]]);export{V as M}; import{d as _,n as f,x as u,s as k,f as h,T as v,g as p,O as y,o as s,c as l,a,t as C,b as n,Q as i,q as B}from"./index-Bwmlaq-e.js";import{_ as w}from"./Button-BCQM6xXJ.js";const $={key:0,class:"modal__header"},b={class:"modal__title"},E={class:"modal__body"},g={key:1,class:"modal__footer"},z=_({__name:"Modal",props:{show:{type:Boolean},title:{},size:{default:"md"}},emits:["close"],setup(o,{emit:c}){const t=c;function r(e){e.target.classList.contains("modal")&&t("close")}function m(e){e.key==="Escape"&&t("close")}return f(()=>t,()=>{},{immediate:!0}),u(()=>{document.removeEventListener("keydown",m)}),(e,d)=>(s(),k(y,{to:"body"},[h(v,{name:"modal"},{default:p(()=>[o.show?(s(),l("div",{key:0,class:"modal",onClick:r},[a("div",{class:B(["modal__content",`modal__content--${o.size}`])},[o.title?(s(),l("div",$,[a("h2",b,C(o.title),1),a("button",{class:"modal__close",onClick:d[0]||(d[0]=M=>t("close"))},"×")])):n("",!0),a("div",E,[i(e.$slots,"default",{},void 0,!0)]),e.$slots.footer?(s(),l("div",g,[i(e.$slots,"footer",{},void 0,!0)])):n("",!0)],2)])):n("",!0)]),_:3})]))}}),V=w(z,[["__scopeId","data-v-f5a1757e"]]);export{V as M};

View File

@@ -1 +0,0 @@
import{d as _,n as f,M as u,s as k,f as h,T as v,g as p,N as y,o as s,c as l,a,t as C,b as n,O as i,q as B}from"./index-BuR1WXAD.js";import{_ as w}from"./Button-BgP1U8Po.js";const $={key:0,class:"modal__header"},b={class:"modal__title"},E={class:"modal__body"},M={key:1,class:"modal__footer"},N=_({__name:"Modal",props:{show:{type:Boolean},title:{},size:{default:"md"}},emits:["close"],setup(o,{emit:c}){const t=c;function r(e){e.target.classList.contains("modal")&&t("close")}function m(e){e.key==="Escape"&&t("close")}return f(()=>t,()=>{},{immediate:!0}),u(()=>{document.removeEventListener("keydown",m)}),(e,d)=>(s(),k(y,{to:"body"},[h(v,{name:"modal"},{default:p(()=>[o.show?(s(),l("div",{key:0,class:"modal",onClick:r},[a("div",{class:B(["modal__content",`modal__content--${o.size}`])},[o.title?(s(),l("div",$,[a("h2",b,C(o.title),1),a("button",{class:"modal__close",onClick:d[0]||(d[0]=g=>t("close"))},"×")])):n("",!0),a("div",E,[i(e.$slots,"default",{},void 0,!0)]),e.$slots.footer?(s(),l("div",M,[i(e.$slots,"footer",{},void 0,!0)])):n("",!0)],2)])):n("",!0)]),_:3})]))}}),V=w(N,[["__scopeId","data-v-f5a1757e"]]);export{V as M};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/ProjectView-DGH2EBfC.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.tree[data-v-e9acf25c]{list-style:none;margin:0;padding:0}.tree__item[data-v-e9acf25c]{margin:2px 0}.tree__node[data-v-e9acf25c]{display:flex;align-items:center;gap:.375rem;padding:.375rem .5rem;border-radius:6px;cursor:pointer;transition:background .1s}.tree__node[data-v-e9acf25c]:hover{background:var(--bg-tertiary)}.tree__toggle[data-v-e9acf25c]{display:flex;align-items:center;justify-content:center;width:18px;height:18px;padding:0;background:none;border:none;color:var(--text-secondary);cursor:pointer}.tree__icon[data-v-e9acf25c]{display:flex;align-items:center;justify-content:center;width:18px;color:var(--text-secondary)}.tree__arrow[data-v-e9acf25c]{transition:transform .15s}.tree__arrow--expanded[data-v-e9acf25c]{transform:rotate(90deg)}.tree__label[data-v-e9acf25c]{flex:1;font-size:.875rem;color:var(--text-primary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree__children[data-v-e9acf25c]{margin-left:1rem}.expand-enter-active[data-v-e9acf25c],.expand-leave-active[data-v-e9acf25c]{transition:all .2s ease;overflow:hidden}.expand-enter-from[data-v-e9acf25c],.expand-leave-to[data-v-e9acf25c]{opacity:0;max-height:0}.expand-enter-to[data-v-e9acf25c],.expand-leave-from[data-v-e9acf25c]{max-height:500px}.sidebar[data-v-f979c4be]{width:280px;min-width:280px;height:100%;background:var(--bg-secondary);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden}.sidebar__header[data-v-f979c4be]{padding:1rem;border-bottom:1px solid var(--border)}.sidebar__back[data-v-f979c4be]{display:flex;align-items:center;gap:.5rem;width:100%;padding:.5rem;background:none;border:none;border-radius:6px;font-size:.875rem;color:var(--text-secondary);cursor:pointer;text-align:left;transition:all .15s}.sidebar__back[data-v-f979c4be]:hover{background:var(--bg-tertiary);color:var(--text-primary)}.sidebar__content[data-v-f979c4be]{flex:1;display:flex;flex-direction:column;overflow:hidden}.sidebar__project[data-v-f979c4be]{padding:1rem;border-bottom:1px solid var(--border)}.sidebar__project-name[data-v-f979c4be]{margin:0;font-size:1rem;font-weight:600;color:var(--text-primary)}.sidebar__project-desc[data-v-f979c4be]{margin:.25rem 0 0;font-size:.8125rem;color:var(--text-secondary);line-height:1.4}.sidebar__actions[data-v-f979c4be]{display:flex;gap:.5rem;padding:.75rem 1rem;border-bottom:1px solid var(--border)}.sidebar__tree[data-v-f979c4be]{flex:1;overflow-y:auto;padding:.5rem}.sidebar__empty[data-v-f979c4be]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;padding:2rem;color:var(--text-secondary);text-align:center}.sidebar__empty p[data-v-f979c4be]{margin:0;font-size:.875rem}.layout[data-v-333c564d]{display:flex;flex-direction:column;height:100vh}.layout__body[data-v-333c564d]{flex:1;display:flex;overflow:hidden}.layout__content[data-v-333c564d]{flex:1;overflow-y:auto;background:var(--bg-primary)}.loading[data-v-333c564d],.error[data-v-333c564d]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;height:100%;color:var(--text-secondary)}.loading__spinner[data-v-333c564d]{width:32px;height:32px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin-333c564d .8s linear infinite}@keyframes spin-333c564d{to{transform:rotate(360deg)}}.project[data-v-333c564d]{padding:2rem}.project__welcome[data-v-333c564d]{max-width:600px;margin:0 auto;text-align:center;padding:4rem 2rem}.project__title[data-v-333c564d]{margin:0;font-size:1.75rem;font-weight:600;color:var(--text-primary)}.project__desc[data-v-333c564d]{margin:.75rem 0 0;font-size:1rem;color:var(--text-secondary);line-height:1.5}.project__hint[data-v-333c564d]{margin:1.5rem 0 0;font-size:.9375rem;color:var(--text-secondary)}.form__field[data-v-333c564d]{margin-bottom:1.25rem}.form__field[data-v-333c564d]:last-child{margin-bottom:0}.form__label[data-v-333c564d]{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:var(--text-primary)}.form__input[data-v-333c564d]{width:100%;padding:.75rem 1rem;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;font-size:.9375rem;color:var(--text-primary);outline:none;transition:border-color .15s,box-shadow .15s;box-sizing:border-box}.form__input[data-v-333c564d]::placeholder{color:var(--text-secondary)}.form__input[data-v-333c564d]:focus{border-color:var(--accent);box-shadow:0 0 0 3px #6366f11a}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
import{y as x,r as s,z as E}from"./index-BuR1WXAD.js";const M=x("projects",()=>{const c=s([]),a=s(null),u=s([]),d=s([]),p=s([]),i=s(!1),l=s(null),r=E();async function j(){i.value=!0,l.value=null;try{const e=await r.get("/projects");c.value=e.projects}catch(e){throw l.value=e instanceof Error?e.message:"Failed to fetch projects",e}finally{i.value=!1}}async function y(e){i.value=!0,l.value=null;try{a.value=await r.get(`/projects/${e}`),await f(e)}catch(o){throw l.value=o instanceof Error?o.message:"Failed to fetch project",o}finally{i.value=!1}}async function f(e){try{const[o,t]=await Promise.all([r.get(`/projects/${e}/folders`),r.get(`/projects/${e}/documents`)]);u.value=o.folders,d.value=t.documents,m()}catch(o){throw l.value=o instanceof Error?o.message:"Failed to fetch project tree",o}}function m(){const e=new Map,o=[];for(const t of u.value){const n={id:t.id,name:t.name,type:"folder",children:[],parent_id:t.parent_id};e.set(t.id,n)}for(const t of d.value)if(!t.folder_id){const n={id:t.id,name:t.title,type:"document",parent_id:null};e.set(t.id,n)}for(const t of d.value)if(t.folder_id){const n=e.get(t.folder_id);n&&n.children&&n.children.push({id:t.id,name:t.title,type:"document",parent_id:t.folder_id})}for(const t of e.values())if(t.parent_id===null)o.push(t);else{const n=e.get(t.parent_id);n&&n.children&&n.children.push(t)}p.value=o}async function w(e,o){const t=await r.post("/projects",{name:e,description:o});return c.value.push(t),t}async function _(e,o,t){var h;const n=await r.put(`/projects/${e}`,{name:o,description:t}),v=c.value.findIndex(F=>F.id===e);return v!==-1&&(c.value[v]=n),((h=a.value)==null?void 0:h.id)===e&&(a.value=n),n}async function g(e){var o;await r.delete(`/projects/${e}`),c.value=c.value.filter(t=>t.id!==e),((o=a.value)==null?void 0:o.id)===e&&(a.value=null)}async function P(e,o,t=null){const n=await r.post(`/projects/${e}/folders`,{name:o,parent_id:t});return u.value.push(n),await f(e),n}async function $(e){await r.delete(`/folders/${e}`),a.value&&await f(a.value.id)}return{projects:c,currentProject:a,folders:u,documents:d,treeNodes:p,loading:i,error:l,fetchProjects:j,fetchProject:y,fetchProjectTree:f,createProject:w,updateProject:_,deleteProject:g,createFolder:P,deleteFolder:$}});export{M as u};

View File

@@ -1 +0,0 @@
import{z as F,r as s,A as B,B as R}from"./index-D2fAAQJ2.js";const b=F("projects",()=>{const c=s([]),n=s(null),d=s([]),f=s([]),h=s([]),i=s(!1),l=s(null),a=R();async function j(){i.value=!0,l.value=null;try{const e=await a.get("/projects");c.value=e.projects}catch(e){throw l.value=e instanceof Error?e.message:"Failed to fetch projects",e}finally{i.value=!1}}async function w(e){i.value=!0,l.value=null;try{n.value=await a.get(`/projects/${e}`),await p(e)}catch(o){throw l.value=o instanceof Error?o.message:"Failed to fetch project",o}finally{i.value=!1}}async function p(e){try{const[o,t]=await Promise.all([a.get(`/projects/${e}/folders`),a.get(`/projects/${e}/documents`)]);d.value=o.folders,f.value=t.documents,y()}catch(o){throw l.value=o instanceof Error?o.message:"Failed to fetch project tree",o}}function y(){const e=new Map,o=[];for(const t of d.value){const r={id:t.id,name:t.name,type:"folder",children:[],parent_id:t.parent_id};e.set(t.id,r)}for(const t of f.value)if(!t.folder_id){const r={id:t.id,name:t.title,type:"document",parent_id:null};e.set(t.id,r)}for(const t of f.value)if(t.folder_id){const r=e.get(t.folder_id);r&&r.children&&r.children.push({id:t.id,name:t.title,type:"document",parent_id:t.folder_id})}for(const t of e.values())if(t.parent_id===null)o.push(t);else{const r=e.get(t.parent_id);r&&r.children&&r.children.push(t)}h.value=o}async function m(e,o){const t=await a.post("/projects",{name:e,description:o});return c.value.push(t),t}async function _(e,o,t){var v;const r=await a.put(`/projects/${e}`,{name:o,description:t}),u=c.value.findIndex(A=>A.id===e);return u!==-1&&(c.value[u]=r),((v=n.value)==null?void 0:v.id)===e&&(n.value=r),r}async function $(e){var o;await a.delete(`/projects/${e}`),c.value=c.value.filter(t=>t.id!==e),((o=n.value)==null?void 0:o.id)===e&&(n.value=null)}async function g(e,o,t=null){const r=await a.post(`/projects/${e}/folders`,{name:o,parent_id:t});return d.value.push(r),await p(e),r}async function P(e){await a.delete(`/folders/${e}`),n.value&&await p(n.value.id)}async function E(e,o=2){return await a.get(`/projects/${e}/graph?depth=${o}`)}async function x(e,o,t=!0){const u=await fetch(`http://localhost:8000/api/v1/projects/${e}/export?format=${o}&include_metadata=${t}`,{headers:{Authorization:`Bearer ${B()}`},credentials:"include"});if(!u.ok)throw new Error("Export failed");return await u.blob()}return{projects:c,currentProject:n,folders:d,documents:f,treeNodes:h,loading:i,error:l,fetchProjects:j,fetchProject:w,fetchProjectTree:p,createProject:m,updateProject:_,deleteProject:$,createFolder:g,deleteFolder:P,fetchProjectGraph:E,exportProject:x}});export{b as u};

1
dist/assets/projects-DaKKWK6n.js vendored Normal file
View File

@@ -0,0 +1 @@
import{A as B,r as l,B as R,C as S}from"./index-Bwmlaq-e.js";const N=B("projects",()=>{const c=l([]),n=l(null),d=l([]),f=l([]),h=l([]),u=l(!1),i=l(null),a=S();async function j(){u.value=!0,i.value=null;try{const e=await a.get("/projects");c.value=e.projects}catch(e){throw i.value=e instanceof Error?e.message:"Failed to fetch projects",e}finally{u.value=!1}}async function w(e){u.value=!0,i.value=null;try{n.value=await a.get(`/projects/${e}`),await p(e)}catch(o){throw i.value=o instanceof Error?o.message:"Failed to fetch project",o}finally{u.value=!1}}async function p(e){try{const[o,t]=await Promise.all([a.get(`/projects/${e}/folders`),a.get(`/projects/${e}/documents`)]);d.value=o.folders,f.value=t.documents,y()}catch(o){throw i.value=o instanceof Error?o.message:"Failed to fetch project tree",o}}function y(){const e=new Map,o=[];for(const t of d.value){const r={id:t.id,name:t.name,type:"folder",children:[],parent_id:t.parent_id};e.set(t.id,r)}for(const t of f.value)if(!t.folder_id){const r={id:t.id,name:t.title,type:"document",parent_id:null};e.set(t.id,r)}for(const t of f.value)if(t.folder_id){const r=e.get(t.folder_id);r&&r.children&&r.children.push({id:t.id,name:t.title,type:"document",parent_id:t.folder_id})}for(const t of e.values())if(t.parent_id===null)o.push(t);else{const r=e.get(t.parent_id);r&&r.children&&r.children.push(t)}h.value=o}async function m(e,o){const t=await a.post("/projects",{name:e,description:o});return c.value.push(t),t}async function $(e,o,t){var v;const r=await a.put(`/projects/${e}`,{name:o,description:t}),s=c.value.findIndex(F=>F.id===e);return s!==-1&&(c.value[s]=r),((v=n.value)==null?void 0:v.id)===e&&(n.value=r),r}async function _(e,o){var s;const t=await a.patch(`/projects/${e}`,o),r=c.value.findIndex(v=>v.id===e);return r!==-1&&(c.value[r]=t),((s=n.value)==null?void 0:s.id)===e&&(n.value=t),t}async function g(e){var o;await a.delete(`/projects/${e}`),c.value=c.value.filter(t=>t.id!==e),((o=n.value)==null?void 0:o.id)===e&&(n.value=null)}async function P(e,o,t=null){const r=await a.post(`/projects/${e}/folders`,{name:o,parent_id:t});return d.value.push(r),await p(e),r}async function x(e){await a.delete(`/folders/${e}`),n.value&&await p(n.value.id)}async function E(e,o=2){return await a.get(`/projects/${e}/graph?depth=${o}`)}async function A(e,o,t=!0){const s=await fetch(`http://localhost:8000/api/v1/projects/${e}/export?format=${o}&include_metadata=${t}`,{headers:{Authorization:`Bearer ${R()}`},credentials:"include"});if(!s.ok)throw new Error("Export failed");return await s.blob()}return{projects:c,currentProject:n,folders:d,documents:f,treeNodes:h,loading:u,error:i,fetchProjects:j,fetchProject:w,fetchProjectTree:p,createProject:m,updateProject:$,patchProject:_,deleteProject:g,createFolder:P,deleteFolder:x,fetchProjectGraph:E,exportProject:A}});export{N as u};

2
dist/index.html vendored
View File

@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Claudia Docs</title> <title>Claudia Docs</title>
<script type="module" crossorigin src="/assets/index-BuR1WXAD.js"></script> <script type="module" crossorigin src="/assets/index-Bwmlaq-e.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BG6SLZYI.css"> <link rel="stylesheet" crossorigin href="/assets/index-BG6SLZYI.css">
</head> </head>
<body> <body>

View File

@@ -107,6 +107,8 @@ export function useApi() {
post: <T>(endpoint: string, body?: any) => request<T>('POST', endpoint, body), post: <T>(endpoint: string, body?: any) => request<T>('POST', endpoint, body),
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
put: <T>(endpoint: string, body?: any) => request<T>('PUT', endpoint, body), put: <T>(endpoint: string, body?: any) => request<T>('PUT', endpoint, body),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
patch: <T>(endpoint: string, body?: any) => request<T>('PATCH', endpoint, body),
delete: <T>(endpoint: string) => request<T>('DELETE', endpoint) delete: <T>(endpoint: string) => request<T>('DELETE', endpoint)
} }
} }

View File

@@ -135,6 +135,18 @@ export const useProjectsStore = defineStore('projects', () => {
return project return project
} }
async function patchProject(id: string, data: { name?: string; description?: string }) {
const project = await api.patch<Project>(`/projects/${id}`, data)
const index = projects.value.findIndex(p => p.id === id)
if (index !== -1) {
projects.value[index] = project
}
if (currentProject.value?.id === id) {
currentProject.value = project
}
return project
}
async function deleteProject(id: string) { async function deleteProject(id: string) {
await api.delete(`/projects/${id}`) await api.delete(`/projects/${id}`)
projects.value = projects.value.filter(p => p.id !== id) projects.value = projects.value.filter(p => p.id !== id)
@@ -188,6 +200,7 @@ export const useProjectsStore = defineStore('projects', () => {
fetchProjectTree, fetchProjectTree,
createProject, createProject,
updateProject, updateProject,
patchProject,
deleteProject, deleteProject,
createFolder, createFolder,
deleteFolder, deleteFolder,

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, watch } from 'vue' import { ref, onMounted, watch, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { useProjectsStore } from '@/stores/projects' import { useProjectsStore } from '@/stores/projects'
import { useDocumentsStore } from '@/stores/documents' import { useDocumentsStore } from '@/stores/documents'
@@ -8,6 +8,7 @@ import Sidebar from '@/components/layout/Sidebar.vue'
import Button from '@/components/common/Button.vue' import Button from '@/components/common/Button.vue'
import Modal from '@/components/common/Modal.vue' import Modal from '@/components/common/Modal.vue'
import GraphView from '@/components/common/GraphView.vue' import GraphView from '@/components/common/GraphView.vue'
import type { TreeNode } from '@/types'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
@@ -21,12 +22,23 @@ const newDocName = ref('')
const selectedParentId = ref<string | null>(null) const selectedParentId = ref<string | null>(null)
const creating = ref(false) const creating = ref(false)
// Feature 1: Edit project modal
const showEditProjectModal = ref(false)
const editProjectName = ref('')
const editProjectDesc = ref('')
const savingProject = ref(false)
// Phase 3: Graph & Export // Phase 3: Graph & Export
const showGraphView = ref(false) const showGraphView = ref(false)
const showExportModal = ref(false) const showExportModal = ref(false)
const exportFormat = ref<'zip' | 'json'>('zip') const exportFormat = ref<'zip' | 'json'>('zip')
const exporting = ref(false) const exporting = ref(false)
// Feature 2: Folder picker
const folderPickerOpen = ref(false)
const folderPickerSelectedId = ref<string | null>(null)
const folderPickerExpanded = ref<Set<string>>(new Set())
onMounted(async () => { onMounted(async () => {
const projectId = route.params.id as string const projectId = route.params.id as string
await projectsStore.fetchProject(projectId) await projectsStore.fetchProject(projectId)
@@ -50,6 +62,8 @@ function emitCreateFolder() {
function emitCreateDocument() { function emitCreateDocument() {
selectedParentId.value = null selectedParentId.value = null
showNewDocModal.value = true showNewDocModal.value = true
folderPickerSelectedId.value = null
folderPickerOpen.value = false
} }
async function createFolder() { async function createFolder() {
@@ -80,10 +94,12 @@ async function createDocument() {
projectsStore.currentProject.id, projectsStore.currentProject.id,
newDocName.value.trim(), newDocName.value.trim(),
'', '',
selectedParentId.value folderPickerSelectedId.value
) )
showNewDocModal.value = false showNewDocModal.value = false
newDocName.value = '' newDocName.value = ''
folderPickerSelectedId.value = null
folderPickerOpen.value = false
// Refresh project tree // Refresh project tree
await projectsStore.fetchProjectTree(projectsStore.currentProject.id) await projectsStore.fetchProjectTree(projectsStore.currentProject.id)
// Open the new document // Open the new document
@@ -95,6 +111,71 @@ async function createDocument() {
} }
} }
// Feature 1: Edit project
function openEditProjectModal() {
if (!projectsStore.currentProject) return
editProjectName.value = projectsStore.currentProject.name
editProjectDesc.value = projectsStore.currentProject.description || ''
showEditProjectModal.value = true
}
async function saveProject() {
if (!projectsStore.currentProject || !editProjectName.value.trim()) return
savingProject.value = true
try {
await projectsStore.patchProject(projectsStore.currentProject.id, {
name: editProjectName.value.trim(),
description: editProjectDesc.value.trim() || undefined
})
showEditProjectModal.value = false
} catch (e) {
console.error('Failed to update project:', e)
} finally {
savingProject.value = false
}
}
// Feature 2: Folder picker helpers
const folderOptions = computed(() => {
return buildFlatFolders(projectsStore.treeNodes)
})
function buildFlatFolders(nodes: TreeNode[], depth = 0): Array<{ id: string; name: string; depth: number }> {
const result: Array<{ id: string; name: string; depth: number }> = []
for (const node of nodes) {
if (node.type === 'folder') {
result.push({ id: node.id, name: node.name, depth })
if (node.children) {
result.push(...buildFlatFolders(node.children, depth + 1))
}
}
}
return result
}
function toggleFolderPicker() {
folderPickerOpen.value = !folderPickerOpen.value
if (folderPickerOpen.value) {
// auto-expand all so user can see everything
const allIds = projectsStore.folders.map(f => f.id)
folderPickerExpanded.value = new Set(allIds)
}
}
function selectFolder(id: string) {
folderPickerSelectedId.value = folderPickerSelectedId.value === id ? null : id
}
function clearFolderSelection() {
folderPickerSelectedId.value = null
}
function getSelectedFolderName(): string {
if (!folderPickerSelectedId.value) return 'Root (no folder)'
const folder = projectsStore.folders.find(f => f.id === folderPickerSelectedId.value)
return folder ? folder.name : 'Root (no folder)'
}
// Phase 3: Graph // Phase 3: Graph
function openGraphView() { function openGraphView() {
showGraphView.value = true showGraphView.value = true
@@ -151,7 +232,15 @@ async function handleExport() {
<div v-else-if="projectsStore.currentProject" class="project"> <div v-else-if="projectsStore.currentProject" class="project">
<div class="project__welcome"> <div class="project__welcome">
<div class="project__title-row">
<h1 class="project__title">{{ projectsStore.currentProject.name }}</h1> <h1 class="project__title">{{ projectsStore.currentProject.name }}</h1>
<button class="edit-icon-btn" title="Edit project" @click="openEditProjectModal">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>
</button>
</div>
<p v-if="projectsStore.currentProject.description" class="project__desc"> <p v-if="projectsStore.currentProject.description" class="project__desc">
{{ projectsStore.currentProject.description }} {{ projectsStore.currentProject.description }}
</p> </p>
@@ -218,6 +307,55 @@ async function handleExport() {
autofocus autofocus
/> />
</div> </div>
<!-- Feature 2: Folder selector -->
<div class="form__field">
<label class="form__label">Folder (optional)</label>
<div class="folder-picker">
<button
type="button"
class="folder-picker__trigger"
@click="toggleFolderPicker"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
</svg>
<span>{{ getSelectedFolderName() }}</span>
<svg
:class="['folder-picker__chevron', { 'folder-picker__chevron--open': folderPickerOpen }]"
width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
>
<path d="m6 9 6 6 6-6"/>
</svg>
</button>
<div v-if="folderPickerOpen" class="folder-picker__dropdown">
<button
type="button"
class="folder-picker__option"
:class="{ 'folder-picker__option--selected': !folderPickerSelectedId }"
@click="clearFolderSelection"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
</svg>
Root (no folder)
</button>
<button
v-for="folder in folderOptions"
:key="folder.id"
type="button"
class="folder-picker__option"
:class="{ 'folder-picker__option--selected': folderPickerSelectedId === folder.id }"
:style="{ paddingLeft: `${0.75 + folder.depth * 1}rem` }"
@click="selectFolder(folder.id)"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
</svg>
{{ folder.name }}
</button>
</div>
</div>
</div>
</form> </form>
<template #footer> <template #footer>
<Button variant="secondary" @click="showNewDocModal = false">Cancel</Button> <Button variant="secondary" @click="showNewDocModal = false">Cancel</Button>
@@ -225,6 +363,37 @@ async function handleExport() {
</template> </template>
</Modal> </Modal>
<!-- Feature 1: Edit Project Modal -->
<Modal :show="showEditProjectModal" title="Edit Project" @close="showEditProjectModal = false">
<form @submit.prevent="saveProject">
<div class="form__field">
<label for="edit-proj-name" class="form__label">Project Name</label>
<input
id="edit-proj-name"
v-model="editProjectName"
type="text"
class="form__input"
placeholder="Project name"
autofocus
/>
</div>
<div class="form__field">
<label for="edit-proj-desc" class="form__label">Description</label>
<textarea
id="edit-proj-desc"
v-model="editProjectDesc"
class="form__input form__textarea"
placeholder="Add a description..."
rows="3"
></textarea>
</div>
</form>
<template #footer>
<Button variant="secondary" @click="showEditProjectModal = false">Cancel</Button>
<Button variant="primary" :loading="savingProject" @click="saveProject">Save</Button>
</template>
</Modal>
<!-- Graph View Modal --> <!-- Graph View Modal -->
<Modal :show="showGraphView" title="" size="lg" @close="showGraphView = false"> <Modal :show="showGraphView" title="" size="lg" @close="showGraphView = false">
<GraphView <GraphView
@@ -335,6 +504,34 @@ async function handleExport() {
color: var(--text-primary); color: var(--text-primary);
} }
.project__title-row {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
}
.edit-icon-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
padding: 0;
background: none;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.15s;
}
.edit-icon-btn:hover {
background: var(--bg-secondary);
color: var(--text-primary);
border-color: var(--accent);
}
.project__desc { .project__desc {
margin: 0.75rem 0 0; margin: 0.75rem 0 0;
font-size: 1rem; font-size: 1rem;
@@ -394,6 +591,89 @@ async function handleExport() {
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1); box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
} }
.form__textarea {
resize: vertical;
min-height: 80px;
font-family: inherit;
line-height: 1.5;
}
/* Folder Picker */
.folder-picker {
position: relative;
}
.folder-picker__trigger {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.75rem 1rem;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
font-size: 0.9375rem;
color: var(--text-primary);
cursor: pointer;
text-align: left;
transition: border-color 0.15s;
}
.folder-picker__trigger:hover {
border-color: var(--accent);
}
.folder-picker__trigger span {
flex: 1;
}
.folder-picker__chevron {
transition: transform 0.15s;
color: var(--text-secondary);
}
.folder-picker__chevron--open {
transform: rotate(180deg);
}
.folder-picker__dropdown {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 50;
max-height: 240px;
overflow-y: auto;
}
.folder-picker__option {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.625rem 0.75rem;
background: none;
border: none;
font-size: 0.875rem;
color: var(--text-primary);
cursor: pointer;
text-align: left;
transition: background 0.1s;
}
.folder-picker__option:hover {
background: var(--bg-tertiary);
}
.folder-picker__option--selected {
background: rgba(99, 102, 241, 0.1);
color: var(--accent);
}
/* Export Modal Styles */ /* Export Modal Styles */
.export-formats { .export-formats {
display: flex; display: flex;