blob: e47e4ea0f1bcc242bddabd5f61e49b0849476c40 [file] [log] [blame]
Sean McCullough86b56862025-04-18 13:04:03 -07001import { css, html, LitElement } from "lit";
Philip Zeyliger16fa8b42025-05-02 04:28:16 +00002import { customElement, property, state } from "lit/decorators.js";
Sean McCullough2deac842025-04-21 18:17:57 -07003import { repeat } from "lit/directives/repeat.js";
Sean McCulloughd9f13372025-04-21 15:08:49 -07004import { ToolCall } from "../types";
Sean McCulloughec3ad1a2025-04-18 13:55:16 -07005import "./sketch-tool-card";
Sean McCullough86b56862025-04-18 13:04:03 -07006
7@customElement("sketch-tool-calls")
8export class SketchToolCalls extends LitElement {
9 @property()
10 toolCalls: ToolCall[] = [];
11
Sean McCullough2deac842025-04-21 18:17:57 -070012 @property()
13 open: boolean = false;
14
Philip Zeyliger16fa8b42025-05-02 04:28:16 +000015 @state()
16 expanded: boolean = false;
17
Sean McCullough86b56862025-04-18 13:04:03 -070018 static styles = css`
19 /* Tool calls container styles */
20 .tool-calls-container {
Philip Zeyliger16fa8b42025-05-02 04:28:16 +000021 margin-top: 8px;
22 padding-top: 4px;
Sean McCullough86b56862025-04-18 13:04:03 -070023 }
24
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070025 /* Card container */
Sean McCullough86b56862025-04-18 13:04:03 -070026 .tool-call-card {
27 display: flex;
28 flex-direction: column;
Philip Zeyliger16fa8b42025-05-02 04:28:16 +000029 background-color: rgba(255, 255, 255, 0.6);
30 border-radius: 6px;
31 margin-bottom: 6px;
Sean McCullough86b56862025-04-18 13:04:03 -070032 overflow: hidden;
33 cursor: pointer;
Philip Zeyliger16fa8b42025-05-02 04:28:16 +000034 border-left: 2px solid rgba(0, 0, 0, 0.1);
35 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
Sean McCullough86b56862025-04-18 13:04:03 -070036 }
37
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070038 /* Status indicators for tool calls */
Sean McCullough86b56862025-04-18 13:04:03 -070039 .tool-call-status {
40 margin-right: 4px;
41 text-align: center;
42 }
43
44 .tool-call-status.spinner {
45 animation: spin 1s infinite linear;
46 display: inline-block;
47 width: 1em;
48 }
49
Sean McCullough86b56862025-04-18 13:04:03 -070050 @keyframes spin {
51 0% {
52 transform: rotate(0deg);
53 }
54 100% {
55 transform: rotate(360deg);
56 }
57 }
Philip Zeyliger16fa8b42025-05-02 04:28:16 +000058
59 .tool-call-cards-container {
60 display: block;
61 }
Sean McCullough86b56862025-04-18 13:04:03 -070062 `;
63
64 constructor() {
65 super();
66 }
67
Sean McCullough86b56862025-04-18 13:04:03 -070068 connectedCallback() {
69 super.connectedCallback();
70 }
71
Sean McCullough86b56862025-04-18 13:04:03 -070072 disconnectedCallback() {
73 super.disconnectedCallback();
74 }
75
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070076 cardForToolCall(toolCall: ToolCall, open: boolean) {
Sean McCullough86b56862025-04-18 13:04:03 -070077 switch (toolCall.name) {
Sean McCullough86b56862025-04-18 13:04:03 -070078 case "bash":
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070079 return html`<sketch-tool-card-bash
80 .open=${open}
81 .toolCall=${toolCall}
82 ></sketch-tool-card-bash>`;
Sean McCullough86b56862025-04-18 13:04:03 -070083 case "codereview":
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070084 return html`<sketch-tool-card-codereview
85 .open=${open}
86 .toolCall=${toolCall}
87 ></sketch-tool-card-codereview>`;
Sean McCullough86b56862025-04-18 13:04:03 -070088 case "done":
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070089 return html`<sketch-tool-card-done
90 .open=${open}
91 .toolCall=${toolCall}
92 ></sketch-tool-card-done>`;
Sean McCullough485afc62025-04-28 14:28:39 -070093 case "multiplechoice":
94 return html`<sketch-tool-card-multiple-choice
95 .open=${open}
96 .toolCall=${toolCall}
97 ></sketch-tool-card-multiple-choice>`;
Sean McCulloughec3ad1a2025-04-18 13:55:16 -070098 case "patch":
99 return html`<sketch-tool-card-patch
100 .open=${open}
101 .toolCall=${toolCall}
102 ></sketch-tool-card-patch>`;
103 case "think":
104 return html`<sketch-tool-card-think
105 .open=${open}
106 .toolCall=${toolCall}
107 ></sketch-tool-card-think>`;
108 case "title":
109 return html`<sketch-tool-card-title
110 .open=${open}
111 .toolCall=${toolCall}
112 ></sketch-tool-card-title>`;
Sean McCullough86b56862025-04-18 13:04:03 -0700113 }
Sean McCulloughec3ad1a2025-04-18 13:55:16 -0700114 return html`<sketch-tool-card-generic
115 .open=${open}
116 .toolCall=${toolCall}
117 ></sketch-tool-card-generic>`;
Sean McCullough86b56862025-04-18 13:04:03 -0700118 }
Sean McCulloughec3ad1a2025-04-18 13:55:16 -0700119
Sean McCullough2deac842025-04-21 18:17:57 -0700120 // toolUseKey return value should change, if the toolCall gets a response.
121 toolUseKey(toolCall: ToolCall): string {
Sean McCullough2deac842025-04-21 18:17:57 -0700122 if (!toolCall.result_message) {
123 return toolCall.tool_call_id;
124 }
125 return `${toolCall.tool_call_id}-${toolCall.result_message.idx}`;
126 }
127
Sean McCullough86b56862025-04-18 13:04:03 -0700128 render() {
Philip Zeyliger16fa8b42025-05-02 04:28:16 +0000129 if (!this.toolCalls || this.toolCalls.length === 0) {
130 return html``;
131 }
132
Sean McCulloughec3ad1a2025-04-18 13:55:16 -0700133 return html`<div class="tool-calls-container">
Sean McCullough86b56862025-04-18 13:04:03 -0700134 <div class="tool-call-cards-container">
Philip Zeyliger16fa8b42025-05-02 04:28:16 +0000135 ${repeat(this.toolCalls, this.toolUseKey, (toolCall, idx) => {
136 let lastCall = false;
137 if (idx == this.toolCalls?.length - 1) {
138 lastCall = true;
139 }
140 return html`<div
141 id="${toolCall.tool_call_id}"
142 class="tool-call-card ${toolCall.name}"
143 >
144 ${this.cardForToolCall(toolCall, lastCall && this.open)}
145 </div>`;
146 })}
Sean McCullough86b56862025-04-18 13:04:03 -0700147 </div>
148 </div>`;
149 }
150}
151
152declare global {
153 interface HTMLElementTagNameMap {
154 "sketch-tool-calls": SketchToolCalls;
155 }
156}