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":"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"}
|
||||
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"}
|
||||
Reference in New Issue
Block a user