fix: dark mode shortcuts, Venice priority, browser-in-chat hiding, auto-memory, view toggles CSS, sidebar scroll
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
ef135b6d94
commit
84671d57bf
4 changed files with 206 additions and 11 deletions
|
|
@ -261,3 +261,66 @@ button:active, [role="button"]:active {
|
|||
[class*="AgentInterface"] {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
|
||||
/* ===== VIEW TOGGLE CONTROLS ===== */
|
||||
/* Hide tool call blocks when tools toggled off */
|
||||
.hide-tools tool-message {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide thinking/reasoning blocks */
|
||||
.hide-thinking [data-thinking],
|
||||
.hide-thinking .thinking-block,
|
||||
.hide-thinking [class*="thinking"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide system messages */
|
||||
.hide-system [data-system-message],
|
||||
.hide-system .system-message {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide timestamps on messages */
|
||||
.hide-timestamps time,
|
||||
.hide-timestamps [data-timestamp],
|
||||
.hide-timestamps .message-timestamp {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide browser tool output in chat when browser panel is open */
|
||||
.browser-panel-active tool-message[data-tool-name="browser"],
|
||||
.browser-panel-active tool-message[data-tool-name="web_fetch"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Smooth transition for tool-message visibility */
|
||||
tool-message {
|
||||
transition: opacity 0.2s ease, max-height 0.3s ease;
|
||||
}
|
||||
|
||||
/* ===== SIDEBAR HOVER SCROLLBAR ===== */
|
||||
.jae-sidebar {
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
.jae-sidebar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background: transparent;
|
||||
}
|
||||
.jae-sidebar::-webkit-scrollbar-thumb {
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
.jae-sidebar:hover {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(255,255,255,0.15) transparent;
|
||||
}
|
||||
.jae-sidebar:hover::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
.jae-sidebar:hover::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255,255,255,0.25);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,21 +40,22 @@ export class KeyboardShortcuts extends LitElement {
|
|||
override render() {
|
||||
if (!this.open) return html``;
|
||||
return html`
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center" style="background:rgba(0,0,0,0.5)" @click=${(e: Event) => { if (e.target === e.currentTarget) this.hide(); }}>
|
||||
<div class="jae-glass rounded-2xl shadow-2xl p-6 max-w-lg w-full mx-4 border border-border/60 jae-scale-in">
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center" style="background:rgba(0,0,0,0.6);backdrop-filter:blur(4px)" @click=${(e: Event) => { if (e.target === e.currentTarget) this.hide(); }}>
|
||||
<div class="rounded-2xl shadow-2xl p-6 max-w-lg w-full mx-4 border border-white/10 jae-scale-in"
|
||||
style="background:var(--color-card, hsl(240 6% 10%));color:var(--color-card-foreground, hsl(0 0% 95%))">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-lg font-bold text-foreground">Keyboard Shortcuts</h2>
|
||||
<button @click=${() => this.hide()} class="text-muted-foreground hover:text-foreground transition-colors text-lg">\u2715</button>
|
||||
<h2 class="text-lg font-bold" style="color:var(--color-card-foreground, hsl(0 0% 95%))">⌨ Keyboard Shortcuts</h2>
|
||||
<button @click=${() => this.hide()} class="hover:opacity-70 transition-opacity text-lg" style="color:var(--color-muted-foreground, hsl(0 0% 64%))">×</button>
|
||||
</div>
|
||||
<div class="space-y-4 max-h-[60vh] overflow-y-auto">
|
||||
<div class="space-y-4 max-h-[60vh] overflow-y-auto pr-1">
|
||||
${this.shortcuts.map(g => html`
|
||||
<div>
|
||||
<h3 class="text-xs font-semibold text-muted-foreground uppercase tracking-widest mb-2">${g.group}</h3>
|
||||
<div class="space-y-1.5">
|
||||
<h3 class="text-xs font-semibold uppercase tracking-widest mb-2" style="color:var(--color-muted-foreground, hsl(0 0% 64%))">${g.group}</h3>
|
||||
<div class="space-y-1">
|
||||
${g.items.map(s => html`
|
||||
<div class="flex items-center justify-between py-1.5 px-2 rounded-lg hover:bg-secondary/30 transition-colors duration-150">
|
||||
<span class="text-sm text-foreground/90">${s.desc}</span>
|
||||
<kbd class="px-2 py-0.5 rounded-md text-xs font-mono" style="background:rgba(42,42,62,0.8);color:var(--color-foreground);border:1px solid rgba(255,255,255,0.1)">${s.key}</kbd>
|
||||
<div class="flex items-center justify-between py-1.5 px-3 rounded-lg transition-colors duration-150" style="color:var(--color-card-foreground, hsl(0 0% 95%))" @mouseenter=${(e: Event) => (e.currentTarget as HTMLElement).style.background = "rgba(255,255,255,0.06)"} @mouseleave=${(e: Event) => (e.currentTarget as HTMLElement).style.background = ""}>
|
||||
<span class="text-sm">${s.desc}</span>
|
||||
<kbd class="px-2 py-0.5 rounded-md text-xs font-mono" style="background:rgba(255,255,255,0.08);color:var(--color-card-foreground, hsl(0 0% 90%));border:1px solid rgba(255,255,255,0.12)">${s.key}</kbd>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -112,6 +112,120 @@ RULES:
|
|||
- Be concise and helpful. Use markdown formatting.
|
||||
- Never create files or run commands unless explicitly asked to do so.`;
|
||||
|
||||
|
||||
// ===== AUTO-MEMORY SYSTEM =====
|
||||
const AUTO_MEMORY_DB = "jae-auto-memory";
|
||||
const AUTO_MEMORY_STORE = "entries";
|
||||
|
||||
interface AutoMemory {
|
||||
id: string;
|
||||
content: string;
|
||||
tags: string[];
|
||||
timestamp: string;
|
||||
source: "user" | "agent" | "system";
|
||||
}
|
||||
|
||||
let autoMemoryDb: IDBDatabase | null = null;
|
||||
|
||||
async function openAutoMemoryDb(): Promise<IDBDatabase> {
|
||||
if (autoMemoryDb) return autoMemoryDb;
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = indexedDB.open(AUTO_MEMORY_DB, 1);
|
||||
req.onupgradeneeded = () => {
|
||||
const store = req.result.createObjectStore(AUTO_MEMORY_STORE, { keyPath: "id" });
|
||||
store.createIndex("tags", "tags", { multiEntry: true });
|
||||
store.createIndex("timestamp", "timestamp");
|
||||
};
|
||||
req.onsuccess = () => { autoMemoryDb = req.result; resolve(autoMemoryDb); };
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function autoMemorySave(content: string, tags: string[] = [], source: "user" | "agent" | "system" = "agent"): Promise<void> {
|
||||
const db = await openAutoMemoryDb();
|
||||
const entry: AutoMemory = { id: crypto.randomUUID(), content, tags, timestamp: new Date().toISOString(), source };
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(AUTO_MEMORY_STORE, "readwrite");
|
||||
tx.objectStore(AUTO_MEMORY_STORE).put(entry);
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function autoMemorySearch(query: string, limit = 10): Promise<AutoMemory[]> {
|
||||
const db = await openAutoMemoryDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(AUTO_MEMORY_STORE, "readonly");
|
||||
const req = tx.objectStore(AUTO_MEMORY_STORE).getAll();
|
||||
req.onsuccess = () => {
|
||||
const q = query.toLowerCase();
|
||||
const results = (req.result || []).filter((e: AutoMemory) =>
|
||||
e.content.toLowerCase().includes(q) || e.tags.some(t => t.toLowerCase().includes(q))
|
||||
).slice(-limit);
|
||||
resolve(results);
|
||||
};
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function autoMemoryGetAll(limit = 50): Promise<AutoMemory[]> {
|
||||
const db = await openAutoMemoryDb();
|
||||
return new Promise((resolve, reject) => {
|
||||
const tx = db.transaction(AUTO_MEMORY_STORE, "readonly");
|
||||
const req = tx.objectStore(AUTO_MEMORY_STORE).getAll();
|
||||
req.onsuccess = () => resolve((req.result || []).slice(-limit));
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
// Extract key facts from messages for auto-learning
|
||||
function extractKeyFacts(text: string): string[] {
|
||||
const facts: string[] = [];
|
||||
// Patterns that indicate important information
|
||||
const patterns = [
|
||||
/my name is ([\w\s]+)/i,
|
||||
/i(?:'m| am) a ([\w\s]+)/i,
|
||||
/i work (?:at|for|with) ([\w\s]+)/i,
|
||||
/i live in ([\w\s]+)/i,
|
||||
/i use ([\w\s]+)/i,
|
||||
/i prefer ([\w\s]+)/i,
|
||||
/remember (?:that )?(.+)/i,
|
||||
/my (?:email|phone|address) is ([\w\s@.+]+)/i,
|
||||
/the (?:api key|password|token|url|endpoint) is ([^\s]+)/i,
|
||||
];
|
||||
for (const p of patterns) {
|
||||
const m = text.match(p);
|
||||
if (m && m[1]) facts.push(m[0].trim());
|
||||
}
|
||||
return facts;
|
||||
}
|
||||
|
||||
// Hook into messages to auto-learn
|
||||
function processMessageForMemory(role: string, text: string) {
|
||||
if (!text || text.length < 10) return;
|
||||
const facts = extractKeyFacts(text);
|
||||
for (const fact of facts) {
|
||||
autoMemorySave(fact, ["auto-learned", role], role === "user" ? "user" : "agent");
|
||||
}
|
||||
}
|
||||
|
||||
// Inject memories into system prompt
|
||||
async function getMemoryAugmentedPrompt(basePrompt: string, userMessage: string): Promise<string> {
|
||||
try {
|
||||
const relevant = await autoMemorySearch(userMessage, 5);
|
||||
if (relevant.length === 0) return basePrompt;
|
||||
const memoryBlock = relevant.map(m => `- ${m.content}`).join("\n");
|
||||
return `${basePrompt}
|
||||
|
||||
RELEVANT MEMORIES:
|
||||
${memoryBlock}
|
||||
|
||||
Use these memories to provide more personalised and context-aware responses.`;
|
||||
} catch {
|
||||
return basePrompt;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== AGENT CREATION =====
|
||||
async function createAgent(initialState?: AgentState) {
|
||||
const model = initialState?.model || getModel("venice", "llama-3.3-70b");
|
||||
|
|
@ -171,6 +285,10 @@ async function createAgent(initialState?: AgentState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Auto-learn from user messages
|
||||
if (msg.role === "user" && typeof msg.content === "string") {
|
||||
processMessageForMemory("user", msg.content);
|
||||
}
|
||||
// Update mood based on content
|
||||
const mood = document.querySelector("jae-mood-indicator") as JaeMoodIndicator;
|
||||
if (mood && msg.role === "assistant" && typeof msg.content === "string") {
|
||||
|
|
@ -408,6 +526,8 @@ function getViewClasses(): string {
|
|||
if (!viewState.thinking) classes.push("hide-thinking");
|
||||
if (!viewState.system) classes.push("hide-system");
|
||||
if (!viewState.timestamps) classes.push("hide-timestamps");
|
||||
// Hide browser tool output in chat when browser panel is visible
|
||||
if (rightPanel === "browser") classes.push("browser-panel-active");
|
||||
return classes.join(" ");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,10 @@ export class ModelSelector extends DialogBase {
|
|||
const bIsCurrent = modelsAreEqual(this.currentModel, b.model);
|
||||
if (aIsCurrent && !bIsCurrent) return -1;
|
||||
if (!aIsCurrent && bIsCurrent) return 1;
|
||||
// Venice first
|
||||
const aVenice = a.provider === "venice" ? 0 : 1;
|
||||
const bVenice = b.provider === "venice" ? 0 : 1;
|
||||
if (aVenice !== bVenice) return aVenice - bVenice;
|
||||
return a.provider.localeCompare(b.provider);
|
||||
});
|
||||
}
|
||||
|
|
@ -299,7 +303,14 @@ export class ModelSelector extends DialogBase {
|
|||
if (this.allowedProviders) {
|
||||
return Array.from(seen).filter((p) => this.allowedProviders!.has(p));
|
||||
}
|
||||
return Array.from(seen).sort();
|
||||
// Put Venice first, then sort the rest alphabetically
|
||||
const sorted = Array.from(seen).sort();
|
||||
const veniceIdx = sorted.indexOf("venice");
|
||||
if (veniceIdx > 0) {
|
||||
sorted.splice(veniceIdx, 1);
|
||||
sorted.unshift("venice");
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
private renderProviderTabs() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue