blob: 92bde10e0f2a4bd493bb4f0c89d6becd929b88b5 [file] [log] [blame]
banksean23a35b82025-07-20 21:18:31 +00001import { html } from "lit";
Philip Zeyligere08c7ff2025-06-06 13:22:12 -07002import { customElement, property } from "lit/decorators.js";
3import { ConnectionStatus } from "../data";
banksean23a35b82025-07-20 21:18:31 +00004import { SketchTailwindElement } from "./sketch-tailwind-element";
Philip Zeyligere08c7ff2025-06-06 13:22:12 -07005
6@customElement("mobile-title")
banksean23a35b82025-07-20 21:18:31 +00007export class MobileTitle extends SketchTailwindElement {
Philip Zeyligere08c7ff2025-06-06 13:22:12 -07008 @property({ type: String })
9 connectionStatus: ConnectionStatus = "disconnected";
10
11 @property({ type: Boolean })
12 isThinking = false;
13
Philip Zeyliger0113be52025-06-07 23:53:41 +000014 @property({ type: String })
15 skabandAddr?: string;
16
philip.zeyliger6b8b7662025-06-16 03:06:30 +000017 @property({ type: String })
18 currentView: "chat" | "diff" = "chat";
19
20 @property({ type: String })
21 slug: string = "";
22
banksean23a35b82025-07-20 21:18:31 +000023 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 Zeyligere08c7ff2025-06-06 13:22:12 -070045 }
banksean23a35b82025-07-20 21:18:31 +000046 }
Philip Zeyligere08c7ff2025-06-06 13:22:12 -070047
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.zeyliger6b8b7662025-06-16 03:06:30 +000061 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 Zeyligere08c7ff2025-06-06 13:22:12 -070074 render() {
banksean23a35b82025-07-20 21:18:31 +000075 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 Zeyligere08c7ff2025-06-06 13:22:12 -070082 return html`
banksean23a35b82025-07-20 21:18:31 +000083 <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 Zeyligere08c7ff2025-06-06 13:22:12 -0700104
banksean23a35b82025-07-20 21:18:31 +0000105 <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.zeyliger6b8b7662025-06-16 03:06:30 +0000114
banksean23a35b82025-07-20 21:18:31 +0000115 ${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 Zeyligere08c7ff2025-06-06 13:22:12 -0700130 </div>
banksean23a35b82025-07-20 21:18:31 +0000131 `
132 : html`<span
133 class="w-2 h-2 rounded-full flex-shrink-0 ${statusDotClass}"
134 ></span>`}
135 </div>
Philip Zeyligere08c7ff2025-06-06 13:22:12 -0700136 </div>
137 </div>
138 `;
139 }
140}