diff --git a/docs/superpowers/specs/2026-06-20-issue-53-session-row-fragment-rebuild-design.md b/docs/superpowers/specs/2026-06-20-issue-53-session-row-fragment-rebuild-design.md index dfc5117..e4fe424 100644 --- a/docs/superpowers/specs/2026-06-20-issue-53-session-row-fragment-rebuild-design.md +++ b/docs/superpowers/specs/2026-06-20-issue-53-session-row-fragment-rebuild-design.md @@ -53,15 +53,23 @@ Both consumers go through the same dict builder and the same renderer: rows = [session_row_data(s, device_list, csrf_token) for s in sessions] # → paginated_table_content → SimpleTable → TableRow(data=dict) -# _session_row_fragment -def _session_row_fragment(session, device_list, csrf_token) -> SafeText: - return str(TableRow(session_row_data(session, device_list, csrf_token))) +# single-row htmx fragment — returns a Node, not a stringified SafeText +def session_row(session, device_list, csrf_token) -> Node: + return TableRow(session_row_data(session, device_list, csrf_token)) ``` The fragment is therefore the *same* row the table renders, for a single session. Change a column once in `session_row_data` and list + fragment move together. The old hand-built -`Tr` (4-column, the `#last-session-start` toggle, the yellow "Finish now?" link) is -deleted entirely. +`Tr` (4-column, the `#last-session-start` toggle, the yellow "Finish now?" link) — and the +`_session_row_fragment` helper returning `SafeText` — are deleted entirely. + +**Return `Node`, not `SafeText`.** Per the component-system direction, builders return +`Node` objects and stringification happens only at the `HttpResponse` boundary (Django +str-encodes response content automatically — `HttpResponse(node)` already works across the +codebase, e.g. `purchase.py` `HttpResponse(_refund_confirmation_modal(...))`). `TableRow` +already returns an `Element` (a `Node`), so `session_row` returns it directly with no +`str()`/`mark_safe`. The endpoints combine the row and the OOB navbar with `Fragment` +(also a `Node`) and pass that straight to `HttpResponse`. `session_row_data` reproduces today's `list_sessions` dict exactly: @@ -122,14 +130,15 @@ All three endpoints keep their non-htmx branch (`redirect("games:list_sessions") | Endpoint | htmx response | |---|---| -| `end_session` | `TableRow(session_row_data(...))` **+** `NavbarPlaytime(..., oob=True)` | -| `reset_session_start` | `TableRow(session_row_data(...))` **+** `NavbarPlaytime(..., oob=True)` | +| `end_session` | `HttpResponse(Fragment(session_row(...), NavbarPlaytime(..., oob=True)))` | +| `reset_session_start` | `HttpResponse(Fragment(session_row(...), NavbarPlaytime(..., oob=True)))` | | `new_session_from_existing_session` (clone) | `204 + HX-Refresh: true` | -- **end / reset** return the fresh row plus the OOB navbar fragment in one response body. - The triggering button targets `#session-row-{pk}` with `hx-swap="outerHTML"`; htmx - extracts the OOB `