Write a code offline la

Write a code for offline app for app which calculate all daily expenses it will write and also make to do list

Asking for Preferences

What platform should this offline app be built for?

Building a fully offline Web app (single HTML file) with:
  • Daily Expense Tracker - add, categorize, and view total expenses
  • To-Do List - add, complete, and delete tasks
  • All data stored in localStorage (persists offline, no server needed)
Running Command

mkdir -p /home/daytona/workspace/daily-app && ls /home/daytona/workspace/daily-app

Writing File

~/daily-app/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Daily Planner - Expenses & To-Do</title>
  <style>
    /* ===== RESET & BASE ===== */
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

    :root {
      --bg: #0f172a;
      --surface: #1e293b;
      --surface2: #273449;
      --border: #334155;
      --accent: #6366f1;
      --accent-hover: #4f46e5;
      --green: #22c55e;
      --red: #ef4444;
      --yellow: #f59e0b;
      --text: #f1f5f9;
      --text2: #94a3b8;
      --radius: 12px;
      --shadow: 0 4px 24px rgba(0,0,0,0.4);
    }

    body {
      font-family: 'Segoe UI', system-ui, sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      padding: 20px 16px 60px;
    }

    h1.app-title {
      text-align: center;
      font-size: 1.6rem;
      font-weight: 700;
      margin-bottom: 6px;
      background: linear-gradient(135deg, var(--accent), #a78bfa);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    .app-subtitle {
      text-align: center;
      color: var(--text2);
      font-size: 0.85rem;
      margin-bottom: 28px;
    }

    /* ===== TABS ===== */
    .tabs {
      display: flex;
      gap: 8px;
      justify-content: center;
      margin-bottom: 24px;
    }
    .tab-btn {
      padding: 9px 28px;
      border-radius: 40px;
      border: 2px solid var(--border);
      background: var(--surface);
      color: var(--text2);
      font-size: 0.92rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
    }
    .tab-btn.active {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
    }
    .tab-btn:hover:not(.active) { border-color: var(--accent); color: var(--text); }

    /* ===== PANELS ===== */
    .panel { display: none; max-width: 680px; margin: 0 auto; }
    .panel.active { display: block; }

    /* ===== CARD ===== */
    .card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 20px;
      margin-bottom: 18px;
      box-shadow: var(--shadow);
    }
    .card h2 {
      font-size: 1rem;
      font-weight: 600;
      color: var(--text2);
      text-transform: uppercase;
      letter-spacing: 0.05em;
      margin-bottom: 14px;
    }

    /* ===== INPUTS ===== */
    .input-row {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }
    input[type="text"], input[type="number"], input[type="date"], select {
      flex: 1 1 120px;
      padding: 10px 14px;
      border-radius: 8px;
      border: 1.5px solid var(--border);
      background: var(--surface2);
      color: var(--text);
      font-size: 0.93rem;
      outline: none;
      transition: border 0.2s;
    }
    input:focus, select:focus { border-color: var(--accent); }
    input::placeholder { color: var(--text2); }

    .btn {
      padding: 10px 20px;
      border-radius: 8px;
      border: none;
      font-size: 0.92rem;
      font-weight: 600;
      cursor: pointer;
      transition: background 0.2s, transform 0.1s;
    }
    .btn:active { transform: scale(0.97); }
    .btn-primary { background: var(--accent); color: #fff; }
    .btn-primary:hover { background: var(--accent-hover); }
    .btn-danger  { background: transparent; color: var(--red); border: 1.5px solid var(--red); padding: 5px 10px; font-size: 0.8rem; border-radius: 6px; }
    .btn-danger:hover { background: var(--red); color: #fff; }
    .btn-sm-green { background: transparent; color: var(--green); border: 1.5px solid var(--green); padding: 5px 10px; font-size: 0.8rem; border-radius: 6px; }
    .btn-sm-green:hover { background: var(--green); color: #fff; }

    /* ===== DATE FILTER ===== */
    .date-filter {
      display: flex;
      align-items: center;
      gap: 10px;
      flex-wrap: wrap;
      margin-bottom: 4px;
    }
    .date-filter label { color: var(--text2); font-size: 0.85rem; }

    /* ===== SUMMARY STRIP ===== */
    .summary-strip {
      display: flex;
      gap: 12px;
      flex-wrap: wrap;
      margin-bottom: 18px;
    }
    .summary-box {
      flex: 1 1 140px;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius);
      padding: 16px;
      text-align: center;
    }
    .summary-box .label { font-size: 0.75rem; color: var(--text2); text-transform: uppercase; letter-spacing: 0.05em; }
    .summary-box .value { font-size: 1.5rem; font-weight: 700; margin-top: 4px; }
    .value.total   { color: var(--accent); }
    .value.today   { color: var(--yellow); }
    .value.count   { color: var(--green); }

    /* ===== EXPENSE LIST ===== */
    .expense-list { list-style: none; }
    .expense-list li {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 11px 0;
      border-bottom: 1px solid var(--border);
      font-size: 0.9rem;
    }
    .expense-list li:last-child { border-bottom: none; }
    .exp-icon {
      width: 34px; height: 34px;
      border-radius: 50%;
      display: flex; align-items: center; justify-content: center;
      font-size: 1.1rem;
      flex-shrink: 0;
    }
    .exp-info { flex: 1; }
    .exp-name { font-weight: 600; }
    .exp-meta { font-size: 0.78rem; color: var(--text2); margin-top: 2px; }
    .exp-amount { font-weight: 700; font-size: 1rem; white-space: nowrap; }
    .exp-amount.neg { color: var(--red); }
    .exp-amount.pos { color: var(--green); }

    .empty-msg {
      text-align: center;
      color: var(--text2);
      padding: 28px 0;
      font-size: 0.9rem;
    }

    /* ===== CATEGORY BADGE ===== */
    .badge {
      display: inline-block;
      padding: 2px 8px;
      border-radius: 20px;
      font-size: 0.72rem;
      font-weight: 600;
    }

    /* ===== TODO LIST ===== */
    .todo-list { list-style: none; }
    .todo-item {
      display: flex;
      align-items: flex-start;
      gap: 12px;
      padding: 12px 0;
      border-bottom: 1px solid var(--border);
    }
    .todo-item:last-child { border-bottom: none; }
    .todo-check {
      width: 20px; height: 20px;
      border-radius: 50%;
      border: 2px solid var(--border);
      flex-shrink: 0;
      cursor: pointer;
      margin-top: 2px;
      display: flex; align-items: center; justify-content: center;
      transition: all 0.2s;
    }
    .todo-check.done { background: var(--green); border-color: var(--green); }
    .todo-check.done::after { content: 'โœ“'; color: #fff; font-size: 0.7rem; font-weight: 700; }
    .todo-text-wrap { flex: 1; }
    .todo-text { font-size: 0.93rem; transition: all 0.2s; }
    .todo-text.done { text-decoration: line-through; color: var(--text2); }
    .todo-priority { font-size: 0.72rem; margin-top: 3px; }
    .todo-date-label { font-size: 0.72rem; color: var(--text2); margin-top: 2px; }
    .todo-actions { display: flex; gap: 6px; }

    /* Priority colors */
    .p-high   { color: var(--red); }
    .p-medium { color: var(--yellow); }
    .p-low    { color: var(--green); }

    /* ===== FILTER ROW ===== */
    .filter-row {
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
      margin-bottom: 4px;
    }
    .filter-btn {
      padding: 5px 14px;
      border-radius: 20px;
      border: 1.5px solid var(--border);
      background: transparent;
      color: var(--text2);
      font-size: 0.8rem;
      font-weight: 600;
      cursor: pointer;
      transition: all 0.2s;
    }
    .filter-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

    /* ===== PROGRESS BAR ===== */
    .progress-bar-wrap { margin-top: 8px; }
    .progress-label { display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--text2); margin-bottom: 4px; }
    .progress-bar { height: 6px; background: var(--border); border-radius: 4px; overflow: hidden; }
    .progress-fill { height: 100%; background: var(--accent); border-radius: 4px; transition: width 0.4s; }

    /* ===== CHART AREA ===== */
    .chart-wrap { margin-top: 4px; }
    .cat-row {
      display: flex;
      align-items: center;
      gap: 10px;
      margin-bottom: 10px;
    }
    .cat-label { width: 80px; font-size: 0.8rem; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .cat-bar-wrap { flex: 1; height: 10px; background: var(--border); border-radius: 5px; overflow: hidden; }
    .cat-bar { height: 100%; border-radius: 5px; transition: width 0.4s; }
    .cat-val { font-size: 0.8rem; font-weight: 600; width: 60px; text-align: right; }

    /* ===== CLEAR ALL BUTTON ===== */
    .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
    .section-header h2 { margin-bottom: 0; }

    /* ===== TOAST ===== */
    #toast {
      position: fixed; bottom: 28px; left: 50%; transform: translateX(-50%) translateY(80px);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 40px;
      padding: 10px 24px;
      font-size: 0.88rem;
      color: var(--text);
      box-shadow: var(--shadow);
      transition: transform 0.3s ease;
      z-index: 999;
      white-space: nowrap;
    }
    #toast.show { transform: translateX(-50%) translateY(0); }

    /* ===== SCROLLABLE LIST ===== */
    .scroll-list { max-height: 340px; overflow-y: auto; padding-right: 4px; }
    .scroll-list::-webkit-scrollbar { width: 4px; }
    .scroll-list::-webkit-scrollbar-track { background: transparent; }
    .scroll-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
  </style>
</head>
<body>

<h1 class="app-title">๐Ÿ“‹ Daily Planner</h1>
<p class="app-subtitle">Offline - All data saved in your browser</p>

<!-- TABS -->
<div class="tabs">
  <button class="tab-btn active" onclick="switchTab('expenses')">๐Ÿ’ธ Expenses</button>
  <button class="tab-btn"        onclick="switchTab('todo')">โœ… To-Do List</button>
</div>

<!-- ==================== EXPENSES PANEL ==================== -->
<div class="panel active" id="panel-expenses">

  <!-- Summary -->
  <div class="summary-strip">
    <div class="summary-box">
      <div class="label">Today</div>
      <div class="value today" id="sum-today">โ‚น0</div>
    </div>
    <div class="summary-box">
      <div class="label">This Month</div>
      <div class="value total" id="sum-month">โ‚น0</div>
    </div>
    <div class="summary-box">
      <div class="label">All Time</div>
      <div class="value count" id="sum-all">โ‚น0</div>
    </div>
  </div>

  <!-- Add Expense -->
  <div class="card">
    <h2>Add Expense</h2>
    <div class="input-row" style="margin-bottom:10px">
      <input type="text" id="exp-name" placeholder="Description (e.g. Lunch)" />
      <input type="number" id="exp-amount" placeholder="Amount (โ‚น)" min="0" step="0.01" />
    </div>
    <div class="input-row">
      <select id="exp-category">
        <option value="food">๐Ÿ” Food</option>
        <option value="transport">๐ŸšŒ Transport</option>
        <option value="shopping">๐Ÿ› Shopping</option>
        <option value="health">๐Ÿ’Š Health</option>
        <option value="bills">๐Ÿ’ก Bills</option>
        <option value="entertainment">๐ŸŽฌ Entertainment</option>
        <option value="education">๐Ÿ“š Education</option>
        <option value="other">๐Ÿ“ฆ Other</option>
      </select>
      <input type="date" id="exp-date" />
      <button class="btn btn-primary" onclick="addExpense()">+ Add</button>
    </div>
  </div>

  <!-- Category Chart -->
  <div class="card" id="chart-card">
    <h2>Spending by Category</h2>
    <div class="chart-wrap" id="cat-chart"></div>
  </div>

  <!-- Expense List -->
  <div class="card">
    <div class="section-header">
      <h2>Expense Log</h2>
      <button class="btn btn-danger" onclick="clearAllExpenses()">Clear All</button>
    </div>
    <div class="date-filter">
      <label>Filter:</label>
      <input type="date" id="filter-from" onchange="renderExpenses()" />
      <span style="color:var(--text2)">to</span>
      <input type="date" id="filter-to" onchange="renderExpenses()" />
      <button class="btn" style="padding:6px 12px;font-size:0.8rem;background:var(--surface2);color:var(--text2)" onclick="clearFilter()">Reset</button>
    </div>
    <div class="scroll-list">
      <ul class="expense-list" id="expense-list"></ul>
    </div>
  </div>

</div>

<!-- ==================== TODO PANEL ==================== -->
<div class="panel" id="panel-todo">

  <!-- Add Todo -->
  <div class="card">
    <h2>Add Task</h2>
    <div class="input-row" style="margin-bottom:10px">
      <input type="text" id="todo-text" placeholder="Task description..." />
    </div>
    <div class="input-row">
      <select id="todo-priority">
        <option value="high">๐Ÿ”ด High Priority</option>
        <option value="medium" selected>๐ŸŸก Medium Priority</option>
        <option value="low">๐ŸŸข Low Priority</option>
      </select>
      <input type="date" id="todo-due" title="Due date (optional)" />
      <button class="btn btn-primary" onclick="addTodo()">+ Add Task</button>
    </div>
  </div>

  <!-- Progress -->
  <div class="card">
    <h2>Progress</h2>
    <div id="todo-progress-text" style="font-size:0.9rem;color:var(--text2)">0 of 0 tasks completed</div>
    <div class="progress-bar-wrap">
      <div class="progress-bar"><div class="progress-fill" id="todo-progress-fill" style="width:0%"></div></div>
    </div>
  </div>

  <!-- Todo List -->
  <div class="card">
    <div class="section-header">
      <h2>Tasks</h2>
      <button class="btn btn-danger" onclick="clearDoneTodos()">Clear Done</button>
    </div>
    <div class="filter-row">
      <button class="filter-btn active" onclick="setTodoFilter('all', this)">All</button>
      <button class="filter-btn" onclick="setTodoFilter('active', this)">Active</button>
      <button class="filter-btn" onclick="setTodoFilter('done', this)">Done</button>
      <button class="filter-btn" onclick="setTodoFilter('high', this)">๐Ÿ”ด High</button>
    </div>
    <div class="scroll-list" style="margin-top:8px">
      <ul class="todo-list" id="todo-list"></ul>
    </div>
  </div>

</div>

<!-- Toast -->
<div id="toast"></div>

<script>
// ============================================================
//  UTILITIES
// ============================================================
const $ = id => document.getElementById(id);

function toast(msg, color = '#6366f1') {
  const t = $('toast');
  t.textContent = msg;
  t.style.borderColor = color;
  t.classList.add('show');
  setTimeout(() => t.classList.remove('show'), 2600);
}

function fmtMoney(n) {
  return 'โ‚น' + Number(n).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function todayStr() {
  return new Date().toISOString().split('T')[0];
}

function formatDate(str) {
  if (!str) return '';
  const d = new Date(str + 'T00:00:00');
  return d.toLocaleDateString('en-IN', { day: 'numeric', month: 'short', year: 'numeric' });
}

// ============================================================
//  TABS
// ============================================================
function switchTab(tab) {
  document.querySelectorAll('.tab-btn').forEach((b, i) => b.classList.toggle('active', i === (tab === 'expenses' ? 0 : 1)));
  $('panel-expenses').classList.toggle('active', tab === 'expenses');
  $('panel-todo').classList.toggle('active', tab === 'todo');
}

// ============================================================
//  CATEGORY CONFIG
// ============================================================
const CATS = {
  food:          { icon: '๐Ÿ”', color: '#f59e0b', bg: '#78350f22' },
  transport:     { icon: '๐ŸšŒ', color: '#3b82f6', bg: '#1e3a8a22' },
  shopping:      { icon: '๐Ÿ›',  color: '#a855f7', bg: '#4c1d9522' },
  health:        { icon: '๐Ÿ’Š', color: '#22c55e', bg: '#14532d22' },
  bills:         { icon: '๐Ÿ’ก', color: '#ef4444', bg: '#7f1d1d22' },
  entertainment: { icon: '๐ŸŽฌ', color: '#ec4899', bg: '#83185322' },
  education:     { icon: '๐Ÿ“š', color: '#06b6d4', bg: '#16424422' },
  other:         { icon: '๐Ÿ“ฆ', color: '#94a3b8', bg: '#33415522' },
};

// ============================================================
//  EXPENSES
// ============================================================
let expenses = JSON.parse(localStorage.getItem('dp_expenses') || '[]');

function saveExpenses() { localStorage.setItem('dp_expenses', JSON.stringify(expenses)); }

function addExpense() {
  const name   = $('exp-name').value.trim();
  const amount = parseFloat($('exp-amount').value);
  const cat    = $('exp-category').value;
  const date   = $('exp-date').value || todayStr();

  if (!name)         return toast('Enter a description', '#ef4444');
  if (!amount || amount <= 0) return toast('Enter a valid amount', '#ef4444');

  expenses.unshift({ id: Date.now(), name, amount, cat, date });
  saveExpenses();
  $('exp-name').value = '';
  $('exp-amount').value = '';
  renderExpenses();
  toast('Expense added!', '#22c55e');
}

function deleteExpense(id) {
  expenses = expenses.filter(e => e.id !== id);
  saveExpenses();
  renderExpenses();
  toast('Deleted', '#ef4444');
}

function clearAllExpenses() {
  if (!confirm('Delete ALL expenses?')) return;
  expenses = [];
  saveExpenses();
  renderExpenses();
  toast('All expenses cleared');
}

function clearFilter() {
  $('filter-from').value = '';
  $('filter-to').value = '';
  renderExpenses();
}

function renderExpenses() {
  const from = $('filter-from').value;
  const to   = $('filter-to').value;

  const filtered = expenses.filter(e => {
    if (from && e.date < from) return false;
    if (to   && e.date > to)   return false;
    return true;
  });

  // Summary
  const today = todayStr();
  const thisMonth = today.slice(0, 7);
  const todaySum  = expenses.filter(e => e.date === today).reduce((s, e) => s + e.amount, 0);
  const monthSum  = expenses.filter(e => e.date.startsWith(thisMonth)).reduce((s, e) => s + e.amount, 0);
  const allSum    = expenses.reduce((s, e) => s + e.amount, 0);
  $('sum-today').textContent = fmtMoney(todaySum);
  $('sum-month').textContent = fmtMoney(monthSum);
  $('sum-all').textContent   = fmtMoney(allSum);

  // List
  const ul = $('expense-list');
  if (filtered.length === 0) {
    ul.innerHTML = '<li><div class="empty-msg">No expenses found.</div></li>';
  } else {
    ul.innerHTML = filtered.map(e => {
      const c = CATS[e.cat] || CATS.other;
      return `
        <li>
          <div class="exp-icon" style="background:${c.bg};">${c.icon}</div>
          <div class="exp-info">
            <div class="exp-name">${escHtml(e.name)}</div>
            <div class="exp-meta">
              <span class="badge" style="background:${c.bg};color:${c.color}">${e.cat}</span>
              &nbsp;${formatDate(e.date)}
            </div>
          </div>
          <div class="exp-amount neg">${fmtMoney(e.amount)}</div>
          <button class="btn btn-danger" onclick="deleteExpense(${e.id})">โœ•</button>
        </li>`;
    }).join('');
  }

  // Category chart
  renderCatChart(expenses);
}

function renderCatChart(data) {
  const totals = {};
  data.forEach(e => { totals[e.cat] = (totals[e.cat] || 0) + e.amount; });
  const grand = Object.values(totals).reduce((s, v) => s + v, 0);
  const sorted = Object.entries(totals).sort((a, b) => b[1] - a[1]);

  if (sorted.length === 0) {
    $('cat-chart').innerHTML = '<div class="empty-msg">No data yet.</div>';
    return;
  }

  $('cat-chart').innerHTML = sorted.map(([cat, val]) => {
    const c = CATS[cat] || CATS.other;
    const pct = grand > 0 ? (val / grand * 100).toFixed(1) : 0;
    return `
      <div class="cat-row">
        <div class="cat-label">${c.icon} ${cat}</div>
        <div class="cat-bar-wrap">
          <div class="cat-bar" style="width:${pct}%;background:${c.color}"></div>
        </div>
        <div class="cat-val" style="color:${c.color}">${fmtMoney(val)}</div>
      </div>`;
  }).join('');
}

// ============================================================
//  TO-DO LIST
// ============================================================
let todos = JSON.parse(localStorage.getItem('dp_todos') || '[]');
let todoFilter = 'all';

function saveTodos() { localStorage.setItem('dp_todos', JSON.stringify(todos)); }

function addTodo() {
  const text = $('todo-text').value.trim();
  const priority = $('todo-priority').value;
  const due = $('todo-due').value;

  if (!text) return toast('Enter a task description', '#ef4444');

  todos.unshift({ id: Date.now(), text, priority, due, done: false, createdAt: todayStr() });
  saveTodos();
  $('todo-text').value = '';
  $('todo-due').value = '';
  renderTodos();
  toast('Task added!', '#22c55e');
}

function toggleTodo(id) {
  const t = todos.find(t => t.id === id);
  if (t) { t.done = !t.done; saveTodos(); renderTodos(); }
}

function deleteTodo(id) {
  todos = todos.filter(t => t.id !== id);
  saveTodos();
  renderTodos();
  toast('Task removed', '#ef4444');
}

function clearDoneTodos() {
  const count = todos.filter(t => t.done).length;
  if (count === 0) return toast('No completed tasks to clear');
  todos = todos.filter(t => !t.done);
  saveTodos();
  renderTodos();
  toast(`${count} task(s) cleared`);
}

function setTodoFilter(f, btn) {
  todoFilter = f;
  document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
  btn.classList.add('active');
  renderTodos();
}

function renderTodos() {
  // Progress
  const total = todos.length;
  const done  = todos.filter(t => t.done).length;
  $('todo-progress-text').textContent = `${done} of ${total} tasks completed`;
  const pct = total > 0 ? (done / total * 100) : 0;
  $('todo-progress-fill').style.width = pct + '%';

  // Filter
  const filtered = todos.filter(t => {
    if (todoFilter === 'active') return !t.done;
    if (todoFilter === 'done')   return t.done;
    if (todoFilter === 'high')   return t.priority === 'high' && !t.done;
    return true;
  });

  const PRIORITY_LABELS = { high: '๐Ÿ”ด High', medium: '๐ŸŸก Medium', low: '๐ŸŸข Low' };
  const PRIORITY_CLASS  = { high: 'p-high', medium: 'p-medium', low: 'p-low' };

  const ul = $('todo-list');
  if (filtered.length === 0) {
    ul.innerHTML = '<li><div class="empty-msg">No tasks here.</div></li>';
    return;
  }

  // Sort: undone high first, then medium, low; done at bottom
  const order = { high: 0, medium: 1, low: 2 };
  const sorted = [...filtered].sort((a, b) => {
    if (a.done !== b.done) return a.done ? 1 : -1;
    return order[a.priority] - order[b.priority];
  });

  ul.innerHTML = sorted.map(t => {
    const dueLabel = t.due ? `<div class="todo-date-label">๐Ÿ“… Due: ${formatDate(t.due)}</div>` : '';
    const overdue  = t.due && t.due < todayStr() && !t.done ? '<span style="color:var(--red);font-size:0.72rem"> โš  Overdue</span>' : '';
    return `
      <li class="todo-item">
        <div class="todo-check ${t.done ? 'done' : ''}" onclick="toggleTodo(${t.id})"></div>
        <div class="todo-text-wrap">
          <div class="todo-text ${t.done ? 'done' : ''}">${escHtml(t.text)}${overdue}</div>
          <div class="todo-priority ${PRIORITY_CLASS[t.priority]}">${PRIORITY_LABELS[t.priority]}</div>
          ${dueLabel}
        </div>
        <div class="todo-actions">
          <button class="btn btn-danger" onclick="deleteTodo(${t.id})">โœ•</button>
        </div>
      </li>`;
  }).join('');
}

// ============================================================
//  UTILS
// ============================================================
function escHtml(s) {
  return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}

// Allow Enter key to submit
$('exp-name').addEventListener('keydown', e => { if (e.key === 'Enter') addExpense(); });
$('exp-amount').addEventListener('keydown', e => { if (e.key === 'Enter') addExpense(); });
$('todo-text').addEventListener('keydown', e => { if (e.key === 'Enter') addTodo(); });

// ============================================================
//  INIT
// ============================================================
$('exp-date').value = todayStr();
$('todo-due').value = '';
renderExpenses();
renderTodos();
</script>
</body>
</html>
Running Command

ls -lh /home/daytona/workspace/daily-app/index.html

Your offline app is ready! Download it here:

index

HTML File ยท HTML

Just save this single file anywhere on your device and open it in any browser - no internet required, ever.

What's Inside

๐Ÿ’ธ Expense Tracker

FeatureDetails
Add expensesName, amount, category, date
8 categoriesFood, Transport, Shopping, Health, Bills, Entertainment, Education, Other
Summary cardsToday's spend, This Month, All Time
Category chartVisual bar chart showing spending breakdown
Date filterFilter expense log by date range
Delete / Clear AllRemove individual or all expenses

โœ… To-Do List

FeatureDetails
Add tasksDescription, priority level, due date
3 prioritiesHigh (๐Ÿ”ด), Medium (๐ŸŸก), Low (๐ŸŸข)
Overdue warningTasks past due date get a red โš  tag
Progress barShows how many tasks completed
Filter tabsAll / Active / Done / High Priority
Toggle doneClick the circle to mark complete
Clear DoneRemove all completed tasks at once

๐Ÿ”’ Offline & Private

  • Zero internet needed - works fully offline
  • localStorage - all data stays in your browser permanently
  • No account, no server, no tracking
  • Press Enter to quickly add expenses or tasks
This is a shared conversation. Sign in to Orris to start your own chat.