feat(ui,docs): the open tab lives in the conversation url, admin screenshots in the readme
This commit is contained in:
@@ -7,6 +7,8 @@ boundary - without an opinion about what the agent is for. A setup is one
|
|||||||
holds the primitives, [beaver-agent](https://git.kotikot.com/beaver/beaver-agent)
|
holds the primitives, [beaver-agent](https://git.kotikot.com/beaver/beaver-agent)
|
||||||
is a full setup built on them.
|
is a full setup built on them.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## What it gives a setup
|
## What it gives a setup
|
||||||
|
|
||||||
- **agents/** - what an agent is: a Claude agent with a prompt per
|
- **agents/** - what an agent is: a Claude agent with a prompt per
|
||||||
@@ -32,7 +34,13 @@ is a full setup built on them.
|
|||||||
- **storage/** - SQLModel tables and the Postgres session store.
|
- **storage/** - SQLModel tables and the Postgres session store.
|
||||||
|
|
||||||
`config.py` defines `Gateway` and loads a setup; `app.py` builds the runtime
|
`config.py` defines `Gateway` and loads a setup; `app.py` builds the runtime
|
||||||
from it; `cli.py` is the entrypoint. Every string the gateway puts in front of
|
from it; `cli.py` is the entrypoint. The admin SPA in `ui/` is the browser
|
||||||
|
window on all of it: conversations by day, the thread with the tree of tool
|
||||||
|
calls, the context of the last turn, memory, jobs, usage, tokens and the audit.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
 Every string the gateway puts in front of
|
||||||
a model or a user has an English default and is overridable
|
a model or a user has an English default and is overridable
|
||||||
(`ConversationTexts`, `TelegramTexts`).
|
(`ConversationTexts`, `TelegramTexts`).
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
@@ -18,6 +18,7 @@
|
|||||||
onOpen,
|
onOpen,
|
||||||
showTitle = true,
|
showTitle = true,
|
||||||
initialView = "chat",
|
initialView = "chat",
|
||||||
|
onViewChange,
|
||||||
}: {
|
}: {
|
||||||
client: ApiClient;
|
client: ApiClient;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -27,6 +28,8 @@
|
|||||||
showTitle?: boolean;
|
showTitle?: boolean;
|
||||||
// "activity" beside a deep chat's own note: the file is the thread.
|
// "activity" beside a deep chat's own note: the file is the thread.
|
||||||
initialView?: string;
|
initialView?: string;
|
||||||
|
// The page keeps the open tab in the URL so it survives a reload.
|
||||||
|
onViewChange?: (view: string) => void;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const host = usePanelHost();
|
const host = usePanelHost();
|
||||||
@@ -50,6 +53,10 @@
|
|||||||
historyKey += 1;
|
historyKey += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
onViewChange?.(view);
|
||||||
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const { running } = feed.model;
|
const { running } = feed.model;
|
||||||
if (!running) {
|
if (!running) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from "$app/navigation";
|
import { goto, replaceState } from "$app/navigation";
|
||||||
import { base } from "$app/paths";
|
import { base } from "$app/paths";
|
||||||
import { page } from "$app/state";
|
import { page } from "$app/state";
|
||||||
import ConversationView from "$lib/panel/conversation-view.svelte";
|
import ConversationView from "$lib/panel/conversation-view.svelte";
|
||||||
@@ -7,6 +7,22 @@
|
|||||||
|
|
||||||
const id = $derived(page.params.id ?? "");
|
const id = $derived(page.params.id ?? "");
|
||||||
const href = (target: string) => `${base}/conversations/${target}`;
|
const href = (target: string) => `${base}/conversations/${target}`;
|
||||||
|
|
||||||
|
// ``?view=context`` opens that tab and stays in the address bar, so a
|
||||||
|
// reload (or a restart) lands where the reader was. Thread is the default
|
||||||
|
// and keeps the URL clean.
|
||||||
|
const initialView = $derived(page.url.searchParams.get("view") ?? "chat");
|
||||||
|
function remember(view: string) {
|
||||||
|
const url = new URL(page.url);
|
||||||
|
if (view === "chat") {
|
||||||
|
url.searchParams.delete("view");
|
||||||
|
} else {
|
||||||
|
url.searchParams.set("view", view);
|
||||||
|
}
|
||||||
|
if (url.href !== page.url.href) {
|
||||||
|
replaceState(url, page.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head><title>Conversation · Beaver</title></svelte:head>
|
<svelte:head><title>Conversation · Beaver</title></svelte:head>
|
||||||
@@ -17,7 +33,9 @@
|
|||||||
client={session.client}
|
client={session.client}
|
||||||
{href}
|
{href}
|
||||||
{id}
|
{id}
|
||||||
|
{initialView}
|
||||||
onOpen={(target) => goto(href(target))}
|
onOpen={(target) => goto(href(target))}
|
||||||
|
onViewChange={remember}
|
||||||
/>
|
/>
|
||||||
{/key}
|
{/key}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user