Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>LifeOS — local-first personal assistant</title> | |
| <link rel="stylesheet" href="/static/style.css"> | |
| </head> | |
| <body> | |
| <!-- ============ ICON SPRITE (stroke, currentColor) ============ --> | |
| <svg width="0" height="0" style="position:absolute" aria-hidden="true"> | |
| <defs> | |
| <symbol id="i-bolt" viewBox="0 0 24 24"><path d="M13 2 4 14h6l-1 8 9-12h-6l1-8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-brain" viewBox="0 0 24 24"><path d="M9 4a3 3 0 0 0-3 3 3 3 0 0 0-2 3c0 1 .4 1.8 1 2.4A3 3 0 0 0 6 17a3 3 0 0 0 3 3c.8 0 1.6-.3 2.1-.9V4.9A3 3 0 0 0 9 4zm6 0a3 3 0 0 1 3 3 3 3 0 0 1 2 3c0 1-.4 1.8-1 2.4A3 3 0 0 1 18 17a3 3 0 0 1-3 3c-.8 0-1.6-.3-2.1-.9V4.9A3 3 0 0 1 15 4z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-doc" viewBox="0 0 24 24"><path d="M6 3h8l4 4v14H6V3zm8 0v4h4" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M9 12h6M9 16h6" stroke="currentColor" stroke-width="1.6"/></symbol> | |
| <symbol id="i-receipt" viewBox="0 0 24 24"><path d="M6 3h12v18l-2-1.5L14 21l-2-1.5L10 21l-2-1.5L6 21V3z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M9 8h6M9 12h6" stroke="currentColor" stroke-width="1.6"/></symbol> | |
| <symbol id="i-camera" viewBox="0 0 24 24"><path d="M4 7h3l2-2h6l2 2h3v13H4V7z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><circle cx="12" cy="13" r="4" fill="none" stroke="currentColor" stroke-width="1.6"/></symbol> | |
| <symbol id="i-mic" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M5 11a7 7 0 0 0 14 0M12 18v3" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol> | |
| <symbol id="i-speaker" viewBox="0 0 24 24"><path d="M4 9v6h4l5 4V5L8 9H4z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M16 9a4 4 0 0 1 0 6M18.5 6.5a8 8 0 0 1 0 11" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol> | |
| <symbol id="i-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M3 12h18M12 3c3 3.5 3 14.5 0 18-3-3.5-3-14.5 0-18z" fill="none" stroke="currentColor" stroke-width="1.6"/></symbol> | |
| <symbol id="i-bell" viewBox="0 0 24 24"><path d="M12 4a5 5 0 0 0-5 5v4l-2 3h14l-2-3V9a5 5 0 0 0-5-5zM10 19a2 2 0 0 0 4 0" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-trash" viewBox="0 0 24 24"><path d="M5 7h14M9 7V5h6v2M8 7l1 13h6l1-13" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol> | |
| <symbol id="i-x" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol> | |
| <symbol id="i-pan" viewBox="0 0 24 24"><circle cx="10" cy="12" r="6" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M16 12h6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol> | |
| <symbol id="i-heart" viewBox="0 0 24 24"><path d="M12 20S4 14.5 4 9.5A4.5 4.5 0 0 1 12 6a4.5 4.5 0 0 1 8 3.5C20 14.5 12 20 12 20z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M4.5 12H9l1.5-3 2 5 1.5-2h5" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-coin" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 6v12M15 8.5c-.6-1-1.7-1.5-3-1.5-1.7 0-3 1-3 2.5s1.3 2 3 2.5 3 1 3 2.5-1.3 2.5-3 2.5c-1.3 0-2.4-.5-3-1.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></symbol> | |
| <symbol id="i-target" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="1.6"/><circle cx="12" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="1.6"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/></symbol> | |
| <symbol id="i-chat" viewBox="0 0 24 24"><path d="M4 5h16v11H9l-5 4V5z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M8 9h8M8 12h5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol> | |
| <symbol id="i-user" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M4 21c1-4 4.5-6 8-6s7 2 8 6" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol> | |
| <symbol id="i-gear" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 2.8v3M12 18.2v3M2.8 12h3M18.2 12h3M5.5 5.5l2.1 2.1M16.4 16.4l2.1 2.1M18.5 5.5l-2.1 2.1M7.6 16.4l-2.1 2.1" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></symbol> | |
| <symbol id="i-edit" viewBox="0 0 24 24"><path d="M4 20h4L20 8l-4-4L4 16v4z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/><path d="M14 6l4 4" stroke="currentColor" stroke-width="1.6"/></symbol> | |
| <symbol id="i-check" viewBox="0 0 24 24"><path d="M5 13l4 4L19 7" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-arrow" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></symbol> | |
| <symbol id="i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></symbol> | |
| <symbol id="i-moon" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></symbol> | |
| </defs> | |
| </svg> | |
| <div class="app"> | |
| <!-- ============ SIDEBAR ============ --> | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="side-logo"><svg class="icon icon-accent"><use href="#i-bolt"/></svg><span class="side-label wordmark-text">Life<b>OS</b></span></div> | |
| <nav class="side-nav" id="side-nav"> | |
| <button class="nav-item active" data-nav="kitchen"><svg class="icon"><use href="#i-pan"/></svg><span class="side-label">Kitchen</span></button> | |
| <button class="nav-item" data-nav="health"><svg class="icon"><use href="#i-heart"/></svg><span class="side-label">Health</span></button> | |
| <button class="nav-item" data-nav="money"><svg class="icon"><use href="#i-coin"/></svg><span class="side-label">Money</span></button> | |
| <button class="nav-item" data-nav="goals"><svg class="icon"><use href="#i-target"/></svg><span class="side-label">Goals</span></button> | |
| <button class="nav-item" data-nav="chat"><svg class="icon"><use href="#i-chat"/></svg><span class="side-label">Chat</span></button> | |
| <button class="nav-item" data-nav="profile"><svg class="icon"><use href="#i-user"/></svg><span class="side-label">Profile</span></button> | |
| <button class="nav-item" data-nav="memory"><svg class="icon"><use href="#i-brain"/></svg><span class="side-label">Memory</span></button> | |
| </nav> | |
| <div class="side-bottom"> | |
| <button class="nav-item" data-nav="settings"><svg class="icon"><use href="#i-gear"/></svg><span class="side-label">Settings</span></button> | |
| </div> | |
| </aside> | |
| <!-- ============ MAIN COLUMN ============ --> | |
| <div class="app-main"> | |
| <header class="topbar"> | |
| <div class="chips"> | |
| <span class="chip chip-accent">100% LOCAL</span> | |
| <span class="chip">Nemotron-3-Nano-4B</span> | |
| <span class="chip">llama.cpp</span> | |
| <span class="chip">RAG memory</span> | |
| </div> | |
| <div class="header-right"> | |
| <button class="btn btn-ghost" id="theme-toggle" title="Toggle theme"> | |
| <svg class="icon" id="theme-icon-sun" style="display:none;"><use href="#i-sun"/></svg> | |
| <svg class="icon" id="theme-icon-moon" style="display:none;"><use href="#i-moon"/></svg> | |
| <span id="theme-text">light</span> | |
| </button> | |
| <div class="model-status" id="model-status"> | |
| <span class="dot" id="status-dot"></span> | |
| <span id="status-text">model: ready</span> | |
| </div> | |
| </div> | |
| </header> | |
| <main> | |
| <!-- ============ KITCHEN ============ --> | |
| <section class="panel active" id="panel-kitchen"> | |
| <div class="card"> | |
| <h2>// flyer intake</h2> | |
| <div class="dropzone" id="flyer-drop"> | |
| <input type="file" id="flyer-file" accept=".pdf,image/*" hidden> | |
| <div class="dz-inner"> | |
| <svg class="icon dz-icon"><use href="#i-doc"/></svg> | |
| <p>drop a grocery flyer here (PDF / image)<br><span class="muted">or click to browse</span></p> | |
| </div> | |
| </div> | |
| <div class="or-divider"><span>or paste flyer text</span></div> | |
| <textarea id="flyer-text" rows="4" placeholder="Chicken thighs $2.99/lb Roma tomatoes $0.99/lb ..."></textarea> | |
| <div class="row"> | |
| <button class="btn" id="flyer-text-btn">Extract deals from text</button> | |
| </div> | |
| <div class="deal-chips" id="deal-chips"> | |
| <p class="empty-hint">no deals yet — upload a flyer or paste its text</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>// recipe picks</h2> | |
| <div class="row"> | |
| <button class="btn btn-primary" id="recipe-btn" disabled>Get recipe picks</button> | |
| </div> | |
| <div class="result md" id="food-result"> | |
| <p class="empty-hint">extract some deals first, then ask for recipe picks tuned to your week</p> | |
| </div> | |
| </div> | |
| <div class="card span-2"> | |
| <h2>// meal photo</h2> | |
| <div class="dropzone" id="meal-drop"> | |
| <input type="file" id="meal-file" accept="image/*" hidden> | |
| <div class="dz-inner"> | |
| <svg class="icon dz-icon"><use href="#i-camera"/></svg> | |
| <p>drop a photo of a meal or receipt<br><span class="muted">or click to browse — identifies food items locally</span></p> | |
| </div> | |
| </div> | |
| <div id="meal-ocr-wrap" hidden> | |
| <div class="ocr-label muted" id="meal-ocr-title">identified items</div> | |
| <div class="ocr-snippet" id="meal-ocr"></div> | |
| <div class="row"> | |
| <label>meal label | |
| <select id="meal-label"> | |
| <option value="breakfast">breakfast</option> | |
| <option value="lunch" selected>lunch</option> | |
| <option value="dinner">dinner</option> | |
| </select> | |
| </label> | |
| <button class="btn btn-primary" id="meal-analyze-btn">Analyze</button> | |
| </div> | |
| </div> | |
| <div class="result md" id="meal-result"> | |
| <p class="empty-hint">upload a meal photo to get a nutrition read on it</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ============ HEALTH ============ --> | |
| <section class="panel" id="panel-health"> | |
| <div class="card"> | |
| <h2>// log a workout</h2> | |
| <form id="workout-form" class="row form-row"> | |
| <label>type | |
| <select id="workout-type"> | |
| <option value="run">run</option> | |
| <option value="push (chest/triceps)">push (chest/triceps)</option> | |
| <option value="pull (back/biceps)">pull (back/biceps)</option> | |
| <option value="legs">legs</option> | |
| <option value="cycling">cycling</option> | |
| <option value="swim">swim</option> | |
| <option value="yoga">yoga</option> | |
| <option value="other">other</option> | |
| </select> | |
| </label> | |
| <label class="other-describe" id="workout-other-wrap" hidden>describe | |
| <input type="text" id="workout-other" placeholder="e.g. rock climbing"> | |
| </label> | |
| <label>duration (min) | |
| <input type="number" id="workout-duration" min="1" max="600" value="45"> | |
| </label> | |
| <button type="submit" class="btn btn-primary" id="workout-btn">Log workout</button> | |
| </form> | |
| <h2 style="margin-top:18px">// last 7 days</h2> | |
| <div class="week-strip" id="week-strip"></div> | |
| <div class="legend muted">filled = trained · empty = rest</div> | |
| <h2 style="margin-top:18px">// logged workouts</h2> | |
| <p class="table-legend muted">date = day logged · workout = training type · duration = minutes trained</p> | |
| <div class="workout-log" id="workout-log"><p class="empty-hint">no workouts logged yet</p></div> | |
| <button class="btn btn-ghost" id="workout-history-btn" hidden></button> | |
| </div> | |
| <div class="card"> | |
| <h2>// workout schedule</h2> | |
| <div class="day-chip-row" id="sched-days"> | |
| <button class="day-chip" data-day="mon">mon</button> | |
| <button class="day-chip" data-day="tue">tue</button> | |
| <button class="day-chip" data-day="wed">wed</button> | |
| <button class="day-chip" data-day="thu">thu</button> | |
| <button class="day-chip" data-day="fri">fri</button> | |
| <button class="day-chip" data-day="sat">sat</button> | |
| <button class="day-chip" data-day="sun">sun</button> | |
| </div> | |
| <div class="row"> | |
| <label>time | |
| <input type="time" id="sched-time" value="07:00"> | |
| </label> | |
| <button class="btn btn-primary" id="sched-save-btn">Save schedule</button> | |
| <button class="btn" id="remind-btn" title="schedules a browser notification for your next workout slot (while this page is open)"><svg class="icon"><use href="#i-bell"/></svg> remind me</button> | |
| </div> | |
| <p class="empty-hint" id="sched-hint">pick training days and a time</p> | |
| </div> | |
| <div class="card span-2"> | |
| <div class="card-head"> | |
| <h2>// calendar — next 7 days</h2> | |
| <button class="btn" id="cal-view-btn" title="open the day-by-day timed view"><svg class="icon"><use href="#i-arrow"/></svg> View schedule</button> | |
| </div> | |
| <div class="cal-strip" id="cal-strip"></div> | |
| <div class="event-list" id="event-list"><p class="empty-hint">no events in the next 7 days</p></div> | |
| <form class="row form-row" id="event-form"> | |
| <label>title<input type="text" id="ev-title" placeholder="dentist"></label> | |
| <label>date<input type="date" id="ev-date"></label> | |
| <label>start<input type="time" id="ev-start" value="09:00"></label> | |
| <label>end<input type="time" id="ev-end" value="10:00"></label> | |
| <label>kind | |
| <select id="ev-kind"> | |
| <option value="appointment">appointment</option> | |
| <option value="work">work</option> | |
| <option value="social">social</option> | |
| <option value="workout">workout</option> | |
| <option value="other">other</option> | |
| </select> | |
| </label> | |
| <label class="other-describe" id="ev-other-wrap" hidden>describe | |
| <input type="text" id="ev-other" placeholder="e.g. volunteering"> | |
| </label> | |
| <button type="submit" class="btn btn-primary" id="ev-add-btn"><svg class="icon"><use href="#i-plus"/></svg> Add event</button> | |
| </form> | |
| </div> | |
| <div class="card span-2"> | |
| <h2>// coach</h2> | |
| <div class="row"> | |
| <button class="btn btn-primary" id="health-btn">What should I do tomorrow?</button> | |
| <button class="btn btn-primary" id="plan-week-btn">Plan my training week</button> | |
| </div> | |
| <div id="free-slots" hidden></div> | |
| <div class="result md" id="health-result"> | |
| <p class="empty-hint">ask the coach — it reads your training week from memory</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ============ MONEY ============ --> | |
| <section class="panel" id="panel-money"> | |
| <div class="card"> | |
| <h2>// transactions</h2> | |
| <div class="dropzone" id="csv-drop"> | |
| <input type="file" id="csv-file" accept=".csv,text/csv" hidden> | |
| <div class="dz-inner"> | |
| <svg class="icon dz-icon"><use href="#i-receipt"/></svg> | |
| <p>drop a bank CSV here<br><span class="muted">or click to browse</span></p> | |
| </div> | |
| </div> | |
| <div id="subs-table-wrap"> | |
| <p class="empty-hint">no subscriptions detected yet — upload a transactions CSV</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>// audit</h2> | |
| <div class="row"> | |
| <button class="btn btn-primary" id="money-btn" disabled>Review my subscriptions</button> | |
| </div> | |
| <div class="result md" id="money-result"> | |
| <p class="empty-hint">detect subscriptions first, then run the audit</p> | |
| </div> | |
| </div> | |
| <div class="card span-2"> | |
| <h2>// monthly payments</h2> | |
| <div class="pay-head"> | |
| <span class="ph-name" title="what the payment is for, e.g. rent or Spotify">name / type</span> | |
| <span class="ph-amount" title="how much you pay each month">amount $/mo</span> | |
| <span class="ph-due" title="day of the month it's due (1–31)">pay day</span> | |
| <span class="ph-del"></span> | |
| </div> | |
| <div id="pay-rows"></div> | |
| <p class="table-legend muted">name = what it's for · amount = monthly cost · pay day = day of the month it's charged</p> | |
| <div class="row"> | |
| <button class="btn" id="pay-add-btn"><svg class="icon"><use href="#i-plus"/></svg> add row</button> | |
| <button class="btn btn-primary" id="pay-save-btn">Save payments</button> | |
| <button class="btn btn-ghost" id="money-to-goals"><svg class="icon"><use href="#i-target"/></svg> savings goals live in Goals <svg class="icon"><use href="#i-arrow"/></svg></button> | |
| </div> | |
| <div class="result md" id="pay-impact" hidden></div> | |
| </div> | |
| </section> | |
| <!-- ============ GOALS ============ --> | |
| <section class="panel" id="panel-goals"> | |
| <div class="card span-2"> | |
| <h2>// your goals</h2> | |
| <div class="goals-grid" id="goals-grid"> | |
| <p class="empty-hint">no goals yet — add one below</p> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2 id="goal-form-title">// new goal</h2> | |
| <form id="goal-edit-form" class="profile-grid"> | |
| <input type="hidden" id="g-id"> | |
| <label class="span-2">title<input type="text" id="g-title" placeholder="emergency fund"></label> | |
| <label>target amount ($)<input type="number" id="g-target" min="0" step="10" placeholder="3000"></label> | |
| <label>already saved ($)<input type="number" id="g-saved" min="0" step="10" placeholder="450"></label> | |
| <label>deadline<input type="month" id="g-deadline"></label> | |
| <div class="row span-2"> | |
| <button type="submit" class="btn btn-primary" id="g-save-btn"><svg class="icon"><use href="#i-check"/></svg> Save goal</button> | |
| <button type="button" class="btn btn-ghost" id="g-cancel-btn" hidden>Cancel edit</button> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="card"> | |
| <h2>// goal coach</h2> | |
| <div id="goal-summary"><p class="empty-hint">pick a goal with "Coach me" — or just start typing</p></div> | |
| <div class="chat-list mini" id="goal-list"> | |
| <p class="empty-hint">talk it through — the coach asks questions, you find the plan</p> | |
| </div> | |
| <form class="chat-input-row" id="goal-form"> | |
| <input type="text" id="goal-input" placeholder="e.g. how do I hit my deadline?" autocomplete="off"> | |
| <button type="submit" class="btn btn-primary" id="goal-send">Send</button> | |
| </form> | |
| </div> | |
| </section> | |
| <!-- ============ CHAT ============ --> | |
| <section class="panel" id="panel-chat"> | |
| <div class="card chat-card span-2"> | |
| <div class="chat-toggles"> | |
| <button class="toggle" id="tts-toggle" aria-pressed="false" title="speak assistant replies aloud"> | |
| <svg class="icon"><use href="#i-speaker"/></svg> voice replies <span class="toggle-state">off</span> | |
| </button> | |
| <button class="toggle toggle-warn" id="web-toggle" aria-pressed="false" title="lets the assistant search the web"> | |
| <svg class="icon"><use href="#i-globe"/></svg> web search <span class="toggle-state">off</span> | |
| <span class="toggle-note">uses internet (breaks off-grid)</span> | |
| </button> | |
| </div> | |
| <div class="chat-list" id="chat-list"> | |
| <div class="chat-greeting" id="chat-greeting"> | |
| <div class="greet-hello" id="greet-hello">Hello</div> | |
| <div class="greet-sub">your local-first copilot for meals, training and money — ask anything</div> | |
| </div> | |
| </div> | |
| <div class="example-chips" id="example-chips"> | |
| <button class="chip chip-btn">Plan my week: under $80 groceries, high protein, run 3x</button> | |
| <button class="chip chip-btn">What should I cancel to afford a $55/mo climbing gym?</button> | |
| <button class="chip chip-btn">What's a good dinner tonight given my week?</button> | |
| </div> | |
| <div class="ref-row" id="ref-row"> | |
| <span class="ref-label">context refs</span> | |
| <button class="ref-chip" data-ref="kitchen" aria-pressed="false"><svg class="icon"><use href="#i-pan"/></svg> @Kitchen</button> | |
| <button class="ref-chip" data-ref="health" aria-pressed="false"><svg class="icon"><use href="#i-heart"/></svg> @Health</button> | |
| <button class="ref-chip" data-ref="money" aria-pressed="false"><svg class="icon"><use href="#i-coin"/></svg> @Money</button> | |
| </div> | |
| <form class="chat-input-row" id="chat-form"> | |
| <button type="button" class="btn btn-ghost mic-btn" id="mic-btn" title="speak your message" hidden><svg class="icon"><use href="#i-mic"/></svg></button> | |
| <input type="text" id="chat-input" placeholder="type a message..." autocomplete="off"> | |
| <button type="submit" class="btn btn-primary" id="chat-send">Send</button> | |
| </form> | |
| </div> | |
| </section> | |
| <!-- ============ PROFILE ============ --> | |
| <section class="panel" id="panel-profile"> | |
| <div class="card"> | |
| <h2>// your profile</h2> | |
| <form id="profile-form" class="profile-grid"> | |
| <label>first name<input type="text" id="pf-first" placeholder="Alex"></label> | |
| <label>last name<input type="text" id="pf-last" placeholder="Chen"></label> | |
| <label class="span-2">street address<input type="text" id="pf-address" placeholder="123 King St W"></label> | |
| <label>city<input type="text" id="pf-city" placeholder="Toronto"></label> | |
| <label>postal code<input type="text" id="pf-postal" placeholder="M5V 1J2"></label> | |
| <label class="span-2">country<input type="text" id="pf-country" placeholder="Canada"></label> | |
| <label>monthly income ($)<input type="number" id="pf-income" min="0" step="50" placeholder="3200"></label> | |
| <label>weekly budget ($)<input type="number" id="pf-budget" min="0" step="5" placeholder="80"></label> | |
| <label class="span-2">dietary prefs (comma-separated)<input type="text" id="pf-diet" placeholder="high protein, no pork"></label> | |
| <label class="span-2">fitness goal<input type="text" id="pf-fitness" placeholder="run a sub-25 5k"></label> | |
| <div class="row span-2"> | |
| <button type="submit" class="btn btn-primary" id="pf-save-btn">Save profile</button> | |
| <span class="muted" id="pf-saved-state"></span> | |
| </div> | |
| </form> | |
| </div> | |
| <div class="card"> | |
| <h2>// local flyer deals</h2> | |
| <div class="row"> | |
| <button class="btn btn-primary" id="local-deals-btn" disabled title="set your city first">Find local flyer deals</button> | |
| </div> | |
| <p class="online-note"><svg class="icon"><use href="#i-globe"/></svg> uses internet — optional</p> | |
| <div class="result md" id="local-deals-result"> | |
| <p class="empty-hint">save a city in your profile, then look up nearby grocery deals</p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ============ MEMORY ============ --> | |
| <section class="panel" id="panel-memory"> | |
| <div class="card span-2"> | |
| <h2>// memory</h2> | |
| <div class="seg-row" id="mem-segs"> | |
| <button class="seg active" data-seg="short">Short-term</button> | |
| <button class="seg" data-seg="long">Long-term</button> | |
| </div> | |
| <div id="mem-short"> | |
| <div class="row"> | |
| <label>section | |
| <select id="mem-section"> | |
| <option value="meals">meals</option> | |
| <option value="workouts">workouts</option> | |
| <option value="calendar">calendar</option> | |
| <option value="subscriptions">subscriptions</option> | |
| <option value="monthly_payments">monthly payments</option> | |
| </select> | |
| </label> | |
| <button class="btn" id="mem-add-row"><svg class="icon"><use href="#i-plus"/></svg> add row</button> | |
| <button class="btn btn-primary" id="mem-save-btn"><svg class="icon"><use href="#i-check"/></svg> Save section</button> | |
| </div> | |
| <div class="mem-rows" id="mem-rows"></div> | |
| <p class="empty-hint" id="mem-short-hint"></p> | |
| </div> | |
| <div id="mem-long" hidden> | |
| <div class="notes-list" id="notes-list"><p class="empty-hint">loading notes...</p></div> | |
| </div> | |
| </div> | |
| <div class="card span-2 danger-card"> | |
| <h2>// danger zone</h2> | |
| <div class="row"> | |
| <button class="btn btn-warn reset-btn">Reset data</button> | |
| <span class="muted">wipes meals, workouts, calendar, finances and notes back to the seed across the whole app</span> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- ============ SETTINGS ============ --> | |
| <section class="panel" id="panel-settings"> | |
| <div class="card"> | |
| <h2>// defaults</h2> | |
| <div class="settings-rows"> | |
| <button class="toggle" id="set-tts" aria-pressed="false"><svg class="icon"><use href="#i-speaker"/></svg> voice replies on by default <span class="toggle-state">off</span></button> | |
| <button class="toggle toggle-warn" id="set-web" aria-pressed="false"><svg class="icon"><use href="#i-globe"/></svg> web search on by default <span class="toggle-state">off</span></button> | |
| </div> | |
| <p class="empty-hint">saved in this browser (localStorage) and applied to the chat toggles on load</p> | |
| <div class="row" style="margin-top:14px"> | |
| <button class="btn btn-warn reset-btn">Reset data</button> | |
| </div> | |
| </div> | |
| <div class="card"> | |
| <h2>// about</h2> | |
| <p><strong>LifeOS</strong> is a local-first personal assistant: meals, training, money and goals in one place, with nothing leaving your machine.</p> | |
| <p class="muted">100% local inference — <span class="mono-inline">Nemotron-3-Nano-4B</span> running on <span class="mono-inline">llama.cpp</span>, plus a small on-disk RAG memory. Web search is strictly opt-in.</p> | |
| <div class="chips" style="margin-top:10px"> | |
| <span class="chip">Off the Grid</span> | |
| <span class="chip">Tiny Titan (4B)</span> | |
| <span class="chip">llama.cpp</span> | |
| <span class="chip">Off-Brand</span> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <footer> | |
| <p class="muted">built small for the HF Build Small hackathon</p> | |
| </footer> | |
| </div><!-- /app-main --> | |
| </div><!-- /app --> | |
| <div class="toast-stack" id="toast-stack"></div> | |
| <!-- ============ CALENDAR DAY VIEW (modal) ============ --> | |
| <div class="modal-overlay" id="cal-modal" hidden> | |
| <div class="modal cal-modal" role="dialog" aria-modal="true" aria-label="Schedule"> | |
| <div class="modal-head"> | |
| <h2 id="cal-modal-title">// schedule</h2> | |
| <button class="btn btn-ghost" id="cal-modal-close" title="close"><svg class="icon"><use href="#i-x"/></svg></button> | |
| </div> | |
| <div class="cal-grid" id="cal-grid"></div> | |
| </div> | |
| </div> | |
| <script src="/static/app.js"></script> | |
| </body> | |
| </html> | |