);
}
/* ═══════ CREATE NOTEBOOK MODAL ═══════ */
function CreateNotebookModal({onClose,onCreate,dark}) {
const [name,setName]=useState("");const [sel,setSel]=useState(0);
const T=useMemo(()=>({bg:dark?"#161b22":"#fff",border:dark?"#30363d":"#d0d7de",text:dark?"#e6edf3":"#1f2328",muted:dark?"#8b949e":"#656d76",active:dark?"#388bfd":"#0969da"}),[dark]);
return (
e.stopPropagation()}>
New Notebook
setName(e.target.value)} placeholder="Notebook name..." autoFocus
style={{width:"100%",padding:"10px 14px",borderRadius:10,border:`1px solid ${T.border}`,background:dark?"#0d1117":"#f6f8fa",color:T.text,fontSize:14,outline:"none",marginBottom:16,boxSizing:"border-box",fontFamily:"'DM Sans',sans-serif"}}
onKeyDown={e=>{if(e.key==="Enter"&&name.trim())onCreate({name:name.trim(),cover:sel});}}/>
Choose cover style
{COVERS.map((c,i)=>
);
}
/* ═══════════════════════ MAIN APP ═══════════════════════ */
export default function NoteApp() {
const [dark,setDark]=useState(true);
const [sidebarOpen,setSidebarOpen]=useState(true);
const [activeFolder,setActiveFolder]=useState("dashboard");
const [activeNote,setActiveNote]=useState(null);
const [searchQuery,setSearchQuery]=useState("");
const [showNewNb,setShowNewNb]=useState(false);
const [syncStatus,setSyncStatus]=useState("synced");
const [ctxMenu,setCtxMenu]=useState(null);
const [confirmDialog,setConfirmDialog]=useState(null);
const [editingNb,setEditingNb]=useState(null);
const [dragNbId,setDragNbId]=useState(null);
const [dragOverNbId,setDragOverNbId]=useState(null);
const fileRef=useRef(null);
const [notebooks,setNotebooks]=useState([
{id:"nb1",name:"Personal",cover:0},{id:"nb2",name:"Work Projects",cover:2},
{id:"nb3",name:"Ideas & Sketches",cover:4},{id:"nb4",name:"Study Notes",cover:7},
]);
const [notes,setNotes]=useState([
{id:"n1",title:"Welcome to InkNote",folderId:"nb1",content:"
Welcome to InkNote ✨
Each note supports both writing and drawing — switch modes with the tabs. Drawings stay visible while you write!
- Rich text with formatting, lists, checkboxes
- Freehand drawing layered on top
",drawingData:null,starred:true,createdAt:"2025-06-10T10:00:00Z",updatedAt:now()},
{id:"n2",title:"Sprint Planning",folderId:"nb2",content:"
Q3 Roadmap
- Sync engine
- Mobile gestures
- PDF export
Review: Friday 3pm
",drawingData:null,starred:false,createdAt:"2025-06-12T14:00:00Z",updatedAt:now()},
{id:"n3",title:"UI Wireframes",folderId:"nb3",content:"
Dashboard layout concepts...
",drawingData:null,starred:false,createdAt:"2025-06-11T08:30:00Z",updatedAt:now()},
{id:"n4",title:"Data Structures",folderId:"nb4",content:"
Binary Trees
Each node has at most two children. Operations: insert, delete, search, traversal.
",drawingData:null,starred:true,createdAt:"2025-06-09T11:00:00Z",updatedAt:now()},
]);
const [pdfs,setPdfs]=useState([]);
const T=useMemo(()=>dark?{
bg:"#0d1117",sidebar:"#010409",sidebarActive:"#161b22",card:"#161b22",toolbar:"#161b22",
border:"#30363d",borderLight:"#21262d",text:"#e6edf3",textSec:"#c9d1d9",muted:"#8b949e",faint:"#484f58",
accent:"#388bfd",accentSoft:"#388bfd22",accentText:"#58a6ff",danger:"#f85149",success:"#3fb950",warning:"#d29922",inputBg:"#0d1117",dashBg:"#0d1117",
}:{
bg:"#f6f8fa",sidebar:"#ffffff",sidebarActive:"#eef2ff",card:"#ffffff",toolbar:"#ffffff",
border:"#d0d7de",borderLight:"#e5e7eb",text:"#1f2328",textSec:"#424a53",muted:"#656d76",faint:"#afb8c1",
accent:"#0969da",accentSoft:"#0969da18",accentText:"#0550ae",danger:"#cf222e",success:"#1a7f37",warning:"#9a6700",inputBg:"#f6f8fa",dashBg:"#f0f2f5",
},[dark]);
const filteredNotes=useMemo(()=>{
let list=notes;
if(activeFolder==="starred")list=list.filter(n=>n.starred);
else if(activeFolder==="pdfs")return pdfs;
else if(activeFolder==="trash")return[];
else if(activeFolder!=="all"&&activeFolder!=="dashboard")list=list.filter(n=>n.folderId===activeFolder);
if(searchQuery){const q=searchQuery.toLowerCase();list=list.filter(n=>n.title.toLowerCase().includes(q)||(n.content||"").toLowerCase().includes(q));}
return list.sort((a,b)=>new Date(b.updatedAt)-new Date(a.updatedAt));
},[notes,pdfs,activeFolder,searchQuery]);
const currentNote=activeNote?notes.find(n=>n.id===activeNote)||pdfs.find(p=>p.id===activeNote):null;
const createNote=()=>{
const fid=["all","starred","pdfs","trash","dashboard"].includes(activeFolder)?(notebooks[0]?.id||"nb1"):activeFolder;
const n={id:uid(),title:"Untitled Note",folderId:fid,content:"",drawingData:null,starred:false,createdAt:now(),updatedAt:now()};
setNotes(p=>[n,...p]);setActiveNote(n.id);if(activeFolder==="dashboard")setActiveFolder(fid);sim();
};
const updateNote=(id,u)=>{setNotes(p=>p.map(n=>n.id===id?{...n,...u,updatedAt:now()}:n));sim();};
const requestDeleteNote=(id)=>{
const note=notes.find(n=>n.id===id);
setConfirmDialog({title:"Delete Note",message:`Are you sure you want to delete "${note?.title||"this note"}"? This cannot be undone.`,
onConfirm:()=>{setNotes(p=>p.filter(n=>n.id!==id));if(activeNote===id)setActiveNote(null);sim();setConfirmDialog(null);}});
};
const toggleStar=(id)=>setNotes(p=>p.map(n=>n.id===id?{...n,starred:!n.starred}:n));
const importPDF=(e)=>{const f=e.target.files[0];if(!f)return;const p={id:uid(),type:"pdf",title:f.name,starred:false,createdAt:now(),updatedAt:now()};setPdfs(prev=>[p,...prev]);setActiveNote(p.id);setActiveFolder("pdfs");sim();};
const createNb=({name,cover})=>{const nb={id:uid(),name,cover};setNotebooks(p=>[...p,nb]);setShowNewNb(false);setActiveFolder(nb.id);};
const updateNb=(id,updates)=>{setNotebooks(p=>p.map(nb=>nb.id===id?{...nb,...updates}:nb));setEditingNb(null);sim();};
const requestDeleteNb=(id)=>{
const nb=notebooks.find(n=>n.id===id);const nc=notes.filter(n=>n.folderId===id).length;
setConfirmDialog({title:"Delete Notebook",message:`Delete "${nb?.name}"${nc>0?` and its ${nc} note${nc>1?"s":""}`:""} permanently?`,
onConfirm:()=>{setNotes(p=>p.filter(n=>n.folderId!==id));setNotebooks(p=>p.filter(n=>n.id!==id));if(activeFolder===id)setActiveFolder("dashboard");setConfirmDialog(null);sim();}});
};
const sim=()=>{setSyncStatus("syncing");setTimeout(()=>setSyncStatus("synced"),1200);};
// Drag & Drop
const handleNbDragStart=(e,id)=>{setDragNbId(id);e.dataTransfer.effectAllowed="move";};
const handleNbDragOver=(e,id)=>{e.preventDefault();if(dragNbId&&dragNbId!==id)setDragOverNbId(id);};
const handleNbDrop=(e,targetId)=>{e.preventDefault();if(!dragNbId||dragNbId===targetId)return;
setNotebooks(prev=>{const a=[...prev];const fi=a.findIndex(n=>n.id===dragNbId);const ti=a.findIndex(n=>n.id===targetId);if(fi<0||ti<0)return prev;const[item]=a.splice(fi,1);a.splice(ti,0,item);return a;});
setDragNbId(null);setDragOverNbId(null);};
const handleNbDragEnd=()=>{setDragNbId(null);setDragOverNbId(null);};
const handleNbContextMenu=(e,nb)=>{e.preventDefault();e.stopPropagation();
setCtxMenu({x:Math.min(e.clientX,window.innerWidth-200),y:Math.min(e.clientY,window.innerHeight-180),
items:[{label:"Rename",icon:I.edit,action:()=>setEditingNb(nb)},{label:"Change Cover",icon:I.palette,action:()=>setEditingNb(nb)},{divider:true},{label:"Delete Notebook",icon:I.trash,danger:true,action:()=>requestDeleteNb(nb.id)}]});
};
useEffect(()=>{const l=document.createElement("link");l.href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300;0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400&family=DM+Sans:wght@400;500;600;700&display=swap";l.rel="stylesheet";document.head.appendChild(l);},[]);
const renderSidebar=()=>(
{DEFAULT_FOLDERS.map(f=>(
))}
Notebooks
{notebooks.map(nb=>(
handleNbDragStart(e,nb.id)} onDragOver={e=>handleNbDragOver(e,nb.id)} onDrop={e=>handleNbDrop(e,nb.id)} onDragEnd={handleNbDragEnd} onContextMenu={e=>handleNbContextMenu(e,nb)}
style={{display:"flex",alignItems:"center",borderRadius:10,transition:"all .15s",background:dragOverNbId===nb.id?T.accentSoft:activeFolder===nb.id?T.sidebarActive:"transparent",borderTop:dragOverNbId===nb.id?`2px solid ${T.accent}`:"2px solid transparent"}}>
{I.grip}
))}
●
{syncStatus==="synced"?"All changes synced":syncStatus==="syncing"?"Syncing...":"Offline"}
);
const renderDashboard=()=>(
My Notebooks
{notebooks.map(nb=>(
n.folderId===nb.id).length}
onClick={()=>{setActiveFolder(nb.id);setActiveNote(null);}} onContextMenu={e=>handleNbContextMenu(e,nb)}
isActive={activeFolder===nb.id} isDragOver={dragOverNbId===nb.id} draggable
onDragStart={e=>handleNbDragStart(e,nb.id)} onDragEnd={handleNbDragEnd} onDragOver={e=>handleNbDragOver(e,nb.id)} onDrop={e=>handleNbDrop(e,nb.id)}/>
))}
Recent Notes
{notes.slice(0,6).map(note=>{const nb=notebooks.find(b=>b.id===note.folderId);return(
);
const renderNoteList=()=>{
const fname=(()=>{const sf=DEFAULT_FOLDERS.find(f=>f.id===activeFolder);if(sf)return sf.name;const nb=notebooks.find(n=>n.id===activeFolder);return nb?nb.name:"Notes";})();
return(
{fname}
{filteredNotes.length===0?(
):filteredNotes.map(note=>(
))}
);};
const renderEditor=()=>{
if(!currentNote)return(
✏️
Select a note to edit
);
return(
updateNote(currentNote.id,{title:e.target.value})}
style={{flex:1,border:"none",background:"transparent",outline:"none",fontSize:17,fontWeight:600,color:T.text,fontFamily:"'Newsreader',Georgia,serif"}}/>
updateNote(currentNote.id,u)} dark={dark}/>
);};
return(
{renderSidebar()}
{activeFolder==="dashboard"?renderDashboard():(<>{renderNoteList()}{renderEditor()}>)}
{showNewNb&&setShowNewNb(false)} onCreate={createNb} dark={dark}/>}
{editingNb&&setEditingNb(null)} onSave={u=>updateNb(editingNb.id,u)} dark={dark}/>}
{confirmDialog&&setConfirmDialog(null)} dark={dark}/>}
{ctxMenu&&setCtxMenu(null)} dark={dark}/>}
);
}