Two things about titles were wrong. Every chat spent a second completion on its name, including an agent chat whose opening words are already a title -- somebody starting one states an objective, not a topic. An agent chat now takes `fallback_title` from its first prompt and makes no request at all; an ordinary chat, which opens with a question whose *answer* is what makes a title worth asking for, is unchanged. And renaming existed only as the `/title` slash command, which set the heading and left the sidebar row showing the old name until the next reload -- a rename that looks half-applied is one people do twice. There are pencil buttons on the heading and on every sidebar row now, both PATCHing the route that was already there, and `update_chat` answers a rename with the out-of-band pair the `done` frame has always sent, so one response moves both. Only on a rename: sending it for every PATCH would overwrite the heading from an unrelated save. `/title` sets both spans itself, being a bare fetch rather than htmx. The dialog is the `data-prompt` mechanism the folder work added, which is why the heading keeps a button rather than becoming an inline field: it sits in a flex row beside the badges and the connection chip, and swapping it for a text box moves all of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
40 lines
1.9 KiB
HTML
40 lines
1.9 KiB
HTML
{% from "_macros.html" import icon %}
|
|
{#
|
|
A single chat row. `chat_item` rather than `chat` so this can be included
|
|
from a page that already has the open chat bound to `chat`.
|
|
#}
|
|
<div class="nav-item {% if chat and chat.id == chat_item.id %}is-active{% endif %}"
|
|
data-chat-id="{{ chat_item.id }}">
|
|
<a class="nav-item__link" href="/chat/{{ chat_item.id }}">
|
|
{# The kind, in the one place a person looks. Legible even with the switch
|
|
off, which is what it is for: a chat that can run commands should not look
|
|
like one that cannot. #}
|
|
{{ icon("terminal" if chat_item.kind == "agent" else "chat", "icon--sm") }}
|
|
<span class="nav-item__label" id="chat-link-label-{{ chat_item.id }}">{{ chat_item.title }}</span>
|
|
{# Toggled out of band by the unread poll; see /api/chats/unread. #}
|
|
<span id="unread-{{ chat_item.id }}" class="unread-dot"
|
|
{{ '' if chat_item.unread else 'hidden' }} title="New reply"></span>
|
|
</a>
|
|
<span class="nav-item__actions">
|
|
{# Works from any page carrying the sidebar, not only from inside the chat.
|
|
The response carries both out-of-band spans, so the heading follows if
|
|
this happens to be the open chat. #}
|
|
<button class="btn btn--icon btn--sm" type="button"
|
|
hx-patch="/api/chats/{{ chat_item.id }}" hx-swap="none"
|
|
data-prompt="What should this chat be called?"
|
|
data-prompt-title="Rename chat" data-prompt-field="title"
|
|
data-prompt-value="{{ chat_item.title }}"
|
|
aria-label="Rename chat" title="Rename chat">
|
|
{{ icon("pencil", "icon--sm") }}
|
|
</button>
|
|
<button class="btn btn--icon btn--sm" type="button"
|
|
hx-delete="/api/chats/{{ chat_item.id }}"
|
|
hx-confirm="Delete “{{ chat_item.title }}”? This cannot be undone."
|
|
data-confirm-title="Delete chat"
|
|
hx-swap="none"
|
|
aria-label="Delete chat">
|
|
{{ icon("trash", "icon--sm") }}
|
|
</button>
|
|
</span>
|
|
</div>
|