1 item left
All | Active | Completed

Source code

{{#partial post add}}
  {{#param text maxlength=100}}
  {{#let current_total = COUNT(*) from todos}}
  {{#if :current_total < 20}}
    {{#insert into todos(text) values (:text)}}
  {{/if}}
{{/partial}}

{{#partial post :id/toggle}}
  {{#update todos set completed = 1 - completed WHERE id = :id}}
{{/partial}}

{{#partial patch :id}}
  {{#param text maxlength=100}}
  {{#update todos set text = :text WHERE id = :id}}
{{/partial}}

{{#partial post toggle_all}}
  {{#let active_count = COUNT(*) from todos WHERE completed = 0}}
  {{#update todos set completed = IIF(:active_count = 0, 0, 1)}}
{{/partial}}

{{#partial delete :id}}
  {{#delete from todos WHERE id = :id}}
{{/partial}}

{{#partial post clear_completed}}
  {{#delete from todos WHERE completed = 1}}
{{/partial}}

{{#param filter default='all' pattern="^(all|active|completed)$"}}
{{!-- Ensure the table exists (harmless if already created) --}}
{{#create table if not exists todos (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    text TEXT NOT NULL,
    completed INTEGER DEFAULT 0 CHECK(completed IN (0,1))
)}}

{{#let active_count    = COUNT(*) from todos WHERE completed = 0}}
{{#let completed_count = COUNT(*) from todos WHERE completed = 1}}
{{#let total_count     = COUNT(*) from todos}}
{{#let all_complete    = (:active_count == 0 AND :total_count > 0)}}

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>TODOMVC</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 2rem; }
    ul { list-style: none; padding: 0; }
    li { margin-bottom: 0.5rem; }
    li.completed label { text-decoration: line-through; color: #777; }
    .filters { margin-top: 1rem; }
    .filters a { text-decoration: none; margin: 0 0.5rem; }
    .filters a.selected { font-weight: bold; }
    .back-link {
      margin-bottom: 2rem;
      padding-bottom: 1rem;
      border-bottom: 1px solid #ccc;
      font-size: 1.8rem;
      font-weight: bold;
    }
    .back-link a {
      color: #be5028;
      text-decoration: none;
    }
    .code-column {
      overflow-x: auto;
    }
    
    /* Mobile styles */
    @media (max-width: 768px) {
      body { 
        margin: 0; 
        padding: 0;
      }
      .back-link {
        margin-bottom: 1rem;
      }
    }
  </style>
</head>
<body>
  <div class="back-link"><a href="/">&larr; PageQL</a> <span style="font-size: 1.8rem; font-weight: bold; color:rgb(40, 170, 190);">TODOMVC</span></div>
  <h1>TODOMVC</h1>
  <div>
      {{#if :total_count < 20}}
      <input name="text" placeholder="What needs to be done?" maxlength="100" autofocus autocomplete="off"
        hx-post="/todos/add" hx-trigger="keyup[key=='Enter']" hx-on:htmx:after-on-load="this.value=''">
      {{/if}}
      <ul>
        {{#from todos
          WHERE (:filter == 'all')
                OR (:filter == 'active'    AND completed = 0)
                OR (:filter == 'completed' AND completed = 1)
                ORDER BY id}}
            <li {{#if completed}}class="completed"{{/if}}>
                <input hx-post="/todos/{{id}}/toggle" class="toggle" type="checkbox" {{#if completed}}checked{{/if}}>
                <label
                  contenteditable="false"
                  onclick="this.contentEditable=true;this.focus();"
                  onblur="this.contentEditable=false;"
                  onkeydown="if(event.key==='Enter'){event.preventDefault();this.blur();}"
                  oninput="if(this.innerText.length>100){this.innerText=this.innerText.slice(0,100);}"
                  hx-patch="/todos/{{id}}"
                  hx-trigger="blur"
                  hx-vals='js:{text: event.target.innerText.slice(0, 100)}'
                  hx-swap="none"
                >{{text}}</label>
                
                <button hx-delete="/todos/{{id}}" class="destroy"
                  style="cursor:pointer; background:none; border:none; color:#ac4a1a;"></button>
            </li>
        {{/from}}
      </ul>
      <input id="toggle-all" class="toggle-all" type="checkbox"
        {{#if all_complete}}checked{{/if}} hx-post="/todos/toggle_all">
      <label for="toggle-all">Mark all as complete</label>
<span class="todo-count">
  <strong>{{active_count}}</strong>
  item{{#if :active_count != 1}}s{{/if}} left
</span>

{{#if :completed_count > 0}}
  <button class="clear-completed" hx-post="/todos/clear_completed">Clear completed</button>
{{/if}}
<div class="filters">
  <a {{#if :filter == 'all'}}class="selected"{{/if}} href="/todos?filter=all">All</a> |
  <a {{#if :filter == 'active'}}class="selected"{{/if}} href="/todos?filter=active">Active</a> |
  <a {{#if :filter == 'completed'}}class="selected"{{/if}} href="/todos?filter=completed">Completed</a>
</div>
</div>
</body>
</html>