Show HN: Embed reactive (live) SQL into HTML

7 hours ago 2
{{#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>
Read Entire Article