+ );
+}
+
+
+/* ═══════════════════════ 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:"
",drawingData:null,starred:true,createdAt:"2025-06-10T10:00:00Z",updatedAt:now()},
+ {id:"n2",title:"Sprint Planning",folderId:"nb2",content:"
",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:"
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=()=>(
+
+
+
+
+
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}/>}
+
+ );
+}
\ No newline at end of file
diff --git a/note-app.jsx b/note-app.jsx
deleted file mode 100644
index 0401a99..0000000
--- a/note-app.jsx
+++ /dev/null
@@ -1,1116 +0,0 @@
-import { useState, useRef, useEffect, useCallback, useMemo } from "react";
-
-/* ─────────────────────── helpers ─────────────────────── */
-const uid = () => Math.random().toString(36).slice(2, 10);
-const now = () => new Date().toISOString();
-const fmtDate = (d) => {
- const dt = new Date(d);
- const opts = { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" };
- return dt.toLocaleDateString("en-US", opts);
-};
-
-/* ─────────────────────── default data ─────────────────────── */
-const DEFAULT_FOLDERS = [
- { id: "all", name: "All Notes", icon: "📋", system: true },
- { id: "starred", name: "Starred", icon: "⭐", system: true },
- { id: "drawings", name: "Drawings", icon: "🎨", system: true },
- { id: "pdfs", name: "PDFs", icon: "📄", system: true },
- { id: "trash", name: "Trash", icon: "🗑️", system: true },
-];
-
-const COLORS = [
- "#1a1a2e", "#e94560", "#0f3460", "#16213e", "#533483",
- "#e07c24", "#21bf73", "#fd5e53", "#3282b8", "#ff6b6b",
- "#4ecdc4", "#f7dc6f", "#bb8fce", "#85c1e9", "#f8c471",
- "#82e0aa", "#f1948a", "#aeb6bf", "#ffffff", "#000000",
-];
-
-const PEN_SIZES = [1, 2, 3, 5, 8, 12, 20];
-
-const COVER_GRADIENTS = [
- "linear-gradient(135deg, #0f0c29, #302b63, #24243e)",
- "linear-gradient(135deg, #e44d26, #f16529, #ffd700)",
- "linear-gradient(135deg, #134e5e, #71b280)",
- "linear-gradient(135deg, #8e2de2, #4a00e0)",
- "linear-gradient(135deg, #fc4a1a, #f7b733)",
- "linear-gradient(135deg, #00b4db, #0083b0)",
- "linear-gradient(135deg, #2c3e50, #3498db)",
- "linear-gradient(135deg, #c94b4b, #4b134f)",
- "linear-gradient(135deg, #11998e, #38ef7d)",
- "linear-gradient(135deg, #373b44, #4286f4)",
- "linear-gradient(135deg, #141e30, #243b55)",
- "linear-gradient(135deg, #f12711, #f5af19)",
-];
-
-/* ────────────────────── Icons (inline SVG) ────────────────────── */
-const Icons = {
- plus: (
-
- ),
- search: (
-
- ),
- bold:
,
- italic:
,
- underline:
,
- list:
,
- listOrdered:
,
- checkbox:
,
- trash:
,
- star:
,
- starFill:
,
- pen:
,
- eraser:
,
- undo:
,
- redo:
,
- sun:
,
- moon:
,
- folder:
,
- back:
,
- menu:
,
- download:
,
- highlight:
,
- notebook:
,
- settings:
,
- edit:
,
- sync:
,
- image:
,
- close:
,
-};
-
-
-/* ────────────────────── Drawing Canvas Component ────────────────────── */
-function DrawingCanvas({ drawingData, onSave, dark, standalone = false, overlayMode = false }) {
- const canvasRef = useRef(null);
- const ctxRef = useRef(null);
- const [isDrawing, setIsDrawing] = useState(false);
- const [tool, setTool] = useState("pen");
- const [color, setColor] = useState("#1a1a2e");
- const [size, setSize] = useState(3);
- const [history, setHistory] = useState([]);
- const [historyIdx, setHistoryIdx] = useState(-1);
- const [showColorPicker, setShowColorPicker] = useState(false);
- const [showSizePicker, setShowSizePicker] = useState(false);
- const lastPoint = useRef(null);
-
- const T = useMemo(() => ({
- bg: dark ? "#0d1117" : "#ffffff",
- toolbar: dark ? "#161b22" : "#f6f8fa",
- border: dark ? "#30363d" : "#d0d7de",
- text: dark ? "#e6edf3" : "#1f2328",
- muted: dark ? "#8b949e" : "#656d76",
- active: dark ? "#388bfd" : "#0969da",
- hover: dark ? "#1c2128" : "#eef1f4",
- }), [dark]);
-
- useEffect(() => {
- const canvas = canvasRef.current;
- if (!canvas) return;
- const parent = canvas.parentElement;
- if (standalone || overlayMode) {
- canvas.width = parent.clientWidth;
- canvas.height = parent.clientHeight;
- } else {
- canvas.width = parent.clientWidth;
- canvas.height = Math.max(600, parent.clientHeight);
- }
- const ctx = canvas.getContext("2d");
- ctx.lineCap = "round";
- ctx.lineJoin = "round";
- ctxRef.current = ctx;
-
- if (drawingData && drawingData.length > 0) {
- const img = new Image();
- img.onload = () => {
- ctx.drawImage(img, 0, 0);
- saveToHistory();
- };
- img.src = drawingData;
- } else {
- saveToHistory();
- }
- }, []);
-
- const saveToHistory = () => {
- const canvas = canvasRef.current;
- if (!canvas) return;
- const data = canvas.toDataURL();
- setHistory((prev) => {
- const next = prev.slice(0, historyIdx + 1 || prev.length);
- next.push(data);
- return next;
- });
- setHistoryIdx((prev) => prev + 1);
- };
-
- const undo = () => {
- if (historyIdx <= 0) return;
- const newIdx = historyIdx - 1;
- setHistoryIdx(newIdx);
- const img = new Image();
- img.onload = () => {
- const ctx = ctxRef.current;
- ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
- ctx.drawImage(img, 0, 0);
- };
- img.src = history[newIdx];
- };
-
- const redo = () => {
- if (historyIdx >= history.length - 1) return;
- const newIdx = historyIdx + 1;
- setHistoryIdx(newIdx);
- const img = new Image();
- img.onload = () => {
- const ctx = ctxRef.current;
- ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
- ctx.drawImage(img, 0, 0);
- };
- img.src = history[newIdx];
- };
-
- const getPos = (e) => {
- const canvas = canvasRef.current;
- const rect = canvas.getBoundingClientRect();
- const scaleX = canvas.width / rect.width;
- const scaleY = canvas.height / rect.height;
- if (e.touches) {
- return {
- x: (e.touches[0].clientX - rect.left) * scaleX,
- y: (e.touches[0].clientY - rect.top) * scaleY,
- };
- }
- return {
- x: (e.clientX - rect.left) * scaleX,
- y: (e.clientY - rect.top) * scaleY,
- };
- };
-
- const startDraw = (e) => {
- e.preventDefault();
- const pos = getPos(e);
- lastPoint.current = pos;
- setIsDrawing(true);
- const ctx = ctxRef.current;
- ctx.beginPath();
- ctx.moveTo(pos.x, pos.y);
- if (tool === "eraser") {
- ctx.globalCompositeOperation = "destination-out";
- ctx.lineWidth = size * 4;
- } else if (tool === "highlighter") {
- ctx.globalCompositeOperation = "source-over";
- ctx.strokeStyle = color + "55";
- ctx.lineWidth = size * 6;
- } else {
- ctx.globalCompositeOperation = "source-over";
- ctx.strokeStyle = color;
- ctx.lineWidth = size;
- }
- };
-
- const draw = (e) => {
- if (!isDrawing) return;
- e.preventDefault();
- const pos = getPos(e);
- const ctx = ctxRef.current;
- ctx.lineTo(pos.x, pos.y);
- ctx.stroke();
- lastPoint.current = pos;
- };
-
- const stopDraw = (e) => {
- if (!isDrawing) return;
- e?.preventDefault();
- setIsDrawing(false);
- ctxRef.current.closePath();
- ctxRef.current.globalCompositeOperation = "source-over";
- saveToHistory();
- if (onSave) {
- onSave(canvasRef.current.toDataURL());
- }
- };
-
- const clearCanvas = () => {
- const ctx = ctxRef.current;
- ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
- saveToHistory();
- if (onSave) onSave(canvasRef.current.toDataURL());
- };
-
- const toolbarStyle = {
- display: "flex",
- alignItems: "center",
- gap: "4px",
- padding: "6px 10px",
- background: T.toolbar,
- borderBottom: `1px solid ${T.border}`,
- flexWrap: "wrap",
- position: overlayMode ? "absolute" : "relative",
- top: overlayMode ? 0 : undefined,
- left: overlayMode ? 0 : undefined,
- right: overlayMode ? 0 : undefined,
- zIndex: overlayMode ? 20 : undefined,
- borderRadius: overlayMode ? "0 0 8px 8px" : 0,
- };
-
- const btnStyle = (active) => ({
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- width: 34,
- height: 34,
- borderRadius: 8,
- border: "none",
- cursor: "pointer",
- background: active ? T.active + "22" : "transparent",
- color: active ? T.active : T.text,
- transition: "all 0.15s",
- });
-
- return (
-
-
-
-
-
-
-
-
-
-
- {/* Color */}
-
-
- {showColorPicker && (
-
- {COLORS.map((c) => (
-
- )}
-
-
- {/* Size */}
-
-
- {showSizePicker && (
-
- {PEN_SIZES.map((s) => (
-
- ))}
-
- )}
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-
-/* ────────────────────── Rich Text Editor ────────────────────── */
-function RichTextEditor({ content, onChange, dark }) {
- const editorRef = useRef(null);
-
- const T = useMemo(() => ({
- bg: dark ? "#0d1117" : "#ffffff",
- toolbar: dark ? "#161b22" : "#f6f8fa",
- border: dark ? "#30363d" : "#d0d7de",
- text: dark ? "#e6edf3" : "#1f2328",
- muted: dark ? "#8b949e" : "#656d76",
- active: dark ? "#388bfd" : "#0969da",
- }), [dark]);
-
- useEffect(() => {
- if (editorRef.current && editorRef.current.innerHTML !== content) {
- editorRef.current.innerHTML = content || "";
- }
- }, [content]);
-
- const exec = (cmd, val = null) => {
- document.execCommand(cmd, false, val);
- editorRef.current?.focus();
- if (onChange) onChange(editorRef.current.innerHTML);
- };
-
- const handleInput = () => {
- if (onChange) onChange(editorRef.current.innerHTML);
- };
-
- const btnStyle = {
- display: "flex", alignItems: "center", justifyContent: "center",
- width: 32, height: 32, borderRadius: 6, border: "none",
- cursor: "pointer", background: "transparent", color: T.text,
- transition: "all 0.15s",
- };
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-
-/* ────────────────────── PDF Viewer with Annotation ────────────────────── */
-function PDFViewer({ pdfData, annotations, onAnnotationSave, dark }) {
- const [drawMode, setDrawMode] = useState(false);
- const [pageNum, setPageNum] = useState(1);
- const [totalPages, setTotalPages] = useState(1);
-
- const T = useMemo(() => ({
- bg: dark ? "#0d1117" : "#f6f8fa",
- toolbar: dark ? "#161b22" : "#ffffff",
- border: dark ? "#30363d" : "#d0d7de",
- text: dark ? "#e6edf3" : "#1f2328",
- muted: dark ? "#8b949e" : "#656d76",
- active: dark ? "#388bfd" : "#0969da",
- }), [dark]);
-
- return (
-
-
-
-
-
-
Page {pageNum} / {totalPages}
-
-
-
-
- {pdfData ? (
-
-
-
📄
-
PDF loaded — {pdfData.name}
-
- In a full deployment, PDF.js renders pages here. The annotation canvas overlays each page for freehand drawing, highlighting, and text annotations.
-
-
- {drawMode && (
-
-
-
- )}
-
- ) : (
-
-
📄
-
No PDF loaded
-
Import a PDF to view and annotate it
-
- )}
-
-
- );
-}
-
-
-/* ────────────────────── Create Notebook Modal ────────────────────── */
-function CreateNotebookModal({ onClose, onCreate, dark }) {
- const [name, setName] = useState("");
- const [selectedCover, setSelectedCover] = useState(0);
-
- const T = useMemo(() => ({
- bg: dark ? "#161b22" : "#ffffff",
- border: dark ? "#30363d" : "#d0d7de",
- text: dark ? "#e6edf3" : "#1f2328",
- muted: dark ? "#8b949e" : "#656d76",
- active: dark ? "#388bfd" : "#0969da",
- }), [dark]);
-
- return (
-
-
e.stopPropagation()}>
-
- New Notebook
-
-
-
-
- {name || "Notebook"}
-
-
-
-
setName(e.target.value)}
- placeholder="Notebook name..."
- 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",
- }}
- />
-
-
-
Choose cover
-
- {COVER_GRADIENTS.map((g, i) => (
-
-
-
-
-
-
-
-
-
- );
-}
-
-
-/* ────────────────────────── MAIN APP ────────────────────────── */
-export default function NoteApp() {
- const [dark, setDark] = useState(true);
- const [sidebarOpen, setSidebarOpen] = useState(true);
- const [activeFolder, setActiveFolder] = useState("all");
- const [activeNote, setActiveNote] = useState(null);
- const [view, setView] = useState("notes"); // notes | drawing | pdf | settings
- const [searchQuery, setSearchQuery] = useState("");
- const [showNewNotebook, setShowNewNotebook] = useState(false);
- const [syncStatus, setSyncStatus] = useState("synced"); // synced | syncing | offline
- const fileInputRef = useRef(null);
-
- const [notebooks, setNotebooks] = useState([
- { id: "nb1", name: "Personal", cover: 0, createdAt: now() },
- { id: "nb2", name: "Work", cover: 2, createdAt: now() },
- { id: "nb3", name: "Ideas", cover: 4, createdAt: now() },
- ]);
-
- const [notes, setNotes] = useState([
- {
- id: "n1", type: "text", title: "Welcome to InkNote", folderId: "nb1",
- content: "
Welcome to InkNote ✨
Your cross-platform note-taking companion.
Features include:
- Rich text editing with formatting
- Freehand drawing with pen & highlighter
- PDF import & annotation
- Notebook organization with custom covers
- Dark & light themes
Start creating! Click + to add a new note.
",
- starred: true, createdAt: "2025-06-10T10:00:00Z", updatedAt: now(),
- },
- {
- id: "n2", type: "drawing", title: "Quick Sketch", folderId: "nb3",
- drawingData: null, starred: false, createdAt: "2025-06-11T08:30:00Z", updatedAt: now(),
- },
- {
- id: "n3", type: "text", title: "Meeting Notes", folderId: "nb2",
- content: "
Sprint Planning
Discussed roadmap for Q3. Key points:
- Sync engine improvements
- Mobile gesture support
- PDF annotation export
",
- starred: false, createdAt: "2025-06-12T14:00:00Z", updatedAt: now(),
- },
- ]);
-
- const [pdfs, setPdfs] = useState([]);
-
- // Theme
- const T = useMemo(() => dark ? {
- bg: "#0d1117", sidebar: "#010409", sidebarHover: "#161b22", sidebarActive: "#161b22",
- card: "#161b22", cardHover: "#1c2128", toolbar: "#161b22",
- border: "#30363d", borderLight: "#21262d",
- text: "#e6edf3", textSecondary: "#c9d1d9", muted: "#8b949e", faint: "#484f58",
- accent: "#388bfd", accentSoft: "#388bfd22", accentText: "#58a6ff",
- danger: "#f85149", dangerSoft: "#f8514922",
- success: "#3fb950", successSoft: "#3fb95022",
- warning: "#d29922",
- shadow: "0 8px 24px rgba(0,0,0,0.4)",
- inputBg: "#0d1117",
- } : {
- bg: "#f6f8fa", sidebar: "#ffffff", sidebarHover: "#f3f4f6", sidebarActive: "#eef2ff",
- card: "#ffffff", cardHover: "#f9fafb", toolbar: "#ffffff",
- border: "#d0d7de", borderLight: "#e5e7eb",
- text: "#1f2328", textSecondary: "#424a53", muted: "#656d76", faint: "#afb8c1",
- accent: "#0969da", accentSoft: "#0969da18", accentText: "#0550ae",
- danger: "#cf222e", dangerSoft: "#cf222e15",
- success: "#1a7f37", successSoft: "#1a7f3715",
- warning: "#9a6700",
- shadow: "0 8px 24px rgba(0,0,0,0.08)",
- inputBg: "#f6f8fa",
- }, [dark]);
-
- // Filtered notes
- const filteredNotes = useMemo(() => {
- let list = notes;
- if (activeFolder === "starred") list = list.filter((n) => n.starred);
- else if (activeFolder === "drawings") list = list.filter((n) => n.type === "drawing");
- else if (activeFolder === "pdfs") return pdfs;
- else if (activeFolder === "trash") return [];
- else if (activeFolder !== "all") 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;
-
- // Actions
- const createNote = (type = "text") => {
- const fid = ["all", "starred", "drawings", "pdfs", "trash"].includes(activeFolder) ? (notebooks[0]?.id || "nb1") : activeFolder;
- const note = {
- id: uid(), type, title: type === "drawing" ? "New Drawing" : "Untitled Note",
- folderId: fid, content: "", drawingData: null, starred: false,
- createdAt: now(), updatedAt: now(),
- };
- setNotes((prev) => [note, ...prev]);
- setActiveNote(note.id);
- setView(type === "drawing" ? "drawing" : "notes");
- simulateSync();
- };
-
- const updateNote = (id, updates) => {
- setNotes((prev) => prev.map((n) => n.id === id ? { ...n, ...updates, updatedAt: now() } : n));
- simulateSync();
- };
-
- const deleteNote = (id) => {
- setNotes((prev) => prev.filter((n) => n.id !== id));
- if (activeNote === id) setActiveNote(null);
- simulateSync();
- };
-
- const toggleStar = (id) => {
- setNotes((prev) => prev.map((n) => n.id === id ? { ...n, starred: !n.starred } : n));
- };
-
- const importPDF = (e) => {
- const file = e.target.files[0];
- if (!file) return;
- const pdf = {
- id: uid(), type: "pdf", title: file.name, name: file.name,
- pdfData: { name: file.name, size: file.size },
- annotations: null, starred: false,
- createdAt: now(), updatedAt: now(),
- };
- setPdfs((prev) => [pdf, ...prev]);
- setActiveNote(pdf.id);
- setView("pdf");
- setActiveFolder("pdfs");
- simulateSync();
- };
-
- const createNotebook = ({ name, cover }) => {
- const nb = { id: uid(), name, cover, createdAt: now() };
- setNotebooks((prev) => [...prev, nb]);
- setShowNewNotebook(false);
- setActiveFolder(nb.id);
- };
-
- const simulateSync = () => {
- setSyncStatus("syncing");
- setTimeout(() => setSyncStatus("synced"), 1200);
- };
-
- // Font import
- useEffect(() => {
- const link = document.createElement("link");
- link.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";
- link.rel = "stylesheet";
- document.head.appendChild(link);
- }, []);
-
- /* ─── SIDEBAR ─── */
- const renderSidebar = () => (
-
- {/* Logo area */}
-
-
-
-
-
-
-
- {/* Search */}
-
-
- {Icons.search}
- setSearchQuery(e.target.value)}
- placeholder="Search notes..."
- style={{
- border: "none", background: "transparent", outline: "none", flex: 1,
- color: T.text, fontSize: 13, fontFamily: "'DM Sans', sans-serif",
- }}
- />
-
-
-
- {/* System folders */}
-
- {DEFAULT_FOLDERS.map((f) => (
-
- ))}
-
-
- {/* Notebooks */}
-
-
- Notebooks
-
-
- {notebooks.map((nb) => (
-
- ))}
-
-
- {/* Bottom actions */}
-
-
- ●
- {syncStatus === "synced" ? "All changes synced" : syncStatus === "syncing" ? "Syncing..." : "Offline"}
-
-
-
- );
-
- /* ─── NOTE LIST ─── */
- const renderNoteList = () => {
- const currentFolderName = (() => {
- 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 (
-
- {/* Header */}
-
-
-
-
- {currentFolderName}
-
-
-
-
-
-
-
-
-
-
- {/* Note cards */}
-
- {filteredNotes.length === 0 ? (
-
-
{activeFolder === "pdfs" ? "📄" : "📝"}
-
- {activeFolder === "pdfs" ? "No PDFs yet. Import one to get started." : "No notes yet. Create one to get started."}
-
-
- ) : (
- filteredNotes.map((note) => (
-
- ))
- )}
-
-
- );
- };
-
- /* ─── EDITOR AREA ─── */
- const renderEditor = () => {
- if (!currentNote) {
- return (
-
-
✏️
-
Select a note to edit
-
Or create a new one
-
-
-
-
-
- );
- }
-
- return (
-
- {/* Note header */}
-
- {
- if (currentNote.type === "pdf") {
- setPdfs((prev) => prev.map((p) => p.id === currentNote.id ? { ...p, title: e.target.value } : p));
- } else {
- 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",
- }}
- />
-
-
-
-
- {/* Editor content */}
-
- {currentNote.type === "drawing" ? (
-
updateNote(currentNote.id, { drawingData: data })}
- dark={dark}
- standalone
- />
- ) : currentNote.type === "pdf" ? (
- {
- setPdfs((prev) => prev.map((p) => p.id === currentNote.id ? { ...p, annotations: data, updatedAt: now() } : p));
- }}
- dark={dark}
- />
- ) : (
- updateNote(currentNote.id, { content: html })}
- dark={dark}
- />
- )}
-
-
- );
- };
-
- /* ─── MAIN RENDER ─── */
- return (
-
-
-
- {renderSidebar()}
- {renderNoteList()}
- {renderEditor()}
-
- {showNewNotebook && (
- setShowNewNotebook(false)}
- onCreate={createNotebook}
- dark={dark}
- />
- )}
-
- );
-}
\ No newline at end of file