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
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.