Add a guided setup after signup, and a no-model notice in conversations

The first super admin now lands on Settings > Setup after signing up: a
skippable walk through the app name, connecting a model provider and
picking models, ending with where to go next. It stays under Settings to
come back to.

While a bot has no text model, its conversation shows a notice in the
thread (centred when empty): super admins get Connect a provider and
Guided setup, everyone else is told the super admin sets it up. The
models modal keeps it current.

The Models page's data and save logic move into useModelsOverview, shared
with the setup.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-28 18:26:20 +01:00
co-authored by Claude Opus 5.5
parent c9a24c05af
commit 1e7834ba7f
19 changed files with 556 additions and 39 deletions
@@ -8,7 +8,7 @@ import Dropdown from "@moduletrace/twui/elements/Dropdown";
import useStatus from "@moduletrace/twui/hooks/useStatus";
import { CpuAi, EllipsisVertical, Monitor } from "lucide-react";
import type { BUN_SQLITE_COBBLE_WORKSPACES } from "@/db/types/db";
import type { BotWithLastMessage } from "@/src/types";
import type { BotTextModel, BotWithLastMessage } from "@/src/types";
import { AppContext } from "@/src/pages/__root";
import WorkspaceAvatar from "@/src/components/general/workspace-avatar";
import RailDrawerButton from "@/src/layouts/bots/(partials)/rail-drawer-button";
@@ -27,6 +27,12 @@ type Props = {
*/
workspace?: BUN_SQLITE_COBBLE_WORKSPACES | null;
has_other_unread?: boolean;
/**
* The text model the bot runs on, kept by the conversation (which
* shows a notice while there's none).
*/
model?: BotTextModel | null;
onModelChange: (model: BotTextModel | null) => void;
};
/**
@@ -40,10 +46,11 @@ export default function ConversationHeader({
bot,
workspace,
has_other_unread,
model,
onModelChange,
}: Props) {
const { computers } = useContext(BotComputersContext);
const { pageProps } = useContext(AppContext);
const [model, setModel] = useState(pageProps?.bot_text_model);
/**
* The menu's toggle shows its change at once; a push (here or from
* another tab) brings the live bot's.
@@ -143,7 +150,7 @@ export default function ConversationHeader({
(h) => h.harness == "text",
)?.current;
setModel(
onModelChange(
current
? {
name: current.name,