mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-17 10:02:23 +00:00
Fix message editor height adjustment, requires Chrome +123
This commit is contained in:
parent
b6b64dff86
commit
92f8801864
11 changed files with 41 additions and 38 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
"clean": "npm run clean --workspaces",
|
"clean": "npm run clean --workspaces",
|
||||||
"build": "npm run build -w @mariozechner/pi-tui && npm run build -w @mariozechner/pi-ai && npm run build -w @mariozechner/pi-agent && npm run build -w @mariozechner/pi-agent-old && npm run build -w @mariozechner/coding-agent && npm run build -w @mariozechner/pi-web-ui && npm run build -w @mariozechner/pi-proxy && npm run build -w @mariozechner/pi",
|
"build": "npm run build -w @mariozechner/pi-tui && npm run build -w @mariozechner/pi-ai && npm run build -w @mariozechner/pi-agent && npm run build -w @mariozechner/pi-agent-old && npm run build -w @mariozechner/coding-agent && npm run build -w @mariozechner/pi-web-ui && npm run build -w @mariozechner/pi-proxy && npm run build -w @mariozechner/pi",
|
||||||
"dev": "concurrently --names \"ai,web-ui,tui,proxy\" --prefix-colors \"cyan,green,magenta,blue\" \"npm run dev -w @mariozechner/pi-ai\" \"npm run dev -w @mariozechner/pi-web-ui\" \"npm run dev -w @mariozechner/pi-tui\" \"npm run dev -w @mariozechner/pi-proxy\"",
|
"dev": "concurrently --names \"ai,web-ui,tui,proxy\" --prefix-colors \"cyan,green,magenta,blue\" \"npm run dev -w @mariozechner/pi-ai\" \"npm run dev -w @mariozechner/pi-web-ui\" \"npm run dev -w @mariozechner/pi-tui\" \"npm run dev -w @mariozechner/pi-proxy\"",
|
||||||
|
"dev:tsc": "concurrently --names \"ai,web-ui\" --prefix-colors \"cyan,green\" \"npm run dev:tsc -w @mariozechner/pi-ai\" \"npm run dev:tsc -w @mariozechner/pi-web-ui\"",
|
||||||
"check": "biome check --write . && npm run check --workspaces && tsc --noEmit",
|
"check": "biome check --write . && npm run check --workspaces && tsc --noEmit",
|
||||||
"test": "npm run test --workspaces --if-present",
|
"test": "npm run test --workspaces --if-present",
|
||||||
"version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js",
|
"version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
"generate-models": "npx tsx scripts/generate-models.ts",
|
"generate-models": "npx tsx scripts/generate-models.ts",
|
||||||
"build": "npm run generate-models && tsc -p tsconfig.build.json",
|
"build": "npm run generate-models && tsc -p tsconfig.build.json",
|
||||||
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
||||||
|
"dev:tsc": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
||||||
"check": "biome check --write .",
|
"check": "biome check --write .",
|
||||||
"test": "vitest --run",
|
"test": "vitest --run",
|
||||||
"prepublishOnly": "npm run clean && npm run build"
|
"prepublishOnly": "npm run clean && npm run build"
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
"clean": "rm -rf dist",
|
"clean": "rm -rf dist",
|
||||||
"build": "tsc -p tsconfig.build.json && tailwindcss -i ./src/app.css -o ./dist/app.css --minify",
|
"build": "tsc -p tsconfig.build.json && tailwindcss -i ./src/app.css -o ./dist/app.css --minify",
|
||||||
"dev": "concurrently --names \"build,example\" --prefix-colors \"cyan,green\" \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tailwindcss -i ./src/app.css -o ./dist/app.css --watch\" \"npm run dev --prefix example\"",
|
"dev": "concurrently --names \"build,example\" --prefix-colors \"cyan,green\" \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tailwindcss -i ./src/app.css -o ./dist/app.css --watch\" \"npm run dev --prefix example\"",
|
||||||
|
"dev:tsc": "concurrently --names \"build\" --prefix-colors \"cyan\" \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tailwindcss -i ./src/app.css -o ./dist/app.css --watch\"",
|
||||||
"typecheck": "tsc --noEmit && cd example && tsc --noEmit",
|
"typecheck": "tsc --noEmit && cd example && tsc --noEmit",
|
||||||
"check": "npm run typecheck"
|
"check": "npm run typecheck"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ export class ChatPanel extends LitElement {
|
||||||
config?: {
|
config?: {
|
||||||
onApiKeyRequired?: (provider: string) => Promise<boolean>;
|
onApiKeyRequired?: (provider: string) => Promise<boolean>;
|
||||||
onBeforeSend?: () => void | Promise<void>;
|
onBeforeSend?: () => void | Promise<void>;
|
||||||
|
onCostClick?: () => void;
|
||||||
sandboxUrlProvider?: () => string;
|
sandboxUrlProvider?: () => string;
|
||||||
toolsFactory?: (
|
toolsFactory?: (
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
|
|
@ -79,6 +80,7 @@ export class ChatPanel extends LitElement {
|
||||||
this.agentInterface.showThemeToggle = false;
|
this.agentInterface.showThemeToggle = false;
|
||||||
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
|
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
|
||||||
this.agentInterface.onBeforeSend = config?.onBeforeSend;
|
this.agentInterface.onBeforeSend = config?.onBeforeSend;
|
||||||
|
this.agentInterface.onCostClick = config?.onCostClick;
|
||||||
|
|
||||||
// Set up artifacts panel
|
// Set up artifacts panel
|
||||||
this.artifactsPanel = new ArtifactsPanel();
|
this.artifactsPanel = new ArtifactsPanel();
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ export class AgentInterface extends LitElement {
|
||||||
@property({ attribute: false }) onBeforeSend?: () => void | Promise<void>;
|
@property({ attribute: false }) onBeforeSend?: () => void | Promise<void>;
|
||||||
// Optional callback called before executing a tool call - return false to prevent execution
|
// Optional callback called before executing a tool call - return false to prevent execution
|
||||||
@property({ attribute: false }) onBeforeToolCall?: (toolName: string, args: any) => boolean | Promise<boolean>;
|
@property({ attribute: false }) onBeforeToolCall?: (toolName: string, args: any) => boolean | Promise<boolean>;
|
||||||
|
// Optional callback called when cost display is clicked
|
||||||
|
@property({ attribute: false }) onCostClick?: () => void;
|
||||||
|
|
||||||
// References
|
// References
|
||||||
@query("message-editor") private _messageEditor!: MessageEditor;
|
@query("message-editor") private _messageEditor!: MessageEditor;
|
||||||
|
|
@ -226,6 +228,7 @@ export class AgentInterface extends LitElement {
|
||||||
.tools=${state.tools}
|
.tools=${state.tools}
|
||||||
.pendingToolCalls=${this.session ? this.session.state.pendingToolCalls : new Set<string>()}
|
.pendingToolCalls=${this.session ? this.session.state.pendingToolCalls : new Set<string>()}
|
||||||
.isStreaming=${state.isStreaming}
|
.isStreaming=${state.isStreaming}
|
||||||
|
.onCostClick=${this.onCostClick}
|
||||||
></message-list>
|
></message-list>
|
||||||
|
|
||||||
<!-- Streaming message container - manages its own updates -->
|
<!-- Streaming message container - manages its own updates -->
|
||||||
|
|
@ -235,6 +238,7 @@ export class AgentInterface extends LitElement {
|
||||||
.isStreaming=${state.isStreaming}
|
.isStreaming=${state.isStreaming}
|
||||||
.pendingToolCalls=${state.pendingToolCalls}
|
.pendingToolCalls=${state.pendingToolCalls}
|
||||||
.toolResultsById=${toolResultsById}
|
.toolResultsById=${toolResultsById}
|
||||||
|
.onCostClick=${this.onCostClick}
|
||||||
></streaming-message-container>
|
></streaming-message-container>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
@ -275,7 +279,15 @@ export class AgentInterface extends LitElement {
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
${this.showThemeToggle ? html`<theme-toggle></theme-toggle>` : html``}
|
${this.showThemeToggle ? html`<theme-toggle></theme-toggle>` : html``}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex ml-auto items-center gap-3">${totalsText ? html`<span>${totalsText}</span>` : ""}</div>
|
<div class="flex ml-auto items-center gap-3">
|
||||||
|
${
|
||||||
|
totalsText
|
||||||
|
? this.onCostClick
|
||||||
|
? html`<span class="cursor-pointer hover:text-foreground transition-colors" @click=${this.onCostClick}>${totalsText}</span>`
|
||||||
|
: html`<span>${totalsText}</span>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,6 @@ export class MessageEditor extends LitElement {
|
||||||
const oldValue = this._value;
|
const oldValue = this._value;
|
||||||
this._value = val;
|
this._value = val;
|
||||||
this.requestUpdate("value", oldValue);
|
this.requestUpdate("value", oldValue);
|
||||||
this.updateComplete.then(() => {
|
|
||||||
const textarea = this.textareaRef.value;
|
|
||||||
if (textarea) {
|
|
||||||
textarea.style.height = "auto";
|
|
||||||
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property() isStreaming = false;
|
@property() isStreaming = false;
|
||||||
|
|
@ -60,8 +53,6 @@ export class MessageEditor extends LitElement {
|
||||||
private handleTextareaInput = (e: Event) => {
|
private handleTextareaInput = (e: Event) => {
|
||||||
const textarea = e.target as HTMLTextAreaElement;
|
const textarea = e.target as HTMLTextAreaElement;
|
||||||
this.value = textarea.value;
|
this.value = textarea.value;
|
||||||
textarea.style.height = "auto";
|
|
||||||
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
||||||
this.onInput?.(this.value);
|
this.onInput?.(this.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -231,33 +222,13 @@ export class MessageEditor extends LitElement {
|
||||||
this.processingFiles = false;
|
this.processingFiles = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
private adjustTextareaHeight() {
|
|
||||||
const textarea = this.textareaRef.value;
|
|
||||||
if (textarea) {
|
|
||||||
// Reset height to auto to get accurate scrollHeight
|
|
||||||
textarea.style.height = "auto";
|
|
||||||
// Only adjust if there's content, otherwise keep minimal height
|
|
||||||
if (this.value.trim()) {
|
|
||||||
textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override firstUpdated() {
|
override firstUpdated() {
|
||||||
const textarea = this.textareaRef.value;
|
const textarea = this.textareaRef.value;
|
||||||
if (textarea) {
|
if (textarea) {
|
||||||
// Don't adjust height on first render - let it be minimal
|
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override updated() {
|
|
||||||
// Only adjust height when component updates if there's content
|
|
||||||
if (this.value) {
|
|
||||||
this.adjustTextareaHeight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
// Check if current model supports thinking/reasoning
|
// Check if current model supports thinking/reasoning
|
||||||
const model = this.currentModel;
|
const model = this.currentModel;
|
||||||
|
|
@ -304,7 +275,7 @@ export class MessageEditor extends LitElement {
|
||||||
class="w-full bg-transparent p-4 text-foreground placeholder-muted-foreground outline-none resize-none overflow-y-auto"
|
class="w-full bg-transparent p-4 text-foreground placeholder-muted-foreground outline-none resize-none overflow-y-auto"
|
||||||
placeholder=${i18n("Type a message...")}
|
placeholder=${i18n("Type a message...")}
|
||||||
rows="1"
|
rows="1"
|
||||||
style="max-height: 200px;"
|
style="max-height: 200px; field-sizing: content; min-height: 1lh; height: auto;"
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
@input=${this.handleTextareaInput}
|
@input=${this.handleTextareaInput}
|
||||||
@keydown=${this.handleKeyDown}
|
@keydown=${this.handleKeyDown}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export class MessageList extends LitElement {
|
||||||
@property({ type: Array }) tools: AgentTool[] = [];
|
@property({ type: Array }) tools: AgentTool[] = [];
|
||||||
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
||||||
@property({ type: Boolean }) isStreaming: boolean = false;
|
@property({ type: Boolean }) isStreaming: boolean = false;
|
||||||
|
@property({ attribute: false }) onCostClick?: () => void;
|
||||||
|
|
||||||
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -68,6 +69,7 @@ export class MessageList extends LitElement {
|
||||||
.pendingToolCalls=${this.pendingToolCalls}
|
.pendingToolCalls=${this.pendingToolCalls}
|
||||||
.toolResultsById=${resultByCallId}
|
.toolResultsById=${resultByCallId}
|
||||||
.hideToolCalls=${false}
|
.hideToolCalls=${false}
|
||||||
|
.onCostClick=${this.onCostClick}
|
||||||
></assistant-message>`,
|
></assistant-message>`,
|
||||||
});
|
});
|
||||||
index++;
|
index++;
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ export class AssistantMessage extends LitElement {
|
||||||
@property({ type: Boolean }) hideToolCalls = false;
|
@property({ type: Boolean }) hideToolCalls = false;
|
||||||
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessageType>;
|
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessageType>;
|
||||||
@property({ type: Boolean }) isStreaming: boolean = false;
|
@property({ type: Boolean }) isStreaming: boolean = false;
|
||||||
|
@property({ attribute: false }) onCostClick?: () => void;
|
||||||
|
|
||||||
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
protected override createRenderRoot(): HTMLElement | DocumentFragment {
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -133,7 +134,9 @@ export class AssistantMessage extends LitElement {
|
||||||
${orderedParts.length ? html` <div class="px-4 flex flex-col gap-3">${orderedParts}</div> ` : ""}
|
${orderedParts.length ? html` <div class="px-4 flex flex-col gap-3">${orderedParts}</div> ` : ""}
|
||||||
${
|
${
|
||||||
this.message.usage
|
this.message.usage
|
||||||
? html` <div class="px-4 mt-2 text-xs text-muted-foreground">${formatUsage(this.message.usage)}</div> `
|
? this.onCostClick
|
||||||
|
? html` <div class="px-4 mt-2 text-xs text-muted-foreground cursor-pointer hover:text-foreground transition-colors" @click=${this.onCostClick}>${formatUsage(this.message.usage)}</div> `
|
||||||
|
: html` <div class="px-4 mt-2 text-xs text-muted-foreground">${formatUsage(this.message.usage)}</div> `
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
${
|
${
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ export class StreamingMessageContainer extends LitElement {
|
||||||
@property({ type: Boolean }) isStreaming = false;
|
@property({ type: Boolean }) isStreaming = false;
|
||||||
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
@property({ type: Object }) pendingToolCalls?: Set<string>;
|
||||||
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessage>;
|
@property({ type: Object }) toolResultsById?: Map<string, ToolResultMessage>;
|
||||||
|
@property({ attribute: false }) onCostClick?: () => void;
|
||||||
|
|
||||||
@state() private _message: Message | null = null;
|
@state() private _message: Message | null = null;
|
||||||
private _pendingMessage: Message | null = null;
|
private _pendingMessage: Message | null = null;
|
||||||
|
|
@ -87,6 +88,7 @@ export class StreamingMessageContainer extends LitElement {
|
||||||
.pendingToolCalls=${this.pendingToolCalls}
|
.pendingToolCalls=${this.pendingToolCalls}
|
||||||
.toolResultsById=${this.toolResultsById}
|
.toolResultsById=${this.toolResultsById}
|
||||||
.hideToolCalls=${false}
|
.hideToolCalls=${false}
|
||||||
|
.onCostClick=${this.onCostClick}
|
||||||
></assistant-message>
|
></assistant-message>
|
||||||
${this.isStreaming ? html`<span class="mx-4 inline-block w-2 h-4 bg-muted-foreground animate-pulse"></span>` : ""}
|
${this.isStreaming ? html`<span class="mx-4 inline-block w-2 h-4 bg-muted-foreground animate-pulse"></span>` : ""}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ export class ProxyTab extends SettingsTab {
|
||||||
return html`
|
return html`
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
${i18n("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.")}
|
${i18n("Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.")}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
|
|
@ -109,6 +109,9 @@ export class ProxyTab extends SettingsTab {
|
||||||
},
|
},
|
||||||
onChange: () => this.saveProxySettings(),
|
onChange: () => this.saveProxySettings(),
|
||||||
})}
|
})}
|
||||||
|
<p class="text-xs text-muted-foreground">
|
||||||
|
${i18n("Format: The proxy must accept requests as <proxy-url>/?url=<target-url>")}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
||||||
|
|
@ -137,11 +137,12 @@ declare module "@mariozechner/mini-lit" {
|
||||||
Proxy: string;
|
Proxy: string;
|
||||||
"Use CORS Proxy": string;
|
"Use CORS Proxy": string;
|
||||||
"Proxy URL": string;
|
"Proxy URL": string;
|
||||||
|
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>": string;
|
||||||
"Settings are stored locally in your browser": string;
|
"Settings are stored locally in your browser": string;
|
||||||
Clear: string;
|
Clear: string;
|
||||||
"API Key Required": string;
|
"API Key Required": string;
|
||||||
"Enter your API key for {provider}": 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;
|
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.": string;
|
||||||
Off: string;
|
Off: string;
|
||||||
Minimal: string;
|
Minimal: string;
|
||||||
Low: string;
|
Low: string;
|
||||||
|
|
@ -312,12 +313,14 @@ export const translations = {
|
||||||
Proxy: "Proxy",
|
Proxy: "Proxy",
|
||||||
"Use CORS Proxy": "Use CORS Proxy",
|
"Use CORS Proxy": "Use CORS Proxy",
|
||||||
"Proxy URL": "Proxy URL",
|
"Proxy URL": "Proxy URL",
|
||||||
|
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>":
|
||||||
|
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>",
|
||||||
"Settings are stored locally in your browser": "Settings are stored locally in your browser",
|
"Settings are stored locally in your browser": "Settings are stored locally in your browser",
|
||||||
Clear: "Clear",
|
Clear: "Clear",
|
||||||
"API Key Required": "API Key Required",
|
"API Key Required": "API Key Required",
|
||||||
"Enter your API key for {provider}": "Enter your API key for {provider}",
|
"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.":
|
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.":
|
||||||
"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.",
|
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.",
|
||||||
Off: "Off",
|
Off: "Off",
|
||||||
Minimal: "Minimal",
|
Minimal: "Minimal",
|
||||||
Low: "Low",
|
Low: "Low",
|
||||||
|
|
@ -491,12 +494,14 @@ export const translations = {
|
||||||
Proxy: "Proxy",
|
Proxy: "Proxy",
|
||||||
"Use CORS Proxy": "CORS-Proxy verwenden",
|
"Use CORS Proxy": "CORS-Proxy verwenden",
|
||||||
"Proxy URL": "Proxy-URL",
|
"Proxy URL": "Proxy-URL",
|
||||||
|
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>":
|
||||||
|
"Format: Der Proxy muss Anfragen als <proxy-url>/?url=<ziel-url> akzeptieren",
|
||||||
"Settings are stored locally in your browser": "Einstellungen werden lokal in Ihrem Browser gespeichert",
|
"Settings are stored locally in your browser": "Einstellungen werden lokal in Ihrem Browser gespeichert",
|
||||||
Clear: "Löschen",
|
Clear: "Löschen",
|
||||||
"API Key Required": "API-Schlüssel erforderlich",
|
"API Key Required": "API-Schlüssel erforderlich",
|
||||||
"Enter your API key for {provider}": "Geben Sie Ihren API-Schlüssel für {provider} ein",
|
"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.":
|
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.":
|
||||||
"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.",
|
"Ermöglicht browserbasierten Anwendungen, CORS-Einschränkungen beim Aufruf von LLM-Anbietern zu umgehen. Erforderlich für Z-AI und Anthropic mit OAuth-Token.",
|
||||||
Off: "Aus",
|
Off: "Aus",
|
||||||
Minimal: "Minimal",
|
Minimal: "Minimal",
|
||||||
Low: "Niedrig",
|
Low: "Niedrig",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue