improve session activity and model display

This commit is contained in:
Michael committed 2026-06-30 19:19:49 +02:00
1 parent a7290039f6
commit f6793daed3
6 files changed
+65 -19

No files matched your search

+2 -1
View File
@@ -3,13 +3,14 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Press+Start+2P&display=swap"
rel="stylesheet"
/>
<title>Loki — Agent local</title>
<title>Loki - Agent local</title>
</head>
<body>
<div id="root"></div>
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="10" fill="#18181b"/>
<rect x="11" y="11" width="38" height="38" rx="7" fill="#ff5436" stroke="#ffffff" stroke-width="6"/>
<rect x="25" y="25" width="12" height="12" fill="#ffffff"/>
<path d="M18 54h38V16" fill="none" stroke="#ff5436" stroke-width="5" stroke-linecap="square"/>
</svg>

After

Width:  |  Height:  |  Size: 386 B

+7 -4
View File
@@ -20,12 +20,13 @@ export function ModelSelector() {
<div className="relative" ref={ref}>
<button
onClick={() => setOpen((v) => !v)}
className="flex h-[34px] items-center gap-2 border-[3px] border-white bg-accent px-3 text-white shadow-accent-soft"
className="flex h-[34px] min-w-0 max-w-[260px] items-center gap-2 border-[3px] border-white bg-accent px-3 text-white shadow-accent-soft"
title={selectedModel || undefined}
style={{ borderRadius: 7 }}
>
<span className="h-2.5 w-2.5 border-2 border-line bg-white" />
<span className="text-[14px] leading-none">{selectedModel || "—"}</span>
<ChevronDown size={12} />
<span className="min-w-0 truncate text-[14px] leading-none">{selectedModel || "—"}</span>
<ChevronDown size={12} className="flex-none" />
</button>
{open && (
@@ -51,7 +52,9 @@ export function ModelSelector() {
m.name === selectedModel ? "bg-accent" : "bg-card"
}`}
/>
<span className="flex-1 text-xs text-ink-2">{m.name}</span>
<span className="min-w-0 flex-1 truncate text-xs text-ink-2" title={m.name}>
{m.name}
</span>
{m.size_go > 0 && (
<span className="text-[10px] text-muted-2">{m.size_go} Go</span>
)}
+22 -7
View File
@@ -11,6 +11,7 @@ export function ChatPanel() {
selectedModel,
messages,
streaming,
streamingSessionId,
streamContent,
streamThinking,
streamStatus,
@@ -22,6 +23,7 @@ export function ChatPanel() {
pendingShell,
approveShell,
rejectShell,
sessions,
} = useStore();
const activeTools = config
@@ -49,7 +51,9 @@ export function ChatPanel() {
}
};
const empty = messages.length === 0 && !streaming;
const showingStreaming = streaming && currentSessionId === streamingSessionId;
const workingSession = sessions.find((s) => s.id === streamingSessionId);
const empty = messages.length === 0 && !showingStreaming;
return (
<div className="flex min-w-0 flex-1 flex-col bg-base">
@@ -88,7 +92,7 @@ export function ChatPanel() {
{messages.map((m) => (
<Bubble key={m.id} msg={m} />
))}
{streaming && (
{showingStreaming && (
<Bubble
msg={{
id: "stream",
@@ -105,7 +109,7 @@ export function ChatPanel() {
thinking={streamThinking}
/>
)}
{pendingShell && (
{showingStreaming && pendingShell && (
<ShellConfirm
command={pendingShell}
onApprove={approveShell}
@@ -132,12 +136,23 @@ export function ChatPanel() {
<button className="flex h-8 w-8 items-center justify-center border-2 border-line text-ink-2">
<ClipIcon />
</button>
<div className="flex h-8 items-center gap-1.5 border-2 border-line px-2.5 text-[13px] text-ink-2">
<div
className="flex h-8 min-w-0 max-w-[220px] items-center gap-1.5 border-2 border-line px-2.5 text-[13px] text-ink-2"
title={selectedModel || undefined}
>
<span className="h-2 w-2 border-2 border-line bg-accent" />
{selectedModel || "—"}
<span className="min-w-0 truncate">{selectedModel || "—"}</span>
</div>
<div className="flex-1" />
<span className="text-[13px] text-muted-3">⏎ envoyer · ⇧⏎ ligne</span>
{streaming && !showingStreaming ? (
<span className="min-w-0 flex-1 truncate text-[13px] text-accent">
Travail en cours : {workingSession?.title ?? "session ouverte"}
</span>
) : (
<>
<div className="flex-1" />
<span className="text-[13px] text-muted-3">⏎ envoyer · ⇧⏎ ligne</span>
</>
)}
<button
onClick={submit}
disabled={streaming || !draft.trim()}
+10
View File
@@ -8,6 +8,7 @@ export function LeftPanel() {
const {
sessions,
currentSessionId,
streamingSessionId,
refreshSessions,
refreshFiles,
fileTree,
@@ -43,6 +44,7 @@ export function LeftPanel() {
)}
{sessions.map((s) => {
const active = s.id === currentSessionId;
const working = s.id === streamingSessionId;
return (
<div
key={s.id}
@@ -60,6 +62,14 @@ export function LeftPanel() {
>
{s.title}
</div>
{working && (
<span
className="flex-none border-2 border-line bg-accent px-1.5 py-0.5 text-[10px] leading-none text-white"
title="Session en cours de travail"
>
EN COURS
</span>
)}
<button
onClick={(e) => {
e.stopPropagation();
+18 -7
View File
@@ -31,6 +31,7 @@ interface LokiState {
currentSessionId: string | null;
messages: Message[];
streaming: boolean;
streamingSessionId: string | null;
streamContent: string; // réponse de l'agent en cours de frappe
streamThinking: string; // raisonnement de l'agent en cours
streamStatus: string;
@@ -73,6 +74,7 @@ export const useStore = create<LokiState>((set, get) => ({
currentSessionId: null,
messages: [],
streaming: false,
streamingSessionId: null,
streamContent: "",
streamThinking: "",
streamStatus: "",
@@ -174,24 +176,26 @@ export const useStore = create<LokiState>((set, get) => ({
newSession: async () => {
const s = await createSession(get().selectedModel || undefined);
const streaming = get().streaming;
set({
currentSessionId: s.id,
messages: [],
streamContent: "",
streamStatus: "",
streamNotice: null,
...(streaming
? {}
: { streamContent: "", streamStatus: "", streamNotice: null }),
});
await get().refreshSessions();
},
openSession: async (id) => {
const { messages } = await getSession(id);
const streaming = get().streaming;
set({
currentSessionId: id,
messages,
streamContent: "",
streamStatus: "",
streamNotice: null,
...(streaming
? {}
: { streamContent: "", streamStatus: "", streamNotice: null }),
});
},
@@ -251,6 +255,7 @@ export const useStore = create<LokiState>((set, get) => ({
set({
messages: [...get().messages, userMsg],
streaming: true,
streamingSessionId: sid,
streamContent: "",
streamThinking: "",
streamStatus: "Connexion à Ollama…",
@@ -292,6 +297,7 @@ export const useStore = create<LokiState>((set, get) => ({
);
set({
streaming: false,
streamingSessionId: null,
streamContent: "",
streamThinking: "",
streamStatus: "",
@@ -314,13 +320,18 @@ export const useStore = create<LokiState>((set, get) => ({
};
set({
streaming: false,
streamingSessionId: null,
streamContent: "",
streamThinking: "",
streamStatus: "",
streamNotice: null,
streamTools: [],
messages: [...get().messages, errMsg],
messages:
get().currentSessionId === sid
? [...get().messages, errMsg]
: get().messages,
});
void get().refreshSessions();
},
}
);