Prompt suggestions on the new-chat screen

A blank composer is the least helpful thing a chat client can show someone
who has just installed one. Three cards now sit under the empty state, and
an administrator manages them at /admin/suggestions.

Clicking a card fills the composer and stops there. It deliberately does not
send: every default ends mid-sentence, because a card is a starting point
rather than a question somebody already asked, and the caret lands where the
person has to start typing.

Seeding is guarded by a settings flag, not by "is the table empty" --
otherwise an administrator who decided against them would get all three back
on every restart. Capped at twelve, six shown: past a dozen this is a menu,
and a menu on the empty screen is a worse blank page than a blank page.

The cards are gated on there being no chat at all, not on the thread being
empty. An empty chat someone opened on purpose already has a model and a
prompt chosen.

Also fixes a pre-existing bug the position test caught. Both this and
_refresh_models wrote `coalesce(max(position), -1) or -1`, and position 0 is
falsy -- so the second row landed back on 0 on top of the first. The
coalesce was already doing that job; the `or` was undoing it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaroslav Beneš
2026-08-01 00:49:12 +02:00
parent 09eecbdd9a
commit 26793b1317
13 changed files with 650 additions and 1 deletions
+4 -1
View File
@@ -192,7 +192,10 @@ async def _refresh_models(db: DBSession, connection: Connection) -> tuple[int, s
# New models land after everything already ordered, rather than all at
# position 0 where they would sort by id and shuffle the existing list.
next_position = (db.scalar(select(func.coalesce(func.max(Model.position), -1))) or -1) + 1
# No `or -1` after the coalesce: position 0 is falsy, so that idiom sent the
# second discovered model back to 0 on top of the first.
highest = db.scalar(select(func.coalesce(func.max(Model.position), -1)))
next_position = int(highest if highest is not None else -1) + 1
for entry in discovered:
model_id = str(entry["id"])[:300]