| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 1 | import { html } from "lit"; |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 2 | import { customElement, property } from "lit/decorators.js"; |
| 3 | import { ConnectionStatus } from "../data"; |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 4 | import { SketchTailwindElement } from "./sketch-tailwind-element"; |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 5 | |
| 6 | @customElement("mobile-title") |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 7 | export class MobileTitle extends SketchTailwindElement { |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 8 | @property({ type: String }) |
| 9 | connectionStatus: ConnectionStatus = "disconnected"; |
| 10 | |
| 11 | @property({ type: Boolean }) |
| 12 | isThinking = false; |
| 13 | |
| Philip Zeyliger | 0113be5 | 2025-06-07 23:53:41 +0000 | [diff] [blame] | 14 | @property({ type: String }) |
| 15 | skabandAddr?: string; |
| 16 | |
| philip.zeyliger | 6b8b766 | 2025-06-16 03:06:30 +0000 | [diff] [blame] | 17 | @property({ type: String }) |
| 18 | currentView: "chat" | "diff" = "chat"; |
| 19 | |
| 20 | @property({ type: String }) |
| 21 | slug: string = ""; |
| 22 | |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 23 | connectedCallback() { |
| 24 | super.connectedCallback(); |
| 25 | // Add animation styles to document head if not already present |
| 26 | if (!document.getElementById("mobile-title-animations")) { |
| 27 | const style = document.createElement("style"); |
| 28 | style.id = "mobile-title-animations"; |
| 29 | style.textContent = ` |
| 30 | @keyframes pulse { |
| 31 | 0%, 100% { opacity: 1; } |
| 32 | 50% { opacity: 0.5; } |
| 33 | } |
| 34 | @keyframes thinking { |
| 35 | 0%, 80%, 100% { transform: scale(0); } |
| 36 | 40% { transform: scale(1); } |
| 37 | } |
| 38 | .pulse-animation { animation: pulse 1.5s ease-in-out infinite; } |
| 39 | .thinking-animation { animation: thinking 1.4s ease-in-out infinite both; } |
| 40 | .thinking-animation:nth-child(1) { animation-delay: -0.32s; } |
| 41 | .thinking-animation:nth-child(2) { animation-delay: -0.16s; } |
| 42 | .thinking-animation:nth-child(3) { animation-delay: 0; } |
| 43 | `; |
| 44 | document.head.appendChild(style); |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 45 | } |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 46 | } |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 47 | |
| 48 | private getStatusText() { |
| 49 | switch (this.connectionStatus) { |
| 50 | case "connected": |
| 51 | return "Connected"; |
| 52 | case "connecting": |
| 53 | return "Connecting..."; |
| 54 | case "disconnected": |
| 55 | return "Disconnected"; |
| 56 | default: |
| 57 | return "Unknown"; |
| 58 | } |
| 59 | } |
| 60 | |
| philip.zeyliger | 6b8b766 | 2025-06-16 03:06:30 +0000 | [diff] [blame] | 61 | private handleViewChange(event: Event) { |
| 62 | const select = event.target as HTMLSelectElement; |
| 63 | const view = select.value as "chat" | "diff"; |
| 64 | if (view !== this.currentView) { |
| 65 | const changeEvent = new CustomEvent("view-change", { |
| 66 | detail: { view }, |
| 67 | bubbles: true, |
| 68 | composed: true, |
| 69 | }); |
| 70 | this.dispatchEvent(changeEvent); |
| 71 | } |
| 72 | } |
| 73 | |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 74 | render() { |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 75 | const statusDotClass = |
| 76 | { |
| 77 | connected: "bg-green-500", |
| 78 | connecting: "bg-yellow-500 pulse-animation", |
| 79 | disconnected: "bg-red-500", |
| 80 | }[this.connectionStatus] || "bg-gray-500"; |
| 81 | |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 82 | return html` |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 83 | <div class="block bg-gray-50 border-b border-gray-200 p-3"> |
| 84 | <div class="flex items-start justify-between"> |
| 85 | <div class="flex-1 min-w-0"> |
| 86 | <h1 class="text-lg font-semibold text-gray-900 m-0"> |
| 87 | ${this.skabandAddr |
| 88 | ? html`<a |
| 89 | href="${this.skabandAddr}" |
| 90 | target="_blank" |
| 91 | rel="noopener noreferrer" |
| 92 | class="text-inherit no-underline transition-opacity duration-200 flex items-center gap-2 hover:opacity-80 hover:underline" |
| 93 | > |
| 94 | <img |
| 95 | src="${this.skabandAddr}/sketch.dev.png" |
| 96 | alt="sketch" |
| 97 | class="w-[18px] h-[18px] rounded" |
| 98 | /> |
| 99 | ${this.slug || "Sketch"} |
| 100 | </a>` |
| 101 | : html`${this.slug || "Sketch"}`} |
| 102 | </h1> |
| 103 | </div> |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 104 | |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 105 | <div class="flex items-center gap-3"> |
| 106 | <select |
| 107 | class="bg-transparent border border-gray-200 rounded px-2 py-1.5 text-sm font-medium cursor-pointer transition-all duration-200 text-gray-700 min-w-[60px] hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:border-blue-500 focus:shadow-sm focus:ring-2 focus:ring-blue-200" |
| 108 | .value=${this.currentView} |
| 109 | @change=${this.handleViewChange} |
| 110 | > |
| 111 | <option value="chat">Chat</option> |
| 112 | <option value="diff">Diff</option> |
| 113 | </select> |
| philip.zeyliger | 6b8b766 | 2025-06-16 03:06:30 +0000 | [diff] [blame] | 114 | |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 115 | ${this.isThinking |
| 116 | ? html` |
| 117 | <div class="flex items-center gap-1.5 text-gray-500 text-xs"> |
| 118 | <span>thinking</span> |
| 119 | <div class="flex gap-0.5"> |
| 120 | <div |
| 121 | class="w-1 h-1 rounded-full bg-gray-500 thinking-animation" |
| 122 | ></div> |
| 123 | <div |
| 124 | class="w-1 h-1 rounded-full bg-gray-500 thinking-animation" |
| 125 | ></div> |
| 126 | <div |
| 127 | class="w-1 h-1 rounded-full bg-gray-500 thinking-animation" |
| 128 | ></div> |
| 129 | </div> |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 130 | </div> |
| banksean | 23a35b8 | 2025-07-20 21:18:31 +0000 | [diff] [blame] | 131 | ` |
| 132 | : html`<span |
| 133 | class="w-2 h-2 rounded-full flex-shrink-0 ${statusDotClass}" |
| 134 | ></span>`} |
| 135 | </div> |
| Philip Zeyliger | e08c7ff | 2025-06-06 13:22:12 -0700 | [diff] [blame] | 136 | </div> |
| 137 | </div> |
| 138 | `; |
| 139 | } |
| 140 | } |