Problem
On mobile, the Long-term and Short-term memory tables are nearly unusable. The Content column renders the full text of every memory entry inline with break-all, and memories are often full sentences or paragraphs. This makes every row disproportionately tall — a single memory can take up the whole screen, and with 25 entries per page you have to scroll endlessly past walls of text.
The same problem exists on the Short-term tab.
Current markup
<td class="text-xs break-all">
<span x-show="!editing">{{ m.content }}</span>
</td>
break-all forces word-break at every character, and there is no truncation, max-height, or max-width. On a ~375px mobile screen the content column is maybe 200px wide — a 300-character memory wraps into 10+ lines.
Proposed solution
A layered approach that works from the simplest fix to the richest interaction:
Layer 1 — Truncate content by default (minimum viable)
Add truncate (Tailwind's single-line ellipsis) to the content cell. The user sees the first line with … and the row stays compact.
<td class="text-xs truncate max-w-0">
<span class="truncate block">{{ m.content }}</span>
</td>
max-w-0 with truncate forces the cell to shrink-to-fit within the table. This alone fixes the giant-row problem in one CSS change.
Layer 2 — Inline expand/collapse
Add an Alpine toggle so tapping the content reveals the full text in-place without leaving the table:
<td class="text-xs" x-data="{ expanded: false }">
<span x-show="!expanded" class="truncate block cursor-pointer"
@click="expanded = true">{{ m.content }}</span>
<span x-show="expanded" class="cursor-pointer"
@click="expanded = false">{{ m.content }}</span>
</td>
The … hints there's more; tapping shows it all; tapping again collapses. Works on both desktop and mobile with zero JS overhead.
Layer 3 — Detail modal (best for mobile)
On small screens, replace the expandable inline text with a tap-to-open modal. When the user taps a memory row (or a dedicated "view" button), a full-screen modal/drawer slides up showing:
- Content (full, readable text, no truncation)
- Metadata: Category, Subject, Scope, ID, Importance, Last accessed, Created
- Actions: Edit and Delete buttons (the same inline edit form works inside the modal)
- Close: Tap the backdrop or a close button
The modal uses Alpine.js x-data/x-show and a simple overlay:
<!-- In each row -->
<button class="btn btn-sm" @click="$dispatch('open-memory', { id: {{ m.id }}, ... })">view</button>
<!-- Modal (outside the table, rendered once) -->
<div x-data="memoryModal()"
x-on:open-memory.window="open($event.detail)"
x-show="isOpen"
class="fixed inset-0 z-50 ...">
...
</div>
The modal loads memory data from a data-* attribute or via a lightweight HTMX fetch (hx-get="/partials/memory/detail/{{ m.id }}").
Layer 4 (stretch) — Card layout on mobile
On very small screens (< 640px), switch from a table to a card list using Tailwind's responsive utilities. Each card shows:
[ID] Subject
Content summary...
Category · Scope
[edit] [delete]
Cards are naturally compact and tap-friendly. The table layout stays for sm: and up.
Implementation notes
- Layer 1 is a one-line Tailwind change (
truncate + max-w-0) and can be done immediately
- Layer 2 adds an Alpine toggle per row (negligible weight)
- Layer 3 needs a new modal partial and HTMX endpoint for memory detail
- Layer 4 is the most involved but replaces the table with a responsive grid
Priority order: Layer 1 → Layer 2 → Layer 3 → Layer 4. Even Layer 1 alone (truncation) would be a meaningful improvement over the current state.
Acceptance criteria
Problem
On mobile, the Long-term and Short-term memory tables are nearly unusable. The Content column renders the full text of every memory entry inline with
break-all, and memories are often full sentences or paragraphs. This makes every row disproportionately tall — a single memory can take up the whole screen, and with 25 entries per page you have to scroll endlessly past walls of text.The same problem exists on the Short-term tab.
Current markup
break-allforces word-break at every character, and there is no truncation, max-height, or max-width. On a ~375px mobile screen the content column is maybe 200px wide — a 300-character memory wraps into 10+ lines.Proposed solution
A layered approach that works from the simplest fix to the richest interaction:
Layer 1 — Truncate content by default (minimum viable)
Add
truncate(Tailwind's single-line ellipsis) to the content cell. The user sees the first line with…and the row stays compact.max-w-0withtruncateforces the cell to shrink-to-fit within the table. This alone fixes the giant-row problem in one CSS change.Layer 2 — Inline expand/collapse
Add an Alpine toggle so tapping the content reveals the full text in-place without leaving the table:
The
…hints there's more; tapping shows it all; tapping again collapses. Works on both desktop and mobile with zero JS overhead.Layer 3 — Detail modal (best for mobile)
On small screens, replace the expandable inline text with a tap-to-open modal. When the user taps a memory row (or a dedicated "view" button), a full-screen modal/drawer slides up showing:
The modal uses Alpine.js
x-data/x-showand a simple overlay:The modal loads memory data from a
data-*attribute or via a lightweight HTMX fetch (hx-get="/partials/memory/detail/{{ m.id }}").Layer 4 (stretch) — Card layout on mobile
On very small screens (
< 640px), switch from a table to a card list using Tailwind's responsive utilities. Each card shows:Cards are naturally compact and tap-friendly. The table layout stays for
sm:and up.Implementation notes
truncate+max-w-0) and can be done immediatelyPriority order: Layer 1 → Layer 2 → Layer 3 → Layer 4. Even Layer 1 alone (truncation) would be a meaningful improvement over the current state.
Acceptance criteria