mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-30 23:41:47 +00:00
mac: bundle web chat assets
This commit is contained in:
19
apps/macos/Sources/Clawdis/Resources/WebChat/utils/attachment-utils.d.ts
vendored
Normal file
19
apps/macos/Sources/Clawdis/Resources/WebChat/utils/attachment-utils.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface Attachment {
|
||||
id: string;
|
||||
type: "image" | "document";
|
||||
fileName: string;
|
||||
mimeType: string;
|
||||
size: number;
|
||||
content: string;
|
||||
extractedText?: string;
|
||||
preview?: string;
|
||||
}
|
||||
/**
|
||||
* Load an attachment from various sources
|
||||
* @param source - URL string, File, Blob, or ArrayBuffer
|
||||
* @param fileName - Optional filename override
|
||||
* @returns Promise<Attachment>
|
||||
* @throws Error if loading fails
|
||||
*/
|
||||
export declare function loadAttachment(source: string | File | Blob | ArrayBuffer, fileName?: string): Promise<Attachment>;
|
||||
//# sourceMappingURL=attachment-utils.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"attachment-utils.d.ts","sourceRoot":"","sources":["../../src/utils/attachment-utils.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,WAAW,EAC1C,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CAyKrB"}
|
||||
@@ -0,0 +1,415 @@
|
||||
import { parseAsync } from "docx-preview";
|
||||
import JSZip from "jszip";
|
||||
import * as pdfjsLib from "pdfjs-dist";
|
||||
import * as XLSX from "xlsx";
|
||||
import { i18n } from "./i18n.js";
|
||||
// Configure PDF.js worker - we'll need to bundle this
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString();
|
||||
/**
|
||||
* Load an attachment from various sources
|
||||
* @param source - URL string, File, Blob, or ArrayBuffer
|
||||
* @param fileName - Optional filename override
|
||||
* @returns Promise<Attachment>
|
||||
* @throws Error if loading fails
|
||||
*/
|
||||
export async function loadAttachment(source, fileName) {
|
||||
let arrayBuffer;
|
||||
let detectedFileName = fileName || "unnamed";
|
||||
let mimeType = "application/octet-stream";
|
||||
let size = 0;
|
||||
// Convert source to ArrayBuffer
|
||||
if (typeof source === "string") {
|
||||
// It's a URL - fetch it
|
||||
const response = await fetch(source);
|
||||
if (!response.ok) {
|
||||
throw new Error(i18n("Failed to fetch file"));
|
||||
}
|
||||
arrayBuffer = await response.arrayBuffer();
|
||||
size = arrayBuffer.byteLength;
|
||||
mimeType = response.headers.get("content-type") || mimeType;
|
||||
if (!fileName) {
|
||||
// Try to extract filename from URL
|
||||
const urlParts = source.split("/");
|
||||
detectedFileName = urlParts[urlParts.length - 1] || "document";
|
||||
}
|
||||
}
|
||||
else if (source instanceof File) {
|
||||
arrayBuffer = await source.arrayBuffer();
|
||||
size = source.size;
|
||||
mimeType = source.type || mimeType;
|
||||
detectedFileName = fileName || source.name;
|
||||
}
|
||||
else if (source instanceof Blob) {
|
||||
arrayBuffer = await source.arrayBuffer();
|
||||
size = source.size;
|
||||
mimeType = source.type || mimeType;
|
||||
}
|
||||
else if (source instanceof ArrayBuffer) {
|
||||
arrayBuffer = source;
|
||||
size = source.byteLength;
|
||||
}
|
||||
else {
|
||||
throw new Error(i18n("Invalid source type"));
|
||||
}
|
||||
// Convert ArrayBuffer to base64 - handle large files properly
|
||||
const uint8Array = new Uint8Array(arrayBuffer);
|
||||
let binary = "";
|
||||
const chunkSize = 0x8000; // Process in 32KB chunks to avoid stack overflow
|
||||
for (let i = 0; i < uint8Array.length; i += chunkSize) {
|
||||
const chunk = uint8Array.slice(i, i + chunkSize);
|
||||
binary += String.fromCharCode(...chunk);
|
||||
}
|
||||
const base64Content = btoa(binary);
|
||||
// Detect type and process accordingly
|
||||
const id = `${detectedFileName}_${Date.now()}_${Math.random()}`;
|
||||
// Check if it's a PDF
|
||||
if (mimeType === "application/pdf" || detectedFileName.toLowerCase().endsWith(".pdf")) {
|
||||
const { extractedText, preview } = await processPdf(arrayBuffer, detectedFileName);
|
||||
return {
|
||||
id,
|
||||
type: "document",
|
||||
fileName: detectedFileName,
|
||||
mimeType: "application/pdf",
|
||||
size,
|
||||
content: base64Content,
|
||||
extractedText,
|
||||
preview,
|
||||
};
|
||||
}
|
||||
// Check if it's a DOCX file
|
||||
if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ||
|
||||
detectedFileName.toLowerCase().endsWith(".docx")) {
|
||||
const { extractedText } = await processDocx(arrayBuffer, detectedFileName);
|
||||
return {
|
||||
id,
|
||||
type: "document",
|
||||
fileName: detectedFileName,
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
size,
|
||||
content: base64Content,
|
||||
extractedText,
|
||||
};
|
||||
}
|
||||
// Check if it's a PPTX file
|
||||
if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation" ||
|
||||
detectedFileName.toLowerCase().endsWith(".pptx")) {
|
||||
const { extractedText } = await processPptx(arrayBuffer, detectedFileName);
|
||||
return {
|
||||
id,
|
||||
type: "document",
|
||||
fileName: detectedFileName,
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
size,
|
||||
content: base64Content,
|
||||
extractedText,
|
||||
};
|
||||
}
|
||||
// Check if it's an Excel file (XLSX/XLS)
|
||||
const excelMimeTypes = [
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"application/vnd.ms-excel",
|
||||
];
|
||||
if (excelMimeTypes.includes(mimeType) ||
|
||||
detectedFileName.toLowerCase().endsWith(".xlsx") ||
|
||||
detectedFileName.toLowerCase().endsWith(".xls")) {
|
||||
const { extractedText } = await processExcel(arrayBuffer, detectedFileName);
|
||||
return {
|
||||
id,
|
||||
type: "document",
|
||||
fileName: detectedFileName,
|
||||
mimeType: mimeType.startsWith("application/vnd")
|
||||
? mimeType
|
||||
: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
size,
|
||||
content: base64Content,
|
||||
extractedText,
|
||||
};
|
||||
}
|
||||
// Check if it's an image
|
||||
if (mimeType.startsWith("image/")) {
|
||||
return {
|
||||
id,
|
||||
type: "image",
|
||||
fileName: detectedFileName,
|
||||
mimeType,
|
||||
size,
|
||||
content: base64Content,
|
||||
preview: base64Content, // For images, preview is the same as content
|
||||
};
|
||||
}
|
||||
// Check if it's a text document
|
||||
const textExtensions = [
|
||||
".txt",
|
||||
".md",
|
||||
".json",
|
||||
".xml",
|
||||
".html",
|
||||
".css",
|
||||
".js",
|
||||
".ts",
|
||||
".jsx",
|
||||
".tsx",
|
||||
".yml",
|
||||
".yaml",
|
||||
];
|
||||
const isTextFile = mimeType.startsWith("text/") || textExtensions.some((ext) => detectedFileName.toLowerCase().endsWith(ext));
|
||||
if (isTextFile) {
|
||||
const decoder = new TextDecoder();
|
||||
const text = decoder.decode(arrayBuffer);
|
||||
return {
|
||||
id,
|
||||
type: "document",
|
||||
fileName: detectedFileName,
|
||||
mimeType: mimeType.startsWith("text/") ? mimeType : "text/plain",
|
||||
size,
|
||||
content: base64Content,
|
||||
extractedText: text,
|
||||
};
|
||||
}
|
||||
throw new Error(`Unsupported file type: ${mimeType}`);
|
||||
}
|
||||
async function processPdf(arrayBuffer, fileName) {
|
||||
let pdf = null;
|
||||
try {
|
||||
pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
|
||||
// Extract text with page structure
|
||||
let extractedText = `<pdf filename="${fileName}">`;
|
||||
for (let i = 1; i <= pdf.numPages; i++) {
|
||||
const page = await pdf.getPage(i);
|
||||
const textContent = await page.getTextContent();
|
||||
const pageText = textContent.items
|
||||
.map((item) => item.str)
|
||||
.filter((str) => str.trim())
|
||||
.join(" ");
|
||||
extractedText += `\n<page number="${i}">\n${pageText}\n</page>`;
|
||||
}
|
||||
extractedText += "\n</pdf>";
|
||||
// Generate preview from first page
|
||||
const preview = await generatePdfPreview(pdf);
|
||||
return { extractedText, preview };
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error processing PDF:", error);
|
||||
throw new Error(`Failed to process PDF: ${String(error)}`);
|
||||
}
|
||||
finally {
|
||||
// Clean up PDF resources
|
||||
if (pdf) {
|
||||
pdf.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
async function generatePdfPreview(pdf) {
|
||||
try {
|
||||
const page = await pdf.getPage(1);
|
||||
const viewport = page.getViewport({ scale: 1.0 });
|
||||
// Create canvas with reasonable size for thumbnail (160x160 max)
|
||||
const scale = Math.min(160 / viewport.width, 160 / viewport.height);
|
||||
const scaledViewport = page.getViewport({ scale });
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) {
|
||||
return undefined;
|
||||
}
|
||||
canvas.height = scaledViewport.height;
|
||||
canvas.width = scaledViewport.width;
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: scaledViewport,
|
||||
canvas: canvas,
|
||||
};
|
||||
await page.render(renderContext).promise;
|
||||
// Return base64 without data URL prefix
|
||||
return canvas.toDataURL("image/png").split(",")[1];
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error generating PDF preview:", error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
async function processDocx(arrayBuffer, fileName) {
|
||||
try {
|
||||
// Parse document structure
|
||||
const wordDoc = await parseAsync(arrayBuffer);
|
||||
// Extract structured text from document body
|
||||
let extractedText = `<docx filename="${fileName}">\n<page number="1">\n`;
|
||||
const body = wordDoc.documentPart?.body;
|
||||
if (body?.children) {
|
||||
// Walk through document elements and extract text
|
||||
const texts = [];
|
||||
for (const element of body.children) {
|
||||
const text = extractTextFromElement(element);
|
||||
if (text) {
|
||||
texts.push(text);
|
||||
}
|
||||
}
|
||||
extractedText += texts.join("\n");
|
||||
}
|
||||
extractedText += `\n</page>\n</docx>`;
|
||||
return { extractedText };
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error processing DOCX:", error);
|
||||
throw new Error(`Failed to process DOCX: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
function extractTextFromElement(element) {
|
||||
let text = "";
|
||||
// Check type with lowercase
|
||||
const elementType = element.type?.toLowerCase() || "";
|
||||
// Handle paragraphs
|
||||
if (elementType === "paragraph" && element.children) {
|
||||
for (const child of element.children) {
|
||||
const childType = child.type?.toLowerCase() || "";
|
||||
if (childType === "run" && child.children) {
|
||||
for (const textChild of child.children) {
|
||||
const textType = textChild.type?.toLowerCase() || "";
|
||||
if (textType === "text") {
|
||||
text += textChild.text || "";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (childType === "text") {
|
||||
text += child.text || "";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle tables
|
||||
else if (elementType === "table") {
|
||||
if (element.children) {
|
||||
const tableTexts = [];
|
||||
for (const row of element.children) {
|
||||
const rowType = row.type?.toLowerCase() || "";
|
||||
if (rowType === "tablerow" && row.children) {
|
||||
const rowTexts = [];
|
||||
for (const cell of row.children) {
|
||||
const cellType = cell.type?.toLowerCase() || "";
|
||||
if (cellType === "tablecell" && cell.children) {
|
||||
const cellTexts = [];
|
||||
for (const cellElement of cell.children) {
|
||||
const cellText = extractTextFromElement(cellElement);
|
||||
if (cellText)
|
||||
cellTexts.push(cellText);
|
||||
}
|
||||
if (cellTexts.length > 0)
|
||||
rowTexts.push(cellTexts.join(" "));
|
||||
}
|
||||
}
|
||||
if (rowTexts.length > 0)
|
||||
tableTexts.push(rowTexts.join(" | "));
|
||||
}
|
||||
}
|
||||
if (tableTexts.length > 0) {
|
||||
text = "\n[Table]\n" + tableTexts.join("\n") + "\n[/Table]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recursively handle other container elements
|
||||
else if (element.children && Array.isArray(element.children)) {
|
||||
const childTexts = [];
|
||||
for (const child of element.children) {
|
||||
const childText = extractTextFromElement(child);
|
||||
if (childText)
|
||||
childTexts.push(childText);
|
||||
}
|
||||
text = childTexts.join(" ");
|
||||
}
|
||||
return text.trim();
|
||||
}
|
||||
async function processPptx(arrayBuffer, fileName) {
|
||||
try {
|
||||
// Load the PPTX file as a ZIP
|
||||
const zip = await JSZip.loadAsync(arrayBuffer);
|
||||
// PPTX slides are stored in ppt/slides/slide[n].xml
|
||||
let extractedText = `<pptx filename="${fileName}">`;
|
||||
// Get all slide files and sort them numerically
|
||||
const slideFiles = Object.keys(zip.files)
|
||||
.filter((name) => name.match(/ppt\/slides\/slide\d+\.xml$/))
|
||||
.sort((a, b) => {
|
||||
const numA = Number.parseInt(a.match(/slide(\d+)\.xml$/)?.[1] || "0", 10);
|
||||
const numB = Number.parseInt(b.match(/slide(\d+)\.xml$/)?.[1] || "0", 10);
|
||||
return numA - numB;
|
||||
});
|
||||
// Extract text from each slide
|
||||
for (let i = 0; i < slideFiles.length; i++) {
|
||||
const slideFile = zip.file(slideFiles[i]);
|
||||
if (slideFile) {
|
||||
const slideXml = await slideFile.async("text");
|
||||
// Extract text from XML (simple regex approach)
|
||||
// Looking for <a:t> tags which contain text in PPTX
|
||||
const textMatches = slideXml.match(/<a:t[^>]*>([^<]+)<\/a:t>/g);
|
||||
if (textMatches) {
|
||||
extractedText += `\n<slide number="${i + 1}">`;
|
||||
const slideTexts = textMatches
|
||||
.map((match) => {
|
||||
const textMatch = match.match(/<a:t[^>]*>([^<]+)<\/a:t>/);
|
||||
return textMatch ? textMatch[1] : "";
|
||||
})
|
||||
.filter((t) => t.trim());
|
||||
if (slideTexts.length > 0) {
|
||||
extractedText += "\n" + slideTexts.join("\n");
|
||||
}
|
||||
extractedText += "\n</slide>";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also try to extract text from notes
|
||||
const notesFiles = Object.keys(zip.files)
|
||||
.filter((name) => name.match(/ppt\/notesSlides\/notesSlide\d+\.xml$/))
|
||||
.sort((a, b) => {
|
||||
const numA = Number.parseInt(a.match(/notesSlide(\d+)\.xml$/)?.[1] || "0", 10);
|
||||
const numB = Number.parseInt(b.match(/notesSlide(\d+)\.xml$/)?.[1] || "0", 10);
|
||||
return numA - numB;
|
||||
});
|
||||
if (notesFiles.length > 0) {
|
||||
extractedText += "\n<notes>";
|
||||
for (const noteFile of notesFiles) {
|
||||
const file = zip.file(noteFile);
|
||||
if (file) {
|
||||
const noteXml = await file.async("text");
|
||||
const textMatches = noteXml.match(/<a:t[^>]*>([^<]+)<\/a:t>/g);
|
||||
if (textMatches) {
|
||||
const noteTexts = textMatches
|
||||
.map((match) => {
|
||||
const textMatch = match.match(/<a:t[^>]*>([^<]+)<\/a:t>/);
|
||||
return textMatch ? textMatch[1] : "";
|
||||
})
|
||||
.filter((t) => t.trim());
|
||||
if (noteTexts.length > 0) {
|
||||
const slideNum = noteFile.match(/notesSlide(\d+)\.xml$/)?.[1];
|
||||
extractedText += `\n[Slide ${slideNum} notes]: ${noteTexts.join(" ")}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
extractedText += "\n</notes>";
|
||||
}
|
||||
extractedText += "\n</pptx>";
|
||||
return { extractedText };
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error processing PPTX:", error);
|
||||
throw new Error(`Failed to process PPTX: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
async function processExcel(arrayBuffer, fileName) {
|
||||
try {
|
||||
// Read the workbook
|
||||
const workbook = XLSX.read(arrayBuffer, { type: "array" });
|
||||
let extractedText = `<excel filename="${fileName}">`;
|
||||
// Process each sheet
|
||||
for (const [index, sheetName] of workbook.SheetNames.entries()) {
|
||||
const worksheet = workbook.Sheets[sheetName];
|
||||
// Extract text as CSV for the extractedText field
|
||||
const csvText = XLSX.utils.sheet_to_csv(worksheet);
|
||||
extractedText += `\n<sheet name="${sheetName}" index="${index + 1}">\n${csvText}\n</sheet>`;
|
||||
}
|
||||
extractedText += "\n</excel>";
|
||||
return { extractedText };
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error processing Excel:", error);
|
||||
throw new Error(`Failed to process Excel: ${String(error)}`);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=attachment-utils.js.map
|
||||
File diff suppressed because one or more lines are too long
3
apps/macos/Sources/Clawdis/Resources/WebChat/utils/auth-token.d.ts
vendored
Normal file
3
apps/macos/Sources/Clawdis/Resources/WebChat/utils/auth-token.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare function getAuthToken(): Promise<string | undefined>;
|
||||
export declare function clearAuthToken(): Promise<void>;
|
||||
//# sourceMappingURL=auth-token.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth-token.d.ts","sourceRoot":"","sources":["../../src/utils/auth-token.ts"],"names":[],"mappings":"AAGA,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAchE;AAED,wBAAsB,cAAc,kBAEnC"}
|
||||
@@ -0,0 +1,19 @@
|
||||
import PromptDialog from "@mariozechner/mini-lit/dist/PromptDialog.js";
|
||||
import { i18n } from "./i18n.js";
|
||||
export async function getAuthToken() {
|
||||
let authToken = localStorage.getItem(`auth-token`) || "";
|
||||
if (authToken)
|
||||
return authToken;
|
||||
while (true) {
|
||||
authToken = (await PromptDialog.ask(i18n("Enter Auth Token"), i18n("Please enter your auth token."), "", true))?.trim();
|
||||
if (authToken) {
|
||||
localStorage.setItem(`auth-token`, authToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return authToken?.trim() || undefined;
|
||||
}
|
||||
export async function clearAuthToken() {
|
||||
localStorage.removeItem(`auth-token`);
|
||||
}
|
||||
//# sourceMappingURL=auth-token.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth-token.js","sourceRoot":"","sources":["../../src/utils/auth-token.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,6CAA6C,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,YAAY;IACjC,IAAI,SAAS,GAAuB,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAC7E,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAEhC,OAAO,IAAI,EAAE,CAAC;QACb,SAAS,GAAG,CACX,MAAM,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,IAAI,CAAC,+BAA+B,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,CACjG,EAAE,IAAI,EAAE,CAAC;QACV,IAAI,SAAS,EAAE,CAAC;YACf,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YAC9C,MAAM;QACP,CAAC;IACF,CAAC;IACD,OAAO,SAAS,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IACnC,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACvC,CAAC"}
|
||||
6
apps/macos/Sources/Clawdis/Resources/WebChat/utils/format.d.ts
vendored
Normal file
6
apps/macos/Sources/Clawdis/Resources/WebChat/utils/format.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { Usage } from "@mariozechner/pi-ai";
|
||||
export declare function formatCost(cost: number): string;
|
||||
export declare function formatModelCost(cost: any): string;
|
||||
export declare function formatUsage(usage: Usage): string;
|
||||
export declare function formatTokenCount(count: number): string;
|
||||
//# sourceMappingURL=format.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAejD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,UAWvC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAItD"}
|
||||
47
apps/macos/Sources/Clawdis/Resources/WebChat/utils/format.js
Normal file
47
apps/macos/Sources/Clawdis/Resources/WebChat/utils/format.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import { i18n } from "@mariozechner/mini-lit";
|
||||
export function formatCost(cost) {
|
||||
return `$${cost.toFixed(4)}`;
|
||||
}
|
||||
export function formatModelCost(cost) {
|
||||
if (!cost)
|
||||
return i18n("Free");
|
||||
const input = cost.input || 0;
|
||||
const output = cost.output || 0;
|
||||
if (input === 0 && output === 0)
|
||||
return i18n("Free");
|
||||
// Format numbers with appropriate precision
|
||||
const formatNum = (num) => {
|
||||
if (num >= 100)
|
||||
return num.toFixed(0);
|
||||
if (num >= 10)
|
||||
return num.toFixed(1).replace(/\.0$/, "");
|
||||
if (num >= 1)
|
||||
return num.toFixed(2).replace(/\.?0+$/, "");
|
||||
return num.toFixed(3).replace(/\.?0+$/, "");
|
||||
};
|
||||
return `$${formatNum(input)}/$${formatNum(output)}`;
|
||||
}
|
||||
export function formatUsage(usage) {
|
||||
if (!usage)
|
||||
return "";
|
||||
const parts = [];
|
||||
if (usage.input)
|
||||
parts.push(`↑${formatTokenCount(usage.input)}`);
|
||||
if (usage.output)
|
||||
parts.push(`↓${formatTokenCount(usage.output)}`);
|
||||
if (usage.cacheRead)
|
||||
parts.push(`R${formatTokenCount(usage.cacheRead)}`);
|
||||
if (usage.cacheWrite)
|
||||
parts.push(`W${formatTokenCount(usage.cacheWrite)}`);
|
||||
if (usage.cost?.total)
|
||||
parts.push(formatCost(usage.cost.total));
|
||||
return parts.join(" ");
|
||||
}
|
||||
export function formatTokenCount(count) {
|
||||
if (count < 1000)
|
||||
return count.toString();
|
||||
if (count < 10000)
|
||||
return (count / 1000).toFixed(1) + "k";
|
||||
return Math.round(count / 1000) + "k";
|
||||
}
|
||||
//# sourceMappingURL=format.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAG9C,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAS;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAErD,4CAA4C;IAC5C,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE;QACzC,IAAI,GAAG,IAAI,GAAG;YAAE,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,GAAG,IAAI,EAAE;YAAE,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,GAAG,IAAI,CAAC;YAAE,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAY;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,MAAM,KAAK,GAAG,EAAE,CAAC;IACjB,IAAI,KAAK,CAAC,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,MAAM;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnE,IAAI,KAAK,CAAC,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,KAAK,CAAC,UAAU;QAAE,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAC3E,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEhE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC7C,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,KAAK;QAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;IAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC;AACvC,CAAC"}
|
||||
636
apps/macos/Sources/Clawdis/Resources/WebChat/utils/i18n.d.ts
vendored
Normal file
636
apps/macos/Sources/Clawdis/Resources/WebChat/utils/i18n.d.ts
vendored
Normal file
@@ -0,0 +1,636 @@
|
||||
import { type MiniLitRequiredMessages } from "@mariozechner/mini-lit";
|
||||
declare module "@mariozechner/mini-lit" {
|
||||
interface i18nMessages extends MiniLitRequiredMessages {
|
||||
Free: string;
|
||||
"Input Required": string;
|
||||
Cancel: string;
|
||||
Confirm: string;
|
||||
"Select Model": string;
|
||||
"Search models...": string;
|
||||
Format: string;
|
||||
Thinking: string;
|
||||
Vision: string;
|
||||
You: string;
|
||||
Assistant: string;
|
||||
"Thinking...": string;
|
||||
"Type your message...": string;
|
||||
"API Keys Configuration": string;
|
||||
"Configure API keys for LLM providers. Keys are stored locally in your browser.": string;
|
||||
Configured: string;
|
||||
"Not configured": string;
|
||||
"✓ Valid": string;
|
||||
"✗ Invalid": string;
|
||||
"Testing...": string;
|
||||
Update: string;
|
||||
Test: string;
|
||||
Remove: string;
|
||||
Save: string;
|
||||
"Update API key": string;
|
||||
"Enter API key": string;
|
||||
"Type a message...": string;
|
||||
"Failed to fetch file": string;
|
||||
"Invalid source type": string;
|
||||
PDF: string;
|
||||
Document: string;
|
||||
Presentation: string;
|
||||
Spreadsheet: string;
|
||||
Text: string;
|
||||
"Error loading file": string;
|
||||
"No text content available": string;
|
||||
"Failed to load PDF": string;
|
||||
"Failed to load document": string;
|
||||
"Failed to load spreadsheet": string;
|
||||
"Error loading PDF": string;
|
||||
"Error loading document": string;
|
||||
"Error loading spreadsheet": string;
|
||||
"Preview not available for this file type.": string;
|
||||
"Click the download button above to view it on your computer.": string;
|
||||
"No content available": string;
|
||||
"Failed to display text content": string;
|
||||
"API keys are required to use AI models. Get your keys from the provider's website.": string;
|
||||
console: string;
|
||||
"Copy output": string;
|
||||
"Copied!": string;
|
||||
"Error:": string;
|
||||
"Request aborted": string;
|
||||
Call: string;
|
||||
Result: string;
|
||||
"(no result)": string;
|
||||
"Waiting for tool result…": string;
|
||||
"Call was aborted; no result.": string;
|
||||
"No session available": string;
|
||||
"No session set": string;
|
||||
"Preparing tool parameters...": string;
|
||||
"(no output)": string;
|
||||
Input: string;
|
||||
Output: string;
|
||||
"Writing expression...": string;
|
||||
"Waiting for expression...": string;
|
||||
Calculating: string;
|
||||
"Getting current time in": string;
|
||||
"Getting current date and time": string;
|
||||
"Waiting for command...": string;
|
||||
"Writing command...": string;
|
||||
"Running command...": string;
|
||||
"Command failed:": string;
|
||||
"Enter Auth Token": string;
|
||||
"Please enter your auth token.": string;
|
||||
"Auth token is required for proxy transport": string;
|
||||
"Execution aborted": string;
|
||||
"Code parameter is required": string;
|
||||
"Unknown error": string;
|
||||
"Code executed successfully (no output)": string;
|
||||
"Execution failed": string;
|
||||
"JavaScript REPL": string;
|
||||
"JavaScript code to execute": string;
|
||||
"Writing JavaScript code...": string;
|
||||
"Executing JavaScript": string;
|
||||
"Preparing JavaScript...": string;
|
||||
"Preparing command...": string;
|
||||
"Preparing calculation...": string;
|
||||
"Preparing tool...": string;
|
||||
"Getting time...": string;
|
||||
"Processing artifact...": string;
|
||||
"Preparing artifact...": string;
|
||||
"Processing artifact": string;
|
||||
"Processed artifact": string;
|
||||
"Creating artifact": string;
|
||||
"Created artifact": string;
|
||||
"Updating artifact": string;
|
||||
"Updated artifact": string;
|
||||
"Rewriting artifact": string;
|
||||
"Rewrote artifact": string;
|
||||
"Getting artifact": string;
|
||||
"Got artifact": string;
|
||||
"Deleting artifact": string;
|
||||
"Deleted artifact": string;
|
||||
"Getting logs": string;
|
||||
"Got logs": string;
|
||||
"An error occurred": string;
|
||||
"Copy logs": string;
|
||||
"Autoscroll enabled": string;
|
||||
"Autoscroll disabled": string;
|
||||
Processing: string;
|
||||
Create: string;
|
||||
Rewrite: string;
|
||||
Get: string;
|
||||
Delete: string;
|
||||
"Get logs": string;
|
||||
"Show artifacts": string;
|
||||
"Close artifacts": string;
|
||||
Artifacts: string;
|
||||
"Copy HTML": string;
|
||||
"Download HTML": string;
|
||||
"Reload HTML": string;
|
||||
"Copy SVG": string;
|
||||
"Download SVG": string;
|
||||
"Copy Markdown": string;
|
||||
"Download Markdown": string;
|
||||
Download: string;
|
||||
"No logs for {filename}": string;
|
||||
"API Keys Settings": string;
|
||||
Settings: string;
|
||||
"API Keys": string;
|
||||
Proxy: string;
|
||||
"Use CORS Proxy": 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;
|
||||
Clear: string;
|
||||
"API Key Required": string;
|
||||
"Enter your API key for {provider}": 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;
|
||||
Minimal: string;
|
||||
Low: string;
|
||||
Medium: string;
|
||||
High: string;
|
||||
"Storage Permission Required": string;
|
||||
"This app needs persistent storage to save your conversations": string;
|
||||
"Why is this needed?": string;
|
||||
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.": string;
|
||||
"What this means:": string;
|
||||
"Your conversations will be saved locally in your browser": string;
|
||||
"Data will not be deleted automatically to free up space": string;
|
||||
"You can still manually clear data at any time": string;
|
||||
"No data is sent to external servers": string;
|
||||
"Continue Anyway": string;
|
||||
"Requesting...": string;
|
||||
"Grant Permission": string;
|
||||
Sessions: string;
|
||||
"Load a previous conversation": string;
|
||||
"No sessions yet": string;
|
||||
"Delete this session?": string;
|
||||
Today: string;
|
||||
Yesterday: string;
|
||||
"{days} days ago": string;
|
||||
messages: string;
|
||||
tokens: string;
|
||||
"Drop files here": string;
|
||||
"Providers & Models": string;
|
||||
"Cloud Providers": string;
|
||||
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": string;
|
||||
"Custom Providers": string;
|
||||
"User-configured servers with auto-discovered or manually defined models.": string;
|
||||
"Add Provider": string;
|
||||
"No custom providers configured. Click 'Add Provider' to get started.": string;
|
||||
Models: string;
|
||||
"auto-discovered": string;
|
||||
Refresh: string;
|
||||
Edit: string;
|
||||
"Are you sure you want to delete this provider?": string;
|
||||
"Edit Provider": string;
|
||||
"Provider Name": string;
|
||||
"e.g., My Ollama Server": string;
|
||||
"Provider Type": string;
|
||||
"Base URL": string;
|
||||
"e.g., http://localhost:11434": string;
|
||||
"API Key (Optional)": string;
|
||||
"Leave empty if not required": string;
|
||||
"Test Connection": string;
|
||||
Discovered: string;
|
||||
models: string;
|
||||
and: string;
|
||||
more: string;
|
||||
"For manual provider types, add models after saving the provider.": string;
|
||||
"Please fill in all required fields": string;
|
||||
"Failed to save provider": string;
|
||||
"OpenAI Completions Compatible": string;
|
||||
"OpenAI Responses Compatible": string;
|
||||
"Anthropic Messages Compatible": string;
|
||||
"Checking...": string;
|
||||
Disconnected: string;
|
||||
}
|
||||
}
|
||||
export declare const translations: {
|
||||
en: {
|
||||
Free: string;
|
||||
"Input Required": string;
|
||||
Cancel: string;
|
||||
Confirm: string;
|
||||
"Select Model": string;
|
||||
"Search models...": string;
|
||||
Format: string;
|
||||
Thinking: string;
|
||||
Vision: string;
|
||||
You: string;
|
||||
Assistant: string;
|
||||
"Thinking...": string;
|
||||
"Type your message...": string;
|
||||
"API Keys Configuration": string;
|
||||
"Configure API keys for LLM providers. Keys are stored locally in your browser.": string;
|
||||
Configured: string;
|
||||
"Not configured": string;
|
||||
"\u2713 Valid": string;
|
||||
"\u2717 Invalid": string;
|
||||
"Testing...": string;
|
||||
Update: string;
|
||||
Test: string;
|
||||
Remove: string;
|
||||
Save: string;
|
||||
"Update API key": string;
|
||||
"Enter API key": string;
|
||||
"Type a message...": string;
|
||||
"Failed to fetch file": string;
|
||||
"Invalid source type": string;
|
||||
PDF: string;
|
||||
Document: string;
|
||||
Presentation: string;
|
||||
Spreadsheet: string;
|
||||
Text: string;
|
||||
"Error loading file": string;
|
||||
"No text content available": string;
|
||||
"Failed to load PDF": string;
|
||||
"Failed to load document": string;
|
||||
"Failed to load spreadsheet": string;
|
||||
"Error loading PDF": string;
|
||||
"Error loading document": string;
|
||||
"Error loading spreadsheet": string;
|
||||
"Preview not available for this file type.": string;
|
||||
"Click the download button above to view it on your computer.": string;
|
||||
"No content available": string;
|
||||
"Failed to display text content": string;
|
||||
"API keys are required to use AI models. Get your keys from the provider's website.": string;
|
||||
console: string;
|
||||
"Copy output": string;
|
||||
"Copied!": string;
|
||||
"Error:": string;
|
||||
"Request aborted": string;
|
||||
Call: string;
|
||||
Result: string;
|
||||
"(no result)": string;
|
||||
"Waiting for tool result\u2026": string;
|
||||
"Call was aborted; no result.": string;
|
||||
"No session available": string;
|
||||
"No session set": string;
|
||||
"Preparing tool parameters...": string;
|
||||
"(no output)": string;
|
||||
Input: string;
|
||||
Output: string;
|
||||
"Waiting for expression...": string;
|
||||
"Writing expression...": string;
|
||||
Calculating: string;
|
||||
"Getting current time in": string;
|
||||
"Getting current date and time": string;
|
||||
"Waiting for command...": string;
|
||||
"Writing command...": string;
|
||||
"Running command...": string;
|
||||
"Command failed": string;
|
||||
"Enter Auth Token": string;
|
||||
"Please enter your auth token.": string;
|
||||
"Auth token is required for proxy transport": string;
|
||||
"Execution aborted": string;
|
||||
"Code parameter is required": string;
|
||||
"Unknown error": string;
|
||||
"Code executed successfully (no output)": string;
|
||||
"Execution failed": string;
|
||||
"JavaScript REPL": string;
|
||||
"JavaScript code to execute": string;
|
||||
"Writing JavaScript code...": string;
|
||||
"Executing JavaScript": string;
|
||||
"Preparing JavaScript...": string;
|
||||
"Preparing command...": string;
|
||||
"Preparing calculation...": string;
|
||||
"Preparing tool...": string;
|
||||
"Getting time...": string;
|
||||
"Processing artifact...": string;
|
||||
"Preparing artifact...": string;
|
||||
"Processing artifact": string;
|
||||
"Processed artifact": string;
|
||||
"Creating artifact": string;
|
||||
"Created artifact": string;
|
||||
"Updating artifact": string;
|
||||
"Updated artifact": string;
|
||||
"Rewriting artifact": string;
|
||||
"Rewrote artifact": string;
|
||||
"Getting artifact": string;
|
||||
"Got artifact": string;
|
||||
"Deleting artifact": string;
|
||||
"Deleted artifact": string;
|
||||
"Getting logs": string;
|
||||
"Got logs": string;
|
||||
"An error occurred": string;
|
||||
"Copy logs": string;
|
||||
"Autoscroll enabled": string;
|
||||
"Autoscroll disabled": string;
|
||||
Processing: string;
|
||||
Create: string;
|
||||
Rewrite: string;
|
||||
Get: string;
|
||||
"Get logs": string;
|
||||
"Show artifacts": string;
|
||||
"Close artifacts": string;
|
||||
Artifacts: string;
|
||||
"Copy HTML": string;
|
||||
"Download HTML": string;
|
||||
"Reload HTML": string;
|
||||
"Copy SVG": string;
|
||||
"Download SVG": string;
|
||||
"Copy Markdown": string;
|
||||
"Download Markdown": string;
|
||||
Download: string;
|
||||
"No logs for {filename}": string;
|
||||
"API Keys Settings": string;
|
||||
Settings: string;
|
||||
"API Keys": string;
|
||||
Proxy: string;
|
||||
"Use CORS Proxy": 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;
|
||||
Clear: string;
|
||||
"API Key Required": string;
|
||||
"Enter your API key for {provider}": 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;
|
||||
Minimal: string;
|
||||
Low: string;
|
||||
Medium: string;
|
||||
High: string;
|
||||
"Storage Permission Required": string;
|
||||
"This app needs persistent storage to save your conversations": string;
|
||||
"Why is this needed?": string;
|
||||
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.": string;
|
||||
"What this means:": string;
|
||||
"Your conversations will be saved locally in your browser": string;
|
||||
"Data will not be deleted automatically to free up space": string;
|
||||
"You can still manually clear data at any time": string;
|
||||
"No data is sent to external servers": string;
|
||||
"Continue Anyway": string;
|
||||
"Requesting...": string;
|
||||
"Grant Permission": string;
|
||||
Sessions: string;
|
||||
"Load a previous conversation": string;
|
||||
"No sessions yet": string;
|
||||
"Delete this session?": string;
|
||||
Today: string;
|
||||
Yesterday: string;
|
||||
"{days} days ago": string;
|
||||
messages: string;
|
||||
tokens: string;
|
||||
Delete: string;
|
||||
"Drop files here": string;
|
||||
"Command failed:": string;
|
||||
"Providers & Models": string;
|
||||
"Cloud Providers": string;
|
||||
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": string;
|
||||
"Custom Providers": string;
|
||||
"User-configured servers with auto-discovered or manually defined models.": string;
|
||||
"Add Provider": string;
|
||||
"No custom providers configured. Click 'Add Provider' to get started.": string;
|
||||
"auto-discovered": string;
|
||||
Refresh: string;
|
||||
Edit: string;
|
||||
"Are you sure you want to delete this provider?": string;
|
||||
"Edit Provider": string;
|
||||
"Provider Name": string;
|
||||
"e.g., My Ollama Server": string;
|
||||
"Provider Type": string;
|
||||
"Base URL": string;
|
||||
"e.g., http://localhost:11434": string;
|
||||
"API Key (Optional)": string;
|
||||
"Leave empty if not required": string;
|
||||
"Test Connection": string;
|
||||
Discovered: string;
|
||||
Models: string;
|
||||
models: string;
|
||||
and: string;
|
||||
more: string;
|
||||
"For manual provider types, add models after saving the provider.": string;
|
||||
"Please fill in all required fields": string;
|
||||
"Failed to save provider": string;
|
||||
"OpenAI Completions Compatible": string;
|
||||
"OpenAI Responses Compatible": string;
|
||||
"Anthropic Messages Compatible": string;
|
||||
"Checking...": string;
|
||||
Disconnected: string;
|
||||
"*": string;
|
||||
Copy: string;
|
||||
"Copy code": string;
|
||||
Close: string;
|
||||
Preview: string;
|
||||
Code: string;
|
||||
"Loading...": string;
|
||||
"Select an option": string;
|
||||
"Mode 1": string;
|
||||
"Mode 2": string;
|
||||
Required: string;
|
||||
Optional: string;
|
||||
};
|
||||
de: {
|
||||
Free: string;
|
||||
"Input Required": string;
|
||||
Cancel: string;
|
||||
Confirm: string;
|
||||
"Select Model": string;
|
||||
"Search models...": string;
|
||||
Format: string;
|
||||
Thinking: string;
|
||||
Vision: string;
|
||||
You: string;
|
||||
Assistant: string;
|
||||
"Thinking...": string;
|
||||
"Type your message...": string;
|
||||
"API Keys Configuration": string;
|
||||
"Configure API keys for LLM providers. Keys are stored locally in your browser.": string;
|
||||
Configured: string;
|
||||
"Not configured": string;
|
||||
"\u2713 Valid": string;
|
||||
"\u2717 Invalid": string;
|
||||
"Testing...": string;
|
||||
Update: string;
|
||||
Test: string;
|
||||
Remove: string;
|
||||
Save: string;
|
||||
"Update API key": string;
|
||||
"Enter API key": string;
|
||||
"Type a message...": string;
|
||||
"Failed to fetch file": string;
|
||||
"Invalid source type": string;
|
||||
PDF: string;
|
||||
Document: string;
|
||||
Presentation: string;
|
||||
Spreadsheet: string;
|
||||
Text: string;
|
||||
"Error loading file": string;
|
||||
"No text content available": string;
|
||||
"Failed to load PDF": string;
|
||||
"Failed to load document": string;
|
||||
"Failed to load spreadsheet": string;
|
||||
"Error loading PDF": string;
|
||||
"Error loading document": string;
|
||||
"Error loading spreadsheet": string;
|
||||
"Preview not available for this file type.": string;
|
||||
"Click the download button above to view it on your computer.": string;
|
||||
"No content available": string;
|
||||
"Failed to display text content": string;
|
||||
"API keys are required to use AI models. Get your keys from the provider's website.": string;
|
||||
console: string;
|
||||
"Copy output": string;
|
||||
"Copied!": string;
|
||||
"Error:": string;
|
||||
"Request aborted": string;
|
||||
Call: string;
|
||||
Result: string;
|
||||
"(no result)": string;
|
||||
"Waiting for tool result\u2026": string;
|
||||
"Call was aborted; no result.": string;
|
||||
"No session available": string;
|
||||
"No session set": string;
|
||||
"Preparing tool parameters...": string;
|
||||
"(no output)": string;
|
||||
Input: string;
|
||||
Output: string;
|
||||
"Waiting for expression...": string;
|
||||
"Writing expression...": string;
|
||||
Calculating: string;
|
||||
"Getting current time in": string;
|
||||
"Getting current date and time": string;
|
||||
"Waiting for command...": string;
|
||||
"Writing command...": string;
|
||||
"Running command...": string;
|
||||
"Command failed": string;
|
||||
"Enter Auth Token": string;
|
||||
"Please enter your auth token.": string;
|
||||
"Auth token is required for proxy transport": string;
|
||||
"Execution aborted": string;
|
||||
"Code parameter is required": string;
|
||||
"Unknown error": string;
|
||||
"Code executed successfully (no output)": string;
|
||||
"Execution failed": string;
|
||||
"JavaScript REPL": string;
|
||||
"JavaScript code to execute": string;
|
||||
"Writing JavaScript code...": string;
|
||||
"Executing JavaScript": string;
|
||||
"Preparing JavaScript...": string;
|
||||
"Preparing command...": string;
|
||||
"Preparing calculation...": string;
|
||||
"Preparing tool...": string;
|
||||
"Getting time...": string;
|
||||
"Processing artifact...": string;
|
||||
"Preparing artifact...": string;
|
||||
"Processing artifact": string;
|
||||
"Processed artifact": string;
|
||||
"Creating artifact": string;
|
||||
"Created artifact": string;
|
||||
"Updating artifact": string;
|
||||
"Updated artifact": string;
|
||||
"Rewriting artifact": string;
|
||||
"Rewrote artifact": string;
|
||||
"Getting artifact": string;
|
||||
"Got artifact": string;
|
||||
"Deleting artifact": string;
|
||||
"Deleted artifact": string;
|
||||
"Getting logs": string;
|
||||
"Got logs": string;
|
||||
"An error occurred": string;
|
||||
"Copy logs": string;
|
||||
"Autoscroll enabled": string;
|
||||
"Autoscroll disabled": string;
|
||||
Processing: string;
|
||||
Create: string;
|
||||
Rewrite: string;
|
||||
Get: string;
|
||||
"Get logs": string;
|
||||
"Show artifacts": string;
|
||||
"Close artifacts": string;
|
||||
Artifacts: string;
|
||||
"Copy HTML": string;
|
||||
"Download HTML": string;
|
||||
"Reload HTML": string;
|
||||
"Copy SVG": string;
|
||||
"Download SVG": string;
|
||||
"Copy Markdown": string;
|
||||
"Download Markdown": string;
|
||||
Download: string;
|
||||
"No logs for {filename}": string;
|
||||
"API Keys Settings": string;
|
||||
Settings: string;
|
||||
"API Keys": string;
|
||||
Proxy: string;
|
||||
"Use CORS Proxy": 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;
|
||||
Clear: string;
|
||||
"API Key Required": string;
|
||||
"Enter your API key for {provider}": 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;
|
||||
Minimal: string;
|
||||
Low: string;
|
||||
Medium: string;
|
||||
High: string;
|
||||
"Storage Permission Required": string;
|
||||
"This app needs persistent storage to save your conversations": string;
|
||||
"Why is this needed?": string;
|
||||
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.": string;
|
||||
"What this means:": string;
|
||||
"Your conversations will be saved locally in your browser": string;
|
||||
"Data will not be deleted automatically to free up space": string;
|
||||
"You can still manually clear data at any time": string;
|
||||
"No data is sent to external servers": string;
|
||||
"Continue Anyway": string;
|
||||
"Requesting...": string;
|
||||
"Grant Permission": string;
|
||||
Sessions: string;
|
||||
"Load a previous conversation": string;
|
||||
"No sessions yet": string;
|
||||
"Delete this session?": string;
|
||||
Today: string;
|
||||
Yesterday: string;
|
||||
"{days} days ago": string;
|
||||
messages: string;
|
||||
tokens: string;
|
||||
Delete: string;
|
||||
"Drop files here": string;
|
||||
"Command failed:": string;
|
||||
"Providers & Models": string;
|
||||
"Cloud Providers": string;
|
||||
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": string;
|
||||
"Custom Providers": string;
|
||||
"User-configured servers with auto-discovered or manually defined models.": string;
|
||||
"Add Provider": string;
|
||||
"No custom providers configured. Click 'Add Provider' to get started.": string;
|
||||
"auto-discovered": string;
|
||||
Refresh: string;
|
||||
Edit: string;
|
||||
"Are you sure you want to delete this provider?": string;
|
||||
"Edit Provider": string;
|
||||
"Provider Name": string;
|
||||
"e.g., My Ollama Server": string;
|
||||
"Provider Type": string;
|
||||
"Base URL": string;
|
||||
"e.g., http://localhost:11434": string;
|
||||
"API Key (Optional)": string;
|
||||
"Leave empty if not required": string;
|
||||
"Test Connection": string;
|
||||
Discovered: string;
|
||||
Models: string;
|
||||
models: string;
|
||||
and: string;
|
||||
more: string;
|
||||
"For manual provider types, add models after saving the provider.": string;
|
||||
"Please fill in all required fields": string;
|
||||
"Failed to save provider": string;
|
||||
"OpenAI Completions Compatible": string;
|
||||
"OpenAI Responses Compatible": string;
|
||||
"Anthropic Messages Compatible": string;
|
||||
"Checking...": string;
|
||||
Disconnected: string;
|
||||
"*": string;
|
||||
Copy: string;
|
||||
"Copy code": string;
|
||||
Close: string;
|
||||
Preview: string;
|
||||
Code: string;
|
||||
"Loading...": string;
|
||||
"Select an option": string;
|
||||
"Mode 1": string;
|
||||
"Mode 2": string;
|
||||
Required: string;
|
||||
Optional: string;
|
||||
};
|
||||
};
|
||||
export * from "@mariozechner/mini-lit/dist/i18n.js";
|
||||
//# sourceMappingURL=i18n.d.ts.map
|
||||
File diff suppressed because one or more lines are too long
418
apps/macos/Sources/Clawdis/Resources/WebChat/utils/i18n.js
Normal file
418
apps/macos/Sources/Clawdis/Resources/WebChat/utils/i18n.js
Normal file
@@ -0,0 +1,418 @@
|
||||
import { defaultEnglish, defaultGerman, setTranslations } from "@mariozechner/mini-lit";
|
||||
export const translations = {
|
||||
en: {
|
||||
...defaultEnglish,
|
||||
Free: "Free",
|
||||
"Input Required": "Input Required",
|
||||
Cancel: "Cancel",
|
||||
Confirm: "Confirm",
|
||||
"Select Model": "Select Model",
|
||||
"Search models...": "Search models...",
|
||||
Format: "Format",
|
||||
Thinking: "Thinking",
|
||||
Vision: "Vision",
|
||||
You: "You",
|
||||
Assistant: "Assistant",
|
||||
"Thinking...": "Thinking...",
|
||||
"Type your message...": "Type your message...",
|
||||
"API Keys Configuration": "API Keys Configuration",
|
||||
"Configure API keys for LLM providers. Keys are stored locally in your browser.": "Configure API keys for LLM providers. Keys are stored locally in your browser.",
|
||||
Configured: "Configured",
|
||||
"Not configured": "Not configured",
|
||||
"✓ Valid": "✓ Valid",
|
||||
"✗ Invalid": "✗ Invalid",
|
||||
"Testing...": "Testing...",
|
||||
Update: "Update",
|
||||
Test: "Test",
|
||||
Remove: "Remove",
|
||||
Save: "Save",
|
||||
"Update API key": "Update API key",
|
||||
"Enter API key": "Enter API key",
|
||||
"Type a message...": "Type a message...",
|
||||
"Failed to fetch file": "Failed to fetch file",
|
||||
"Invalid source type": "Invalid source type",
|
||||
PDF: "PDF",
|
||||
Document: "Document",
|
||||
Presentation: "Presentation",
|
||||
Spreadsheet: "Spreadsheet",
|
||||
Text: "Text",
|
||||
"Error loading file": "Error loading file",
|
||||
"No text content available": "No text content available",
|
||||
"Failed to load PDF": "Failed to load PDF",
|
||||
"Failed to load document": "Failed to load document",
|
||||
"Failed to load spreadsheet": "Failed to load spreadsheet",
|
||||
"Error loading PDF": "Error loading PDF",
|
||||
"Error loading document": "Error loading document",
|
||||
"Error loading spreadsheet": "Error loading spreadsheet",
|
||||
"Preview not available for this file type.": "Preview not available for this file type.",
|
||||
"Click the download button above to view it on your computer.": "Click the download button above to view it on your computer.",
|
||||
"No content available": "No content available",
|
||||
"Failed to display text content": "Failed to display text content",
|
||||
"API keys are required to use AI models. Get your keys from the provider's website.": "API keys are required to use AI models. Get your keys from the provider's website.",
|
||||
console: "console",
|
||||
"Copy output": "Copy output",
|
||||
"Copied!": "Copied!",
|
||||
"Error:": "Error:",
|
||||
"Request aborted": "Request aborted",
|
||||
Call: "Call",
|
||||
Result: "Result",
|
||||
"(no result)": "(no result)",
|
||||
"Waiting for tool result…": "Waiting for tool result…",
|
||||
"Call was aborted; no result.": "Call was aborted; no result.",
|
||||
"No session available": "No session available",
|
||||
"No session set": "No session set",
|
||||
"Preparing tool parameters...": "Preparing tool parameters...",
|
||||
"(no output)": "(no output)",
|
||||
Input: "Input",
|
||||
Output: "Output",
|
||||
"Waiting for expression...": "Waiting for expression...",
|
||||
"Writing expression...": "Writing expression...",
|
||||
Calculating: "Calculating",
|
||||
"Getting current time in": "Getting current time in",
|
||||
"Getting current date and time": "Getting current date and time",
|
||||
"Waiting for command...": "Waiting for command...",
|
||||
"Writing command...": "Writing command...",
|
||||
"Running command...": "Running command...",
|
||||
"Command failed": "Command failed",
|
||||
"Enter Auth Token": "Enter Auth Token",
|
||||
"Please enter your auth token.": "Please enter your auth token.",
|
||||
"Auth token is required for proxy transport": "Auth token is required for proxy transport",
|
||||
// JavaScript REPL strings
|
||||
"Execution aborted": "Execution aborted",
|
||||
"Code parameter is required": "Code parameter is required",
|
||||
"Unknown error": "Unknown error",
|
||||
"Code executed successfully (no output)": "Code executed successfully (no output)",
|
||||
"Execution failed": "Execution failed",
|
||||
"JavaScript REPL": "JavaScript REPL",
|
||||
"JavaScript code to execute": "JavaScript code to execute",
|
||||
"Writing JavaScript code...": "Writing JavaScript code...",
|
||||
"Executing JavaScript": "Executing JavaScript",
|
||||
"Preparing JavaScript...": "Preparing JavaScript...",
|
||||
"Preparing command...": "Preparing command...",
|
||||
"Preparing calculation...": "Preparing calculation...",
|
||||
"Preparing tool...": "Preparing tool...",
|
||||
"Getting time...": "Getting time...",
|
||||
// Artifacts strings
|
||||
"Processing artifact...": "Processing artifact...",
|
||||
"Preparing artifact...": "Preparing artifact...",
|
||||
"Processing artifact": "Processing artifact",
|
||||
"Processed artifact": "Processed artifact",
|
||||
"Creating artifact": "Creating artifact",
|
||||
"Created artifact": "Created artifact",
|
||||
"Updating artifact": "Updating artifact",
|
||||
"Updated artifact": "Updated artifact",
|
||||
"Rewriting artifact": "Rewriting artifact",
|
||||
"Rewrote artifact": "Rewrote artifact",
|
||||
"Getting artifact": "Getting artifact",
|
||||
"Got artifact": "Got artifact",
|
||||
"Deleting artifact": "Deleting artifact",
|
||||
"Deleted artifact": "Deleted artifact",
|
||||
"Getting logs": "Getting logs",
|
||||
"Got logs": "Got logs",
|
||||
"An error occurred": "An error occurred",
|
||||
"Copy logs": "Copy logs",
|
||||
"Autoscroll enabled": "Autoscroll enabled",
|
||||
"Autoscroll disabled": "Autoscroll disabled",
|
||||
Processing: "Processing",
|
||||
Create: "Create",
|
||||
Rewrite: "Rewrite",
|
||||
Get: "Get",
|
||||
"Get logs": "Get logs",
|
||||
"Show artifacts": "Show artifacts",
|
||||
"Close artifacts": "Close artifacts",
|
||||
Artifacts: "Artifacts",
|
||||
"Copy HTML": "Copy HTML",
|
||||
"Download HTML": "Download HTML",
|
||||
"Reload HTML": "Reload HTML",
|
||||
"Copy SVG": "Copy SVG",
|
||||
"Download SVG": "Download SVG",
|
||||
"Copy Markdown": "Copy Markdown",
|
||||
"Download Markdown": "Download Markdown",
|
||||
Download: "Download",
|
||||
"No logs for {filename}": "No logs for {filename}",
|
||||
"API Keys Settings": "API Keys Settings",
|
||||
Settings: "Settings",
|
||||
"API Keys": "API Keys",
|
||||
Proxy: "Proxy",
|
||||
"Use CORS Proxy": "Use CORS Proxy",
|
||||
"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",
|
||||
Clear: "Clear",
|
||||
"API Key Required": "API Key Required",
|
||||
"Enter your API key for {provider}": "Enter your API key for {provider}",
|
||||
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.": "Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.",
|
||||
Off: "Off",
|
||||
Minimal: "Minimal",
|
||||
Low: "Low",
|
||||
Medium: "Medium",
|
||||
High: "High",
|
||||
"Storage Permission Required": "Storage Permission Required",
|
||||
"This app needs persistent storage to save your conversations": "This app needs persistent storage to save your conversations",
|
||||
"Why is this needed?": "Why is this needed?",
|
||||
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.": "Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.",
|
||||
"What this means:": "What this means:",
|
||||
"Your conversations will be saved locally in your browser": "Your conversations will be saved locally in your browser",
|
||||
"Data will not be deleted automatically to free up space": "Data will not be deleted automatically to free up space",
|
||||
"You can still manually clear data at any time": "You can still manually clear data at any time",
|
||||
"No data is sent to external servers": "No data is sent to external servers",
|
||||
"Continue Anyway": "Continue Anyway",
|
||||
"Requesting...": "Requesting...",
|
||||
"Grant Permission": "Grant Permission",
|
||||
Sessions: "Sessions",
|
||||
"Load a previous conversation": "Load a previous conversation",
|
||||
"No sessions yet": "No sessions yet",
|
||||
"Delete this session?": "Delete this session?",
|
||||
Today: "Today",
|
||||
Yesterday: "Yesterday",
|
||||
"{days} days ago": "{days} days ago",
|
||||
messages: "messages",
|
||||
tokens: "tokens",
|
||||
Delete: "Delete",
|
||||
"Drop files here": "Drop files here",
|
||||
"Command failed:": "Command failed:",
|
||||
// Providers & Models
|
||||
"Providers & Models": "Providers & Models",
|
||||
"Cloud Providers": "Cloud Providers",
|
||||
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": "Cloud LLM providers with predefined models. API keys are stored locally in your browser.",
|
||||
"Custom Providers": "Custom Providers",
|
||||
"User-configured servers with auto-discovered or manually defined models.": "User-configured servers with auto-discovered or manually defined models.",
|
||||
"Add Provider": "Add Provider",
|
||||
"No custom providers configured. Click 'Add Provider' to get started.": "No custom providers configured. Click 'Add Provider' to get started.",
|
||||
"auto-discovered": "auto-discovered",
|
||||
Refresh: "Refresh",
|
||||
Edit: "Edit",
|
||||
"Are you sure you want to delete this provider?": "Are you sure you want to delete this provider?",
|
||||
"Edit Provider": "Edit Provider",
|
||||
"Provider Name": "Provider Name",
|
||||
"e.g., My Ollama Server": "e.g., My Ollama Server",
|
||||
"Provider Type": "Provider Type",
|
||||
"Base URL": "Base URL",
|
||||
"e.g., http://localhost:11434": "e.g., http://localhost:11434",
|
||||
"API Key (Optional)": "API Key (Optional)",
|
||||
"Leave empty if not required": "Leave empty if not required",
|
||||
"Test Connection": "Test Connection",
|
||||
Discovered: "Discovered",
|
||||
Models: "Models",
|
||||
models: "models",
|
||||
and: "and",
|
||||
more: "more",
|
||||
"For manual provider types, add models after saving the provider.": "For manual provider types, add models after saving the provider.",
|
||||
"Please fill in all required fields": "Please fill in all required fields",
|
||||
"Failed to save provider": "Failed to save provider",
|
||||
"OpenAI Completions Compatible": "OpenAI Completions Compatible",
|
||||
"OpenAI Responses Compatible": "OpenAI Responses Compatible",
|
||||
"Anthropic Messages Compatible": "Anthropic Messages Compatible",
|
||||
"Checking...": "Checking...",
|
||||
Disconnected: "Disconnected",
|
||||
},
|
||||
de: {
|
||||
...defaultGerman,
|
||||
Free: "Kostenlos",
|
||||
"Input Required": "Eingabe erforderlich",
|
||||
Cancel: "Abbrechen",
|
||||
Confirm: "Bestätigen",
|
||||
"Select Model": "Modell auswählen",
|
||||
"Search models...": "Modelle suchen...",
|
||||
Format: "Formatieren",
|
||||
Thinking: "Thinking",
|
||||
Vision: "Vision",
|
||||
You: "Sie",
|
||||
Assistant: "Assistent",
|
||||
"Thinking...": "Denkt nach...",
|
||||
"Type your message...": "Geben Sie Ihre Nachricht ein...",
|
||||
"API Keys Configuration": "API-Schlüssel-Konfiguration",
|
||||
"Configure API keys for LLM providers. Keys are stored locally in your browser.": "Konfigurieren Sie API-Schlüssel für LLM-Anbieter. Schlüssel werden lokal in Ihrem Browser gespeichert.",
|
||||
Configured: "Konfiguriert",
|
||||
"Not configured": "Nicht konfiguriert",
|
||||
"✓ Valid": "✓ Gültig",
|
||||
"✗ Invalid": "✗ Ungültig",
|
||||
"Testing...": "Teste...",
|
||||
Update: "Aktualisieren",
|
||||
Test: "Testen",
|
||||
Remove: "Entfernen",
|
||||
Save: "Speichern",
|
||||
"Update API key": "API-Schlüssel aktualisieren",
|
||||
"Enter API key": "API-Schlüssel eingeben",
|
||||
"Type a message...": "Nachricht eingeben...",
|
||||
"Failed to fetch file": "Datei konnte nicht abgerufen werden",
|
||||
"Invalid source type": "Ungültiger Quellentyp",
|
||||
PDF: "PDF",
|
||||
Document: "Dokument",
|
||||
Presentation: "Präsentation",
|
||||
Spreadsheet: "Tabelle",
|
||||
Text: "Text",
|
||||
"Error loading file": "Fehler beim Laden der Datei",
|
||||
"No text content available": "Kein Textinhalt verfügbar",
|
||||
"Failed to load PDF": "PDF konnte nicht geladen werden",
|
||||
"Failed to load document": "Dokument konnte nicht geladen werden",
|
||||
"Failed to load spreadsheet": "Tabelle konnte nicht geladen werden",
|
||||
"Error loading PDF": "Fehler beim Laden des PDFs",
|
||||
"Error loading document": "Fehler beim Laden des Dokuments",
|
||||
"Error loading spreadsheet": "Fehler beim Laden der Tabelle",
|
||||
"Preview not available for this file type.": "Vorschau für diesen Dateityp nicht verfügbar.",
|
||||
"Click the download button above to view it on your computer.": "Klicken Sie oben auf die Download-Schaltfläche, um die Datei auf Ihrem Computer anzuzeigen.",
|
||||
"No content available": "Kein Inhalt verfügbar",
|
||||
"Failed to display text content": "Textinhalt konnte nicht angezeigt werden",
|
||||
"API keys are required to use AI models. Get your keys from the provider's website.": "API-Schlüssel sind erforderlich, um KI-Modelle zu verwenden. Holen Sie sich Ihre Schlüssel von der Website des Anbieters.",
|
||||
console: "Konsole",
|
||||
"Copy output": "Ausgabe kopieren",
|
||||
"Copied!": "Kopiert!",
|
||||
"Error:": "Fehler:",
|
||||
"Request aborted": "Anfrage abgebrochen",
|
||||
Call: "Aufruf",
|
||||
Result: "Ergebnis",
|
||||
"(no result)": "(kein Ergebnis)",
|
||||
"Waiting for tool result…": "Warte auf Tool-Ergebnis…",
|
||||
"Call was aborted; no result.": "Aufruf wurde abgebrochen; kein Ergebnis.",
|
||||
"No session available": "Keine Sitzung verfügbar",
|
||||
"No session set": "Keine Sitzung gesetzt",
|
||||
"Preparing tool parameters...": "Bereite Tool-Parameter vor...",
|
||||
"(no output)": "(keine Ausgabe)",
|
||||
Input: "Eingabe",
|
||||
Output: "Ausgabe",
|
||||
"Waiting for expression...": "Warte auf Ausdruck",
|
||||
"Writing expression...": "Schreibe Ausdruck...",
|
||||
Calculating: "Berechne",
|
||||
"Getting current time in": "Hole aktuelle Zeit in",
|
||||
"Getting current date and time": "Hole aktuelles Datum und Uhrzeit",
|
||||
"Waiting for command...": "Warte auf Befehl...",
|
||||
"Writing command...": "Schreibe Befehl...",
|
||||
"Running command...": "Führe Befehl aus...",
|
||||
"Command failed": "Befehl fehlgeschlagen",
|
||||
"Enter Auth Token": "Auth-Token eingeben",
|
||||
"Please enter your auth token.": "Bitte geben Sie Ihr Auth-Token ein.",
|
||||
"Auth token is required for proxy transport": "Auth-Token ist für Proxy-Transport erforderlich",
|
||||
// JavaScript REPL strings
|
||||
"Execution aborted": "Ausführung abgebrochen",
|
||||
"Code parameter is required": "Code-Parameter ist erforderlich",
|
||||
"Unknown error": "Unbekannter Fehler",
|
||||
"Code executed successfully (no output)": "Code erfolgreich ausgeführt (keine Ausgabe)",
|
||||
"Execution failed": "Ausführung fehlgeschlagen",
|
||||
"JavaScript REPL": "JavaScript REPL",
|
||||
"JavaScript code to execute": "Auszuführender JavaScript-Code",
|
||||
"Writing JavaScript code...": "Schreibe JavaScript-Code...",
|
||||
"Executing JavaScript": "Führe JavaScript aus",
|
||||
"Preparing JavaScript...": "Bereite JavaScript vor...",
|
||||
"Preparing command...": "Bereite Befehl vor...",
|
||||
"Preparing calculation...": "Bereite Berechnung vor...",
|
||||
"Preparing tool...": "Bereite Tool vor...",
|
||||
"Getting time...": "Hole Zeit...",
|
||||
// Artifacts strings
|
||||
"Processing artifact...": "Verarbeite Artefakt...",
|
||||
"Preparing artifact...": "Bereite Artefakt vor...",
|
||||
"Processing artifact": "Verarbeite Artefakt",
|
||||
"Processed artifact": "Artefakt verarbeitet",
|
||||
"Creating artifact": "Erstelle Artefakt",
|
||||
"Created artifact": "Artefakt erstellt",
|
||||
"Updating artifact": "Aktualisiere Artefakt",
|
||||
"Updated artifact": "Artefakt aktualisiert",
|
||||
"Rewriting artifact": "Überschreibe Artefakt",
|
||||
"Rewrote artifact": "Artefakt überschrieben",
|
||||
"Getting artifact": "Hole Artefakt",
|
||||
"Got artifact": "Artefakt geholt",
|
||||
"Deleting artifact": "Lösche Artefakt",
|
||||
"Deleted artifact": "Artefakt gelöscht",
|
||||
"Getting logs": "Hole Logs",
|
||||
"Got logs": "Logs geholt",
|
||||
"An error occurred": "Ein Fehler ist aufgetreten",
|
||||
"Copy logs": "Logs kopieren",
|
||||
"Autoscroll enabled": "Automatisches Scrollen aktiviert",
|
||||
"Autoscroll disabled": "Automatisches Scrollen deaktiviert",
|
||||
Processing: "Verarbeitung",
|
||||
Create: "Erstellen",
|
||||
Rewrite: "Überschreiben",
|
||||
Get: "Abrufen",
|
||||
"Get logs": "Logs abrufen",
|
||||
"Show artifacts": "Artefakte anzeigen",
|
||||
"Close artifacts": "Artefakte schließen",
|
||||
Artifacts: "Artefakte",
|
||||
"Copy HTML": "HTML kopieren",
|
||||
"Download HTML": "HTML herunterladen",
|
||||
"Reload HTML": "HTML neu laden",
|
||||
"Copy SVG": "SVG kopieren",
|
||||
"Download SVG": "SVG herunterladen",
|
||||
"Copy Markdown": "Markdown kopieren",
|
||||
"Download Markdown": "Markdown herunterladen",
|
||||
Download: "Herunterladen",
|
||||
"No logs for {filename}": "Keine Logs für {filename}",
|
||||
"API Keys Settings": "API-Schlüssel Einstellungen",
|
||||
Settings: "Einstellungen",
|
||||
"API Keys": "API-Schlüssel",
|
||||
Proxy: "Proxy",
|
||||
"Use CORS Proxy": "CORS-Proxy verwenden",
|
||||
"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",
|
||||
Clear: "Löschen",
|
||||
"API Key Required": "API-Schlüssel erforderlich",
|
||||
"Enter your API key for {provider}": "Geben Sie Ihren API-Schlüssel für {provider} ein",
|
||||
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.": "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",
|
||||
Minimal: "Minimal",
|
||||
Low: "Niedrig",
|
||||
Medium: "Mittel",
|
||||
High: "Hoch",
|
||||
"Storage Permission Required": "Speicherberechtigung erforderlich",
|
||||
"This app needs persistent storage to save your conversations": "Diese App benötigt dauerhaften Speicher, um Ihre Konversationen zu speichern",
|
||||
"Why is this needed?": "Warum wird das benötigt?",
|
||||
"Without persistent storage, your browser may delete saved conversations when it needs disk space. Granting this permission ensures your chat history is preserved.": "Ohne dauerhaften Speicher kann Ihr Browser gespeicherte Konversationen löschen, wenn Speicherplatz benötigt wird. Diese Berechtigung stellt sicher, dass Ihr Chatverlauf erhalten bleibt.",
|
||||
"What this means:": "Was das bedeutet:",
|
||||
"Your conversations will be saved locally in your browser": "Ihre Konversationen werden lokal in Ihrem Browser gespeichert",
|
||||
"Data will not be deleted automatically to free up space": "Daten werden nicht automatisch gelöscht, um Speicherplatz freizugeben",
|
||||
"You can still manually clear data at any time": "Sie können Daten jederzeit manuell löschen",
|
||||
"No data is sent to external servers": "Keine Daten werden an externe Server gesendet",
|
||||
"Continue Anyway": "Trotzdem fortfahren",
|
||||
"Requesting...": "Anfrage läuft...",
|
||||
"Grant Permission": "Berechtigung erteilen",
|
||||
Sessions: "Sitzungen",
|
||||
"Load a previous conversation": "Frühere Konversation laden",
|
||||
"No sessions yet": "Noch keine Sitzungen",
|
||||
"Delete this session?": "Diese Sitzung löschen?",
|
||||
Today: "Heute",
|
||||
Yesterday: "Gestern",
|
||||
"{days} days ago": "vor {days} Tagen",
|
||||
messages: "Nachrichten",
|
||||
tokens: "Tokens",
|
||||
Delete: "Löschen",
|
||||
"Drop files here": "Dateien hier ablegen",
|
||||
"Command failed:": "Befehl fehlgeschlagen:",
|
||||
// Providers & Models
|
||||
"Providers & Models": "Anbieter & Modelle",
|
||||
"Cloud Providers": "Cloud-Anbieter",
|
||||
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": "Cloud-LLM-Anbieter mit vordefinierten Modellen. API-Schlüssel werden lokal in Ihrem Browser gespeichert.",
|
||||
"Custom Providers": "Benutzerdefinierte Anbieter",
|
||||
"User-configured servers with auto-discovered or manually defined models.": "Benutzerkonfigurierte Server mit automatisch erkannten oder manuell definierten Modellen.",
|
||||
"Add Provider": "Anbieter hinzufügen",
|
||||
"No custom providers configured. Click 'Add Provider' to get started.": "Keine benutzerdefinierten Anbieter konfiguriert. Klicken Sie auf 'Anbieter hinzufügen', um zu beginnen.",
|
||||
"auto-discovered": "automatisch erkannt",
|
||||
Refresh: "Aktualisieren",
|
||||
Edit: "Bearbeiten",
|
||||
"Are you sure you want to delete this provider?": "Sind Sie sicher, dass Sie diesen Anbieter löschen möchten?",
|
||||
"Edit Provider": "Anbieter bearbeiten",
|
||||
"Provider Name": "Anbietername",
|
||||
"e.g., My Ollama Server": "z.B. Mein Ollama Server",
|
||||
"Provider Type": "Anbietertyp",
|
||||
"Base URL": "Basis-URL",
|
||||
"e.g., http://localhost:11434": "z.B. http://localhost:11434",
|
||||
"API Key (Optional)": "API-Schlüssel (Optional)",
|
||||
"Leave empty if not required": "Leer lassen, falls nicht erforderlich",
|
||||
"Test Connection": "Verbindung testen",
|
||||
Discovered: "Erkannt",
|
||||
Models: "Modelle",
|
||||
models: "Modelle",
|
||||
and: "und",
|
||||
more: "mehr",
|
||||
"For manual provider types, add models after saving the provider.": "Für manuelle Anbietertypen fügen Sie Modelle nach dem Speichern des Anbieters hinzu.",
|
||||
"Please fill in all required fields": "Bitte füllen Sie alle erforderlichen Felder aus",
|
||||
"Failed to save provider": "Fehler beim Speichern des Anbieters",
|
||||
"OpenAI Completions Compatible": "OpenAI Completions Kompatibel",
|
||||
"OpenAI Responses Compatible": "OpenAI Responses Kompatibel",
|
||||
"Anthropic Messages Compatible": "Anthropic Messages Kompatibel",
|
||||
"Checking...": "Überprüfe...",
|
||||
Disconnected: "Getrennt",
|
||||
},
|
||||
};
|
||||
setTranslations(translations);
|
||||
export * from "@mariozechner/mini-lit/dist/i18n.js";
|
||||
//# sourceMappingURL=i18n.js.map
|
||||
File diff suppressed because one or more lines are too long
38
apps/macos/Sources/Clawdis/Resources/WebChat/utils/model-discovery.d.ts
vendored
Normal file
38
apps/macos/Sources/Clawdis/Resources/WebChat/utils/model-discovery.d.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { Model } from "@mariozechner/pi-ai";
|
||||
/**
|
||||
* Discover models from an Ollama server.
|
||||
* @param baseUrl - Base URL of the Ollama server (e.g., "http://localhost:11434")
|
||||
* @param apiKey - Optional API key (currently unused by Ollama)
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export declare function discoverOllamaModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]>;
|
||||
/**
|
||||
* Discover models from a llama.cpp server via OpenAI-compatible /v1/models endpoint.
|
||||
* @param baseUrl - Base URL of the llama.cpp server (e.g., "http://localhost:8080")
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export declare function discoverLlamaCppModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]>;
|
||||
/**
|
||||
* Discover models from a vLLM server via OpenAI-compatible /v1/models endpoint.
|
||||
* @param baseUrl - Base URL of the vLLM server (e.g., "http://localhost:8000")
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export declare function discoverVLLMModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]>;
|
||||
/**
|
||||
* Discover models from an LM Studio server using the LM Studio SDK.
|
||||
* @param baseUrl - Base URL of the LM Studio server (e.g., "http://localhost:1234")
|
||||
* @param apiKey - Optional API key (unused for LM Studio SDK)
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export declare function discoverLMStudioModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]>;
|
||||
/**
|
||||
* Convenience function to discover models based on provider type.
|
||||
* @param type - Provider type
|
||||
* @param baseUrl - Base URL of the server
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export declare function discoverModels(type: "ollama" | "llama.cpp" | "vllm" | "lmstudio", baseUrl: string, apiKey?: string): Promise<Model<any>[]>;
|
||||
//# sourceMappingURL=model-discovery.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"model-discovery.d.ts","sourceRoot":"","sources":["../../src/utils/model-discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGjD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAkEnG;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAsDpG;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAsDhG;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CA4CrG;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,IAAI,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,UAAU,EAClD,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAWvB"}
|
||||
@@ -0,0 +1,243 @@
|
||||
import { LMStudioClient } from "@lmstudio/sdk";
|
||||
import { Ollama } from "ollama/browser";
|
||||
/**
|
||||
* Discover models from an Ollama server.
|
||||
* @param baseUrl - Base URL of the Ollama server (e.g., "http://localhost:11434")
|
||||
* @param apiKey - Optional API key (currently unused by Ollama)
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export async function discoverOllamaModels(baseUrl, _apiKey) {
|
||||
try {
|
||||
// Create Ollama client
|
||||
const ollama = new Ollama({ host: baseUrl });
|
||||
// Get list of available models
|
||||
const { models } = await ollama.list();
|
||||
// Fetch details for each model and convert to Model format
|
||||
const ollamaModelPromises = models.map(async (model) => {
|
||||
try {
|
||||
// Get model details
|
||||
const details = await ollama.show({
|
||||
model: model.name,
|
||||
});
|
||||
// Check capabilities - filter out models that don't support tools
|
||||
const capabilities = details.capabilities || [];
|
||||
if (!capabilities.includes("tools")) {
|
||||
console.debug(`Skipping model ${model.name}: does not support tools`);
|
||||
return null;
|
||||
}
|
||||
// Extract model info
|
||||
const modelInfo = details.model_info || {};
|
||||
// Get context window size - look for architecture-specific keys
|
||||
const architecture = modelInfo["general.architecture"] || "";
|
||||
const contextKey = `${architecture}.context_length`;
|
||||
const contextWindow = parseInt(modelInfo[contextKey] || "8192", 10);
|
||||
// Ollama caps max tokens at 10x context length
|
||||
const maxTokens = contextWindow * 10;
|
||||
// Ollama only supports completions API
|
||||
const ollamaModel = {
|
||||
id: model.name,
|
||||
name: model.name,
|
||||
api: "openai-completions",
|
||||
provider: "", // Will be set by caller
|
||||
baseUrl: `${baseUrl}/v1`,
|
||||
reasoning: capabilities.includes("thinking"),
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: contextWindow,
|
||||
maxTokens: maxTokens,
|
||||
};
|
||||
return ollamaModel;
|
||||
}
|
||||
catch (err) {
|
||||
console.error(`Failed to fetch details for model ${model.name}:`, err);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
const results = await Promise.all(ollamaModelPromises);
|
||||
return results.filter((m) => m !== null);
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Failed to discover Ollama models:", err);
|
||||
throw new Error(`Ollama discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Discover models from a llama.cpp server via OpenAI-compatible /v1/models endpoint.
|
||||
* @param baseUrl - Base URL of the llama.cpp server (e.g., "http://localhost:8080")
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export async function discoverLlamaCppModels(baseUrl, apiKey) {
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (apiKey) {
|
||||
headers.Authorization = `Bearer ${apiKey}`;
|
||||
}
|
||||
const response = await fetch(`${baseUrl}/v1/models`, {
|
||||
method: "GET",
|
||||
headers,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!data.data || !Array.isArray(data.data)) {
|
||||
throw new Error("Invalid response format from llama.cpp server");
|
||||
}
|
||||
return data.data.map((model) => {
|
||||
// llama.cpp doesn't always provide context window info
|
||||
const contextWindow = model.context_length || 8192;
|
||||
const maxTokens = model.max_tokens || 4096;
|
||||
const llamaModel = {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
api: "openai-completions",
|
||||
provider: "", // Will be set by caller
|
||||
baseUrl: `${baseUrl}/v1`,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: contextWindow,
|
||||
maxTokens: maxTokens,
|
||||
};
|
||||
return llamaModel;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Failed to discover llama.cpp models:", err);
|
||||
throw new Error(`llama.cpp discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Discover models from a vLLM server via OpenAI-compatible /v1/models endpoint.
|
||||
* @param baseUrl - Base URL of the vLLM server (e.g., "http://localhost:8000")
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export async function discoverVLLMModels(baseUrl, apiKey) {
|
||||
try {
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
if (apiKey) {
|
||||
headers.Authorization = `Bearer ${apiKey}`;
|
||||
}
|
||||
const response = await fetch(`${baseUrl}/v1/models`, {
|
||||
method: "GET",
|
||||
headers,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!data.data || !Array.isArray(data.data)) {
|
||||
throw new Error("Invalid response format from vLLM server");
|
||||
}
|
||||
return data.data.map((model) => {
|
||||
// vLLM provides max_model_len which is the context window
|
||||
const contextWindow = model.max_model_len || 8192;
|
||||
const maxTokens = Math.min(contextWindow, 4096); // Cap max tokens
|
||||
const vllmModel = {
|
||||
id: model.id,
|
||||
name: model.id,
|
||||
api: "openai-completions",
|
||||
provider: "", // Will be set by caller
|
||||
baseUrl: `${baseUrl}/v1`,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: contextWindow,
|
||||
maxTokens: maxTokens,
|
||||
};
|
||||
return vllmModel;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Failed to discover vLLM models:", err);
|
||||
throw new Error(`vLLM discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Discover models from an LM Studio server using the LM Studio SDK.
|
||||
* @param baseUrl - Base URL of the LM Studio server (e.g., "http://localhost:1234")
|
||||
* @param apiKey - Optional API key (unused for LM Studio SDK)
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export async function discoverLMStudioModels(baseUrl, _apiKey) {
|
||||
try {
|
||||
// Extract host and port from baseUrl
|
||||
const url = new URL(baseUrl);
|
||||
const port = url.port ? parseInt(url.port, 10) : 1234;
|
||||
// Create LM Studio client
|
||||
const client = new LMStudioClient({ baseUrl: `ws://${url.hostname}:${port}` });
|
||||
// List all downloaded models
|
||||
const models = await client.system.listDownloadedModels();
|
||||
// Filter to only LLM models and map to our Model format
|
||||
return models
|
||||
.filter((model) => model.type === "llm")
|
||||
.map((model) => {
|
||||
const contextWindow = model.maxContextLength;
|
||||
// Use 10x context length like Ollama does
|
||||
const maxTokens = contextWindow;
|
||||
const lmStudioModel = {
|
||||
id: model.path,
|
||||
name: model.displayName || model.path,
|
||||
api: "openai-completions",
|
||||
provider: "", // Will be set by caller
|
||||
baseUrl: `${baseUrl}/v1`,
|
||||
reasoning: model.trainedForToolUse || false,
|
||||
input: model.vision ? ["text", "image"] : ["text"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: contextWindow,
|
||||
maxTokens: maxTokens,
|
||||
};
|
||||
return lmStudioModel;
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.error("Failed to discover LM Studio models:", err);
|
||||
throw new Error(`LM Studio discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convenience function to discover models based on provider type.
|
||||
* @param type - Provider type
|
||||
* @param baseUrl - Base URL of the server
|
||||
* @param apiKey - Optional API key
|
||||
* @returns Array of discovered models
|
||||
*/
|
||||
export async function discoverModels(type, baseUrl, apiKey) {
|
||||
switch (type) {
|
||||
case "ollama":
|
||||
return discoverOllamaModels(baseUrl, apiKey);
|
||||
case "llama.cpp":
|
||||
return discoverLlamaCppModels(baseUrl, apiKey);
|
||||
case "vllm":
|
||||
return discoverVLLMModels(baseUrl, apiKey);
|
||||
case "lmstudio":
|
||||
return discoverLMStudioModels(baseUrl, apiKey);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=model-discovery.js.map
|
||||
File diff suppressed because one or more lines are too long
37
apps/macos/Sources/Clawdis/Resources/WebChat/utils/proxy-utils.d.ts
vendored
Normal file
37
apps/macos/Sources/Clawdis/Resources/WebChat/utils/proxy-utils.d.ts
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Api, Model } from "@mariozechner/pi-ai";
|
||||
/**
|
||||
* Centralized proxy decision logic.
|
||||
*
|
||||
* Determines whether to use a CORS proxy for LLM API requests based on:
|
||||
* - Provider name
|
||||
* - API key pattern (for providers where it matters)
|
||||
*/
|
||||
/**
|
||||
* Check if a provider/API key combination requires a CORS proxy.
|
||||
*
|
||||
* @param provider - Provider name (e.g., "anthropic", "openai", "zai")
|
||||
* @param apiKey - API key for the provider
|
||||
* @returns true if proxy is required, false otherwise
|
||||
*/
|
||||
export declare function shouldUseProxyForProvider(provider: string, apiKey: string): boolean;
|
||||
/**
|
||||
* Apply CORS proxy to a model's baseUrl if needed.
|
||||
*
|
||||
* @param model - The model to potentially proxy
|
||||
* @param apiKey - API key for the provider
|
||||
* @param proxyUrl - CORS proxy URL (e.g., "https://proxy.mariozechner.at/proxy")
|
||||
* @returns Model with modified baseUrl if proxy is needed, otherwise original model
|
||||
*/
|
||||
export declare function applyProxyIfNeeded<T extends Api>(model: Model<T>, apiKey: string, proxyUrl?: string): Model<T>;
|
||||
/**
|
||||
* Check if an error is likely a CORS error.
|
||||
*
|
||||
* CORS errors in browsers typically manifest as:
|
||||
* - TypeError with message "Failed to fetch"
|
||||
* - NetworkError
|
||||
*
|
||||
* @param error - The error to check
|
||||
* @returns true if error is likely a CORS error
|
||||
*/
|
||||
export declare function isCorsError(error: unknown): boolean;
|
||||
//# sourceMappingURL=proxy-utils.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"proxy-utils.d.ts","sourceRoot":"","sources":["../../src/utils/proxy-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA2BnF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAqB9G;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAwBnD"}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Centralized proxy decision logic.
|
||||
*
|
||||
* Determines whether to use a CORS proxy for LLM API requests based on:
|
||||
* - Provider name
|
||||
* - API key pattern (for providers where it matters)
|
||||
*/
|
||||
/**
|
||||
* Check if a provider/API key combination requires a CORS proxy.
|
||||
*
|
||||
* @param provider - Provider name (e.g., "anthropic", "openai", "zai")
|
||||
* @param apiKey - API key for the provider
|
||||
* @returns true if proxy is required, false otherwise
|
||||
*/
|
||||
export function shouldUseProxyForProvider(provider, apiKey) {
|
||||
switch (provider.toLowerCase()) {
|
||||
case "zai":
|
||||
// Z-AI always requires proxy
|
||||
return true;
|
||||
case "anthropic":
|
||||
// Anthropic OAuth tokens (sk-ant-oat-*) require proxy
|
||||
// Regular API keys (sk-ant-api-*) do NOT require proxy
|
||||
return apiKey.startsWith("sk-ant-oat");
|
||||
// These providers work without proxy
|
||||
case "openai":
|
||||
case "google":
|
||||
case "groq":
|
||||
case "openrouter":
|
||||
case "cerebras":
|
||||
case "xai":
|
||||
case "ollama":
|
||||
case "lmstudio":
|
||||
return false;
|
||||
// Unknown providers - assume no proxy needed
|
||||
// This allows new providers to work by default
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Apply CORS proxy to a model's baseUrl if needed.
|
||||
*
|
||||
* @param model - The model to potentially proxy
|
||||
* @param apiKey - API key for the provider
|
||||
* @param proxyUrl - CORS proxy URL (e.g., "https://proxy.mariozechner.at/proxy")
|
||||
* @returns Model with modified baseUrl if proxy is needed, otherwise original model
|
||||
*/
|
||||
export function applyProxyIfNeeded(model, apiKey, proxyUrl) {
|
||||
// If no proxy URL configured, return original model
|
||||
if (!proxyUrl) {
|
||||
return model;
|
||||
}
|
||||
// If model has no baseUrl, can't proxy it
|
||||
if (!model.baseUrl) {
|
||||
return model;
|
||||
}
|
||||
// Check if this provider/key needs proxy
|
||||
if (!shouldUseProxyForProvider(model.provider, apiKey)) {
|
||||
return model;
|
||||
}
|
||||
// Apply proxy to baseUrl
|
||||
return {
|
||||
...model,
|
||||
baseUrl: `${proxyUrl}/?url=${encodeURIComponent(model.baseUrl)}`,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Check if an error is likely a CORS error.
|
||||
*
|
||||
* CORS errors in browsers typically manifest as:
|
||||
* - TypeError with message "Failed to fetch"
|
||||
* - NetworkError
|
||||
*
|
||||
* @param error - The error to check
|
||||
* @returns true if error is likely a CORS error
|
||||
*/
|
||||
export function isCorsError(error) {
|
||||
if (!(error instanceof Error)) {
|
||||
return false;
|
||||
}
|
||||
// Check for common CORS error patterns
|
||||
const message = error.message.toLowerCase();
|
||||
// "Failed to fetch" is the standard CORS error in most browsers
|
||||
if (error.name === "TypeError" && message.includes("failed to fetch")) {
|
||||
return true;
|
||||
}
|
||||
// Some browsers report "NetworkError"
|
||||
if (error.name === "NetworkError") {
|
||||
return true;
|
||||
}
|
||||
// CORS-specific messages
|
||||
if (message.includes("cors") || message.includes("cross-origin")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//# sourceMappingURL=proxy-utils.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"proxy-utils.js","sourceRoot":"","sources":["../../src/utils/proxy-utils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,MAAc;IACzE,QAAQ,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAChC,KAAK,KAAK;YACT,6BAA6B;YAC7B,OAAO,IAAI,CAAC;QAEb,KAAK,WAAW;YACf,sDAAsD;YACtD,uDAAuD;YACvD,OAAO,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAExC,qCAAqC;QACrC,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,MAAM,CAAC;QACZ,KAAK,YAAY,CAAC;QAClB,KAAK,UAAU,CAAC;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,QAAQ,CAAC;QACd,KAAK,UAAU;YACd,OAAO,KAAK,CAAC;QAEd,6CAA6C;QAC7C,+CAA+C;QAC/C;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAgB,KAAe,EAAE,MAAc,EAAE,QAAiB;IACnG,oDAAoD;IACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACd,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,yBAAyB;IACzB,OAAO;QACN,GAAG,KAAK;QACR,OAAO,EAAE,GAAG,QAAQ,SAAS,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;KAChE,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,uCAAuC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAE5C,gEAAgE;IAChE,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,sCAAsC;IACtC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,yBAAyB;IACzB,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC"}
|
||||
347
apps/macos/Sources/Clawdis/Resources/WebChat/utils/test-sessions.d.ts
vendored
Normal file
347
apps/macos/Sources/Clawdis/Resources/WebChat/utils/test-sessions.d.ts
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
export declare const simpleHtml: {
|
||||
systemPrompt: string;
|
||||
model: {
|
||||
id: string;
|
||||
name: string;
|
||||
api: string;
|
||||
provider: string;
|
||||
baseUrl: string;
|
||||
reasoning: boolean;
|
||||
input: string[];
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
};
|
||||
contextWindow: number;
|
||||
maxTokens: number;
|
||||
};
|
||||
messages: ({
|
||||
role: string;
|
||||
content: {
|
||||
type: string;
|
||||
text: string;
|
||||
}[];
|
||||
api?: undefined;
|
||||
provider?: undefined;
|
||||
model?: undefined;
|
||||
usage?: undefined;
|
||||
stopReason?: undefined;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: ({
|
||||
type: string;
|
||||
text: string;
|
||||
id?: undefined;
|
||||
name?: undefined;
|
||||
arguments?: undefined;
|
||||
} | {
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: {
|
||||
command: string;
|
||||
filename: string;
|
||||
content: string;
|
||||
};
|
||||
text?: undefined;
|
||||
})[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
toolCallId: string;
|
||||
toolName: string;
|
||||
output: string;
|
||||
isError: boolean;
|
||||
content?: undefined;
|
||||
api?: undefined;
|
||||
provider?: undefined;
|
||||
model?: undefined;
|
||||
usage?: undefined;
|
||||
stopReason?: undefined;
|
||||
})[];
|
||||
};
|
||||
export declare const longSession: {
|
||||
systemPrompt: string;
|
||||
model: {
|
||||
id: string;
|
||||
name: string;
|
||||
api: string;
|
||||
provider: string;
|
||||
baseUrl: string;
|
||||
reasoning: boolean;
|
||||
input: string[];
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
};
|
||||
contextWindow: number;
|
||||
maxTokens: number;
|
||||
};
|
||||
messages: ({
|
||||
role: string;
|
||||
content: {
|
||||
type: string;
|
||||
text: string;
|
||||
}[];
|
||||
api?: undefined;
|
||||
provider?: undefined;
|
||||
model?: undefined;
|
||||
usage?: undefined;
|
||||
stopReason?: undefined;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: ({
|
||||
type: string;
|
||||
text: string;
|
||||
id?: undefined;
|
||||
name?: undefined;
|
||||
arguments?: undefined;
|
||||
} | {
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: {
|
||||
command: string;
|
||||
filename: string;
|
||||
content: string;
|
||||
};
|
||||
text?: undefined;
|
||||
})[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
toolCallId: string;
|
||||
toolName: string;
|
||||
output: string;
|
||||
isError: boolean;
|
||||
content?: undefined;
|
||||
api?: undefined;
|
||||
provider?: undefined;
|
||||
model?: undefined;
|
||||
usage?: undefined;
|
||||
stopReason?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: ({
|
||||
type: string;
|
||||
text: string;
|
||||
id?: undefined;
|
||||
name?: undefined;
|
||||
arguments?: undefined;
|
||||
} | {
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: {
|
||||
code: string;
|
||||
};
|
||||
text?: undefined;
|
||||
})[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
toolCallId: string;
|
||||
toolName: string;
|
||||
output: string;
|
||||
details: {
|
||||
files: never[];
|
||||
};
|
||||
isError: boolean;
|
||||
content?: undefined;
|
||||
api?: undefined;
|
||||
provider?: undefined;
|
||||
model?: undefined;
|
||||
usage?: undefined;
|
||||
stopReason?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: {
|
||||
type: string;
|
||||
text: string;
|
||||
}[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
errorMessage: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: ({
|
||||
type: string;
|
||||
text: string;
|
||||
id?: undefined;
|
||||
name?: undefined;
|
||||
arguments?: undefined;
|
||||
} | {
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: {
|
||||
command: string;
|
||||
filename: string;
|
||||
title: string;
|
||||
content: string;
|
||||
};
|
||||
text?: undefined;
|
||||
})[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
} | {
|
||||
role: string;
|
||||
content: {
|
||||
type: string;
|
||||
id: string;
|
||||
name: string;
|
||||
arguments: {
|
||||
command: string;
|
||||
filename: string;
|
||||
old_str: string;
|
||||
new_str: string;
|
||||
};
|
||||
}[];
|
||||
api: string;
|
||||
provider: string;
|
||||
model: string;
|
||||
usage: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
cost: {
|
||||
input: number;
|
||||
output: number;
|
||||
cacheRead: number;
|
||||
cacheWrite: number;
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
stopReason: string;
|
||||
toolCallId?: undefined;
|
||||
toolName?: undefined;
|
||||
output?: undefined;
|
||||
isError?: undefined;
|
||||
details?: undefined;
|
||||
errorMessage?: undefined;
|
||||
})[];
|
||||
};
|
||||
//# sourceMappingURL=test-sessions.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"test-sessions.d.ts","sourceRoot":"","sources":["../../src/utils/test-sessions.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqGtB,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8lEvB,CAAC"}
|
||||
2215
apps/macos/Sources/Clawdis/Resources/WebChat/utils/test-sessions.js
Normal file
2215
apps/macos/Sources/Clawdis/Resources/WebChat/utils/test-sessions.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user