Add ollama dependency and dialog backdrop blur

- Add ollama package to web-ui dependencies for ModelSelector
- Add backdrop blur to SettingsDialog (bg-black/50 backdrop-blur-sm)
- Update mini-lit to 0.1.9 for backdropClassName prop support
- Fix TypeScript errors in ModelSelector (ollama import, parameter types)
- Add backward compatibility methods to SessionsStore (saveSession, loadSession, getLatestSessionId)
This commit is contained in:
Mario Zechner 2025-10-08 17:49:56 +02:00
parent db34ef01bf
commit 91c1dc6475
10 changed files with 133 additions and 70 deletions

View file

@ -1,4 +1,4 @@
import { Button, html, icon } from "@mariozechner/mini-lit";
import { html } from "@mariozechner/mini-lit";
import type {
AgentTool,
AssistantMessage as AssistantMessageType,
@ -7,8 +7,7 @@ import type {
UserMessage as UserMessageType,
} from "@mariozechner/pi-ai";
import { LitElement, type TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { Bug, Loader, Wrench } from "lucide";
import { customElement, property } from "lit/decorators.js";
import { renderTool } from "../tools/index.js";
import type { Attachment } from "../utils/attachment-utils.js";
import { formatUsage } from "../utils/format.js";

View file

@ -101,7 +101,7 @@ export class ModelSelector extends DialogBase {
// Fetch details for each model and convert to Model format
const ollamaModelPromises: Promise<Model<any> | null>[] = models
.map(async (model) => {
.map(async (model: any) => {
try {
// Get model details
const details = await ollama.show({
@ -144,7 +144,7 @@ export class ModelSelector extends DialogBase {
return null;
}
})
.filter((m) => m !== null);
.filter((m: any) => m !== null);
const results = await Promise.all(ollamaModelPromises);
this.ollamaModels = results.filter((m): m is Model<any> => m !== null);

View file

@ -179,6 +179,7 @@ export class SettingsDialog extends LitElement {
},
width: "min(1000px, 90vw)",
height: "min(800px, 90vh)",
backdropClassName: "bg-black/50 backdrop-blur-sm",
children: html`
${DialogContent({
className: "h-full p-6",

View file

@ -17,7 +17,7 @@ export class IndexedDBStorageBackend implements StorageBackend {
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
request.onupgradeneeded = (_event) => {
const db = request.result;
// Create object stores from config
@ -145,7 +145,7 @@ export class IndexedDBStorageBackend implements StorageBackend {
}
async getQuotaInfo(): Promise<{ usage: number; quota: number; percent: number }> {
if (navigator.storage && navigator.storage.estimate) {
if (navigator.storage?.estimate) {
const estimate = await navigator.storage.estimate();
return {
usage: estimate.usage || 0,
@ -157,7 +157,7 @@ export class IndexedDBStorageBackend implements StorageBackend {
}
async requestPersistence(): Promise<boolean> {
if (navigator.storage && navigator.storage.persist) {
if (navigator.storage?.persist) {
return await navigator.storage.persist();
}
return false;

View file

@ -83,4 +83,51 @@ export class SessionsStore extends Store {
async requestPersistence(): Promise<boolean> {
return this.getBackend().requestPersistence();
}
// Alias methods for backward compatibility
async saveSession(id: string, state: any, metadata: SessionMetadata | undefined, title?: string): Promise<void> {
// If metadata is provided, use it; otherwise create it from state
const meta: SessionMetadata = metadata || {
id,
title: title || "",
createdAt: new Date().toISOString(),
lastModified: new Date().toISOString(),
messageCount: state.messages?.length || 0,
usage: state.usage || {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
},
modelId: state.model?.id || null,
thinkingLevel: state.thinkingLevel || "off",
preview: "",
};
const data: SessionData = {
id,
title: title || meta.title,
model: state.model,
thinkingLevel: state.thinkingLevel,
messages: state.messages || [],
createdAt: meta.createdAt,
lastModified: new Date().toISOString(),
};
await this.save(data, meta);
}
async loadSession(id: string): Promise<SessionData | null> {
return this.get(id);
}
async getLatestSessionId(): Promise<string | null> {
const allMetadata = await this.getAllMetadata();
if (allMetadata.length === 0) return null;
// Sort by lastModified descending
allMetadata.sort((a, b) => b.lastModified.localeCompare(a.lastModified));
return allMetadata[0].id;
}
}

View file

@ -1,8 +1,8 @@
import { Diff, html, icon, iconDOM, type TemplateResult } from "@mariozechner/mini-lit";
import { Diff, html, type TemplateResult } from "@mariozechner/mini-lit";
import "@mariozechner/mini-lit/dist/CodeBlock.js";
import type { ToolResultMessage } from "@mariozechner/pi-ai";
import { createRef, ref } from "lit/directives/ref.js";
import { ChevronDown, ChevronRight, FileCode2, Loader } from "lucide";
import { FileCode2 } from "lucide";
import "../../components/ConsoleBlock.js";
import { i18n } from "../../utils/i18n.js";
import { renderCollapsibleHeader, renderHeader } from "../renderer-registry.js";
@ -232,7 +232,6 @@ export class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, unde
</div>
`;
case "delete":
default:
return html`
<div>

View file

@ -1,5 +1,6 @@
import { Button, icon } from "@mariozechner/mini-lit";
import { type AgentTool, type Message, StringEnum, type ToolCall, type ToolResultMessage } from "@mariozechner/pi-ai";
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
import { type AgentTool, type Message, StringEnum, type ToolCall } from "@mariozechner/pi-ai";
import { type Static, Type } from "@sinclair/typebox";
import { html, LitElement, type TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators.js";
@ -12,7 +13,6 @@ import { HtmlArtifact } from "./HtmlArtifact.js";
import { MarkdownArtifact } from "./MarkdownArtifact.js";
import { SvgArtifact } from "./SvgArtifact.js";
import { TextArtifact } from "./TextArtifact.js";
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
// Simple artifact model
export interface Artifact {