mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-30 22:11:45 +00:00
mac: bundle web chat assets
This commit is contained in:
9
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ArtifactElement.d.ts
vendored
Normal file
9
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ArtifactElement.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { LitElement, type TemplateResult } from "lit";
|
||||
export declare abstract class ArtifactElement extends LitElement {
|
||||
filename: string;
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
abstract get content(): string;
|
||||
abstract set content(value: string);
|
||||
abstract getHeaderButtons(): TemplateResult | HTMLElement;
|
||||
}
|
||||
//# sourceMappingURL=ArtifactElement.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ArtifactElement.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/ArtifactElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAEtD,8BAAsB,eAAgB,SAAQ,UAAU;IAChD,QAAQ,SAAM;cAEF,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAIrE,aAAoB,OAAO,IAAI,MAAM,CAAC;IACtC,aAAoB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE;IAE3C,QAAQ,CAAC,gBAAgB,IAAI,cAAc,GAAG,WAAW;CACzD"}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { LitElement } from "lit";
|
||||
export class ArtifactElement extends LitElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.filename = "";
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM for shared styles
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ArtifactElement.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ArtifactElement.js","sourceRoot":"","sources":["../../../src/tools/artifacts/ArtifactElement.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAuB,MAAM,KAAK,CAAC;AAEtD,MAAM,OAAgB,eAAgB,SAAQ,UAAU;IAAxD;;QACQ,aAAQ,GAAG,EAAE,CAAC;IAUtB,CAAC;IARmB,gBAAgB;QAClC,OAAO,IAAI,CAAC,CAAC,8BAA8B;IAC5C,CAAC;CAMD"}
|
||||
4
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ArtifactPill.d.ts
vendored
Normal file
4
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ArtifactPill.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import type { ArtifactsPanel } from "./artifacts.js";
|
||||
export declare function ArtifactPill(filename: string, artifactsPanel?: ArtifactsPanel): TemplateResult;
|
||||
//# sourceMappingURL=ArtifactPill.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ArtifactPill.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/ArtifactPill.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAEhD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,cAAc,CAoB9F"}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { icon } from "@mariozechner/mini-lit";
|
||||
import { html } from "lit";
|
||||
import { FileCode2 } from "lucide";
|
||||
export function ArtifactPill(filename, artifactsPanel) {
|
||||
const handleClick = (e) => {
|
||||
if (!artifactsPanel)
|
||||
return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// openArtifact will show the artifact and call onOpen() to open the panel if needed
|
||||
artifactsPanel.openArtifact(filename);
|
||||
};
|
||||
return html `
|
||||
<span
|
||||
class="inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-muted/50 border border-border rounded ${artifactsPanel ? "cursor-pointer hover:bg-muted transition-colors" : ""}"
|
||||
@click=${artifactsPanel ? handleClick : null}
|
||||
>
|
||||
${icon(FileCode2, "sm")}
|
||||
<span class="text-foreground">${filename}</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
//# sourceMappingURL=ArtifactPill.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ArtifactPill.js","sourceRoot":"","sources":["../../../src/tools/artifacts/ArtifactPill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAGnC,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,cAA+B;IAC7E,MAAM,WAAW,GAAG,CAAC,CAAQ,EAAE,EAAE;QAChC,IAAI,CAAC,cAAc;YAAE,OAAO;QAC5B,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,CAAC,CAAC,eAAe,EAAE,CAAC;QACpB,oFAAoF;QACpF,cAAc,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC,CAAC;IAEF,OAAO,IAAI,CAAA;;wGAGR,cAAc,CAAC,CAAC,CAAC,iDAAiD,CAAC,CAAC,CAAC,EACtE;YACS,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI;;KAE1C,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC;mCACS,QAAQ;;EAEzC,CAAC;AACH,CAAC"}
|
||||
18
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/Console.d.ts
vendored
Normal file
18
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/Console.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { LitElement, type TemplateResult } from "lit";
|
||||
interface LogEntry {
|
||||
type: "log" | "error";
|
||||
text: string;
|
||||
}
|
||||
export declare class Console extends LitElement {
|
||||
logs: LogEntry[];
|
||||
private expanded;
|
||||
private autoscroll;
|
||||
private logsContainerRef;
|
||||
protected createRenderRoot(): this;
|
||||
updated(): void;
|
||||
private getLogsText;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=Console.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Console.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/Console.ts"],"names":[],"mappings":"AACA,OAAO,2CAA2C,CAAC;AACnD,OAAO,EAAQ,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAO5D,UAAU,QAAQ;IACjB,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,qBACa,OAAQ,SAAQ,UAAU;IACN,IAAI,EAAE,QAAQ,EAAE,CAAM;IAC7C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,gBAAgB,CAAoC;IAE5D,SAAS,CAAC,gBAAgB;IAIjB,OAAO;IAOhB,OAAO,CAAC,WAAW;IAIV,MAAM,IAAI,cAAc;CAwDjC"}
|
||||
@@ -0,0 +1,95 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { icon } from "@mariozechner/mini-lit";
|
||||
import "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
import { ChevronDown, ChevronRight, ChevronsDown, Lock } from "lucide";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
let Console = class Console extends LitElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.logs = [];
|
||||
this.expanded = false;
|
||||
this.autoscroll = true;
|
||||
this.logsContainerRef = createRef();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM
|
||||
}
|
||||
updated() {
|
||||
// Autoscroll to bottom when new logs arrive
|
||||
if (this.autoscroll && this.expanded && this.logsContainerRef.value) {
|
||||
this.logsContainerRef.value.scrollTop = this.logsContainerRef.value.scrollHeight;
|
||||
}
|
||||
}
|
||||
getLogsText() {
|
||||
return this.logs.map((l) => `[${l.type}] ${l.text}`).join("\n");
|
||||
}
|
||||
render() {
|
||||
const errorCount = this.logs.filter((l) => l.type === "error").length;
|
||||
const summary = errorCount > 0
|
||||
? `${i18n("console")} (${errorCount} ${errorCount === 1 ? "error" : "errors"})`
|
||||
: `${i18n("console")} (${this.logs.length})`;
|
||||
return html `
|
||||
<div class="border-t border-border p-2">
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<button
|
||||
@click=${() => {
|
||||
this.expanded = !this.expanded;
|
||||
}}
|
||||
class="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors flex-1 text-left"
|
||||
>
|
||||
${icon(this.expanded ? ChevronDown : ChevronRight, "sm")}
|
||||
<span>${summary}</span>
|
||||
</button>
|
||||
${this.expanded
|
||||
? html `
|
||||
<button
|
||||
@click=${() => {
|
||||
this.autoscroll = !this.autoscroll;
|
||||
}}
|
||||
class="p-1 rounded transition-colors ${this.autoscroll ? "bg-accent text-accent-foreground" : "hover:bg-muted"}"
|
||||
title=${this.autoscroll ? i18n("Autoscroll enabled") : i18n("Autoscroll disabled")}
|
||||
>
|
||||
${icon(this.autoscroll ? ChevronsDown : Lock, "sm")}
|
||||
</button>
|
||||
<copy-button .text=${this.getLogsText()} title=${i18n("Copy logs")} .showText=${false} class="!bg-transparent hover:!bg-accent"></copy-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
${this.expanded
|
||||
? html `
|
||||
<div class="max-h-48 overflow-y-auto space-y-1 mt-2" ${ref(this.logsContainerRef)}>
|
||||
${repeat(this.logs, (_log, index) => index, (log) => html `
|
||||
<div class="text-xs font-mono ${log.type === "error" ? "text-destructive" : "text-muted-foreground"}">
|
||||
[${log.type}] ${log.text}
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], Console.prototype, "logs", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Console.prototype, "expanded", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], Console.prototype, "autoscroll", void 0);
|
||||
Console = __decorate([
|
||||
customElement("artifact-console")
|
||||
], Console);
|
||||
export { Console };
|
||||
//# sourceMappingURL=Console.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"Console.js","sourceRoot":"","sources":["../../../src/tools/artifacts/Console.ts"],"names":[],"mappings":"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"}
|
||||
22
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/DocxArtifact.d.ts
vendored
Normal file
22
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/DocxArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class DocxArtifact extends ArtifactElement {
|
||||
private _content;
|
||||
private error;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
private base64ToArrayBuffer;
|
||||
private decodeBase64;
|
||||
getHeaderButtons(): TemplateResult<1>;
|
||||
updated(changedProperties: Map<string, any>): Promise<void>;
|
||||
private renderDocx;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"docx-artifact": DocxArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=DocxArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DocxArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/DocxArtifact.ts"],"names":[],"mappings":"AAEA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,YAAa,SAAQ,eAAe;IACpB,OAAO,CAAC,QAAQ,CAAM;IACzC,OAAO,CAAC,KAAK,CAAuB;IAE7C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAIxB;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,YAAY;IAiBb,gBAAgB;IAaR,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;YAQ5C,UAAU;IAoGf,MAAM,IAAI,cAAc;CAkBjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,eAAe,EAAE,YAAY,CAAC;KAC9B;CACD"}
|
||||
@@ -0,0 +1,208 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { renderAsync } from "docx-preview";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let DocxArtifact = class DocxArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._content = "";
|
||||
this.error = null;
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.error = null;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
}
|
||||
base64ToArrayBuffer(base64) {
|
||||
// Remove data URL prefix if present
|
||||
let base64Data = base64;
|
||||
if (base64.startsWith("data:")) {
|
||||
const base64Match = base64.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
decodeBase64() {
|
||||
let base64Data = this._content;
|
||||
if (this._content.startsWith("data:")) {
|
||||
const base64Match = this._content.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${DownloadButton({
|
||||
content: this.decodeBase64(),
|
||||
filename: this.filename,
|
||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
async updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("_content") && this._content && !this.error) {
|
||||
await this.renderDocx();
|
||||
}
|
||||
}
|
||||
async renderDocx() {
|
||||
const container = this.querySelector("#docx-container");
|
||||
if (!container || !this._content)
|
||||
return;
|
||||
try {
|
||||
const arrayBuffer = this.base64ToArrayBuffer(this._content);
|
||||
// Clear container first
|
||||
container.innerHTML = "";
|
||||
// Create a wrapper div for the document
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "docx-wrapper-custom";
|
||||
container.appendChild(wrapper);
|
||||
// Render the DOCX file into the wrapper
|
||||
await renderAsync(arrayBuffer, wrapper, undefined, {
|
||||
className: "docx",
|
||||
inWrapper: true,
|
||||
ignoreWidth: true,
|
||||
ignoreHeight: false,
|
||||
ignoreFonts: false,
|
||||
breakPages: true,
|
||||
ignoreLastRenderedPageBreak: true,
|
||||
experimental: false,
|
||||
trimXmlDeclaration: true,
|
||||
useBase64URL: false,
|
||||
renderHeaders: true,
|
||||
renderFooters: true,
|
||||
renderFootnotes: true,
|
||||
renderEndnotes: true,
|
||||
});
|
||||
// Apply custom styles to match theme and fix sizing
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
#docx-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#docx-container .docx-wrapper-custom {
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
#docx-container .docx-wrapper {
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
background: transparent !important;
|
||||
padding: 0em !important;
|
||||
}
|
||||
|
||||
#docx-container .docx-wrapper > section.docx {
|
||||
box-shadow: none !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
margin: 0 !important;
|
||||
padding: 2em !important;
|
||||
background: white !important;
|
||||
color: black !important;
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
|
||||
/* Fix tables and wide content */
|
||||
#docx-container table {
|
||||
max-width: 100% !important;
|
||||
width: auto !important;
|
||||
overflow-x: auto !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
#docx-container img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Fix paragraphs and text */
|
||||
#docx-container p,
|
||||
#docx-container span,
|
||||
#docx-container div {
|
||||
max-width: 100% !important;
|
||||
word-wrap: break-word !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
|
||||
/* Hide page breaks in web view */
|
||||
#docx-container .docx-page-break {
|
||||
display: none !important;
|
||||
}
|
||||
`;
|
||||
container.appendChild(style);
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error rendering DOCX:", error);
|
||||
this.error = error?.message || i18n("Failed to load document");
|
||||
}
|
||||
}
|
||||
render() {
|
||||
if (this.error) {
|
||||
return html `
|
||||
<div class="h-full flex items-center justify-center bg-background p-4">
|
||||
<div class="bg-destructive/10 border border-destructive text-destructive p-4 rounded-lg max-w-2xl">
|
||||
<div class="font-medium mb-1">${i18n("Error loading document")}</div>
|
||||
<div class="text-sm opacity-90">${this.error}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html `
|
||||
<div class="h-full flex flex-col bg-background overflow-auto">
|
||||
<div id="docx-container" class="flex-1 overflow-auto"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ type: String })
|
||||
], DocxArtifact.prototype, "_content", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], DocxArtifact.prototype, "error", void 0);
|
||||
DocxArtifact = __decorate([
|
||||
customElement("docx-artifact")
|
||||
], DocxArtifact);
|
||||
export { DocxArtifact };
|
||||
//# sourceMappingURL=DocxArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DocxArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/DocxArtifact.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGhD,IAAM,YAAY,GAAlB,MAAM,YAAa,SAAQ,eAAe;IAA1C;;QAC8B,aAAQ,GAAG,EAAE,CAAC;QACjC,UAAK,GAAkB,IAAI,CAAC;IAoM9C,CAAC;IAlMA,IAAI,OAAO;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IAED,IAAI,OAAO,CAAC,KAAa;QACxB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAEkB,gBAAgB;QAClC,OAAO,IAAI,CAAC;IACb,CAAC;IAEQ,iBAAiB;QACzB,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;IAC5B,CAAC;IAEO,mBAAmB,CAAC,MAAc;QACzC,oCAAoC;QACpC,IAAI,UAAU,GAAG,MAAM,CAAC;QACxB,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YAChD,IAAI,WAAW,EAAE,CAAC;gBACjB,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACF,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,KAAK,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC;IACrB,CAAC;IAEO,YAAY;QACnB,IAAI,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;YACvD,IAAI,WAAW,EAAE,CAAC;gBACjB,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC7B,CAAC;QACF,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,KAAK,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;IAEM,gBAAgB;QACtB,OAAO,IAAI,CAAA;;MAEP,cAAc,CAAC;YAChB,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE;YAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,yEAAyE;YACnF,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC;SACvB,CAAC;;GAEH,CAAC;IACH,CAAC;IAEQ,KAAK,CAAC,OAAO,CAAC,iBAAmC;QACzD,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAEjC,IAAI,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACvE,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACzB,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,UAAU;QACvB,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEzC,IAAI,CAAC;YACJ,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE5D,wBAAwB;YACxB,SAAS,CAAC,SAAS,GAAG,EAAE,CAAC;YAEzB,wCAAwC;YACxC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC9C,OAAO,CAAC,SAAS,GAAG,qBAAqB,CAAC;YAC1C,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAE/B,wCAAwC;YACxC,MAAM,WAAW,CAAC,WAAW,EAAE,OAAsB,EAAE,SAAS,EAAE;gBACjE,SAAS,EAAE,MAAM;gBACjB,SAAS,EAAE,IAAI;gBACf,WAAW,EAAE,IAAI;gBACjB,YAAY,EAAE,KAAK;gBACnB,WAAW,EAAE,KAAK;gBAClB,UAAU,EAAE,IAAI;gBAChB,2BAA2B,EAAE,IAAI;gBACjC,YAAY,EAAE,KAAK;gBACnB,kBAAkB,EAAE,IAAI;gBACxB,YAAY,EAAE,KAAK;gBACnB,aAAa,EAAE,IAAI;gBACnB,aAAa,EAAE,IAAI;gBACnB,eAAe,EAAE,IAAI;gBACrB,cAAc,EAAE,IAAI;aACpB,CAAC,CAAC;YAEH,oDAAoD;YACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC9C,KAAK,CAAC,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyDnB,CAAC;YACF,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;YAC9C,IAAI,CAAC,KAAK,GAAG,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAChE,CAAC;IACF,CAAC;IAEQ,MAAM;QACd,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO,IAAI,CAAA;;;sCAGwB,IAAI,CAAC,wBAAwB,CAAC;wCAC5B,IAAI,CAAC,KAAK;;;IAG9C,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;;;;GAIV,CAAC;IACH,CAAC;CACD,CAAA;AArMoC;IAAnC,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;8CAAuB;AACjC;IAAhB,KAAK,EAAE;2CAAqC;AAFjC,YAAY;IADxB,aAAa,CAAC,eAAe,CAAC;GAClB,YAAY,CAsMxB"}
|
||||
24
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ExcelArtifact.d.ts
vendored
Normal file
24
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ExcelArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class ExcelArtifact extends ArtifactElement {
|
||||
private _content;
|
||||
private error;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
private base64ToArrayBuffer;
|
||||
private decodeBase64;
|
||||
private getMimeType;
|
||||
getHeaderButtons(): TemplateResult<1>;
|
||||
updated(changedProperties: Map<string, any>): Promise<void>;
|
||||
private renderExcel;
|
||||
private renderExcelSheet;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"excel-artifact": ExcelArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ExcelArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ExcelArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/ExcelArtifact.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAIhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,aAAc,SAAQ,eAAe;IACrB,OAAO,CAAC,QAAQ,CAAM;IACzC,OAAO,CAAC,KAAK,CAAuB;IAE7C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAIxB;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,WAAW;IAMZ,gBAAgB;IAaR,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;YAQ5C,WAAW;IAuEzB,OAAO,CAAC,gBAAgB;IAyCf,MAAM,IAAI,cAAc;CAkBjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
||||
@@ -0,0 +1,216 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import * as XLSX from "xlsx";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let ExcelArtifact = class ExcelArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._content = "";
|
||||
this.error = null;
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.error = null;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
}
|
||||
base64ToArrayBuffer(base64) {
|
||||
// Remove data URL prefix if present
|
||||
let base64Data = base64;
|
||||
if (base64.startsWith("data:")) {
|
||||
const base64Match = base64.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
decodeBase64() {
|
||||
let base64Data = this._content;
|
||||
if (this._content.startsWith("data:")) {
|
||||
const base64Match = this._content.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
getMimeType() {
|
||||
const ext = this.filename.split(".").pop()?.toLowerCase();
|
||||
if (ext === "xls")
|
||||
return "application/vnd.ms-excel";
|
||||
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
}
|
||||
getHeaderButtons() {
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${DownloadButton({
|
||||
content: this.decodeBase64(),
|
||||
filename: this.filename,
|
||||
mimeType: this.getMimeType(),
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
async updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("_content") && this._content && !this.error) {
|
||||
await this.renderExcel();
|
||||
}
|
||||
}
|
||||
async renderExcel() {
|
||||
const container = this.querySelector("#excel-container");
|
||||
if (!container || !this._content)
|
||||
return;
|
||||
try {
|
||||
const arrayBuffer = this.base64ToArrayBuffer(this._content);
|
||||
const workbook = XLSX.read(arrayBuffer, { type: "array" });
|
||||
container.innerHTML = "";
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "overflow-auto h-full flex flex-col";
|
||||
container.appendChild(wrapper);
|
||||
// Create tabs for multiple sheets
|
||||
if (workbook.SheetNames.length > 1) {
|
||||
const tabContainer = document.createElement("div");
|
||||
tabContainer.className = "flex gap-2 mb-4 border-b border-border sticky top-0 bg-background z-10";
|
||||
const sheetContents = [];
|
||||
workbook.SheetNames.forEach((sheetName, index) => {
|
||||
// Create tab button
|
||||
const tab = document.createElement("button");
|
||||
tab.textContent = sheetName;
|
||||
tab.className =
|
||||
index === 0
|
||||
? "px-4 py-2 text-sm font-medium border-b-2 border-primary text-primary"
|
||||
: "px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:border-b-2 hover:border-border transition-colors";
|
||||
// Create sheet content
|
||||
const sheetDiv = document.createElement("div");
|
||||
sheetDiv.style.display = index === 0 ? "flex" : "none";
|
||||
sheetDiv.className = "flex-1 overflow-auto";
|
||||
sheetDiv.appendChild(this.renderExcelSheet(workbook.Sheets[sheetName], sheetName));
|
||||
sheetContents.push(sheetDiv);
|
||||
// Tab click handler
|
||||
tab.onclick = () => {
|
||||
// Update tab styles
|
||||
tabContainer.querySelectorAll("button").forEach((btn, btnIndex) => {
|
||||
if (btnIndex === index) {
|
||||
btn.className = "px-4 py-2 text-sm font-medium border-b-2 border-primary text-primary";
|
||||
}
|
||||
else {
|
||||
btn.className =
|
||||
"px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:border-b-2 hover:border-border transition-colors";
|
||||
}
|
||||
});
|
||||
// Show/hide sheets
|
||||
sheetContents.forEach((content, contentIndex) => {
|
||||
content.style.display = contentIndex === index ? "flex" : "none";
|
||||
});
|
||||
};
|
||||
tabContainer.appendChild(tab);
|
||||
});
|
||||
wrapper.appendChild(tabContainer);
|
||||
sheetContents.forEach((content) => {
|
||||
wrapper.appendChild(content);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Single sheet
|
||||
const sheetName = workbook.SheetNames[0];
|
||||
wrapper.appendChild(this.renderExcelSheet(workbook.Sheets[sheetName], sheetName));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error rendering Excel:", error);
|
||||
this.error = error?.message || i18n("Failed to load spreadsheet");
|
||||
}
|
||||
}
|
||||
renderExcelSheet(worksheet, sheetName) {
|
||||
const sheetDiv = document.createElement("div");
|
||||
// Generate HTML table
|
||||
const htmlTable = XLSX.utils.sheet_to_html(worksheet, { id: `sheet-${sheetName}` });
|
||||
const tempDiv = document.createElement("div");
|
||||
tempDiv.innerHTML = htmlTable;
|
||||
// Find and style the table
|
||||
const table = tempDiv.querySelector("table");
|
||||
if (table) {
|
||||
table.className = "w-full border-collapse text-foreground";
|
||||
// Style all cells
|
||||
table.querySelectorAll("td, th").forEach((cell) => {
|
||||
const cellEl = cell;
|
||||
cellEl.className = "border border-border px-3 py-2 text-sm text-left";
|
||||
});
|
||||
// Style header row
|
||||
const headerCells = table.querySelectorAll("thead th, tr:first-child td");
|
||||
if (headerCells.length > 0) {
|
||||
headerCells.forEach((th) => {
|
||||
const thEl = th;
|
||||
thEl.className =
|
||||
"border border-border px-3 py-2 text-sm font-semibold bg-muted text-foreground sticky top-0";
|
||||
});
|
||||
}
|
||||
// Alternate row colors
|
||||
table.querySelectorAll("tbody tr:nth-child(even)").forEach((row) => {
|
||||
const rowEl = row;
|
||||
rowEl.className = "bg-muted/30";
|
||||
});
|
||||
sheetDiv.appendChild(table);
|
||||
}
|
||||
return sheetDiv;
|
||||
}
|
||||
render() {
|
||||
if (this.error) {
|
||||
return html `
|
||||
<div class="h-full flex items-center justify-center bg-background p-4">
|
||||
<div class="bg-destructive/10 border border-destructive text-destructive p-4 rounded-lg max-w-2xl">
|
||||
<div class="font-medium mb-1">${i18n("Error loading spreadsheet")}</div>
|
||||
<div class="text-sm opacity-90">${this.error}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html `
|
||||
<div class="h-full flex flex-col bg-background overflow-auto">
|
||||
<div id="excel-container" class="flex-1 overflow-auto"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ type: String })
|
||||
], ExcelArtifact.prototype, "_content", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], ExcelArtifact.prototype, "error", void 0);
|
||||
ExcelArtifact = __decorate([
|
||||
customElement("excel-artifact")
|
||||
], ExcelArtifact);
|
||||
export { ExcelArtifact };
|
||||
//# sourceMappingURL=ExcelArtifact.js.map
|
||||
File diff suppressed because one or more lines are too long
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/GenericArtifact.d.ts
vendored
Normal file
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/GenericArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class GenericArtifact extends ArtifactElement {
|
||||
private _content;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
private decodeBase64;
|
||||
private getMimeType;
|
||||
getHeaderButtons(): TemplateResult<1>;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"generic-artifact": GenericArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=GenericArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GenericArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/GenericArtifact.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,eAAgB,SAAQ,eAAe;IACvB,OAAO,CAAC,QAAQ,CAAM;IAElD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAGxB;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,YAAY;IAiBpB,OAAO,CAAC,WAAW;IAuBZ,gBAAgB;IAad,MAAM,IAAI,cAAc;CA4BjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,kBAAkB,EAAE,eAAe,CAAC;KACpC;CACD"}
|
||||
@@ -0,0 +1,117 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let GenericArtifact = class GenericArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._content = "";
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
}
|
||||
decodeBase64() {
|
||||
let base64Data = this._content;
|
||||
if (this._content.startsWith("data:")) {
|
||||
const base64Match = this._content.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
getMimeType() {
|
||||
const ext = this.filename.split(".").pop()?.toLowerCase();
|
||||
// Add common MIME types
|
||||
const mimeTypes = {
|
||||
pdf: "application/pdf",
|
||||
zip: "application/zip",
|
||||
tar: "application/x-tar",
|
||||
gz: "application/gzip",
|
||||
rar: "application/vnd.rar",
|
||||
"7z": "application/x-7z-compressed",
|
||||
mp3: "audio/mpeg",
|
||||
mp4: "video/mp4",
|
||||
avi: "video/x-msvideo",
|
||||
mov: "video/quicktime",
|
||||
wav: "audio/wav",
|
||||
ogg: "audio/ogg",
|
||||
json: "application/json",
|
||||
xml: "application/xml",
|
||||
bin: "application/octet-stream",
|
||||
};
|
||||
return mimeTypes[ext || ""] || "application/octet-stream";
|
||||
}
|
||||
getHeaderButtons() {
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${DownloadButton({
|
||||
content: this.decodeBase64(),
|
||||
filename: this.filename,
|
||||
mimeType: this.getMimeType(),
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
render() {
|
||||
return html `
|
||||
<div class="h-full flex items-center justify-center bg-background p-8">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="text-muted-foreground text-lg mb-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-16 w-16 mx-auto mb-4 text-muted-foreground/50"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
<div class="font-medium text-foreground mb-2">${this.filename}</div>
|
||||
<p class="text-sm">
|
||||
${i18n("Preview not available for this file type.")} ${i18n("Click the download button above to view it on your computer.")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ type: String })
|
||||
], GenericArtifact.prototype, "_content", void 0);
|
||||
GenericArtifact = __decorate([
|
||||
customElement("generic-artifact")
|
||||
], GenericArtifact);
|
||||
export { GenericArtifact };
|
||||
//# sourceMappingURL=GenericArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GenericArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/GenericArtifact.ts"],"names":[],"mappings":"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"}
|
||||
27
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/HtmlArtifact.d.ts
vendored
Normal file
27
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/HtmlArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
import { type Ref } from "lit/directives/ref.js";
|
||||
import type { SandboxIframe } from "../../components/SandboxedIframe.js";
|
||||
import type { SandboxRuntimeProvider } from "../../components/sandbox/SandboxRuntimeProvider.js";
|
||||
import "../../components/SandboxedIframe.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
import "./Console.js";
|
||||
export declare class HtmlArtifact extends ArtifactElement {
|
||||
filename: string;
|
||||
runtimeProviders: SandboxRuntimeProvider[];
|
||||
sandboxUrlProvider?: () => string;
|
||||
private _content;
|
||||
private logs;
|
||||
sandboxIframeRef: Ref<SandboxIframe>;
|
||||
private consoleRef;
|
||||
private viewMode;
|
||||
private setViewMode;
|
||||
getHeaderButtons(): import("lit-html").TemplateResult<1>;
|
||||
set content(value: string);
|
||||
executeContent(html: string): void;
|
||||
get content(): string;
|
||||
disconnectedCallback(): void;
|
||||
firstUpdated(): void;
|
||||
updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
||||
getLogs(): string;
|
||||
render(): import("lit-html").TemplateResult<1>;
|
||||
}
|
||||
//# sourceMappingURL=HtmlArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"HtmlArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/HtmlArtifact.ts"],"names":[],"mappings":"AAGA,OAAO,EAAa,KAAK,GAAG,EAAO,MAAM,uBAAuB,CAAC;AAGjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAEjG,OAAO,qCAAqC,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,cAAc,CAAC;AAOtB,qBACa,YAAa,SAAQ,eAAe;IAC3B,QAAQ,SAAM;IACH,gBAAgB,EAAE,sBAAsB,EAAE,CAAM;IAChD,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAElE,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,IAAI,CAAsD;IAG3D,gBAAgB,EAAE,GAAG,CAAC,aAAa,CAAC,CAAe;IAC1D,OAAO,CAAC,UAAU,CAA6B;IAEtC,OAAO,CAAC,QAAQ,CAAiC;IAE1D,OAAO,CAAC,WAAW;IAIZ,gBAAgB;IAwCvB,IAAa,OAAO,CAAC,KAAK,EAAE,MAAM,EAYjC;IAEM,cAAc,CAAC,IAAI,EAAE,MAAM;IA6ClC,IAAa,OAAO,IAAI,MAAM,CAE7B;IAEQ,oBAAoB;IAOpB,YAAY;IAOZ,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC;IASnE,OAAO,IAAI,MAAM;IAKf,MAAM;CAwBf"}
|
||||
@@ -0,0 +1,189 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import hljs from "highlight.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { RefreshCw } from "lucide";
|
||||
import { RUNTIME_MESSAGE_ROUTER } from "../../components/sandbox/RuntimeMessageRouter.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import "../../components/SandboxedIframe.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
import "./Console.js";
|
||||
import { icon } from "@mariozechner/mini-lit";
|
||||
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
||||
import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
|
||||
let HtmlArtifact = class HtmlArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.filename = "";
|
||||
this.runtimeProviders = [];
|
||||
this._content = "";
|
||||
this.logs = [];
|
||||
// Refs for DOM elements
|
||||
this.sandboxIframeRef = createRef();
|
||||
this.consoleRef = createRef();
|
||||
this.viewMode = "preview";
|
||||
}
|
||||
setViewMode(mode) {
|
||||
this.viewMode = mode;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
const toggle = new PreviewCodeToggle();
|
||||
toggle.mode = this.viewMode;
|
||||
toggle.addEventListener("mode-change", (e) => {
|
||||
this.setViewMode(e.detail);
|
||||
});
|
||||
const copyButton = new CopyButton();
|
||||
copyButton.text = this._content;
|
||||
copyButton.title = i18n("Copy HTML");
|
||||
copyButton.showText = false;
|
||||
// Generate standalone HTML with all runtime code injected for download
|
||||
const sandbox = this.sandboxIframeRef.value;
|
||||
const sandboxId = `artifact-${this.filename}`;
|
||||
const downloadContent = sandbox?.prepareHtmlDocument(sandboxId, this._content, this.runtimeProviders || [], {
|
||||
isHtmlArtifact: true,
|
||||
isStandalone: true, // Skip runtime bridge and navigation interceptor for standalone downloads
|
||||
}) || this._content;
|
||||
return html `
|
||||
<div class="flex items-center gap-2">
|
||||
${toggle}
|
||||
${Button({
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => {
|
||||
this.logs = [];
|
||||
this.executeContent(this._content);
|
||||
},
|
||||
title: i18n("Reload HTML"),
|
||||
children: icon(RefreshCw, "sm"),
|
||||
})}
|
||||
${copyButton}
|
||||
${DownloadButton({ content: downloadContent, filename: this.filename, mimeType: "text/html", title: i18n("Download HTML") })}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
set content(value) {
|
||||
const oldValue = this._content;
|
||||
this._content = value;
|
||||
if (oldValue !== value) {
|
||||
// Reset logs when content changes
|
||||
this.logs = [];
|
||||
this.requestUpdate();
|
||||
// Execute content in sandbox if it exists
|
||||
if (this.sandboxIframeRef.value && value) {
|
||||
this.executeContent(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
executeContent(html) {
|
||||
const sandbox = this.sandboxIframeRef.value;
|
||||
if (!sandbox)
|
||||
return;
|
||||
// Configure sandbox URL provider if provided (for browser extensions)
|
||||
if (this.sandboxUrlProvider) {
|
||||
sandbox.sandboxUrlProvider = this.sandboxUrlProvider;
|
||||
}
|
||||
const sandboxId = `artifact-${this.filename}`;
|
||||
// Create consumer for console messages
|
||||
const consumer = {
|
||||
handleMessage: async (message) => {
|
||||
if (message.type === "console") {
|
||||
// Create new array reference for Lit reactivity
|
||||
this.logs = [
|
||||
...this.logs,
|
||||
{
|
||||
type: message.method === "error" ? "error" : "log",
|
||||
text: message.text,
|
||||
},
|
||||
];
|
||||
this.requestUpdate(); // Re-render to show console
|
||||
}
|
||||
},
|
||||
};
|
||||
// Inject window.complete() call at the end of the HTML to signal when page is loaded
|
||||
// HTML artifacts don't time out - they call complete() when ready
|
||||
let modifiedHtml = html;
|
||||
if (modifiedHtml.includes("</html>")) {
|
||||
modifiedHtml = modifiedHtml.replace("</html>", "<script>if (window.complete) window.complete();</script></html>");
|
||||
}
|
||||
else {
|
||||
// If no closing </html> tag, append the script
|
||||
modifiedHtml += "<script>if (window.complete) window.complete();</script>";
|
||||
}
|
||||
// Load content - this handles sandbox registration, consumer registration, and iframe creation
|
||||
sandbox.loadContent(sandboxId, modifiedHtml, this.runtimeProviders, [consumer]);
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
// Unregister sandbox when element is removed from DOM
|
||||
const sandboxId = `artifact-${this.filename}`;
|
||||
RUNTIME_MESSAGE_ROUTER.unregisterSandbox(sandboxId);
|
||||
}
|
||||
firstUpdated() {
|
||||
// Execute initial content
|
||||
if (this._content && this.sandboxIframeRef.value) {
|
||||
this.executeContent(this._content);
|
||||
}
|
||||
}
|
||||
updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
// If we have content but haven't executed yet (e.g., during reconstruction),
|
||||
// execute when the iframe ref becomes available
|
||||
if (this._content && this.sandboxIframeRef.value && this.logs.length === 0) {
|
||||
this.executeContent(this._content);
|
||||
}
|
||||
}
|
||||
getLogs() {
|
||||
if (this.logs.length === 0)
|
||||
return i18n("No logs for {filename}").replace("{filename}", this.filename);
|
||||
return this.logs.map((l) => `[${l.type}] ${l.text}`).join("\n");
|
||||
}
|
||||
render() {
|
||||
return html `
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex-1 overflow-hidden relative">
|
||||
<!-- Preview container - always in DOM, just hidden when not active -->
|
||||
<div class="absolute inset-0 flex flex-col" style="display: ${this.viewMode === "preview" ? "flex" : "none"}">
|
||||
<sandbox-iframe class="flex-1" ${ref(this.sandboxIframeRef)}></sandbox-iframe>
|
||||
${this.logs.length > 0
|
||||
? html `<artifact-console .logs=${this.logs} ${ref(this.consoleRef)}></artifact-console>`
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
<!-- Code view - always in DOM, just hidden when not active -->
|
||||
<div class="absolute inset-0 overflow-auto bg-background" style="display: ${this.viewMode === "code" ? "block" : "none"}">
|
||||
<pre class="m-0 p-4 text-xs"><code class="hljs language-html">${unsafeHTML(hljs.highlight(this._content, { language: "html" }).value)}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property()
|
||||
], HtmlArtifact.prototype, "filename", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], HtmlArtifact.prototype, "runtimeProviders", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], HtmlArtifact.prototype, "sandboxUrlProvider", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], HtmlArtifact.prototype, "viewMode", void 0);
|
||||
HtmlArtifact = __decorate([
|
||||
customElement("html-artifact")
|
||||
], HtmlArtifact);
|
||||
export { HtmlArtifact };
|
||||
//# sourceMappingURL=HtmlArtifact.js.map
|
||||
File diff suppressed because one or more lines are too long
20
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ImageArtifact.d.ts
vendored
Normal file
20
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/ImageArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class ImageArtifact extends ArtifactElement {
|
||||
private _content;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
private getMimeType;
|
||||
private getImageUrl;
|
||||
private decodeBase64;
|
||||
getHeaderButtons(): TemplateResult<1>;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"image-artifact": ImageArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ImageArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ImageArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/ImageArtifact.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,aAAc,SAAQ,eAAe;IACrB,OAAO,CAAC,QAAQ,CAAM;IAElD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAGxB;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,YAAY;IA6Bb,gBAAgB;IAad,MAAM,IAAI,cAAc;CAkBjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
||||
@@ -0,0 +1,120 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let ImageArtifact = class ImageArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._content = "";
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
}
|
||||
getMimeType() {
|
||||
const ext = this.filename.split(".").pop()?.toLowerCase();
|
||||
if (ext === "jpg" || ext === "jpeg")
|
||||
return "image/jpeg";
|
||||
if (ext === "gif")
|
||||
return "image/gif";
|
||||
if (ext === "webp")
|
||||
return "image/webp";
|
||||
if (ext === "svg")
|
||||
return "image/svg+xml";
|
||||
if (ext === "bmp")
|
||||
return "image/bmp";
|
||||
if (ext === "ico")
|
||||
return "image/x-icon";
|
||||
return "image/png";
|
||||
}
|
||||
getImageUrl() {
|
||||
// If content is already a data URL, use it directly
|
||||
if (this._content.startsWith("data:")) {
|
||||
return this._content;
|
||||
}
|
||||
// Otherwise assume it's base64 and construct data URL
|
||||
return `data:${this.getMimeType()};base64,${this._content}`;
|
||||
}
|
||||
decodeBase64() {
|
||||
let base64Data;
|
||||
// If content is a data URL, extract the base64 part
|
||||
if (this._content.startsWith("data:")) {
|
||||
const base64Match = this._content.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
else {
|
||||
// Not a base64 data URL, return empty
|
||||
return new Uint8Array(0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise use content as-is
|
||||
base64Data = this._content;
|
||||
}
|
||||
// Decode base64 to binary string
|
||||
const binaryString = atob(base64Data);
|
||||
// Convert binary string to Uint8Array
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${DownloadButton({
|
||||
content: this.decodeBase64(),
|
||||
filename: this.filename,
|
||||
mimeType: this.getMimeType(),
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
render() {
|
||||
return html `
|
||||
<div class="h-full flex flex-col bg-background overflow-auto">
|
||||
<div class="flex-1 flex items-center justify-center p-4">
|
||||
<img
|
||||
src="${this.getImageUrl()}"
|
||||
alt="${this.filename}"
|
||||
class="max-w-full max-h-full object-contain"
|
||||
@error=${(e) => {
|
||||
const target = e.target;
|
||||
target.src =
|
||||
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext x='50' y='50' text-anchor='middle' dominant-baseline='middle' fill='%23999'%3EImage Error%3C/text%3E%3C/svg%3E";
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ type: String })
|
||||
], ImageArtifact.prototype, "_content", void 0);
|
||||
ImageArtifact = __decorate([
|
||||
customElement("image-artifact")
|
||||
], ImageArtifact);
|
||||
export { ImageArtifact };
|
||||
//# sourceMappingURL=ImageArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ImageArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/ImageArtifact.ts"],"names":[],"mappings":"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"}
|
||||
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/MarkdownArtifact.d.ts
vendored
Normal file
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/MarkdownArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class MarkdownArtifact extends ArtifactElement {
|
||||
filename: string;
|
||||
private _content;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
private viewMode;
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
private setViewMode;
|
||||
getHeaderButtons(): import("lit-html").TemplateResult<1>;
|
||||
render(): import("lit-html").TemplateResult<1>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"markdown-artifact": MarkdownArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=MarkdownArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"MarkdownArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/MarkdownArtifact.ts"],"names":[],"mappings":"AAKA,OAAO,8CAA8C,CAAC;AAItD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,gBAAiB,SAAQ,eAAe;IAC/B,QAAQ,SAAM;IAEnC,OAAO,CAAC,QAAQ,CAAM;IACtB,IAAa,OAAO,IAAI,MAAM,CAE7B;IACD,IAAa,OAAO,CAAC,KAAK,EAAE,MAAM,EAGjC;IAEQ,OAAO,CAAC,QAAQ,CAAiC;cAEvC,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAIrE,OAAO,CAAC,WAAW;IAIZ,gBAAgB;IA0Bd,MAAM;CAef;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
||||
@@ -0,0 +1,82 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import hljs from "highlight.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
|
||||
import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let MarkdownArtifact = class MarkdownArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.filename = "";
|
||||
this._content = "";
|
||||
this.viewMode = "preview";
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM
|
||||
}
|
||||
setViewMode(mode) {
|
||||
this.viewMode = mode;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
const toggle = new PreviewCodeToggle();
|
||||
toggle.mode = this.viewMode;
|
||||
toggle.addEventListener("mode-change", (e) => {
|
||||
this.setViewMode(e.detail);
|
||||
});
|
||||
const copyButton = new CopyButton();
|
||||
copyButton.text = this._content;
|
||||
copyButton.title = i18n("Copy Markdown");
|
||||
copyButton.showText = false;
|
||||
return html `
|
||||
<div class="flex items-center gap-2">
|
||||
${toggle}
|
||||
${copyButton}
|
||||
${DownloadButton({
|
||||
content: this._content,
|
||||
filename: this.filename,
|
||||
mimeType: "text/markdown",
|
||||
title: i18n("Download Markdown"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
render() {
|
||||
return html `
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex-1 overflow-auto">
|
||||
${this.viewMode === "preview"
|
||||
? html `<div class="p-4"><markdown-block .content=${this.content}></markdown-block></div>`
|
||||
: html `<pre class="m-0 p-4 text-xs whitespace-pre-wrap break-words"><code class="hljs language-markdown">${unsafeHTML(hljs.highlight(this.content, { language: "markdown", ignoreIllegals: true }).value)}</code></pre>`}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property()
|
||||
], MarkdownArtifact.prototype, "filename", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], MarkdownArtifact.prototype, "viewMode", void 0);
|
||||
MarkdownArtifact = __decorate([
|
||||
customElement("markdown-artifact")
|
||||
], MarkdownArtifact);
|
||||
export { MarkdownArtifact };
|
||||
//# sourceMappingURL=MarkdownArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"MarkdownArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/MarkdownArtifact.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,IAAI,MAAM,cAAc,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,8CAA8C,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGhD,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,eAAe;IAA9C;;QACe,aAAQ,GAAG,EAAE,CAAC;QAE3B,aAAQ,GAAG,EAAE,CAAC;QASL,aAAQ,GAAuB,SAAS,CAAC;IAmD3D,CAAC;IA3DA,IAAa,OAAO;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IACD,IAAa,OAAO,CAAC,KAAa;QACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAIkB,gBAAgB;QAClC,OAAO,IAAI,CAAC,CAAC,YAAY;IAC1B,CAAC;IAEO,WAAW,CAAC,IAAwB;QAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACtB,CAAC;IAEM,gBAAgB;QACtB,MAAM,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5B,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAQ,EAAE,EAAE;YACnD,IAAI,CAAC,WAAW,CAAE,CAAiB,CAAC,MAAM,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;QACpC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAChC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;QACzC,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;QAE5B,OAAO,IAAI,CAAA;;MAEP,MAAM;MACN,UAAU;MACV,cAAc,CAAC;YAChB,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,eAAe;YACzB,KAAK,EAAE,IAAI,CAAC,mBAAmB,CAAC;SAChC,CAAC;;GAEH,CAAC;IACH,CAAC;IAEQ,MAAM;QACd,OAAO,IAAI,CAAA;;;OAIP,IAAI,CAAC,QAAQ,KAAK,SAAS;YAC1B,CAAC,CAAC,IAAI,CAAA,6CAA6C,IAAI,CAAC,OAAO,0BAA0B;YACzF,CAAC,CAAC,IAAI,CAAA,qGAAqG,UAAU,CACnH,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAClF,eACJ;;;GAGF,CAAC;IACH,CAAC;CACD,CAAA;AA9DqB;IAApB,QAAQ,EAAE;kDAAwB;AAWlB;IAAhB,KAAK,EAAE;kDAAkD;AAZ9C,gBAAgB;IAD5B,aAAa,CAAC,mBAAmB,CAAC;GACtB,gBAAgB,CA+D5B"}
|
||||
25
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/PdfArtifact.d.ts
vendored
Normal file
25
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/PdfArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class PdfArtifact extends ArtifactElement {
|
||||
private _content;
|
||||
private error;
|
||||
private currentLoadingTask;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
disconnectedCallback(): void;
|
||||
private cleanup;
|
||||
private base64ToArrayBuffer;
|
||||
private decodeBase64;
|
||||
getHeaderButtons(): TemplateResult<1>;
|
||||
updated(changedProperties: Map<string, any>): Promise<void>;
|
||||
private renderPdf;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"pdf-artifact": PdfArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=PdfArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"PdfArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/PdfArtifact.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAIhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKvD,qBACa,WAAY,SAAQ,eAAe;IACnB,OAAO,CAAC,QAAQ,CAAM;IACzC,OAAO,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,kBAAkB,CAAa;IAEvC,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,MAAM,EAIxB;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAMzB,oBAAoB,IAAI,IAAI;IAKrC,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,YAAY;IAiBb,gBAAgB;IAaR,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;YAQ5C,SAAS;IAwEd,MAAM,IAAI,cAAc;CAkBjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,cAAc,EAAE,WAAW,CAAC;KAC5B;CACD"}
|
||||
@@ -0,0 +1,184 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import * as pdfjsLib from "pdfjs-dist";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
// Configure PDF.js worker
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString();
|
||||
let PdfArtifact = class PdfArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._content = "";
|
||||
this.error = null;
|
||||
this.currentLoadingTask = null;
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.error = null;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.cleanup();
|
||||
}
|
||||
cleanup() {
|
||||
if (this.currentLoadingTask) {
|
||||
this.currentLoadingTask.destroy();
|
||||
this.currentLoadingTask = null;
|
||||
}
|
||||
}
|
||||
base64ToArrayBuffer(base64) {
|
||||
// Remove data URL prefix if present
|
||||
let base64Data = base64;
|
||||
if (base64.startsWith("data:")) {
|
||||
const base64Match = base64.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
||||
decodeBase64() {
|
||||
let base64Data = this._content;
|
||||
if (this._content.startsWith("data:")) {
|
||||
const base64Match = this._content.match(/base64,(.+)/);
|
||||
if (base64Match) {
|
||||
base64Data = base64Match[1];
|
||||
}
|
||||
}
|
||||
const binaryString = atob(base64Data);
|
||||
const bytes = new Uint8Array(binaryString.length);
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${DownloadButton({
|
||||
content: this.decodeBase64(),
|
||||
filename: this.filename,
|
||||
mimeType: "application/pdf",
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
async updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("_content") && this._content && !this.error) {
|
||||
await this.renderPdf();
|
||||
}
|
||||
}
|
||||
async renderPdf() {
|
||||
const container = this.querySelector("#pdf-container");
|
||||
if (!container || !this._content)
|
||||
return;
|
||||
let pdf = null;
|
||||
try {
|
||||
const arrayBuffer = this.base64ToArrayBuffer(this._content);
|
||||
// Cancel any existing loading task
|
||||
if (this.currentLoadingTask) {
|
||||
this.currentLoadingTask.destroy();
|
||||
}
|
||||
// Load the PDF
|
||||
this.currentLoadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
|
||||
pdf = await this.currentLoadingTask.promise;
|
||||
this.currentLoadingTask = null;
|
||||
// Clear container
|
||||
container.innerHTML = "";
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "p-4";
|
||||
container.appendChild(wrapper);
|
||||
// Render all pages
|
||||
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
|
||||
const page = await pdf.getPage(pageNum);
|
||||
const pageContainer = document.createElement("div");
|
||||
pageContainer.className = "mb-4 last:mb-0";
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const viewport = page.getViewport({ scale: 1.5 });
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
canvas.className = "w-full max-w-full h-auto block mx-auto bg-white rounded shadow-sm border border-border";
|
||||
if (context) {
|
||||
context.fillStyle = "white";
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
await page.render({
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
canvas: canvas,
|
||||
}).promise;
|
||||
pageContainer.appendChild(canvas);
|
||||
if (pageNum < pdf.numPages) {
|
||||
const separator = document.createElement("div");
|
||||
separator.className = "h-px bg-border my-4";
|
||||
pageContainer.appendChild(separator);
|
||||
}
|
||||
wrapper.appendChild(pageContainer);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error("Error rendering PDF:", error);
|
||||
this.error = error?.message || i18n("Failed to load PDF");
|
||||
}
|
||||
finally {
|
||||
if (pdf) {
|
||||
pdf.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
render() {
|
||||
if (this.error) {
|
||||
return html `
|
||||
<div class="h-full flex items-center justify-center bg-background p-4">
|
||||
<div class="bg-destructive/10 border border-destructive text-destructive p-4 rounded-lg max-w-2xl">
|
||||
<div class="font-medium mb-1">${i18n("Error loading PDF")}</div>
|
||||
<div class="text-sm opacity-90">${this.error}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html `
|
||||
<div class="h-full flex flex-col bg-background overflow-auto">
|
||||
<div id="pdf-container" class="flex-1 overflow-auto"></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property({ type: String })
|
||||
], PdfArtifact.prototype, "_content", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], PdfArtifact.prototype, "error", void 0);
|
||||
PdfArtifact = __decorate([
|
||||
customElement("pdf-artifact")
|
||||
], PdfArtifact);
|
||||
export { PdfArtifact };
|
||||
//# sourceMappingURL=PdfArtifact.js.map
|
||||
File diff suppressed because one or more lines are too long
18
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/SvgArtifact.d.ts
vendored
Normal file
18
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/SvgArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class SvgArtifact extends ArtifactElement {
|
||||
filename: string;
|
||||
private _content;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
private viewMode;
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
private setViewMode;
|
||||
getHeaderButtons(): import("lit-html").TemplateResult<1>;
|
||||
render(): import("lit-html").TemplateResult<1>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"svg-artifact": SvgArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=SvgArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"SvgArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/SvgArtifact.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,qBACa,WAAY,SAAQ,eAAe;IAC1B,QAAQ,SAAM;IAEnC,OAAO,CAAC,QAAQ,CAAM;IACtB,IAAa,OAAO,IAAI,MAAM,CAE7B;IACD,IAAa,OAAO,CAAC,KAAK,EAAE,MAAM,EAGjC;IAEQ,OAAO,CAAC,QAAQ,CAAiC;cAEvC,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAIrE,OAAO,CAAC,WAAW;IAIZ,gBAAgB;IAqBd,MAAM;CAiBf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,cAAc,EAAE,WAAW,CAAC;KAC5B;CACD"}
|
||||
@@ -0,0 +1,78 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import { PreviewCodeToggle } from "@mariozechner/mini-lit/dist/PreviewCodeToggle.js";
|
||||
import hljs from "highlight.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
let SvgArtifact = class SvgArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.filename = "";
|
||||
this._content = "";
|
||||
this.viewMode = "preview";
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM
|
||||
}
|
||||
setViewMode(mode) {
|
||||
this.viewMode = mode;
|
||||
}
|
||||
getHeaderButtons() {
|
||||
const toggle = new PreviewCodeToggle();
|
||||
toggle.mode = this.viewMode;
|
||||
toggle.addEventListener("mode-change", (e) => {
|
||||
this.setViewMode(e.detail);
|
||||
});
|
||||
const copyButton = new CopyButton();
|
||||
copyButton.text = this._content;
|
||||
copyButton.title = i18n("Copy SVG");
|
||||
copyButton.showText = false;
|
||||
return html `
|
||||
<div class="flex items-center gap-2">
|
||||
${toggle}
|
||||
${copyButton}
|
||||
${DownloadButton({ content: this._content, filename: this.filename, mimeType: "image/svg+xml", title: i18n("Download SVG") })}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
render() {
|
||||
return html `
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex-1 overflow-auto">
|
||||
${this.viewMode === "preview"
|
||||
? html `<div class="h-full flex items-center justify-center">
|
||||
${unsafeHTML(this.content.replace(/<svg(\s|>)/i, (_m, p1) => `<svg class="w-full h-full"${p1}`))}
|
||||
</div>`
|
||||
: html `<pre class="m-0 p-4 text-xs"><code class="hljs language-xml">${unsafeHTML(hljs.highlight(this.content, { language: "xml", ignoreIllegals: true }).value)}</code></pre>`}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property()
|
||||
], SvgArtifact.prototype, "filename", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], SvgArtifact.prototype, "viewMode", void 0);
|
||||
SvgArtifact = __decorate([
|
||||
customElement("svg-artifact")
|
||||
], SvgArtifact);
|
||||
export { SvgArtifact };
|
||||
//# sourceMappingURL=SvgArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"SvgArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/SvgArtifact.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+CAA+C,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,IAAI,MAAM,cAAc,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGhD,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,eAAe;IAAzC;;QACe,aAAQ,GAAG,EAAE,CAAC;QAE3B,aAAQ,GAAG,EAAE,CAAC;QASL,aAAQ,GAAuB,SAAS,CAAC;IAgD3D,CAAC;IAxDA,IAAa,OAAO;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACtB,CAAC;IACD,IAAa,OAAO,CAAC,KAAa;QACjC,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,aAAa,EAAE,CAAC;IACtB,CAAC;IAIkB,gBAAgB;QAClC,OAAO,IAAI,CAAC,CAAC,YAAY;IAC1B,CAAC;IAEO,WAAW,CAAC,IAAwB;QAC3C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACtB,CAAC;IAEM,gBAAgB;QACtB,MAAM,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC5B,MAAM,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,CAAQ,EAAE,EAAE;YACnD,IAAI,CAAC,WAAW,CAAE,CAAiB,CAAC,MAAM,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;QACpC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;QAChC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,UAAU,CAAC,QAAQ,GAAG,KAAK,CAAC;QAE5B,OAAO,IAAI,CAAA;;MAEP,MAAM;MACN,UAAU;MACV,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;;GAE9H,CAAC;IACH,CAAC;IAEQ,MAAM;QACd,OAAO,IAAI,CAAA;;;OAIP,IAAI,CAAC,QAAQ,KAAK,SAAS;YAC1B,CAAC,CAAC,IAAI,CAAA;UACH,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,6BAA6B,EAAE,EAAE,CAAC,CAAC;cAC1F;YACP,CAAC,CAAC,IAAI,CAAA,gEAAgE,UAAU,CAC9E,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAC7E,eACJ;;;GAGF,CAAC;IACH,CAAC;CACD,CAAA;AA3DqB;IAApB,QAAQ,EAAE;6CAAwB;AAWlB;IAAhB,KAAK,EAAE;6CAAkD;AAZ9C,WAAW;IADvB,aAAa,CAAC,cAAc,CAAC;GACjB,WAAW,CA4DvB"}
|
||||
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/TextArtifact.d.ts
vendored
Normal file
19
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/TextArtifact.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
export declare class TextArtifact extends ArtifactElement {
|
||||
filename: string;
|
||||
private _content;
|
||||
get content(): string;
|
||||
set content(value: string);
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
private isCode;
|
||||
private getLanguageFromExtension;
|
||||
private getMimeType;
|
||||
getHeaderButtons(): import("lit-html").TemplateResult<1>;
|
||||
render(): import("lit-html").TemplateResult<1>;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"text-artifact": TextArtifact;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=TextArtifact.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TextArtifact.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/TextArtifact.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAiDvD,qBACa,YAAa,SAAQ,eAAe;IAC3B,QAAQ,SAAM;IAEnC,OAAO,CAAC,QAAQ,CAAM;IACtB,IAAa,OAAO,IAAI,MAAM,CAE7B;IACD,IAAa,OAAO,CAAC,KAAK,EAAE,MAAM,EAGjC;cAEkB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAIrE,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,WAAW;IAOZ,gBAAgB;IAmBd,MAAM;CAwBf;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,eAAe,EAAE,YAAY,CAAC;KAC9B;CACD"}
|
||||
@@ -0,0 +1,144 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { CopyButton } from "@mariozechner/mini-lit/dist/CopyButton.js";
|
||||
import { DownloadButton } from "@mariozechner/mini-lit/dist/DownloadButton.js";
|
||||
import hljs from "highlight.js";
|
||||
import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { ArtifactElement } from "./ArtifactElement.js";
|
||||
// Known code file extensions for highlighting
|
||||
const CODE_EXTENSIONS = [
|
||||
"js",
|
||||
"javascript",
|
||||
"ts",
|
||||
"typescript",
|
||||
"jsx",
|
||||
"tsx",
|
||||
"py",
|
||||
"python",
|
||||
"java",
|
||||
"c",
|
||||
"cpp",
|
||||
"cs",
|
||||
"php",
|
||||
"rb",
|
||||
"ruby",
|
||||
"go",
|
||||
"rust",
|
||||
"swift",
|
||||
"kotlin",
|
||||
"scala",
|
||||
"dart",
|
||||
"html",
|
||||
"css",
|
||||
"scss",
|
||||
"sass",
|
||||
"less",
|
||||
"json",
|
||||
"xml",
|
||||
"yaml",
|
||||
"yml",
|
||||
"toml",
|
||||
"sql",
|
||||
"sh",
|
||||
"bash",
|
||||
"ps1",
|
||||
"bat",
|
||||
"r",
|
||||
"matlab",
|
||||
"julia",
|
||||
"lua",
|
||||
"perl",
|
||||
"vue",
|
||||
"svelte",
|
||||
];
|
||||
let TextArtifact = class TextArtifact extends ArtifactElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.filename = "";
|
||||
this._content = "";
|
||||
}
|
||||
get content() {
|
||||
return this._content;
|
||||
}
|
||||
set content(value) {
|
||||
this._content = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM
|
||||
}
|
||||
isCode() {
|
||||
const ext = this.filename.split(".").pop()?.toLowerCase() || "";
|
||||
return CODE_EXTENSIONS.includes(ext);
|
||||
}
|
||||
getLanguageFromExtension(ext) {
|
||||
const languageMap = {
|
||||
js: "javascript",
|
||||
ts: "typescript",
|
||||
py: "python",
|
||||
rb: "ruby",
|
||||
yml: "yaml",
|
||||
ps1: "powershell",
|
||||
bat: "batch",
|
||||
};
|
||||
return languageMap[ext] || ext;
|
||||
}
|
||||
getMimeType() {
|
||||
const ext = this.filename.split(".").pop()?.toLowerCase() || "";
|
||||
if (ext === "svg")
|
||||
return "image/svg+xml";
|
||||
if (ext === "md" || ext === "markdown")
|
||||
return "text/markdown";
|
||||
return "text/plain";
|
||||
}
|
||||
getHeaderButtons() {
|
||||
const copyButton = new CopyButton();
|
||||
copyButton.text = this.content;
|
||||
copyButton.title = i18n("Copy");
|
||||
copyButton.showText = false;
|
||||
return html `
|
||||
<div class="flex items-center gap-1">
|
||||
${copyButton}
|
||||
${DownloadButton({
|
||||
content: this.content,
|
||||
filename: this.filename,
|
||||
mimeType: this.getMimeType(),
|
||||
title: i18n("Download"),
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
render() {
|
||||
const isCode = this.isCode();
|
||||
const ext = this.filename.split(".").pop() || "";
|
||||
return html `
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="flex-1 overflow-auto">
|
||||
${isCode
|
||||
? html `
|
||||
<pre class="m-0 p-4 text-xs"><code class="hljs language-${this.getLanguageFromExtension(ext.toLowerCase())}">${unsafeHTML(hljs.highlight(this.content, {
|
||||
language: this.getLanguageFromExtension(ext.toLowerCase()),
|
||||
ignoreIllegals: true,
|
||||
}).value)}</code></pre>
|
||||
`
|
||||
: html ` <pre class="m-0 p-4 text-xs font-mono">${this.content}</pre> `}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
property()
|
||||
], TextArtifact.prototype, "filename", void 0);
|
||||
TextArtifact = __decorate([
|
||||
customElement("text-artifact")
|
||||
], TextArtifact);
|
||||
export { TextArtifact };
|
||||
//# sourceMappingURL=TextArtifact.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TextArtifact.js","sourceRoot":"","sources":["../../../src/tools/artifacts/TextArtifact.ts"],"names":[],"mappings":"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"}
|
||||
11
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/artifacts-tool-renderer.d.ts
vendored
Normal file
11
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/artifacts-tool-renderer.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import "@mariozechner/mini-lit/dist/CodeBlock.js";
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import "../../components/ConsoleBlock.js";
|
||||
import type { ToolRenderer, ToolRenderResult } from "../types.js";
|
||||
import type { ArtifactsPanel, ArtifactsParams } from "./artifacts.js";
|
||||
export declare class ArtifactsToolRenderer implements ToolRenderer<ArtifactsParams, undefined> {
|
||||
artifactsPanel?: ArtifactsPanel | undefined;
|
||||
constructor(artifactsPanel?: ArtifactsPanel | undefined);
|
||||
render(params: ArtifactsParams | undefined, result: ToolResultMessage<undefined> | undefined, isStreaming?: boolean): ToolRenderResult;
|
||||
}
|
||||
//# sourceMappingURL=artifacts-tool-renderer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"artifacts-tool-renderer.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/artifacts-tool-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG7D,OAAO,kCAAkC,CAAC;AAK1C,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAkDtE,qBAAa,qBAAsB,YAAW,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC;IAClE,cAAc,CAAC,EAAE,cAAc;gBAA/B,cAAc,CAAC,EAAE,cAAc,YAAA;IAElD,MAAM,CACL,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,MAAM,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,SAAS,EAChD,WAAW,CAAC,EAAE,OAAO,GACnB,gBAAgB;CAiPnB"}
|
||||
@@ -0,0 +1,272 @@
|
||||
import "@mariozechner/mini-lit/dist/CodeBlock.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { FileCode2 } from "lucide";
|
||||
import "../../components/ConsoleBlock.js";
|
||||
import { Diff } from "@mariozechner/mini-lit/dist/Diff.js";
|
||||
import { html } from "lit";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { renderCollapsibleHeader, renderHeader } from "../renderer-registry.js";
|
||||
import { ArtifactPill } from "./ArtifactPill.js";
|
||||
// Helper to extract text from content blocks
|
||||
function getTextOutput(result) {
|
||||
if (!result)
|
||||
return "";
|
||||
return (result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "");
|
||||
}
|
||||
// Helper to determine language for syntax highlighting
|
||||
function getLanguageFromFilename(filename) {
|
||||
if (!filename)
|
||||
return "text";
|
||||
const ext = filename.split(".").pop()?.toLowerCase();
|
||||
const languageMap = {
|
||||
js: "javascript",
|
||||
jsx: "javascript",
|
||||
ts: "typescript",
|
||||
tsx: "typescript",
|
||||
html: "html",
|
||||
css: "css",
|
||||
scss: "scss",
|
||||
json: "json",
|
||||
py: "python",
|
||||
md: "markdown",
|
||||
svg: "xml",
|
||||
xml: "xml",
|
||||
yaml: "yaml",
|
||||
yml: "yaml",
|
||||
sh: "bash",
|
||||
bash: "bash",
|
||||
sql: "sql",
|
||||
java: "java",
|
||||
c: "c",
|
||||
cpp: "cpp",
|
||||
cs: "csharp",
|
||||
go: "go",
|
||||
rs: "rust",
|
||||
php: "php",
|
||||
rb: "ruby",
|
||||
swift: "swift",
|
||||
kt: "kotlin",
|
||||
r: "r",
|
||||
};
|
||||
return languageMap[ext || ""] || "text";
|
||||
}
|
||||
export class ArtifactsToolRenderer {
|
||||
constructor(artifactsPanel) {
|
||||
this.artifactsPanel = artifactsPanel;
|
||||
}
|
||||
render(params, result, isStreaming) {
|
||||
const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
|
||||
// Create refs for collapsible sections
|
||||
const contentRef = createRef();
|
||||
const chevronRef = createRef();
|
||||
// Helper to get command labels
|
||||
const getCommandLabels = (command) => {
|
||||
const labels = {
|
||||
create: { streaming: i18n("Creating artifact"), complete: i18n("Created artifact") },
|
||||
update: { streaming: i18n("Updating artifact"), complete: i18n("Updated artifact") },
|
||||
rewrite: { streaming: i18n("Rewriting artifact"), complete: i18n("Rewrote artifact") },
|
||||
get: { streaming: i18n("Getting artifact"), complete: i18n("Got artifact") },
|
||||
delete: { streaming: i18n("Deleting artifact"), complete: i18n("Deleted artifact") },
|
||||
logs: { streaming: i18n("Getting logs"), complete: i18n("Got logs") },
|
||||
};
|
||||
return labels[command] || { streaming: i18n("Processing artifact"), complete: i18n("Processed artifact") };
|
||||
};
|
||||
// Helper to render header text with inline artifact pill
|
||||
const renderHeaderWithPill = (labelText, filename) => {
|
||||
if (filename) {
|
||||
return html `<span>${labelText} ${ArtifactPill(filename, this.artifactsPanel)}</span>`;
|
||||
}
|
||||
return html `<span>${labelText}</span>`;
|
||||
};
|
||||
// Error handling
|
||||
if (result?.isError) {
|
||||
const command = params?.command;
|
||||
const filename = params?.filename;
|
||||
const labels = command
|
||||
? getCommandLabels(command)
|
||||
: { streaming: i18n("Processing artifact"), complete: i18n("Processed artifact") };
|
||||
const headerText = labels.streaming;
|
||||
// For create/update/rewrite errors, show code block + console/error
|
||||
if (command === "create" || command === "update" || command === "rewrite") {
|
||||
const content = params?.content || "";
|
||||
const { old_str, new_str } = params || {};
|
||||
const isDiff = command === "update";
|
||||
const diffContent = old_str !== undefined && new_str !== undefined ? Diff({ oldText: old_str, newText: new_str }) : "";
|
||||
const isHtml = filename?.endsWith(".html");
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
|
||||
${isDiff ? diffContent : content ? html `<code-block .code=${content} language=${getLanguageFromFilename(filename)}></code-block>` : ""}
|
||||
${isHtml
|
||||
? html `<console-block .content=${getTextOutput(result) || i18n("An error occurred")} variant="error"></console-block>`
|
||||
: html `<div class="text-sm text-destructive">${getTextOutput(result) || i18n("An error occurred")}</div>`}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// For other errors, just show error message
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, FileCode2, headerText)}
|
||||
<div class="text-sm text-destructive">${getTextOutput(result) || i18n("An error occurred")}</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Full params + result
|
||||
if (result && params) {
|
||||
const { command, filename, content } = params;
|
||||
const labels = command
|
||||
? getCommandLabels(command)
|
||||
: { streaming: i18n("Processing artifact"), complete: i18n("Processed artifact") };
|
||||
const headerText = labels.complete;
|
||||
// GET command: show code block with file content
|
||||
if (command === "get") {
|
||||
const fileContent = getTextOutput(result) || i18n("(no output)");
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
<code-block .code=${fileContent} language=${getLanguageFromFilename(filename)}></code-block>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// LOGS command: show console block
|
||||
if (command === "logs") {
|
||||
const logs = getTextOutput(result) || i18n("(no output)");
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
<console-block .content=${logs}></console-block>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// CREATE/UPDATE/REWRITE: always show code block, + console block for .html files
|
||||
if (command === "create" || command === "rewrite") {
|
||||
const codeContent = content || "";
|
||||
const isHtml = filename?.endsWith(".html");
|
||||
const logs = getTextOutput(result) || "";
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
|
||||
${codeContent ? html `<code-block .code=${codeContent} language=${getLanguageFromFilename(filename)}></code-block>` : ""}
|
||||
${isHtml && logs ? html `<console-block .content=${logs}></console-block>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
if (command === "update") {
|
||||
const isHtml = filename?.endsWith(".html");
|
||||
const logs = getTextOutput(result) || "";
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
|
||||
${Diff({ oldText: params.old_str || "", newText: params.new_str || "" })}
|
||||
${isHtml && logs ? html `<console-block .content=${logs}></console-block>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// For DELETE, just show header
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, FileCode2, renderHeaderWithPill(headerText, filename))}
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Params only (streaming or waiting for result)
|
||||
if (params) {
|
||||
const { command, filename, content, old_str, new_str } = params;
|
||||
// If no command yet
|
||||
if (!command) {
|
||||
return { content: renderHeader(state, FileCode2, i18n("Preparing artifact...")), isCustom: false };
|
||||
}
|
||||
const labels = getCommandLabels(command);
|
||||
const headerText = labels.streaming;
|
||||
// Render based on command type
|
||||
switch (command) {
|
||||
case "create":
|
||||
case "rewrite":
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
${content
|
||||
? html `<code-block .code=${content} language=${getLanguageFromFilename(filename)}></code-block>`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
case "update":
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
${old_str !== undefined && new_str !== undefined
|
||||
? Diff({ oldText: old_str, newText: new_str })
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
case "get":
|
||||
case "logs":
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileCode2, renderHeaderWithPill(headerText, filename), contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300"></div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderHeader(state, FileCode2, renderHeaderWithPill(headerText, filename))}
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
// No params or result yet
|
||||
return { content: renderHeader(state, FileCode2, i18n("Preparing artifact...")), isCustom: false };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=artifacts-tool-renderer.js.map
|
||||
File diff suppressed because one or more lines are too long
64
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/artifacts.d.ts
vendored
Normal file
64
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/artifacts.d.ts
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
|
||||
import { type AgentTool, type Message } from "@mariozechner/pi-ai";
|
||||
import { type Static } from "@sinclair/typebox";
|
||||
import { LitElement, type TemplateResult } from "lit";
|
||||
import type { Agent } from "../../agent/agent.js";
|
||||
export interface Artifact {
|
||||
filename: string;
|
||||
content: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
declare const artifactsParamsSchema: import("@sinclair/typebox").TObject<{
|
||||
command: import("@sinclair/typebox").TUnsafe<string>;
|
||||
filename: import("@sinclair/typebox").TString;
|
||||
content: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
||||
old_str: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
||||
new_str: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
||||
}>;
|
||||
export type ArtifactsParams = Static<typeof artifactsParamsSchema>;
|
||||
export declare class ArtifactsPanel extends LitElement {
|
||||
private _artifacts;
|
||||
private _activeFilename;
|
||||
private artifactElements;
|
||||
private contentRef;
|
||||
agent?: Agent;
|
||||
sandboxUrlProvider?: () => string;
|
||||
onArtifactsChange?: () => void;
|
||||
onClose?: () => void;
|
||||
onOpen?: () => void;
|
||||
collapsed: boolean;
|
||||
overlay: boolean;
|
||||
get artifacts(): Map<string, Artifact>;
|
||||
private getHtmlArtifactRuntimeProviders;
|
||||
protected createRenderRoot(): HTMLElement | DocumentFragment;
|
||||
connectedCallback(): void;
|
||||
disconnectedCallback(): void;
|
||||
private getFileType;
|
||||
private getOrCreateArtifactElement;
|
||||
private showArtifact;
|
||||
openArtifact(filename: string): void;
|
||||
get tool(): AgentTool<typeof artifactsParamsSchema, undefined>;
|
||||
reconstructFromMessages(messages: Array<Message | {
|
||||
role: "aborted";
|
||||
} | {
|
||||
role: "artifact";
|
||||
}>): Promise<void>;
|
||||
private executeCommand;
|
||||
private waitForHtmlExecution;
|
||||
private reloadAllHtmlArtifacts;
|
||||
private createArtifact;
|
||||
private updateArtifact;
|
||||
private rewriteArtifact;
|
||||
private getArtifact;
|
||||
private deleteArtifact;
|
||||
private getLogs;
|
||||
render(): TemplateResult;
|
||||
}
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"artifacts-panel": ArtifactsPanel;
|
||||
}
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=artifacts.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"artifacts.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/artifacts.ts"],"names":[],"mappings":"AACA,OAAO,8CAA8C,CAAC;AAEtD,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAA6B,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAQ,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAI5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAwBlD,MAAM,WAAW,QAAQ;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CAChB;AAGD,QAAA,MAAM,qBAAqB;;;;;;EAQzB,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEnE,qBACa,cAAe,SAAQ,UAAU;IACpC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,eAAe,CAAuB;IAGvD,OAAO,CAAC,gBAAgB,CAAsC;IAC9D,OAAO,CAAC,UAAU,CAAoC;IAGtB,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;IAElC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEvB,SAAS,UAAS;IAElB,OAAO,UAAS;IAG7C,IAAI,SAAS,0BAEZ;IAGD,OAAO,CAAC,+BAA+B;cAsBpB,gBAAgB,IAAI,WAAW,GAAG,gBAAgB;IAI5D,iBAAiB,IAAI,IAAI;IAmBzB,oBAAoB;IAM7B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,0BAA0B;IA2DlC,OAAO,CAAC,YAAY;IAuBb,YAAY,CAAC,QAAQ,EAAE,MAAM;IASpC,IAAW,IAAI,IAAI,SAAS,CAAC,OAAO,qBAAqB,EAAE,SAAS,CAAC,CAmBpE;IAGY,uBAAuB,CACnC,QAAQ,EAAE,KAAK,CAAC,OAAO,GAAG;QAAE,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC,GACnE,OAAO,CAAC,IAAI,CAAC;YAuHF,cAAc;YAwBd,oBAAoB;IAiBlC,OAAO,CAAC,sBAAsB;YAWhB,cAAc;YAyCd,cAAc;YA4Cd,eAAe;IAwC7B,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,OAAO;IAeN,MAAM,IAAI,cAAc;CAqDjC;AAED,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,iBAAiB,EAAE,cAAc,CAAC;KAClC;CACD"}
|
||||
@@ -0,0 +1,659 @@
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
import { icon } from "@mariozechner/mini-lit";
|
||||
import "@mariozechner/mini-lit/dist/MarkdownBlock.js";
|
||||
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
||||
import { StringEnum } from "@mariozechner/pi-ai";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { X } from "lucide";
|
||||
import { ArtifactsRuntimeProvider } from "../../components/sandbox/ArtifactsRuntimeProvider.js";
|
||||
import { AttachmentsRuntimeProvider } from "../../components/sandbox/AttachmentsRuntimeProvider.js";
|
||||
import { ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO, ARTIFACTS_TOOL_DESCRIPTION, ATTACHMENTS_RUNTIME_DESCRIPTION, } from "../../prompts/prompts.js";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { DocxArtifact } from "./DocxArtifact.js";
|
||||
import { ExcelArtifact } from "./ExcelArtifact.js";
|
||||
import { GenericArtifact } from "./GenericArtifact.js";
|
||||
import { HtmlArtifact } from "./HtmlArtifact.js";
|
||||
import { ImageArtifact } from "./ImageArtifact.js";
|
||||
import { MarkdownArtifact } from "./MarkdownArtifact.js";
|
||||
import { PdfArtifact } from "./PdfArtifact.js";
|
||||
import { SvgArtifact } from "./SvgArtifact.js";
|
||||
import { TextArtifact } from "./TextArtifact.js";
|
||||
// JSON-schema friendly parameters object (LLM-facing)
|
||||
const artifactsParamsSchema = Type.Object({
|
||||
command: StringEnum(["create", "update", "rewrite", "get", "delete", "logs"], {
|
||||
description: "The operation to perform",
|
||||
}),
|
||||
filename: Type.String({ description: "Filename including extension (e.g., 'index.html', 'script.js')" }),
|
||||
content: Type.Optional(Type.String({ description: "File content" })),
|
||||
old_str: Type.Optional(Type.String({ description: "String to replace (for update command)" })),
|
||||
new_str: Type.Optional(Type.String({ description: "Replacement string (for update command)" })),
|
||||
});
|
||||
let ArtifactsPanel = class ArtifactsPanel extends LitElement {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this._artifacts = new Map();
|
||||
this._activeFilename = null;
|
||||
// Programmatically managed artifact elements
|
||||
this.artifactElements = new Map();
|
||||
this.contentRef = createRef();
|
||||
// Collapsed mode: hides panel content but can show a floating reopen pill
|
||||
this.collapsed = false;
|
||||
// Overlay mode: when true, panel renders full-screen overlay (mobile)
|
||||
this.overlay = false;
|
||||
}
|
||||
// Public getter for artifacts
|
||||
get artifacts() {
|
||||
return this._artifacts;
|
||||
}
|
||||
// Get runtime providers for HTML artifacts (read-only: attachments + artifacts)
|
||||
getHtmlArtifactRuntimeProviders() {
|
||||
const providers = [];
|
||||
// Get attachments from agent messages
|
||||
if (this.agent) {
|
||||
const attachments = [];
|
||||
for (const message of this.agent.state.messages) {
|
||||
if (message.role === "user" && message.attachments) {
|
||||
attachments.push(...message.attachments);
|
||||
}
|
||||
}
|
||||
if (attachments.length > 0) {
|
||||
providers.push(new AttachmentsRuntimeProvider(attachments));
|
||||
}
|
||||
}
|
||||
// Add read-only artifacts provider
|
||||
providers.push(new ArtifactsRuntimeProvider(this, this.agent, false));
|
||||
return providers;
|
||||
}
|
||||
createRenderRoot() {
|
||||
return this; // light DOM for shared styles
|
||||
}
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.style.display = "block";
|
||||
this.style.height = "100%";
|
||||
// Reattach existing artifact elements when panel is re-inserted into the DOM
|
||||
requestAnimationFrame(() => {
|
||||
const container = this.contentRef.value;
|
||||
if (!container)
|
||||
return;
|
||||
// Ensure we have an active filename
|
||||
if (!this._activeFilename && this._artifacts.size > 0) {
|
||||
this._activeFilename = Array.from(this._artifacts.keys())[0];
|
||||
}
|
||||
this.artifactElements.forEach((element, name) => {
|
||||
if (!element.parentElement)
|
||||
container.appendChild(element);
|
||||
element.style.display = name === this._activeFilename ? "block" : "none";
|
||||
});
|
||||
});
|
||||
}
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
// Do not tear down artifact elements; keep them to restore on next mount
|
||||
}
|
||||
// Helper to determine file type from extension
|
||||
getFileType(filename) {
|
||||
const ext = filename.split(".").pop()?.toLowerCase();
|
||||
if (ext === "html")
|
||||
return "html";
|
||||
if (ext === "svg")
|
||||
return "svg";
|
||||
if (ext === "md" || ext === "markdown")
|
||||
return "markdown";
|
||||
if (ext === "pdf")
|
||||
return "pdf";
|
||||
if (ext === "xlsx" || ext === "xls")
|
||||
return "excel";
|
||||
if (ext === "docx")
|
||||
return "docx";
|
||||
if (ext === "png" ||
|
||||
ext === "jpg" ||
|
||||
ext === "jpeg" ||
|
||||
ext === "gif" ||
|
||||
ext === "webp" ||
|
||||
ext === "bmp" ||
|
||||
ext === "ico")
|
||||
return "image";
|
||||
// Text files
|
||||
if (ext === "txt" ||
|
||||
ext === "json" ||
|
||||
ext === "xml" ||
|
||||
ext === "yaml" ||
|
||||
ext === "yml" ||
|
||||
ext === "csv" ||
|
||||
ext === "js" ||
|
||||
ext === "ts" ||
|
||||
ext === "jsx" ||
|
||||
ext === "tsx" ||
|
||||
ext === "py" ||
|
||||
ext === "java" ||
|
||||
ext === "c" ||
|
||||
ext === "cpp" ||
|
||||
ext === "h" ||
|
||||
ext === "css" ||
|
||||
ext === "scss" ||
|
||||
ext === "sass" ||
|
||||
ext === "less" ||
|
||||
ext === "sh")
|
||||
return "text";
|
||||
// Everything else gets generic fallback
|
||||
return "generic";
|
||||
}
|
||||
// Get or create artifact element
|
||||
getOrCreateArtifactElement(filename, content) {
|
||||
let element = this.artifactElements.get(filename);
|
||||
if (!element) {
|
||||
const type = this.getFileType(filename);
|
||||
if (type === "html") {
|
||||
element = new HtmlArtifact();
|
||||
element.runtimeProviders = this.getHtmlArtifactRuntimeProviders();
|
||||
if (this.sandboxUrlProvider) {
|
||||
element.sandboxUrlProvider = this.sandboxUrlProvider;
|
||||
}
|
||||
}
|
||||
else if (type === "svg") {
|
||||
element = new SvgArtifact();
|
||||
}
|
||||
else if (type === "markdown") {
|
||||
element = new MarkdownArtifact();
|
||||
}
|
||||
else if (type === "image") {
|
||||
element = new ImageArtifact();
|
||||
}
|
||||
else if (type === "pdf") {
|
||||
element = new PdfArtifact();
|
||||
}
|
||||
else if (type === "excel") {
|
||||
element = new ExcelArtifact();
|
||||
}
|
||||
else if (type === "docx") {
|
||||
element = new DocxArtifact();
|
||||
}
|
||||
else if (type === "text") {
|
||||
element = new TextArtifact();
|
||||
}
|
||||
else {
|
||||
element = new GenericArtifact();
|
||||
}
|
||||
element.filename = filename;
|
||||
element.content = content;
|
||||
element.style.display = "none";
|
||||
element.style.height = "100%";
|
||||
// Store element
|
||||
this.artifactElements.set(filename, element);
|
||||
// Add to DOM - try immediately if container exists, otherwise schedule
|
||||
const newElement = element;
|
||||
if (this.contentRef.value) {
|
||||
this.contentRef.value.appendChild(newElement);
|
||||
}
|
||||
else {
|
||||
requestAnimationFrame(() => {
|
||||
if (this.contentRef.value && !newElement.parentElement) {
|
||||
this.contentRef.value.appendChild(newElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Just update content
|
||||
element.content = content;
|
||||
if (element instanceof HtmlArtifact) {
|
||||
element.runtimeProviders = this.getHtmlArtifactRuntimeProviders();
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
// Show/hide artifact elements
|
||||
showArtifact(filename) {
|
||||
// Ensure the active element is in the DOM
|
||||
requestAnimationFrame(() => {
|
||||
this.artifactElements.forEach((element, name) => {
|
||||
if (this.contentRef.value && !element.parentElement) {
|
||||
this.contentRef.value.appendChild(element);
|
||||
}
|
||||
element.style.display = name === filename ? "block" : "none";
|
||||
});
|
||||
});
|
||||
this._activeFilename = filename;
|
||||
this.requestUpdate(); // Only for tab bar update
|
||||
// Scroll the active tab into view after render
|
||||
requestAnimationFrame(() => {
|
||||
const activeButton = this.querySelector(`button[data-filename="${filename}"]`);
|
||||
if (activeButton) {
|
||||
activeButton.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
|
||||
}
|
||||
});
|
||||
}
|
||||
// Open panel and focus an artifact tab by filename
|
||||
openArtifact(filename) {
|
||||
if (this._artifacts.has(filename)) {
|
||||
this.showArtifact(filename);
|
||||
// Ask host to open panel (AgentInterface demo listens to onOpen)
|
||||
this.onOpen?.();
|
||||
}
|
||||
}
|
||||
// Build the AgentTool (no details payload; return only output strings)
|
||||
get tool() {
|
||||
return {
|
||||
label: "Artifacts",
|
||||
name: "artifacts",
|
||||
get description() {
|
||||
// HTML artifacts have read-only access to attachments and artifacts
|
||||
const runtimeProviderDescriptions = [
|
||||
ATTACHMENTS_RUNTIME_DESCRIPTION,
|
||||
ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO,
|
||||
];
|
||||
return ARTIFACTS_TOOL_DESCRIPTION(runtimeProviderDescriptions);
|
||||
},
|
||||
parameters: artifactsParamsSchema,
|
||||
// Execute mutates our local store and returns a plain output
|
||||
execute: async (_toolCallId, args, _signal) => {
|
||||
const output = await this.executeCommand(args);
|
||||
return { content: [{ type: "text", text: output }], details: undefined };
|
||||
},
|
||||
};
|
||||
}
|
||||
// Re-apply artifacts by scanning a message list (optional utility)
|
||||
async reconstructFromMessages(messages) {
|
||||
const toolCalls = new Map();
|
||||
const artifactToolName = "artifacts";
|
||||
// 1) Collect tool calls from assistant messages
|
||||
for (const message of messages) {
|
||||
if (message.role === "assistant") {
|
||||
for (const block of message.content) {
|
||||
if (block.type === "toolCall" && block.name === artifactToolName) {
|
||||
toolCalls.set(block.id, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) Build an ordered list of successful artifact operations
|
||||
const operations = [];
|
||||
for (const m of messages) {
|
||||
if (m.role === "artifact") {
|
||||
const artifactMsg = m;
|
||||
switch (artifactMsg.action) {
|
||||
case "create":
|
||||
operations.push({
|
||||
command: "create",
|
||||
filename: artifactMsg.filename,
|
||||
content: artifactMsg.content,
|
||||
});
|
||||
break;
|
||||
case "update":
|
||||
operations.push({
|
||||
command: "rewrite",
|
||||
filename: artifactMsg.filename,
|
||||
content: artifactMsg.content,
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
operations.push({
|
||||
command: "delete",
|
||||
filename: artifactMsg.filename,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Handle tool result messages (from artifacts tool calls)
|
||||
else if (m.role === "toolResult" && m.toolName === artifactToolName && !m.isError) {
|
||||
const toolCallId = m.toolCallId;
|
||||
const call = toolCalls.get(toolCallId);
|
||||
if (!call)
|
||||
continue;
|
||||
const params = call.arguments;
|
||||
if (params.command === "get" || params.command === "logs")
|
||||
continue; // no state change
|
||||
operations.push(params);
|
||||
}
|
||||
}
|
||||
// 3) Compute final state per filename by simulating operations in-memory
|
||||
const finalArtifacts = new Map();
|
||||
for (const op of operations) {
|
||||
const filename = op.filename;
|
||||
switch (op.command) {
|
||||
case "create": {
|
||||
if (op.content) {
|
||||
finalArtifacts.set(filename, op.content);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "rewrite": {
|
||||
if (op.content) {
|
||||
finalArtifacts.set(filename, op.content);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "update": {
|
||||
let existing = finalArtifacts.get(filename);
|
||||
if (!existing)
|
||||
break; // skip invalid update (shouldn't happen for successful results)
|
||||
if (op.old_str !== undefined && op.new_str !== undefined) {
|
||||
existing = existing.replace(op.old_str, op.new_str);
|
||||
finalArtifacts.set(filename, existing);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "delete": {
|
||||
finalArtifacts.delete(filename);
|
||||
break;
|
||||
}
|
||||
case "get":
|
||||
case "logs":
|
||||
// Ignored above, just for completeness
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 4) Reset current UI state before bulk create
|
||||
this._artifacts.clear();
|
||||
this.artifactElements.forEach((el) => {
|
||||
el.remove();
|
||||
});
|
||||
this.artifactElements.clear();
|
||||
this._activeFilename = null;
|
||||
this._artifacts = new Map(this._artifacts);
|
||||
// 5) Create artifacts in a single pass without waiting for iframe execution or tab switching
|
||||
for (const [filename, content] of finalArtifacts.entries()) {
|
||||
const createParams = { command: "create", filename, content };
|
||||
try {
|
||||
await this.createArtifact(createParams, { skipWait: true, silent: true });
|
||||
}
|
||||
catch {
|
||||
// Ignore failures during reconstruction
|
||||
}
|
||||
}
|
||||
// 6) Show first artifact if any exist, and notify listeners once
|
||||
if (!this._activeFilename && this._artifacts.size > 0) {
|
||||
this.showArtifact(Array.from(this._artifacts.keys())[0]);
|
||||
}
|
||||
this.onArtifactsChange?.();
|
||||
this.requestUpdate();
|
||||
}
|
||||
// Core command executor
|
||||
async executeCommand(params, options = {}) {
|
||||
switch (params.command) {
|
||||
case "create":
|
||||
return await this.createArtifact(params, options);
|
||||
case "update":
|
||||
return await this.updateArtifact(params, options);
|
||||
case "rewrite":
|
||||
return await this.rewriteArtifact(params, options);
|
||||
case "get":
|
||||
return this.getArtifact(params);
|
||||
case "delete":
|
||||
return this.deleteArtifact(params);
|
||||
case "logs":
|
||||
return this.getLogs(params);
|
||||
default:
|
||||
// Should never happen with TypeBox validation
|
||||
return `Error: Unknown command ${params.command}`;
|
||||
}
|
||||
}
|
||||
// Wait for HTML artifact execution and get logs
|
||||
async waitForHtmlExecution(filename) {
|
||||
const element = this.artifactElements.get(filename);
|
||||
if (!(element instanceof HtmlArtifact)) {
|
||||
return "";
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
// Fallback timeout - just get logs after execution should complete
|
||||
setTimeout(() => {
|
||||
// Get whatever logs we have
|
||||
const logs = element.getLogs();
|
||||
resolve(logs);
|
||||
}, 1500);
|
||||
});
|
||||
}
|
||||
// Reload all HTML artifacts (called when any artifact changes)
|
||||
reloadAllHtmlArtifacts() {
|
||||
this.artifactElements.forEach((element) => {
|
||||
if (element instanceof HtmlArtifact && element.sandboxIframeRef.value) {
|
||||
// Update runtime providers with latest artifact state
|
||||
element.runtimeProviders = this.getHtmlArtifactRuntimeProviders();
|
||||
// Re-execute the HTML content
|
||||
element.executeContent(element.content);
|
||||
}
|
||||
});
|
||||
}
|
||||
async createArtifact(params, options = {}) {
|
||||
if (!params.filename || !params.content) {
|
||||
return "Error: create command requires filename and content";
|
||||
}
|
||||
if (this._artifacts.has(params.filename)) {
|
||||
return `Error: File ${params.filename} already exists`;
|
||||
}
|
||||
const artifact = {
|
||||
filename: params.filename,
|
||||
content: params.content,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
this._artifacts.set(params.filename, artifact);
|
||||
this._artifacts = new Map(this._artifacts);
|
||||
// Create or update element
|
||||
this.getOrCreateArtifactElement(params.filename, params.content);
|
||||
if (!options.silent) {
|
||||
this.showArtifact(params.filename);
|
||||
this.onArtifactsChange?.();
|
||||
this.requestUpdate();
|
||||
}
|
||||
// Reload all HTML artifacts since they might depend on this new artifact
|
||||
this.reloadAllHtmlArtifacts();
|
||||
// For HTML files, wait for execution
|
||||
let result = `Created file ${params.filename}`;
|
||||
if (this.getFileType(params.filename) === "html" && !options.skipWait) {
|
||||
const logs = await this.waitForHtmlExecution(params.filename);
|
||||
result += `\n${logs}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async updateArtifact(params, options = {}) {
|
||||
const artifact = this._artifacts.get(params.filename);
|
||||
if (!artifact) {
|
||||
const files = Array.from(this._artifacts.keys());
|
||||
if (files.length === 0)
|
||||
return `Error: File ${params.filename} not found. No files have been created yet.`;
|
||||
return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
|
||||
}
|
||||
if (!params.old_str || params.new_str === undefined) {
|
||||
return "Error: update command requires old_str and new_str";
|
||||
}
|
||||
if (!artifact.content.includes(params.old_str)) {
|
||||
return `Error: String not found in file. Here is the full content:\n\n${artifact.content}`;
|
||||
}
|
||||
artifact.content = artifact.content.replace(params.old_str, params.new_str);
|
||||
artifact.updatedAt = new Date();
|
||||
this._artifacts.set(params.filename, artifact);
|
||||
// Update element
|
||||
this.getOrCreateArtifactElement(params.filename, artifact.content);
|
||||
if (!options.silent) {
|
||||
this.onArtifactsChange?.();
|
||||
this.requestUpdate();
|
||||
}
|
||||
// Show the artifact
|
||||
this.showArtifact(params.filename);
|
||||
// Reload all HTML artifacts since they might depend on this updated artifact
|
||||
this.reloadAllHtmlArtifacts();
|
||||
// For HTML files, wait for execution
|
||||
let result = `Updated file ${params.filename}`;
|
||||
if (this.getFileType(params.filename) === "html" && !options.skipWait) {
|
||||
const logs = await this.waitForHtmlExecution(params.filename);
|
||||
result += `\n${logs}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async rewriteArtifact(params, options = {}) {
|
||||
const artifact = this._artifacts.get(params.filename);
|
||||
if (!artifact) {
|
||||
const files = Array.from(this._artifacts.keys());
|
||||
if (files.length === 0)
|
||||
return `Error: File ${params.filename} not found. No files have been created yet.`;
|
||||
return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
|
||||
}
|
||||
if (!params.content) {
|
||||
return "Error: rewrite command requires content";
|
||||
}
|
||||
artifact.content = params.content;
|
||||
artifact.updatedAt = new Date();
|
||||
this._artifacts.set(params.filename, artifact);
|
||||
// Update element
|
||||
this.getOrCreateArtifactElement(params.filename, artifact.content);
|
||||
if (!options.silent) {
|
||||
this.onArtifactsChange?.();
|
||||
}
|
||||
// Show the artifact
|
||||
this.showArtifact(params.filename);
|
||||
// Reload all HTML artifacts since they might depend on this rewritten artifact
|
||||
this.reloadAllHtmlArtifacts();
|
||||
// For HTML files, wait for execution
|
||||
let result = "";
|
||||
if (this.getFileType(params.filename) === "html" && !options.skipWait) {
|
||||
const logs = await this.waitForHtmlExecution(params.filename);
|
||||
result += `\n${logs}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getArtifact(params) {
|
||||
const artifact = this._artifacts.get(params.filename);
|
||||
if (!artifact) {
|
||||
const files = Array.from(this._artifacts.keys());
|
||||
if (files.length === 0)
|
||||
return `Error: File ${params.filename} not found. No files have been created yet.`;
|
||||
return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
|
||||
}
|
||||
return artifact.content;
|
||||
}
|
||||
deleteArtifact(params) {
|
||||
const artifact = this._artifacts.get(params.filename);
|
||||
if (!artifact) {
|
||||
const files = Array.from(this._artifacts.keys());
|
||||
if (files.length === 0)
|
||||
return `Error: File ${params.filename} not found. No files have been created yet.`;
|
||||
return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
|
||||
}
|
||||
this._artifacts.delete(params.filename);
|
||||
this._artifacts = new Map(this._artifacts);
|
||||
// Remove element
|
||||
const element = this.artifactElements.get(params.filename);
|
||||
if (element) {
|
||||
element.remove();
|
||||
this.artifactElements.delete(params.filename);
|
||||
}
|
||||
// Show another artifact if this was active
|
||||
if (this._activeFilename === params.filename) {
|
||||
const remaining = Array.from(this._artifacts.keys());
|
||||
if (remaining.length > 0) {
|
||||
this.showArtifact(remaining[0]);
|
||||
}
|
||||
else {
|
||||
this._activeFilename = null;
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
this.onArtifactsChange?.();
|
||||
this.requestUpdate();
|
||||
// Reload all HTML artifacts since they might have depended on this deleted artifact
|
||||
this.reloadAllHtmlArtifacts();
|
||||
return `Deleted file ${params.filename}`;
|
||||
}
|
||||
getLogs(params) {
|
||||
const element = this.artifactElements.get(params.filename);
|
||||
if (!element) {
|
||||
const files = Array.from(this._artifacts.keys());
|
||||
if (files.length === 0)
|
||||
return `Error: File ${params.filename} not found. No files have been created yet.`;
|
||||
return `Error: File ${params.filename} not found. Available files: ${files.join(", ")}`;
|
||||
}
|
||||
if (!(element instanceof HtmlArtifact)) {
|
||||
return `Error: File ${params.filename} is not an HTML file. Logs are only available for HTML files.`;
|
||||
}
|
||||
return element.getLogs();
|
||||
}
|
||||
render() {
|
||||
const artifacts = Array.from(this._artifacts.values());
|
||||
// Panel is hidden when collapsed OR when there are no artifacts
|
||||
const showPanel = artifacts.length > 0 && !this.collapsed;
|
||||
return html `
|
||||
<div
|
||||
class="${showPanel ? "" : "hidden"} ${this.overlay ? "fixed inset-0 z-40 pointer-events-auto backdrop-blur-sm bg-background/95" : "relative"} h-full flex flex-col bg-background text-card-foreground ${!this.overlay ? "border-l border-border" : ""} overflow-hidden shadow-xl"
|
||||
>
|
||||
<!-- Tab bar (always shown when there are artifacts) -->
|
||||
<div class="flex items-center justify-between border-b border-border bg-background">
|
||||
<div class="flex overflow-x-auto">
|
||||
${artifacts.map((a) => {
|
||||
const isActive = a.filename === this._activeFilename;
|
||||
const activeClass = isActive
|
||||
? "border-primary text-primary"
|
||||
: "border-transparent text-muted-foreground hover:text-foreground";
|
||||
return html `
|
||||
<button
|
||||
class="px-3 py-2 whitespace-nowrap border-b-2 ${activeClass}"
|
||||
data-filename="${a.filename}"
|
||||
@click=${() => this.showArtifact(a.filename)}
|
||||
>
|
||||
<span class="font-mono text-xs">${a.filename}</span>
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
<div class="flex items-center gap-1 px-2">
|
||||
${(() => {
|
||||
const active = this._activeFilename ? this.artifactElements.get(this._activeFilename) : undefined;
|
||||
return active ? active.getHeaderButtons() : "";
|
||||
})()}
|
||||
${Button({
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
onClick: () => this.onClose?.(),
|
||||
title: i18n("Close artifacts"),
|
||||
children: icon(X, "sm"),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content area where artifact elements are added programmatically -->
|
||||
<div class="flex-1 overflow-hidden" ${ref(this.contentRef)}></div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
};
|
||||
__decorate([
|
||||
state()
|
||||
], ArtifactsPanel.prototype, "_artifacts", void 0);
|
||||
__decorate([
|
||||
state()
|
||||
], ArtifactsPanel.prototype, "_activeFilename", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], ArtifactsPanel.prototype, "agent", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], ArtifactsPanel.prototype, "sandboxUrlProvider", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], ArtifactsPanel.prototype, "onArtifactsChange", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], ArtifactsPanel.prototype, "onClose", void 0);
|
||||
__decorate([
|
||||
property({ attribute: false })
|
||||
], ArtifactsPanel.prototype, "onOpen", void 0);
|
||||
__decorate([
|
||||
property({ type: Boolean })
|
||||
], ArtifactsPanel.prototype, "collapsed", void 0);
|
||||
__decorate([
|
||||
property({ type: Boolean })
|
||||
], ArtifactsPanel.prototype, "overlay", void 0);
|
||||
ArtifactsPanel = __decorate([
|
||||
customElement("artifacts-panel")
|
||||
], ArtifactsPanel);
|
||||
export { ArtifactsPanel };
|
||||
//# sourceMappingURL=artifacts.js.map
|
||||
File diff suppressed because one or more lines are too long
8
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/index.d.ts
vendored
Normal file
8
apps/macos/Sources/Clawdis/Resources/WebChat/tools/artifacts/index.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export { ArtifactElement } from "./ArtifactElement.js";
|
||||
export { type Artifact, ArtifactsPanel, type ArtifactsParams } from "./artifacts.js";
|
||||
export { ArtifactsToolRenderer } from "./artifacts-tool-renderer.js";
|
||||
export { HtmlArtifact } from "./HtmlArtifact.js";
|
||||
export { MarkdownArtifact } from "./MarkdownArtifact.js";
|
||||
export { SvgArtifact } from "./SvgArtifact.js";
|
||||
export { TextArtifact } from "./TextArtifact.js";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/artifacts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,KAAK,QAAQ,EAAE,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
||||
@@ -0,0 +1,8 @@
|
||||
export { ArtifactElement } from "./ArtifactElement.js";
|
||||
export { ArtifactsPanel } from "./artifacts.js";
|
||||
export { ArtifactsToolRenderer } from "./artifacts-tool-renderer.js";
|
||||
export { HtmlArtifact } from "./HtmlArtifact.js";
|
||||
export { MarkdownArtifact } from "./MarkdownArtifact.js";
|
||||
export { SvgArtifact } from "./SvgArtifact.js";
|
||||
export { TextArtifact } from "./TextArtifact.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/artifacts/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAiB,cAAc,EAAwB,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
|
||||
24
apps/macos/Sources/Clawdis/Resources/WebChat/tools/extract-document.d.ts
vendored
Normal file
24
apps/macos/Sources/Clawdis/Resources/WebChat/tools/extract-document.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { AgentTool } from "@mariozechner/pi-ai";
|
||||
import { type Static } from "@sinclair/typebox";
|
||||
import type { ToolRenderer } from "./types.js";
|
||||
declare const extractDocumentSchema: import("@sinclair/typebox").TObject<{
|
||||
url: import("@sinclair/typebox").TString;
|
||||
}>;
|
||||
export type ExtractDocumentParams = Static<typeof extractDocumentSchema>;
|
||||
export interface ExtractDocumentResult {
|
||||
extractedText: string;
|
||||
format: string;
|
||||
fileName: string;
|
||||
size: number;
|
||||
}
|
||||
export declare function createExtractDocumentTool(): AgentTool<typeof extractDocumentSchema, ExtractDocumentResult> & {
|
||||
corsProxyUrl?: string;
|
||||
};
|
||||
export declare const extractDocumentTool: AgentTool<import("@sinclair/typebox").TObject<{
|
||||
url: import("@sinclair/typebox").TString;
|
||||
}>, ExtractDocumentResult> & {
|
||||
corsProxyUrl?: string;
|
||||
};
|
||||
export declare const extractDocumentRenderer: ToolRenderer<ExtractDocumentParams, ExtractDocumentResult>;
|
||||
export {};
|
||||
//# sourceMappingURL=extract-document.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"extract-document.d.ts","sourceRoot":"","sources":["../../src/tools/extract-document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAQtD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAMjE,QAAA,MAAM,qBAAqB;;EAIzB,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEzE,MAAM,WAAW,qBAAqB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACb;AAMD,wBAAgB,yBAAyB,IAAI,SAAS,CAAC,OAAO,qBAAqB,EAAE,qBAAqB,CAAC,GAAG;IAC7G,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,CA+IA;AAGD,eAAO,MAAM,mBAAmB;;;mBAnJhB,MAAM;CAmJwC,CAAC;AAM/D,eAAO,MAAM,uBAAuB,EAAE,YAAY,CAAC,qBAAqB,EAAE,qBAAqB,CAkF9F,CAAC"}
|
||||
@@ -0,0 +1,216 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { html } from "lit";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { FileText } from "lucide";
|
||||
import { EXTRACT_DOCUMENT_DESCRIPTION } from "../prompts/prompts.js";
|
||||
import { loadAttachment } from "../utils/attachment-utils.js";
|
||||
import { isCorsError } from "../utils/proxy-utils.js";
|
||||
import { registerToolRenderer, renderCollapsibleHeader, renderHeader } from "./renderer-registry.js";
|
||||
// ============================================================================
|
||||
// TYPES
|
||||
// ============================================================================
|
||||
const extractDocumentSchema = Type.Object({
|
||||
url: Type.String({
|
||||
description: "URL of the document to extract text from (PDF, DOCX, XLSX, or PPTX)",
|
||||
}),
|
||||
});
|
||||
// ============================================================================
|
||||
// TOOL
|
||||
// ============================================================================
|
||||
export function createExtractDocumentTool() {
|
||||
const tool = {
|
||||
label: "Extract Document",
|
||||
name: "extract_document",
|
||||
corsProxyUrl: undefined, // Can be set by consumer (e.g., from user settings)
|
||||
description: EXTRACT_DOCUMENT_DESCRIPTION,
|
||||
parameters: extractDocumentSchema,
|
||||
execute: async (_toolCallId, args, signal) => {
|
||||
if (signal?.aborted) {
|
||||
throw new Error("Extract document aborted");
|
||||
}
|
||||
const url = args.url.trim();
|
||||
if (!url) {
|
||||
throw new Error("URL is required");
|
||||
}
|
||||
// Validate URL format
|
||||
try {
|
||||
new URL(url);
|
||||
}
|
||||
catch {
|
||||
throw new Error(`Invalid URL: ${url}`);
|
||||
}
|
||||
// Size limit: 50MB
|
||||
const MAX_SIZE = 50 * 1024 * 1024;
|
||||
// Helper function to fetch and process document
|
||||
const fetchAndProcess = async (fetchUrl) => {
|
||||
const response = await fetch(fetchUrl, { signal });
|
||||
if (!response.ok) {
|
||||
throw new Error(`TELL USER: Unable to download the document (${response.status} ${response.statusText}). The site likely blocks automated downloads.\n\n` +
|
||||
`INSTRUCT USER: Please download the file manually and attach it to your message using the attachment button (paperclip icon) in the message input area. I can then extract the text from the attached file.`);
|
||||
}
|
||||
// Check size before downloading
|
||||
const contentLength = response.headers.get("content-length");
|
||||
if (contentLength) {
|
||||
const size = Number.parseInt(contentLength, 10);
|
||||
if (size > MAX_SIZE) {
|
||||
throw new Error(`Document is too large (${(size / 1024 / 1024).toFixed(1)}MB). Maximum supported size is 50MB.`);
|
||||
}
|
||||
}
|
||||
// Download the document
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const size = arrayBuffer.byteLength;
|
||||
if (size > MAX_SIZE) {
|
||||
throw new Error(`Document is too large (${(size / 1024 / 1024).toFixed(1)}MB). Maximum supported size is 50MB.`);
|
||||
}
|
||||
return arrayBuffer;
|
||||
};
|
||||
// Try without proxy first, fallback to proxy on CORS error
|
||||
let arrayBuffer;
|
||||
try {
|
||||
// Attempt direct fetch first
|
||||
arrayBuffer = await fetchAndProcess(url);
|
||||
}
|
||||
catch (directError) {
|
||||
// If CORS error and proxy is available, retry with proxy
|
||||
if (isCorsError(directError) && tool.corsProxyUrl) {
|
||||
try {
|
||||
const proxiedUrl = tool.corsProxyUrl + encodeURIComponent(url);
|
||||
arrayBuffer = await fetchAndProcess(proxiedUrl);
|
||||
}
|
||||
catch (proxyError) {
|
||||
// Proxy fetch also failed - throw helpful message
|
||||
throw new Error(`TELL USER: Unable to fetch the document due to CORS restrictions.\n\n` +
|
||||
`Tried with proxy but it also failed: ${proxyError.message}\n\n` +
|
||||
`INSTRUCT USER: Please download the file manually and attach it to your message using the attachment button (paperclip icon) in the message input area. I can then extract the text from the attached file.`);
|
||||
}
|
||||
}
|
||||
else if (isCorsError(directError) && !tool.corsProxyUrl) {
|
||||
// CORS error but no proxy configured
|
||||
throw new Error(`TELL USER: Unable to fetch the document due to CORS restrictions (the server blocks requests from browser extensions).\n\n` +
|
||||
`To fix this, you need to configure a CORS proxy in Sitegeist settings:\n` +
|
||||
`1. Open Sitegeist settings\n` +
|
||||
`2. Find "CORS Proxy URL" setting\n` +
|
||||
`3. Enter a proxy URL like: https://corsproxy.io/?\n` +
|
||||
`4. Save and try again\n\n` +
|
||||
`Alternatively, download the file manually and attach it to your message using the attachment button (paperclip icon).`);
|
||||
}
|
||||
else {
|
||||
// Not a CORS error - re-throw
|
||||
throw directError;
|
||||
}
|
||||
}
|
||||
// Extract filename from URL
|
||||
const urlParts = url.split("/");
|
||||
let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
|
||||
if (url.startsWith("https://arxiv.org/")) {
|
||||
fileName = fileName + ".pdf";
|
||||
}
|
||||
// Use loadAttachment to process the document
|
||||
const attachment = await loadAttachment(arrayBuffer, fileName);
|
||||
if (!attachment.extractedText) {
|
||||
throw new Error(`Document format not supported. Supported formats:\n` +
|
||||
`- PDF (.pdf)\n` +
|
||||
`- Word (.docx)\n` +
|
||||
`- Excel (.xlsx, .xls)\n` +
|
||||
`- PowerPoint (.pptx)`);
|
||||
}
|
||||
// Determine format from attachment
|
||||
let format = "unknown";
|
||||
if (attachment.mimeType.includes("pdf")) {
|
||||
format = "pdf";
|
||||
}
|
||||
else if (attachment.mimeType.includes("wordprocessingml")) {
|
||||
format = "docx";
|
||||
}
|
||||
else if (attachment.mimeType.includes("spreadsheetml") || attachment.mimeType.includes("ms-excel")) {
|
||||
format = "xlsx";
|
||||
}
|
||||
else if (attachment.mimeType.includes("presentationml")) {
|
||||
format = "pptx";
|
||||
}
|
||||
return {
|
||||
content: [{ type: "text", text: attachment.extractedText }],
|
||||
details: {
|
||||
extractedText: attachment.extractedText,
|
||||
format,
|
||||
fileName: attachment.fileName,
|
||||
size: attachment.size,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
return tool;
|
||||
}
|
||||
// Export a default instance
|
||||
export const extractDocumentTool = createExtractDocumentTool();
|
||||
// ============================================================================
|
||||
// RENDERER
|
||||
// ============================================================================
|
||||
export const extractDocumentRenderer = {
|
||||
render(params, result, isStreaming) {
|
||||
// Determine status
|
||||
const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
|
||||
// Create refs for collapsible sections
|
||||
const contentRef = createRef();
|
||||
const chevronRef = createRef();
|
||||
// With result: show params + result
|
||||
if (result && params) {
|
||||
const details = result.details;
|
||||
const title = details
|
||||
? result.isError
|
||||
? `Failed to extract ${details.fileName || "document"}`
|
||||
: `Extracted text from ${details.fileName} (${details.format.toUpperCase()}, ${(details.size / 1024).toFixed(1)}KB)`
|
||||
: result.isError
|
||||
? "Failed to extract document"
|
||||
: "Extracted text from document";
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileText, title, contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
|
||||
${params.url
|
||||
? html `<div class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<strong>URL:</strong> ${params.url}
|
||||
</div>`
|
||||
: ""}
|
||||
${output && !result.isError
|
||||
? html `<code-block .code=${output} language="plaintext"></code-block>`
|
||||
: ""}
|
||||
${result.isError && output
|
||||
? html `<console-block .content=${output} .variant=${"error"}></console-block>`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Just params (streaming or waiting for result)
|
||||
if (params) {
|
||||
const title = "Extracting document...";
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, FileText, title, contentRef, chevronRef, false)}
|
||||
<div ${ref(contentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400"><strong>URL:</strong> ${params.url}</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// No params or result yet
|
||||
return {
|
||||
content: renderHeader(state, FileText, "Preparing extraction..."),
|
||||
isCustom: false,
|
||||
};
|
||||
},
|
||||
};
|
||||
// Auto-register the renderer
|
||||
registerToolRenderer("extract_document", extractDocumentRenderer);
|
||||
//# sourceMappingURL=extract-document.js.map
|
||||
File diff suppressed because one or more lines are too long
16
apps/macos/Sources/Clawdis/Resources/WebChat/tools/index.d.ts
vendored
Normal file
16
apps/macos/Sources/Clawdis/Resources/WebChat/tools/index.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import "./javascript-repl.js";
|
||||
import "./extract-document.js";
|
||||
import { getToolRenderer, registerToolRenderer } from "./renderer-registry.js";
|
||||
import type { ToolRenderResult } from "./types.js";
|
||||
/**
|
||||
* Enable or disable show JSON mode
|
||||
* When enabled, all tool renderers will use the default JSON renderer
|
||||
*/
|
||||
export declare function setShowJsonMode(enabled: boolean): void;
|
||||
/**
|
||||
* Render tool - unified function that handles params, result, and streaming state
|
||||
*/
|
||||
export declare function renderTool(toolName: string, params: any | undefined, result: ToolResultMessage | undefined, isStreaming?: boolean): ToolRenderResult;
|
||||
export { getToolRenderer, registerToolRenderer };
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,sBAAsB,CAAC;AAC9B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAUnD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,GAAG,GAAG,SAAS,EACvB,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,WAAW,CAAC,EAAE,OAAO,GACnB,gBAAgB,CAWlB;AAED,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,CAAC"}
|
||||
33
apps/macos/Sources/Clawdis/Resources/WebChat/tools/index.js
Normal file
33
apps/macos/Sources/Clawdis/Resources/WebChat/tools/index.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import "./javascript-repl.js"; // Auto-registers the renderer
|
||||
import "./extract-document.js"; // Auto-registers the renderer
|
||||
import { getToolRenderer, registerToolRenderer } from "./renderer-registry.js";
|
||||
import { BashRenderer } from "./renderers/BashRenderer.js";
|
||||
import { DefaultRenderer } from "./renderers/DefaultRenderer.js";
|
||||
// Register all built-in tool renderers
|
||||
registerToolRenderer("bash", new BashRenderer());
|
||||
const defaultRenderer = new DefaultRenderer();
|
||||
// Global flag to force default JSON rendering for all tools
|
||||
let showJsonMode = false;
|
||||
/**
|
||||
* Enable or disable show JSON mode
|
||||
* When enabled, all tool renderers will use the default JSON renderer
|
||||
*/
|
||||
export function setShowJsonMode(enabled) {
|
||||
showJsonMode = enabled;
|
||||
}
|
||||
/**
|
||||
* Render tool - unified function that handles params, result, and streaming state
|
||||
*/
|
||||
export function renderTool(toolName, params, result, isStreaming) {
|
||||
// If showJsonMode is enabled, always use the default renderer
|
||||
if (showJsonMode) {
|
||||
return defaultRenderer.render(params, result, isStreaming);
|
||||
}
|
||||
const renderer = getToolRenderer(toolName);
|
||||
if (renderer) {
|
||||
return renderer.render(params, result, isStreaming);
|
||||
}
|
||||
return defaultRenderer.render(params, result, isStreaming);
|
||||
}
|
||||
export { getToolRenderer, registerToolRenderer };
|
||||
//# sourceMappingURL=index.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,sBAAsB,CAAC,CAAC,8BAA8B;AAC7D,OAAO,uBAAuB,CAAC,CAAC,8BAA8B;AAC9D,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,uCAAuC;AACvC,oBAAoB,CAAC,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC,CAAC;AAEjD,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;AAE9C,4DAA4D;AAC5D,IAAI,YAAY,GAAG,KAAK,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC/C,YAAY,GAAG,OAAO,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CACzB,QAAgB,EAChB,MAAuB,EACvB,MAAqC,EACrC,WAAqB;IAErB,8DAA8D;IAC9D,IAAI,YAAY,EAAE,CAAC;QAClB,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;AAC5D,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,CAAC"}
|
||||
44
apps/macos/Sources/Clawdis/Resources/WebChat/tools/javascript-repl.d.ts
vendored
Normal file
44
apps/macos/Sources/Clawdis/Resources/WebChat/tools/javascript-repl.d.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { AgentTool } from "@mariozechner/pi-ai";
|
||||
import { type Static } from "@sinclair/typebox";
|
||||
import { type SandboxFile } from "../components/SandboxedIframe.js";
|
||||
import type { SandboxRuntimeProvider } from "../components/sandbox/SandboxRuntimeProvider.js";
|
||||
import type { ToolRenderer } from "./types.js";
|
||||
export declare function executeJavaScript(code: string, runtimeProviders: SandboxRuntimeProvider[], signal?: AbortSignal, sandboxUrlProvider?: () => string): Promise<{
|
||||
output: string;
|
||||
files?: SandboxFile[];
|
||||
}>;
|
||||
export type JavaScriptReplToolResult = {
|
||||
files?: {
|
||||
fileName: string;
|
||||
contentBase64: string;
|
||||
mimeType: string;
|
||||
}[] | undefined;
|
||||
};
|
||||
declare const javascriptReplSchema: import("@sinclair/typebox").TObject<{
|
||||
title: import("@sinclair/typebox").TString;
|
||||
code: import("@sinclair/typebox").TString;
|
||||
}>;
|
||||
export type JavaScriptReplParams = Static<typeof javascriptReplSchema>;
|
||||
interface JavaScriptReplResult {
|
||||
output?: string;
|
||||
files?: Array<{
|
||||
fileName: string;
|
||||
mimeType: string;
|
||||
size: number;
|
||||
contentBase64: string;
|
||||
}>;
|
||||
}
|
||||
export declare function createJavaScriptReplTool(): AgentTool<typeof javascriptReplSchema, JavaScriptReplToolResult> & {
|
||||
runtimeProvidersFactory?: () => SandboxRuntimeProvider[];
|
||||
sandboxUrlProvider?: () => string;
|
||||
};
|
||||
export declare const javascriptReplTool: AgentTool<import("@sinclair/typebox").TObject<{
|
||||
title: import("@sinclair/typebox").TString;
|
||||
code: import("@sinclair/typebox").TString;
|
||||
}>, JavaScriptReplToolResult> & {
|
||||
runtimeProvidersFactory?: () => SandboxRuntimeProvider[];
|
||||
sandboxUrlProvider?: () => string;
|
||||
};
|
||||
export declare const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScriptReplResult>;
|
||||
export {};
|
||||
//# sourceMappingURL=javascript-repl.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"javascript-repl.d.ts","sourceRoot":"","sources":["../../src/tools/javascript-repl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAItD,OAAO,EAAE,KAAK,WAAW,EAAqC,MAAM,kCAAkC,CAAC;AACvG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iDAAiD,CAAC;AAI9F,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAGjE,wBAAsB,iBAAiB,CACtC,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,sBAAsB,EAAE,EAC1C,MAAM,CAAC,EAAE,WAAW,EACpB,kBAAkB,CAAC,EAAE,MAAM,MAAM,GAC/B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,CA2EpD;AAED,MAAM,MAAM,wBAAwB,GAAG;IACtC,KAAK,CAAC,EACH;QACA,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,QAAQ,EAAE,MAAM,CAAC;KAChB,EAAE,GACH,SAAS,CAAC;CACb,CAAC;AAEF,QAAA,MAAM,oBAAoB;;;EAMxB,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,UAAU,oBAAoB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;KACtB,CAAC,CAAC;CACH;AAED,wBAAgB,wBAAwB,IAAI,SAAS,CAAC,OAAO,oBAAoB,EAAE,wBAAwB,CAAC,GAAG;IAC9G,uBAAuB,CAAC,EAAE,MAAM,sBAAsB,EAAE,CAAC;IACzD,kBAAkB,CAAC,EAAE,MAAM,MAAM,CAAC;CAClC,CAgEA;AAGD,eAAO,MAAM,kBAAkB;;;;8BArEJ,MAAM,sBAAsB,EAAE;yBACnC,MAAM,MAAM;CAoE0B,CAAC;AAE7D,eAAO,MAAM,sBAAsB,EAAE,YAAY,CAAC,oBAAoB,EAAE,oBAAoB,CA0F3F,CAAC"}
|
||||
@@ -0,0 +1,224 @@
|
||||
import { i18n } from "@mariozechner/mini-lit";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { html } from "lit";
|
||||
import { createRef, ref } from "lit/directives/ref.js";
|
||||
import { Code } from "lucide";
|
||||
import { SandboxIframe } from "../components/SandboxedIframe.js";
|
||||
import { JAVASCRIPT_REPL_TOOL_DESCRIPTION } from "../prompts/prompts.js";
|
||||
import { registerToolRenderer, renderCollapsibleHeader, renderHeader } from "./renderer-registry.js";
|
||||
// Execute JavaScript code with attachments using SandboxedIframe
|
||||
export async function executeJavaScript(code, runtimeProviders, signal, sandboxUrlProvider) {
|
||||
if (!code) {
|
||||
throw new Error("Code parameter is required");
|
||||
}
|
||||
// Check for abort before starting
|
||||
if (signal?.aborted) {
|
||||
throw new Error("Execution aborted");
|
||||
}
|
||||
// Create a SandboxedIframe instance for execution
|
||||
const sandbox = new SandboxIframe();
|
||||
if (sandboxUrlProvider) {
|
||||
sandbox.sandboxUrlProvider = sandboxUrlProvider;
|
||||
}
|
||||
sandbox.style.display = "none";
|
||||
document.body.appendChild(sandbox);
|
||||
try {
|
||||
const sandboxId = `repl-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
||||
// Pass providers to execute (router handles all message routing)
|
||||
// No additional consumers needed - execute() has its own internal consumer
|
||||
const result = await sandbox.execute(sandboxId, code, runtimeProviders, [], signal);
|
||||
// Remove the sandbox iframe after execution
|
||||
sandbox.remove();
|
||||
// Build plain text response
|
||||
let output = "";
|
||||
// Add console output - result.console contains { type: string, text: string } from sandbox.js
|
||||
if (result.console && result.console.length > 0) {
|
||||
for (const entry of result.console) {
|
||||
output += entry.text + "\n";
|
||||
}
|
||||
}
|
||||
// Add error if execution failed
|
||||
if (!result.success) {
|
||||
if (output)
|
||||
output += "\n";
|
||||
output += `Error: ${result.error?.message || "Unknown error"}\n${result.error?.stack || ""}`;
|
||||
// Throw error so tool call is marked as failed
|
||||
throw new Error(output.trim());
|
||||
}
|
||||
// Add return value if present
|
||||
if (result.returnValue !== undefined) {
|
||||
if (output)
|
||||
output += "\n";
|
||||
output += `=> ${typeof result.returnValue === "object" ? JSON.stringify(result.returnValue, null, 2) : result.returnValue}`;
|
||||
}
|
||||
// Add file notifications
|
||||
if (result.files && result.files.length > 0) {
|
||||
output += `\n[Files returned: ${result.files.length}]\n`;
|
||||
for (const file of result.files) {
|
||||
output += ` - ${file.fileName} (${file.mimeType})\n`;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Explicitly note when no files were returned (helpful for debugging)
|
||||
if (code.includes("returnFile")) {
|
||||
output += "\n[No files returned - check async operations]";
|
||||
}
|
||||
}
|
||||
return {
|
||||
output: output.trim() || "Code executed successfully (no output)",
|
||||
files: result.files,
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
// Clean up on error
|
||||
sandbox.remove();
|
||||
throw new Error(error.message || "Execution failed");
|
||||
}
|
||||
}
|
||||
const javascriptReplSchema = Type.Object({
|
||||
title: Type.String({
|
||||
description: "Brief title describing what the code snippet tries to achieve in active form, e.g. 'Calculating sum'",
|
||||
}),
|
||||
code: Type.String({ description: "JavaScript code to execute" }),
|
||||
});
|
||||
export function createJavaScriptReplTool() {
|
||||
return {
|
||||
label: "JavaScript REPL",
|
||||
name: "javascript_repl",
|
||||
runtimeProvidersFactory: () => [], // default to empty array
|
||||
sandboxUrlProvider: undefined, // optional, for browser extensions
|
||||
get description() {
|
||||
const runtimeProviderDescriptions = this.runtimeProvidersFactory?.()
|
||||
.map((d) => d.getDescription())
|
||||
.filter((d) => d.trim().length > 0) || [];
|
||||
return JAVASCRIPT_REPL_TOOL_DESCRIPTION(runtimeProviderDescriptions);
|
||||
},
|
||||
parameters: javascriptReplSchema,
|
||||
execute: async function (_toolCallId, args, signal) {
|
||||
const result = await executeJavaScript(args.code, this.runtimeProvidersFactory?.() ?? [], signal, this.sandboxUrlProvider);
|
||||
// Convert files to JSON-serializable with base64 payloads
|
||||
const files = (result.files || []).map((f) => {
|
||||
const toBase64 = (input) => {
|
||||
if (input instanceof Uint8Array) {
|
||||
let binary = "";
|
||||
const chunk = 0x8000;
|
||||
for (let i = 0; i < input.length; i += chunk) {
|
||||
binary += String.fromCharCode(...input.subarray(i, i + chunk));
|
||||
}
|
||||
return { base64: btoa(binary), size: input.length };
|
||||
}
|
||||
else if (typeof input === "string") {
|
||||
const enc = new TextEncoder();
|
||||
const bytes = enc.encode(input);
|
||||
let binary = "";
|
||||
const chunk = 0x8000;
|
||||
for (let i = 0; i < bytes.length; i += chunk) {
|
||||
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
||||
}
|
||||
return { base64: btoa(binary), size: bytes.length };
|
||||
}
|
||||
else {
|
||||
const s = String(input);
|
||||
const enc = new TextEncoder();
|
||||
const bytes = enc.encode(s);
|
||||
let binary = "";
|
||||
const chunk = 0x8000;
|
||||
for (let i = 0; i < bytes.length; i += chunk) {
|
||||
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
||||
}
|
||||
return { base64: btoa(binary), size: bytes.length };
|
||||
}
|
||||
};
|
||||
const { base64, size } = toBase64(f.content);
|
||||
return {
|
||||
fileName: f.fileName || "file",
|
||||
mimeType: f.mimeType || "application/octet-stream",
|
||||
size,
|
||||
contentBase64: base64,
|
||||
};
|
||||
});
|
||||
return { content: [{ type: "text", text: result.output }], details: { files } };
|
||||
},
|
||||
};
|
||||
}
|
||||
// Export a default instance for backward compatibility
|
||||
export const javascriptReplTool = createJavaScriptReplTool();
|
||||
export const javascriptReplRenderer = {
|
||||
render(params, result, isStreaming) {
|
||||
// Determine status
|
||||
const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
|
||||
// Create refs for collapsible code section
|
||||
const codeContentRef = createRef();
|
||||
const codeChevronRef = createRef();
|
||||
// With result: show params + result
|
||||
if (result && params) {
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
const files = result.details?.files || [];
|
||||
const attachments = files.map((f, i) => {
|
||||
// Decode base64 content for text files to show in overlay
|
||||
let extractedText;
|
||||
const isTextBased = f.mimeType?.startsWith("text/") ||
|
||||
f.mimeType === "application/json" ||
|
||||
f.mimeType === "application/javascript" ||
|
||||
f.mimeType?.includes("xml");
|
||||
if (isTextBased && f.contentBase64) {
|
||||
try {
|
||||
extractedText = atob(f.contentBase64);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn("Failed to decode base64 content for", f.fileName);
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: `repl-${Date.now()}-${i}`,
|
||||
type: f.mimeType?.startsWith("image/") ? "image" : "document",
|
||||
fileName: f.fileName || `file-${i}`,
|
||||
mimeType: f.mimeType || "application/octet-stream",
|
||||
size: f.size ?? 0,
|
||||
content: f.contentBase64,
|
||||
preview: f.mimeType?.startsWith("image/") ? f.contentBase64 : undefined,
|
||||
extractedText,
|
||||
};
|
||||
});
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, Code, params.title ? params.title : i18n("Executing JavaScript"), codeContentRef, codeChevronRef, false)}
|
||||
<div ${ref(codeContentRef)} class="max-h-0 overflow-hidden transition-all duration-300 space-y-3">
|
||||
<code-block .code=${params.code || ""} language="javascript"></code-block>
|
||||
${output ? html `<console-block .content=${output} .variant=${result.isError ? "error" : "default"}></console-block>` : ""}
|
||||
</div>
|
||||
${attachments.length
|
||||
? html `<div class="flex flex-wrap gap-2 mt-3">
|
||||
${attachments.map((att) => html `<attachment-tile .attachment=${att}></attachment-tile>`)}
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Just params (streaming or waiting for result)
|
||||
if (params) {
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderCollapsibleHeader(state, Code, params.title ? params.title : i18n("Executing JavaScript"), codeContentRef, codeChevronRef, false)}
|
||||
<div ${ref(codeContentRef)} class="max-h-0 overflow-hidden transition-all duration-300">
|
||||
${params.code ? html `<code-block .code=${params.code} language="javascript"></code-block>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// No params or result yet
|
||||
return { content: renderHeader(state, Code, i18n("Preparing JavaScript...")), isCustom: false };
|
||||
},
|
||||
};
|
||||
// Auto-register the renderer
|
||||
registerToolRenderer(javascriptReplTool.name, javascriptReplRenderer);
|
||||
//# sourceMappingURL=javascript-repl.js.map
|
||||
File diff suppressed because one or more lines are too long
23
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderer-registry.d.ts
vendored
Normal file
23
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderer-registry.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { type TemplateResult } from "lit";
|
||||
import type { Ref } from "lit/directives/ref.js";
|
||||
import type { ToolRenderer } from "./types.js";
|
||||
export declare const toolRenderers: Map<string, ToolRenderer<any, any>>;
|
||||
/**
|
||||
* Register a custom tool renderer
|
||||
*/
|
||||
export declare function registerToolRenderer(toolName: string, renderer: ToolRenderer): void;
|
||||
/**
|
||||
* Get a tool renderer by name
|
||||
*/
|
||||
export declare function getToolRenderer(toolName: string): ToolRenderer | undefined;
|
||||
/**
|
||||
* Helper to render a header for tool renderers
|
||||
* Shows icon on left when complete/error, spinner on right when in progress
|
||||
*/
|
||||
export declare function renderHeader(state: "inprogress" | "complete" | "error", toolIcon: any, text: string | TemplateResult): TemplateResult;
|
||||
/**
|
||||
* Helper to render a collapsible header for tool renderers
|
||||
* Same as renderHeader but with a chevron button that toggles visibility of content
|
||||
*/
|
||||
export declare function renderCollapsibleHeader(state: "inprogress" | "complete" | "error", toolIcon: any, text: string | TemplateResult, contentRef: Ref<HTMLElement>, chevronRef: Ref<HTMLElement>, defaultExpanded?: boolean): TemplateResult;
|
||||
//# sourceMappingURL=renderer-registry.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"renderer-registry.d.ts","sourceRoot":"","sources":["../../src/tools/renderer-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAChD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAGjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,eAAO,MAAM,aAAa,qCAAkC,CAAC;AAE7D;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAEnF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,EAC1C,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,MAAM,GAAG,cAAc,GAC3B,cAAc,CA8BhB;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,EAC1C,QAAQ,EAAE,GAAG,EACb,IAAI,EAAE,MAAM,GAAG,cAAc,EAC7B,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,EAC5B,UAAU,EAAE,GAAG,CAAC,WAAW,CAAC,EAC5B,eAAe,UAAQ,GACrB,cAAc,CAsDhB"}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { icon } from "@mariozechner/mini-lit";
|
||||
import { html } from "lit";
|
||||
import { ref } from "lit/directives/ref.js";
|
||||
import { ChevronsUpDown, ChevronUp, Loader } from "lucide";
|
||||
// Registry of tool renderers
|
||||
export const toolRenderers = new Map();
|
||||
/**
|
||||
* Register a custom tool renderer
|
||||
*/
|
||||
export function registerToolRenderer(toolName, renderer) {
|
||||
toolRenderers.set(toolName, renderer);
|
||||
}
|
||||
/**
|
||||
* Get a tool renderer by name
|
||||
*/
|
||||
export function getToolRenderer(toolName) {
|
||||
return toolRenderers.get(toolName);
|
||||
}
|
||||
/**
|
||||
* Helper to render a header for tool renderers
|
||||
* Shows icon on left when complete/error, spinner on right when in progress
|
||||
*/
|
||||
export function renderHeader(state, toolIcon, text) {
|
||||
const statusIcon = (iconComponent, color) => html `<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
|
||||
switch (state) {
|
||||
case "inprogress":
|
||||
return html `
|
||||
<div class="flex items-center justify-between gap-2 text-sm text-muted-foreground">
|
||||
<div class="flex items-center gap-2">
|
||||
${statusIcon(toolIcon, "text-foreground")}
|
||||
${text}
|
||||
</div>
|
||||
${statusIcon(Loader, "text-foreground animate-spin")}
|
||||
</div>
|
||||
`;
|
||||
case "complete":
|
||||
return html `
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
${statusIcon(toolIcon, "text-green-600 dark:text-green-500")}
|
||||
${text}
|
||||
</div>
|
||||
`;
|
||||
case "error":
|
||||
return html `
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
${statusIcon(toolIcon, "text-destructive")}
|
||||
${text}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Helper to render a collapsible header for tool renderers
|
||||
* Same as renderHeader but with a chevron button that toggles visibility of content
|
||||
*/
|
||||
export function renderCollapsibleHeader(state, toolIcon, text, contentRef, chevronRef, defaultExpanded = false) {
|
||||
const statusIcon = (iconComponent, color) => html `<span class="inline-block ${color}">${icon(iconComponent, "sm")}</span>`;
|
||||
const toggleContent = (e) => {
|
||||
e.preventDefault();
|
||||
const content = contentRef.value;
|
||||
const chevron = chevronRef.value;
|
||||
if (content && chevron) {
|
||||
const isCollapsed = content.classList.contains("max-h-0");
|
||||
if (isCollapsed) {
|
||||
content.classList.remove("max-h-0");
|
||||
content.classList.add("max-h-[2000px]", "mt-3");
|
||||
// Show ChevronUp, hide ChevronsUpDown
|
||||
const upIcon = chevron.querySelector(".chevron-up");
|
||||
const downIcon = chevron.querySelector(".chevrons-up-down");
|
||||
if (upIcon && downIcon) {
|
||||
upIcon.classList.remove("hidden");
|
||||
downIcon.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
else {
|
||||
content.classList.remove("max-h-[2000px]", "mt-3");
|
||||
content.classList.add("max-h-0");
|
||||
// Show ChevronsUpDown, hide ChevronUp
|
||||
const upIcon = chevron.querySelector(".chevron-up");
|
||||
const downIcon = chevron.querySelector(".chevrons-up-down");
|
||||
if (upIcon && downIcon) {
|
||||
upIcon.classList.add("hidden");
|
||||
downIcon.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const toolIconColor = state === "complete"
|
||||
? "text-green-600 dark:text-green-500"
|
||||
: state === "error"
|
||||
? "text-destructive"
|
||||
: "text-foreground";
|
||||
return html `
|
||||
<button @click=${toggleContent} class="flex items-center justify-between gap-2 text-sm text-muted-foreground w-full text-left hover:text-foreground transition-colors cursor-pointer">
|
||||
<div class="flex items-center gap-2">
|
||||
${state === "inprogress" ? statusIcon(Loader, "text-foreground animate-spin") : ""}
|
||||
${statusIcon(toolIcon, toolIconColor)}
|
||||
${text}
|
||||
</div>
|
||||
<span class="inline-block text-muted-foreground" ${ref(chevronRef)}>
|
||||
<span class="chevron-up ${defaultExpanded ? "" : "hidden"}">${icon(ChevronUp, "sm")}</span>
|
||||
<span class="chevrons-up-down ${defaultExpanded ? "hidden" : ""}">${icon(ChevronsUpDown, "sm")}</span>
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
//# sourceMappingURL=renderer-registry.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"renderer-registry.js","sourceRoot":"","sources":["../../src/tools/renderer-registry.ts"],"names":[],"mappings":"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"}
|
||||
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/BashRenderer.d.ts
vendored
Normal file
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/BashRenderer.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { ToolRenderer, ToolRenderResult } from "../types.js";
|
||||
interface BashParams {
|
||||
command: string;
|
||||
}
|
||||
export declare class BashRenderer implements ToolRenderer<BashParams, undefined> {
|
||||
render(params: BashParams | undefined, result: ToolResultMessage<undefined> | undefined): ToolRenderResult;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=BashRenderer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BashRenderer.d.ts","sourceRoot":"","sources":["../../../src/tools/renderers/BashRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,UAAU,UAAU;IACnB,OAAO,EAAE,MAAM,CAAC;CAChB;AAGD,qBAAa,YAAa,YAAW,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC;IACvE,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,gBAAgB;CAsC1G"}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { html } from "lit";
|
||||
import { SquareTerminal } from "lucide";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { renderHeader } from "../renderer-registry.js";
|
||||
// Bash tool has undefined details (only uses output)
|
||||
export class BashRenderer {
|
||||
render(params, result) {
|
||||
const state = result ? (result.isError ? "error" : "complete") : "inprogress";
|
||||
// With result: show command + output
|
||||
if (result && params?.command) {
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
const combined = output ? `> ${params.command}\n\n${output}` : `> ${params.command}`;
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, SquareTerminal, i18n("Running command..."))}
|
||||
<console-block .content=${combined} .variant=${result.isError ? "error" : "default"}></console-block>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Just params (streaming or waiting)
|
||||
if (params?.command) {
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, SquareTerminal, i18n("Running command..."))}
|
||||
<console-block .content=${`> ${params.command}`}></console-block>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// No params yet
|
||||
return { content: renderHeader(state, SquareTerminal, i18n("Waiting for command...")), isCustom: false };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=BashRenderer.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"BashRenderer.js","sourceRoot":"","sources":["../../../src/tools/renderers/BashRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAOvD,qDAAqD;AACrD,MAAM,OAAO,YAAY;IACxB,MAAM,CAAC,MAA8B,EAAE,MAAgD;QACtF,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;QAE9E,qCAAqC;QACrC,IAAI,MAAM,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YAC/B,MAAM,MAAM,GACX,MAAM,CAAC,OAAO;gBACb,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;iBACjC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;YACrF,OAAO;gBACN,OAAO,EAAE,IAAI,CAAA;;QAET,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;gCACvC,QAAQ,aAAa,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;;KAEpF;gBACD,QAAQ,EAAE,KAAK;aACf,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACrB,OAAO;gBACN,OAAO,EAAE,IAAI,CAAA;;QAET,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;gCACvC,KAAK,MAAM,CAAC,OAAO,EAAE;;KAEhD;gBACD,QAAQ,EAAE,KAAK;aACf,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC1G,CAAC;CACD"}
|
||||
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/CalculateRenderer.d.ts
vendored
Normal file
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/CalculateRenderer.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { ToolRenderer, ToolRenderResult } from "../types.js";
|
||||
interface CalculateParams {
|
||||
expression: string;
|
||||
}
|
||||
export declare class CalculateRenderer implements ToolRenderer<CalculateParams, undefined> {
|
||||
render(params: CalculateParams | undefined, result: ToolResultMessage<undefined> | undefined): ToolRenderResult;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=CalculateRenderer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CalculateRenderer.d.ts","sourceRoot":"","sources":["../../../src/tools/renderers/CalculateRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,UAAU,eAAe;IACxB,UAAU,EAAE,MAAM,CAAC;CACnB;AAGD,qBAAa,iBAAkB,YAAW,YAAY,CAAC,eAAe,EAAE,SAAS,CAAC;IACjF,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,EAAE,MAAM,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,gBAAgB;CA4C/G"}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { html } from "lit";
|
||||
import { Calculator } from "lucide";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { renderHeader } from "../renderer-registry.js";
|
||||
// Calculate tool has undefined details (only uses output)
|
||||
export class CalculateRenderer {
|
||||
render(params, result) {
|
||||
const state = result ? (result.isError ? "error" : "complete") : "inprogress";
|
||||
// Full params + full result
|
||||
if (result && params?.expression) {
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
// Error: show expression in header, error below
|
||||
if (result.isError) {
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, Calculator, params.expression)}
|
||||
<div class="text-sm text-destructive">${output}</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Success: show expression = result in header
|
||||
return { content: renderHeader(state, Calculator, `${params.expression} = ${output}`), isCustom: false };
|
||||
}
|
||||
// Full params, no result: just show header with expression in it
|
||||
if (params?.expression) {
|
||||
return {
|
||||
content: renderHeader(state, Calculator, `${i18n("Calculating")} ${params.expression}`),
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Partial params (empty expression), no result
|
||||
if (params && !params.expression) {
|
||||
return { content: renderHeader(state, Calculator, i18n("Writing expression...")), isCustom: false };
|
||||
}
|
||||
// No params, no result
|
||||
return { content: renderHeader(state, Calculator, i18n("Waiting for expression...")), isCustom: false };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=CalculateRenderer.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CalculateRenderer.js","sourceRoot":"","sources":["../../../src/tools/renderers/CalculateRenderer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAOvD,0DAA0D;AAC1D,MAAM,OAAO,iBAAiB;IAC7B,MAAM,CAAC,MAAmC,EAAE,MAAgD;QAC3F,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;QAE9E,4BAA4B;QAC5B,IAAI,MAAM,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC;YAClC,MAAM,MAAM,GACX,MAAM,CAAC,OAAO;gBACb,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;iBACjC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;iBACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAEpB,gDAAgD;YAChD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,OAAO;oBACN,OAAO,EAAE,IAAI,CAAA;;SAET,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;+CACZ,MAAM;;MAE/C;oBACD,QAAQ,EAAE,KAAK;iBACf,CAAC;YACH,CAAC;YAED,8CAA8C;YAC9C,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC,UAAU,MAAM,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QAC1G,CAAC;QAED,iEAAiE;QACjE,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC;YACxB,OAAO;gBACN,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACvF,QAAQ,EAAE,KAAK;aACf,CAAC;QACH,CAAC;QAED,+CAA+C;QAC/C,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAClC,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACrG,CAAC;QAED,uBAAuB;QACvB,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,2BAA2B,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACzG,CAAC;CACD"}
|
||||
6
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/DefaultRenderer.d.ts
vendored
Normal file
6
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/DefaultRenderer.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { ToolRenderer, ToolRenderResult } from "../types.js";
|
||||
export declare class DefaultRenderer implements ToolRenderer {
|
||||
render(params: any | undefined, result: ToolResultMessage | undefined, isStreaming?: boolean): ToolRenderResult;
|
||||
}
|
||||
//# sourceMappingURL=DefaultRenderer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultRenderer.d.ts","sourceRoot":"","sources":["../../../src/tools/renderers/DefaultRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,qBAAa,eAAgB,YAAW,YAAY;IACnD,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,SAAS,EAAE,MAAM,EAAE,iBAAiB,GAAG,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,gBAAgB;CA8F/G"}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { html } from "lit";
|
||||
import { Code } from "lucide";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { renderHeader } from "../renderer-registry.js";
|
||||
export class DefaultRenderer {
|
||||
render(params, result, isStreaming) {
|
||||
const state = result ? (result.isError ? "error" : "complete") : isStreaming ? "inprogress" : "complete";
|
||||
// Format params as JSON
|
||||
let paramsJson = "";
|
||||
if (params) {
|
||||
try {
|
||||
paramsJson = JSON.stringify(JSON.parse(params), null, 2);
|
||||
}
|
||||
catch {
|
||||
try {
|
||||
paramsJson = JSON.stringify(params, null, 2);
|
||||
}
|
||||
catch {
|
||||
paramsJson = String(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
// With result: show header + params + result
|
||||
if (result) {
|
||||
let outputJson = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || i18n("(no output)");
|
||||
let outputLanguage = "text";
|
||||
// Try to parse and pretty-print if it's valid JSON
|
||||
try {
|
||||
const parsed = JSON.parse(outputJson);
|
||||
outputJson = JSON.stringify(parsed, null, 2);
|
||||
outputLanguage = "json";
|
||||
}
|
||||
catch {
|
||||
// Not valid JSON, leave as-is and use text highlighting
|
||||
}
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, Code, "Tool Call")}
|
||||
${paramsJson
|
||||
? html `<div>
|
||||
<div class="text-xs font-medium mb-1 text-muted-foreground">${i18n("Input")}</div>
|
||||
<code-block .code=${paramsJson} language="json"></code-block>
|
||||
</div>`
|
||||
: ""}
|
||||
<div>
|
||||
<div class="text-xs font-medium mb-1 text-muted-foreground">${i18n("Output")}</div>
|
||||
<code-block .code=${outputJson} language="${outputLanguage}"></code-block>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Just params (streaming or waiting for result)
|
||||
if (params) {
|
||||
if (isStreaming && (!paramsJson || paramsJson === "{}" || paramsJson === "null")) {
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderHeader(state, Code, "Preparing tool parameters...")}
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, Code, "Tool Call")}
|
||||
<div>
|
||||
<div class="text-xs font-medium mb-1 text-muted-foreground">${i18n("Input")}</div>
|
||||
<code-block .code=${paramsJson} language="json"></code-block>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// No params or result yet
|
||||
return {
|
||||
content: html `
|
||||
<div>
|
||||
${renderHeader(state, Code, "Preparing tool...")}
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=DefaultRenderer.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DefaultRenderer.js","sourceRoot":"","sources":["../../../src/tools/renderers/DefaultRenderer.ts"],"names":[],"mappings":"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"}
|
||||
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/GetCurrentTimeRenderer.d.ts
vendored
Normal file
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/renderers/GetCurrentTimeRenderer.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { ToolRenderer, ToolRenderResult } from "../types.js";
|
||||
interface GetCurrentTimeParams {
|
||||
timezone?: string;
|
||||
}
|
||||
export declare class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams, undefined> {
|
||||
render(params: GetCurrentTimeParams | undefined, result: ToolResultMessage<undefined> | undefined): ToolRenderResult;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=GetCurrentTimeRenderer.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GetCurrentTimeRenderer.d.ts","sourceRoot":"","sources":["../../../src/tools/renderers/GetCurrentTimeRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAK7D,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElE,UAAU,oBAAoB;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,qBAAa,sBAAuB,YAAW,YAAY,CAAC,oBAAoB,EAAE,SAAS,CAAC;IAC3F,MAAM,CACL,MAAM,EAAE,oBAAoB,GAAG,SAAS,EACxC,MAAM,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,SAAS,GAC9C,gBAAgB;CA2EnB"}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { html } from "lit";
|
||||
import { Clock } from "lucide";
|
||||
import { i18n } from "../../utils/i18n.js";
|
||||
import { renderHeader } from "../renderer-registry.js";
|
||||
// GetCurrentTime tool has undefined details (only uses output)
|
||||
export class GetCurrentTimeRenderer {
|
||||
render(params, result) {
|
||||
const state = result ? (result.isError ? "error" : "complete") : "inprogress";
|
||||
// Full params + full result
|
||||
if (result && params) {
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
const headerText = params.timezone
|
||||
? `${i18n("Getting current time in")} ${params.timezone}`
|
||||
: i18n("Getting current date and time");
|
||||
// Error: show header, error below
|
||||
if (result.isError) {
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, Clock, headerText)}
|
||||
<div class="text-sm text-destructive">${output}</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Success: show time in header
|
||||
return { content: renderHeader(state, Clock, `${headerText}: ${output}`), isCustom: false };
|
||||
}
|
||||
// Full result, no params
|
||||
if (result) {
|
||||
const output = result.content
|
||||
?.filter((c) => c.type === "text")
|
||||
.map((c) => c.text)
|
||||
.join("\n") || "";
|
||||
// Error: show header, error below
|
||||
if (result.isError) {
|
||||
return {
|
||||
content: html `
|
||||
<div class="space-y-3">
|
||||
${renderHeader(state, Clock, i18n("Getting current date and time"))}
|
||||
<div class="text-sm text-destructive">${output}</div>
|
||||
</div>
|
||||
`,
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Success: show time in header
|
||||
return {
|
||||
content: renderHeader(state, Clock, `${i18n("Getting current date and time")}: ${output}`),
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Full params, no result: show timezone info in header
|
||||
if (params?.timezone) {
|
||||
return {
|
||||
content: renderHeader(state, Clock, `${i18n("Getting current time in")} ${params.timezone}`),
|
||||
isCustom: false,
|
||||
};
|
||||
}
|
||||
// Partial params (no timezone) or empty params, no result
|
||||
if (params) {
|
||||
return { content: renderHeader(state, Clock, i18n("Getting current date and time")), isCustom: false };
|
||||
}
|
||||
// No params, no result
|
||||
return { content: renderHeader(state, Clock, i18n("Getting time...")), isCustom: false };
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=GetCurrentTimeRenderer.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"GetCurrentTimeRenderer.js","sourceRoot":"","sources":["../../../src/tools/renderers/GetCurrentTimeRenderer.ts"],"names":[],"mappings":"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"}
|
||||
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/types.d.ts
vendored
Normal file
10
apps/macos/Sources/Clawdis/Resources/WebChat/tools/types.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
||||
import type { TemplateResult } from "lit";
|
||||
export interface ToolRenderResult {
|
||||
content: TemplateResult;
|
||||
isCustom: boolean;
|
||||
}
|
||||
export interface ToolRenderer<TParams = any, TDetails = any> {
|
||||
render(params: TParams | undefined, result: ToolResultMessage<TDetails> | undefined, isStreaming?: boolean): ToolRenderResult;
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY,CAAC,OAAO,GAAG,GAAG,EAAE,QAAQ,GAAG,GAAG;IAC1D,MAAM,CACL,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,SAAS,EAC/C,WAAW,CAAC,EAAE,OAAO,GACnB,gBAAgB,CAAC;CACpB"}
|
||||
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":""}
|
||||
Reference in New Issue
Block a user