fix: model badge, empty-state cover, mascot centering, resizable panels, sidebar history
Some checks are pending
CI / build-check-test (push) Waiting to run
Some checks are pending
CI / build-check-test (push) Waiting to run
This commit is contained in:
parent
97cef8b4d3
commit
1514fabd50
2 changed files with 29 additions and 5 deletions
|
|
@ -65,7 +65,7 @@ export class JaeSessionSidebar extends LitElement {
|
|||
const sorted = [...pinned, ...rest];
|
||||
|
||||
return html`
|
||||
<div class="flex flex-col h-full border-r border-border bg-background shrink-0" style="width:200px">
|
||||
<div class="flex flex-col h-full border-r border-border bg-background shrink-0" style="width:100%">
|
||||
<div class="flex items-center justify-between px-3 py-2 border-b border-border shrink-0">
|
||||
<span class="text-[11px] font-semibold text-muted-foreground uppercase tracking-widest">Chats</span>
|
||||
<button @click=${() => this.onNewSession?.()}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ let currentTitle = "";
|
|||
let isEditingTitle = false;
|
||||
let agent: Agent;
|
||||
let rightPanel: 'none' | 'terminal' | 'browser' = 'none';
|
||||
let sidebarWidth = 220;
|
||||
let rightPanelWidth = 480;
|
||||
let hasStarted = false;
|
||||
let terminalPanel: JaeTerminalPanel | null = null;
|
||||
let browserPanel: JaeBrowserPanel | null = null;
|
||||
let chatPanel: ChatPanel;
|
||||
|
|
@ -225,6 +228,7 @@ const createAgent = async (initialState?: Partial<AgentState>) => {
|
|||
},
|
||||
getProviderApiKey: async (provider: string) => providerKeys.get(provider),
|
||||
onStateChange: async (state: AgentState, prevState: AgentState | undefined) => {
|
||||
if (state.messages.length > 0) hasStarted = true;
|
||||
if (prevState?.messages.length !== state.messages.length) {
|
||||
if (!currentTitle) {
|
||||
const generated = generateTitle(state.messages);
|
||||
|
|
@ -254,6 +258,7 @@ const loadSession = async (sessionId: string): Promise<boolean> => {
|
|||
const sessionData = await storage.sessions.get(sessionId);
|
||||
if (!sessionData) return false;
|
||||
currentSessionId = sessionId;
|
||||
hasStarted = sessionData.messages.length > 0;
|
||||
const metadata = await storage.sessions.getMetadata(sessionId);
|
||||
currentTitle = metadata?.title || "";
|
||||
await createAgent({
|
||||
|
|
@ -270,6 +275,7 @@ const newSession = () => {
|
|||
currentSessionId = undefined;
|
||||
currentTitle = "";
|
||||
isEditingTitle = false;
|
||||
hasStarted = false;
|
||||
createAgent().then(() => renderApp());
|
||||
};
|
||||
|
||||
|
|
@ -296,10 +302,16 @@ const handleSuggestion = (e: Event) => {
|
|||
}
|
||||
};
|
||||
|
||||
const getModelLabel = (): string | null => {
|
||||
if (!agent?.state?.model) return null;
|
||||
const m = agent.state.model as any;
|
||||
return m.name || m.id || null;
|
||||
};
|
||||
|
||||
const renderApp = () => {
|
||||
const app = document.getElementById("app");
|
||||
if (!app) return;
|
||||
const hasMessages = agent && agent.state.messages.length > 0;
|
||||
const hasMessages = hasStarted || !!(agent?.state?.messages?.length);
|
||||
render(html`
|
||||
<div class="w-full h-screen flex flex-col bg-background text-foreground overflow-hidden">
|
||||
<div class="flex items-center justify-between border-b border-border shrink-0" style="height:44px">
|
||||
|
|
@ -310,7 +322,7 @@ const renderApp = () => {
|
|||
? isEditingTitle
|
||||
? html`<div class="flex items-center gap-2">${Input({ type: "text", value: currentTitle, className: "text-sm w-64", onChange: async (e: Event) => { const v = (e.target as HTMLInputElement).value.trim(); if (v && v !== currentTitle && storage.sessions && currentSessionId) { await storage.sessions.updateTitle(currentSessionId, v); currentTitle = v; await refreshSidebar(); } isEditingTitle = false; renderApp(); }, onKeyDown: async (e: KeyboardEvent) => { if (e.key === "Enter") { const v = (e.target as HTMLInputElement).value.trim(); if (v && v !== currentTitle && storage.sessions && currentSessionId) { await storage.sessions.updateTitle(currentSessionId, v); currentTitle = v; await refreshSidebar(); } isEditingTitle = false; renderApp(); } else if (e.key === "Escape") { isEditingTitle = false; renderApp(); } } })}</div>`
|
||||
: html`<button class="px-2 py-1 text-sm text-foreground hover:bg-secondary rounded transition-colors max-w-xs truncate" @click=${() => { isEditingTitle = true; renderApp(); requestAnimationFrame(() => { const inp = app.querySelector('input[type="text"]') as HTMLInputElement; if (inp) { inp.focus(); inp.select(); } }); }} title="Click to edit">${currentTitle}</button>`
|
||||
: html`<div class="flex items-center gap-2"><img src="/mascot/jae-default.png" alt="JAE" class="w-7 h-auto header-logo cursor-pointer" /><span class="text-base font-semibold text-foreground">JAE</span></div>`
|
||||
: html`<div class="flex items-center gap-2"><img src="/mascot/jae-default.png" alt="JAE" class="w-7 h-auto header-logo cursor-pointer" /><span class="text-base font-semibold text-foreground">JAE</span>${getModelLabel() ? html`<span class="ml-1 text-[11px] text-muted-foreground bg-muted/80 px-1.5 py-0.5 rounded font-mono truncate max-w-[180px]" title="${getModelLabel()}">${getModelLabel()}</span>` : html``}</div>`
|
||||
}
|
||||
</div>
|
||||
<div class="flex items-center gap-1 px-2">
|
||||
|
|
@ -327,11 +339,18 @@ ${Button({ variant: "ghost", size: "sm", children: icon(Settings, "sm"), onClick
|
|||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 min-h-0 overflow-hidden">
|
||||
<div id="sidebar-wrap" style="width:${sidebarWidth}px;min-width:150px;max-width:420px;flex-shrink:0;display:flex;flex-direction:column;overflow:hidden;transition:width 0.05s">
|
||||
${sidebar}
|
||||
</div>
|
||||
<div id="sb-resize" style="width:5px;cursor:col-resize;flex-shrink:0;background:transparent;z-index:10;transition:background 0.15s"
|
||||
@mousedown=${(e: MouseEvent) => { e.preventDefault(); const sx=e.clientX,sw=sidebarWidth; const mv=(me: MouseEvent)=>{sidebarWidth=Math.max(150,Math.min(420,sw+me.clientX-sx));const w=document.getElementById("sidebar-wrap");if(w)w.style.width=sidebarWidth+"px";}; const up=()=>{document.removeEventListener("mousemove",mv);document.removeEventListener("mouseup",up);renderApp();}; document.addEventListener("mousemove",mv);document.addEventListener("mouseup",up); }}
|
||||
@mouseenter=${(e: Event)=>{(e.currentTarget as HTMLElement).style.background="rgba(128,128,128,0.4)"}}
|
||||
@mouseleave=${(e: Event)=>{(e.currentTarget as HTMLElement).style.background="transparent"}}
|
||||
></div>
|
||||
<div class="flex flex-col flex-1 min-w-0 min-h-0 relative">
|
||||
${!hasMessages ? html`
|
||||
<div class="absolute inset-x-0 top-0 z-10 flex flex-col overflow-y-auto bg-background" style="bottom:130px" @suggestion=${handleSuggestion}>
|
||||
<jae-empty-state></jae-empty-state>
|
||||
<jae-empty-state style="display:flex;flex-direction:column;flex:1;width:100%;min-height:0"></jae-empty-state>
|
||||
</div>
|
||||
` : html``}
|
||||
<div id="chat-wrapper" class="flex flex-col flex-1 min-h-0" >
|
||||
|
|
@ -339,7 +358,12 @@ ${chatPanel}
|
|||
</div>
|
||||
</div>
|
||||
${rightPanel !== 'none' ? html`
|
||||
<div class="flex flex-col border-l border-border" style="width:480px;min-width:320px;max-width:600px">
|
||||
<div id="rp-resize" style="width:5px;cursor:col-resize;flex-shrink:0;background:transparent;z-index:10;transition:background 0.15s"
|
||||
@mousedown=${(e: MouseEvent) => { e.preventDefault(); const sx=e.clientX,sw=rightPanelWidth; const mv=(me: MouseEvent)=>{rightPanelWidth=Math.max(280,Math.min(800,sw-(me.clientX-sx)));const p=document.getElementById("right-panel");if(p)p.style.width=rightPanelWidth+"px";}; const up=()=>{document.removeEventListener("mousemove",mv);document.removeEventListener("mouseup",up);renderApp();}; document.addEventListener("mousemove",mv);document.addEventListener("mouseup",up); }}
|
||||
@mouseenter=${(e: Event)=>{(e.currentTarget as HTMLElement).style.background="rgba(128,128,128,0.4)"}}
|
||||
@mouseleave=${(e: Event)=>{(e.currentTarget as HTMLElement).style.background="transparent"}}
|
||||
></div>
|
||||
<div id="right-panel" class="flex flex-col border-l border-border" style="width:${rightPanelWidth}px;min-width:280px;max-width:800px;flex-shrink:0">
|
||||
<div class="flex items-center gap-1 px-2 shrink-0 border-b border-border bg-muted/20" style="height:36px">
|
||||
<button class="text-xs px-2 py-1 rounded ${
|
||||
rightPanel === 'terminal'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue