Enable thinking selector in MessageEditor

- Import Select component from mini-lit and Brain icon from lucide
- Add thinkingLevel property to track current thinking level (off/minimal/low/medium/high)
- Enable showThinkingSelector (was disabled before)
- Add onThinkingChange callback for thinking level changes
- Render Select component with thinking level options when model supports reasoning
- Position thinking selector on left side next to attachment button
- Add i18n translations for Off/Minimal/Low/Medium/High in English and German
- Rename showThinking → showThinkingSelector in AgentInterface and ChatPanel for consistency
- Remove showDebugToggle property from AgentInterface (unused)
- Clean up browser-javascript tool output message (remove CSP note)
This commit is contained in:
Mario Zechner 2025-10-05 23:32:30 +02:00
parent 414a4eb8fd
commit 53e339ddb8
5 changed files with 54 additions and 12 deletions

View file

@ -309,9 +309,7 @@ This ensures reliable execution.`,
);
return {
output:
output.trim() ||
"Code executed successfully (no output)\n\n⚠ Note: CSP blocked direct execution. Code ran via JailJS interpreter.",
output: output.trim() || "Code executed successfully (no output)",
isError: false,
details: { files },
};

View file

@ -168,7 +168,7 @@ export class ChatPanel extends LitElement {
.session=${this.session}
.enableAttachments=${true}
.enableModelSelector=${true}
.enableThinking=${true}
.showThinkingSelector=${true}
.showThemeToggle=${false}
.showDebugToggle=${false}
.onApiKeyRequired=${this.onApiKeyRequired}

View file

@ -21,9 +21,8 @@ export class AgentInterface extends LitElement {
@property({ attribute: false }) session?: AgentSession;
@property() enableAttachments = true;
@property() enableModelSelector = true;
@property() enableThinking = true;
@property() enableThinkingSelector = true;
@property() showThemeToggle = false;
@property() showDebugToggle = false;
// Optional custom API key prompt handler - if not provided, uses default dialog
@property({ attribute: false }) onApiKeyRequired?: (provider: string) => Promise<boolean>;
@ -286,7 +285,7 @@ export class AgentInterface extends LitElement {
.thinkingLevel=${state.thinkingLevel}
.showAttachmentButton=${this.enableAttachments}
.showModelSelector=${this.enableModelSelector}
.showThinking=${this.enableThinking}
.showThinkingSelector=${this.enableThinkingSelector}
.onSend=${(input: string, attachments: Attachment[]) => {
this.sendMessage(input, attachments);
}}
@ -295,7 +294,7 @@ export class AgentInterface extends LitElement {
ModelSelector.open(state.model, (model) => session.setModel(model));
}}
.onThinkingChange=${
this.enableThinking
this.enableThinkingSelector
? (level: "off" | "minimal" | "low" | "medium" | "high") => {
session.setThinkingLevel(level);
}

View file

@ -1,9 +1,9 @@
import { Button, html, icon } from "@mariozechner/mini-lit";
import { Button, html, icon, Select, type SelectOption } from "@mariozechner/mini-lit";
import type { Model } from "@mariozechner/pi-ai";
import { LitElement } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js";
import { Loader2, Paperclip, Send, Sparkles, Square } from "lucide";
import { Brain, Loader2, Paperclip, Send, Sparkles, Square } from "lucide";
import "./AttachmentTile.js";
import { type Attachment, loadAttachment } from "../utils/attachment-utils.js";
import { i18n } from "../utils/i18n.js";
@ -33,13 +33,15 @@ export class MessageEditor extends LitElement {
@property() isStreaming = false;
@property() currentModel?: Model<any>;
@property() thinkingLevel: "off" | "minimal" | "low" | "medium" | "high" = "off";
@property() showAttachmentButton = true;
@property() showModelSelector = true;
@property() showThinking = false; // Disabled for now
@property() showThinkingSelector = true;
@property() onInput?: (value: string) => void;
@property() onSend?: (input: string, attachments: Attachment[]) => void;
@property() onAbort?: () => void;
@property() onModelSelect?: () => void;
@property() onThinkingChange?: (level: "off" | "minimal" | "low" | "medium" | "high") => void;
@property() onFilesChange?: (files: Attachment[]) => void;
@property() attachments: Attachment[] = [];
@property() maxFiles = 10;
@ -150,6 +152,10 @@ export class MessageEditor extends LitElement {
}
override render() {
// Check if current model supports thinking/reasoning
const model = this.currentModel;
const supportsThinking = model?.reasoning === true; // Models with reasoning:true support thinking
return html`
<div class="bg-card rounded-xl border border-border shadow-sm">
<!-- Attachments -->
@ -194,7 +200,7 @@ export class MessageEditor extends LitElement {
<!-- Button Row -->
<div class="px-2 pb-2 flex items-center justify-between">
<!-- Left side - attachment and quick action buttons -->
<!-- Left side - attachment and thinking selector -->
<div class="flex gap-2 items-center">
${
this.showAttachmentButton
@ -215,6 +221,30 @@ export class MessageEditor extends LitElement {
`
: ""
}
${
supportsThinking && this.showThinkingSelector
? html`
${Select({
value: this.thinkingLevel,
placeholder: i18n("Off"),
options: [
{ value: "off", label: i18n("Off"), icon: icon(Brain, "sm") },
{ value: "minimal", label: i18n("Minimal"), icon: icon(Brain, "sm") },
{ value: "low", label: i18n("Low"), icon: icon(Brain, "sm") },
{ value: "medium", label: i18n("Medium"), icon: icon(Brain, "sm") },
{ value: "high", label: i18n("High"), icon: icon(Brain, "sm") },
] as SelectOption[],
onChange: (value: string) => {
this.onThinkingChange?.(value as "off" | "minimal" | "low" | "medium" | "high");
},
width: "80px",
size: "sm",
variant: "ghost",
fitContent: true,
})}
`
: ""
}
</div>
<!-- Model selector and send on the right -->

View file

@ -108,6 +108,11 @@ declare module "@mariozechner/mini-lit" {
"API Key Required": string;
"Enter your API key for {provider}": string;
"The CORS proxy strips CORS headers from API responses, allowing browser-based apps to make direct calls to LLM providers without CORS restrictions. It forwards requests to providers while removing headers that would otherwise block cross-origin requests.": string;
Off: string;
Minimal: string;
Low: string;
Medium: string;
High: string;
}
}
@ -223,6 +228,11 @@ const translations = {
"Enter your API key for {provider}": "Enter your API key for {provider}",
"The CORS proxy strips CORS headers from API responses, allowing browser-based apps to make direct calls to LLM providers without CORS restrictions. It forwards requests to providers while removing headers that would otherwise block cross-origin requests.":
"The CORS proxy strips CORS headers from API responses, allowing browser-based apps to make direct calls to LLM providers without CORS restrictions. It forwards requests to providers while removing headers that would otherwise block cross-origin requests.",
Off: "Off",
Minimal: "Minimal",
Low: "Low",
Medium: "Medium",
High: "High",
},
de: {
...defaultGerman,
@ -335,6 +345,11 @@ const translations = {
"Enter your API key for {provider}": "Geben Sie Ihren API-Schlüssel für {provider} ein",
"The CORS proxy strips CORS headers from API responses, allowing browser-based apps to make direct calls to LLM providers without CORS restrictions. It forwards requests to providers while removing headers that would otherwise block cross-origin requests.":
"Der CORS-Proxy entfernt CORS-Header aus API-Antworten und ermöglicht browserbasierte Anwendungen, direkte Aufrufe an LLM-Anbieter ohne CORS-Einschränkungen durchzuführen. Er leitet Anfragen an Anbieter weiter und entfernt Header, die sonst Cross-Origin-Anfragen blockieren würden.",
Off: "Aus",
Minimal: "Minimal",
Low: "Niedrig",
Medium: "Mittel",
High: "Hoch",
},
};