Commits
When editing the room title, roomTitleEl.style.display was set to
'none'
but never restored when the edit finished. This caused the title to
remain
hidden until the next WebSocket message triggered renderTitle().
Closes: 4eba213
Assisted-by: Claude Sonnet 4.5 via Crush
- replace disconnect alert with automatic retry/backoff while UI stays
disabled
- pause keyboard shortcuts when the list is offline so input waits for
reconnection
Closes: bug-fab8338
Assisted-by: GPT-5 via Crush
Implements: 73ce387
Assisted-by: Claude Sonnet 4.5 via Crush
Room expiry now tracks ALL activity, not just creation timestamps:
- Voting and unvoting
- Resetting votes
- Editing items
- Deleting items
- Setting room title
- Breaking ties
Added last_activity column to rooms table (schema v3→v4). All
activity operations now update this timestamp. deleteInactiveRooms
simplified to use last_activity directly instead of complex joins.
Assisted-by: Claude Sonnet 4.5 via Crush
Add CLI arguments to configure server bind address and port:
- --host : Bind to specific interface (default: localhost)
- --port : Listen on specific port (default: 8294)
Update documentation in README.md and AGENTS.md to reflect new options.
Use onListen callback to suppress Deno's default "Listening
on..." message.
Assisted-by: Claude Sonnet 4.5 via Crush
- Reflect ES module split (app.js → render.js/ui.js)
- Document tie-breaker protocol (break_tie/tie_broken)
- Note daily inactive-room cleanup
- Update file structure, noscript fallback, and protocol/testing details
Assisted-by: GPT-5 via Crush
Adds deleteInactiveRooms() function to db.ts that finds and removes
rooms
where the most recent item or vote activity is older than 30 days.
The cleanup runs automatically on server startup and then every 24 hours
via setInterval. Foreign key CASCADE constraints handle automatic
deletion
of associated items and votes.
Implements: b3d438a
Assisted-by: Claude Sonnet 4.5 via Crush
The refactor in HEAD^ split app.js into modules with import statements,
but the script tag wasn't updated to support module syntax, causing
'Uncaught SyntaxError: import declarations may only appear at top
level
of a module' and breaking deep links and room joining.
Assisted-by: Claude Sonnet 4.5 via Crush
Users without JavaScript enabled now see a helpful message explaining
that Sift requires JavaScript to function, with an explanation about
WebSocket requirements.
Implements: 6a9a677
Assisted-by: Claude Sonnet 4.5 via Crush
Extract database operations to db.ts, rendering utilities to render.js,
and UI handlers to ui.js. This keeps files under 500 lines and improves
maintainability.
Also fix all lint issues:
- Replace 'as any' with proper message type interfaces
- Remove unused isReady variable
- Wrap switch case blocks with braces
- Replace window with globalThis
Implements: 09cd703
Assisted-by: Claude Sonnet 4.5 via Crush
Previously, the selected item would scroll into view on every render,
including when receiving updates from other clients. Now scrolling only
occurs when the user performs a local action (voting, deleting, or
navigating with keyboard).
Assisted-by: Claude Sonnet 4.5 via Crush
When multiple items share the top score, a 'Break the tie'
button appears
above the list. Clicking it sends a break_tie message to the server,
which
randomly selects one of the tied items and broadcasts the winner to all
clients. The result is displayed next to the button and clears when
votes
change or the tie is resolved.
Server uses crypto.getRandomValues for uniform random selection. No
database
writes are performed; the winner is ephemeral and only shown until state
changes.
Implements: 95021dc
Assisted-by: Claude Sonnet 4.5 via Crush
Implements: 55d3982
Assisted-by: Claude Sonnet 4.5 via Crush
Introduces a comprehensive OKLCH-based color system via palette.css,
replacing the previous RGB/hex approach with perceptually uniform
colors.
Adds visual feedback for top-voted items with checkmark indicator and
3D tactile button press effects. Room URLs now auto-update to include
the room code for easier sharing.
The new palette provides better dark mode support and color consistency
across the interface while maintaining the existing green theme
identity.
Assisted-by: Claude Sonnet 4.5 via Crush
Assisted-by: Claude Sonnet 4.5 via Crush
Copy buttons were using textContent to save/restore button content, but
SVG elements have empty textContent. Changed to innerHTML so the icons
properly restore after showing the checkmark.
Assisted-by: Claude Sonnet 4.5 via Crush
Complete WebSocket-based collaborative list application with:
- Deno backend with SQLite database
- Real-time voting system (upvote, downvote, veto)
- Room-based collaboration with unique IDs
- Automatic item sorting by score
- Client-side keyboard shortcuts (j/k nav, voting, edit, delete)
- Light/dark theme support
- Input validation and security constraints
Assisted-by: Claude Sonnet 4.5 via Crush
Room expiry now tracks ALL activity, not just creation timestamps:
- Voting and unvoting
- Resetting votes
- Editing items
- Deleting items
- Setting room title
- Breaking ties
Added last_activity column to rooms table (schema v3→v4). All
activity operations now update this timestamp. deleteInactiveRooms
simplified to use last_activity directly instead of complex joins.
Assisted-by: Claude Sonnet 4.5 via Crush
Add CLI arguments to configure server bind address and port:
- --host : Bind to specific interface (default: localhost)
- --port : Listen on specific port (default: 8294)
Update documentation in README.md and AGENTS.md to reflect new options.
Use onListen callback to suppress Deno's default "Listening
on..." message.
Assisted-by: Claude Sonnet 4.5 via Crush
Adds deleteInactiveRooms() function to db.ts that finds and removes
rooms
where the most recent item or vote activity is older than 30 days.
The cleanup runs automatically on server startup and then every 24 hours
via setInterval. Foreign key CASCADE constraints handle automatic
deletion
of associated items and votes.
Implements: b3d438a
Assisted-by: Claude Sonnet 4.5 via Crush
The refactor in HEAD^ split app.js into modules with import statements,
but the script tag wasn't updated to support module syntax, causing
'Uncaught SyntaxError: import declarations may only appear at top
level
of a module' and breaking deep links and room joining.
Assisted-by: Claude Sonnet 4.5 via Crush
Extract database operations to db.ts, rendering utilities to render.js,
and UI handlers to ui.js. This keeps files under 500 lines and improves
maintainability.
Also fix all lint issues:
- Replace 'as any' with proper message type interfaces
- Remove unused isReady variable
- Wrap switch case blocks with braces
- Replace window with globalThis
Implements: 09cd703
Assisted-by: Claude Sonnet 4.5 via Crush
When multiple items share the top score, a 'Break the tie'
button appears
above the list. Clicking it sends a break_tie message to the server,
which
randomly selects one of the tied items and broadcasts the winner to all
clients. The result is displayed next to the button and clears when
votes
change or the tie is resolved.
Server uses crypto.getRandomValues for uniform random selection. No
database
writes are performed; the winner is ephemeral and only shown until state
changes.
Implements: 95021dc
Assisted-by: Claude Sonnet 4.5 via Crush
Introduces a comprehensive OKLCH-based color system via palette.css,
replacing the previous RGB/hex approach with perceptually uniform
colors.
Adds visual feedback for top-voted items with checkmark indicator and
3D tactile button press effects. Room URLs now auto-update to include
the room code for easier sharing.
The new palette provides better dark mode support and color consistency
across the interface while maintaining the existing green theme
identity.
Assisted-by: Claude Sonnet 4.5 via Crush
Complete WebSocket-based collaborative list application with:
- Deno backend with SQLite database
- Real-time voting system (upvote, downvote, veto)
- Room-based collaboration with unique IDs
- Automatic item sorting by score
- Client-side keyboard shortcuts (j/k nav, voting, edit, delete)
- Light/dark theme support
- Input validation and security constraints
Assisted-by: Claude Sonnet 4.5 via Crush