blob: d69ec6b1c7432470a35df357e56f35fed0158d13 [file] [log] [blame]
Pokey Ruleef58e062025-05-07 13:32:58 +01001import { LitElement, css, html } from "lit";
2import { customElement, property } from "lit/decorators.js";
3import { ToolCall, MultipleChoiceOption, MultipleChoiceParams } from "../types";
4
5@customElement("sketch-tool-card-multiple-choice")
6export class SketchToolCardMultipleChoice extends LitElement {
7 @property() toolCall: ToolCall;
8 @property() open: boolean;
9 @property() selectedOption: MultipleChoiceOption = null;
10
11 static styles = css`
12 .options-container {
13 display: flex;
14 flex-direction: row;
15 flex-wrap: wrap;
16 gap: 8px;
17 margin: 10px 0;
18 }
19 .option {
20 display: inline-flex;
21 align-items: center;
22 padding: 8px 12px;
23 border-radius: 4px;
24 background-color: #f5f5f5;
25 cursor: pointer;
26 transition: all 0.2s;
27 border: 1px solid transparent;
28 user-select: none;
29 }
30 .option:hover {
31 background-color: #e0e0e0;
32 border-color: #ccc;
33 transform: translateY(-1px);
34 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
35 }
36 .option:active {
37 transform: translateY(0);
38 box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
39 background-color: #d5d5d5;
40 }
41 .option.selected {
42 background-color: #e3f2fd;
43 border-color: #2196f3;
44 border-width: 1px;
45 border-style: solid;
46 }
47 .option-checkmark {
48 margin-left: 6px;
49 color: #2196f3;
50 }
51 .summary-text {
52 font-style: italic;
53 padding: 0.5em;
54 }
55 .summary-text strong {
56 font-style: normal;
57 color: #2196f3;
58 font-weight: 600;
59 }
60 `;
61
62 connectedCallback() {
63 super.connectedCallback();
64 this.updateSelectedOption();
65 }
66
67 updated(changedProps) {
68 if (changedProps.has("toolCall")) {
69 this.updateSelectedOption();
70 }
71 }
72
73 updateSelectedOption() {
74 if (this.toolCall?.result_message?.tool_result) {
75 try {
76 this.selectedOption = JSON.parse(
77 this.toolCall.result_message.tool_result,
78 ).selected;
79 } catch (e) {
80 console.error("Error parsing result:", e);
81 }
82 } else {
83 this.selectedOption = null;
84 }
85 }
86
87 async handleOptionClick(choice) {
88 this.selectedOption = this.selectedOption === choice ? null : choice;
89
90 const event = new CustomEvent("multiple-choice-selected", {
91 detail: {
92 responseText: this.selectedOption.responseText,
93 toolCall: this.toolCall,
94 },
95 bubbles: true,
96 composed: true,
97 });
98 this.dispatchEvent(event);
99 }
100
101 render() {
102 let choices = [];
103 let question = "";
104 try {
105 const inputData = JSON.parse(
106 this.toolCall?.input || "{}",
107 ) as MultipleChoiceParams;
108 choices = inputData.responseOptions || [];
109 question = inputData.question || "Please select an option:";
110 } catch (e) {
111 console.error("Error parsing multiple-choice input:", e);
112 }
113
114 const summaryContent =
115 this.selectedOption !== null
116 ? html`<span class="summary-text">
117 ${question}: <strong>${this.selectedOption.caption}</strong>
118 </span>`
119 : html`<span class="summary-text">${question}</span>`;
120
121 return html`
122 <div class="multiple-choice-card">
123 ${summaryContent}
124 <div class="options-container">
125 ${choices.map((choice) => {
126 const isSelected =
127 this.selectedOption !== null && this.selectedOption === choice;
128 return html`
129 <div
130 class="option ${isSelected ? "selected" : ""}"
131 @click=${() => this.handleOptionClick(choice)}
132 title="${choice.responseText}"
133 >
134 <span class="option-label">${choice.caption}</span>
135 ${isSelected
136 ? html`<span class="option-checkmark">✓</span>`
137 : ""}
138 </div>
139 `;
140 })}
141 </div>
142 </div>
143 `;
144 }
145}
146
147declare global {
148 interface HTMLElementTagNameMap {
149 "sketch-tool-card-multiple-choice": SketchToolCardMultipleChoice;
150 }
151}