| Pokey Rule | ef58e06 | 2025-05-07 13:32:58 +0100 | [diff] [blame^] | 1 | import { LitElement, css, html } from "lit"; |
| 2 | import { customElement, property } from "lit/decorators.js"; |
| 3 | import { ToolCall, MultipleChoiceOption, MultipleChoiceParams } from "../types"; |
| 4 | |
| 5 | @customElement("sketch-tool-card-multiple-choice") |
| 6 | export 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 | |
| 147 | declare global { |
| 148 | interface HTMLElementTagNameMap { |
| 149 | "sketch-tool-card-multiple-choice": SketchToolCardMultipleChoice; |
| 150 | } |
| 151 | } |