Browser tools: initialize lazily and add timeouts.
Also rename browser_screenshot to browser_take_screenshot for clarity\n- Update both Go and UI code to maintain consistency
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s8a5cabff914f88dfk
diff --git a/webui/src/web-components/sketch-tool-card-take-screenshot.ts b/webui/src/web-components/sketch-tool-card-take-screenshot.ts
new file mode 100644
index 0000000..33bfae3
--- /dev/null
+++ b/webui/src/web-components/sketch-tool-card-take-screenshot.ts
@@ -0,0 +1,158 @@
+import { css, html, LitElement } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import { ToolCall } from "../types";
+
+@customElement("sketch-tool-card-take-screenshot")
+export class SketchToolCardTakeScreenshot extends LitElement {
+ @property()
+ toolCall: ToolCall;
+
+ @property()
+ open: boolean;
+
+ @state()
+ imageLoaded: boolean = false;
+
+ @state()
+ loadError: boolean = false;
+
+ static styles = css`
+ .summary-text {
+ font-style: italic;
+ padding: 0.5em;
+ }
+
+ .screenshot-container {
+ margin: 10px 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .screenshot {
+ max-width: 100%;
+ max-height: 500px;
+ border-radius: 4px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+ border: 1px solid #ddd;
+ }
+
+ .loading-indicator {
+ margin: 20px;
+ color: #666;
+ font-style: italic;
+ }
+
+ .error-message {
+ color: #d32f2f;
+ font-style: italic;
+ margin: 10px 0;
+ }
+
+ .screenshot-info {
+ margin-top: 8px;
+ font-size: 12px;
+ color: #666;
+ }
+
+ .selector-info {
+ padding: 4px 8px;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ font-family: monospace;
+ margin: 5px 0;
+ display: inline-block;
+ }
+ `;
+
+ constructor() {
+ super();
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ }
+
+ render() {
+ // Parse the input to get selector
+ let selector = "";
+ try {
+ if (this.toolCall?.input) {
+ const input = JSON.parse(this.toolCall.input);
+ selector = input.selector || "(full page)";
+ }
+ } catch (e) {
+ console.error("Error parsing screenshot input:", e);
+ }
+
+ // Get the screenshot ID from the result
+ let screenshotId = "";
+ let hasResult = false;
+ if (this.toolCall?.result_message?.tool_result) {
+ try {
+ const result = JSON.parse(this.toolCall.result_message.tool_result);
+ screenshotId = result.id;
+ hasResult = true;
+ } catch (e) {
+ console.error("Error parsing screenshot result:", e);
+ }
+ }
+
+ // Construct the URL for the screenshot (using relative URL without leading slash)
+ const screenshotUrl = screenshotId ? `screenshot/${screenshotId}` : "";
+
+ return html`
+ <sketch-tool-card .open=${this.open} .toolCall=${this.toolCall}>
+ <span slot="summary" class="summary-text">
+ Screenshot of ${selector}
+ </span>
+ <div slot="input" class="selector-info">
+ ${selector !== "(full page)"
+ ? `Taking screenshot of element: ${selector}`
+ : `Taking full page screenshot`}
+ </div>
+ <div slot="result">
+ ${hasResult
+ ? html`
+ <div class="screenshot-container">
+ ${!this.imageLoaded && !this.loadError
+ ? html`<div class="loading-indicator">
+ Loading screenshot...
+ </div>`
+ : ""}
+ ${this.loadError
+ ? html`<div class="error-message">
+ Failed to load screenshot
+ </div>`
+ : html`
+ <img
+ class="screenshot"
+ src="${screenshotUrl}"
+ @load=${() => (this.imageLoaded = true)}
+ @error=${() => (this.loadError = true)}
+ ?hidden=${!this.imageLoaded}
+ />
+ ${this.imageLoaded
+ ? html`<div class="screenshot-info">
+ Screenshot ID: ${screenshotId}
+ </div>`
+ : ""}
+ `}
+ </div>
+ `
+ : ""}
+ </div>
+ </sketch-tool-card>
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "sketch-tool-card-take-screenshot": SketchToolCardTakeScreenshot;
+ }
+}