blob: 92bde10e0f2a4bd493bb4f0c89d6becd929b88b5 [file] [log] [blame]
import { html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ConnectionStatus } from "../data";
import { SketchTailwindElement } from "./sketch-tailwind-element";
@customElement("mobile-title")
export class MobileTitle extends SketchTailwindElement {
@property({ type: String })
connectionStatus: ConnectionStatus = "disconnected";
@property({ type: Boolean })
isThinking = false;
@property({ type: String })
skabandAddr?: string;
@property({ type: String })
currentView: "chat" | "diff" = "chat";
@property({ type: String })
slug: string = "";
connectedCallback() {
super.connectedCallback();
// Add animation styles to document head if not already present
if (!document.getElementById("mobile-title-animations")) {
const style = document.createElement("style");
style.id = "mobile-title-animations";
style.textContent = `
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes thinking {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
.pulse-animation { animation: pulse 1.5s ease-in-out infinite; }
.thinking-animation { animation: thinking 1.4s ease-in-out infinite both; }
.thinking-animation:nth-child(1) { animation-delay: -0.32s; }
.thinking-animation:nth-child(2) { animation-delay: -0.16s; }
.thinking-animation:nth-child(3) { animation-delay: 0; }
`;
document.head.appendChild(style);
}
}
private getStatusText() {
switch (this.connectionStatus) {
case "connected":
return "Connected";
case "connecting":
return "Connecting...";
case "disconnected":
return "Disconnected";
default:
return "Unknown";
}
}
private handleViewChange(event: Event) {
const select = event.target as HTMLSelectElement;
const view = select.value as "chat" | "diff";
if (view !== this.currentView) {
const changeEvent = new CustomEvent("view-change", {
detail: { view },
bubbles: true,
composed: true,
});
this.dispatchEvent(changeEvent);
}
}
render() {
const statusDotClass =
{
connected: "bg-green-500",
connecting: "bg-yellow-500 pulse-animation",
disconnected: "bg-red-500",
}[this.connectionStatus] || "bg-gray-500";
return html`
<div class="block bg-gray-50 border-b border-gray-200 p-3">
<div class="flex items-start justify-between">
<div class="flex-1 min-w-0">
<h1 class="text-lg font-semibold text-gray-900 m-0">
${this.skabandAddr
? html`<a
href="${this.skabandAddr}"
target="_blank"
rel="noopener noreferrer"
class="text-inherit no-underline transition-opacity duration-200 flex items-center gap-2 hover:opacity-80 hover:underline"
>
<img
src="${this.skabandAddr}/sketch.dev.png"
alt="sketch"
class="w-[18px] h-[18px] rounded"
/>
${this.slug || "Sketch"}
</a>`
: html`${this.slug || "Sketch"}`}
</h1>
</div>
<div class="flex items-center gap-3">
<select
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"
.value=${this.currentView}
@change=${this.handleViewChange}
>
<option value="chat">Chat</option>
<option value="diff">Diff</option>
</select>
${this.isThinking
? html`
<div class="flex items-center gap-1.5 text-gray-500 text-xs">
<span>thinking</span>
<div class="flex gap-0.5">
<div
class="w-1 h-1 rounded-full bg-gray-500 thinking-animation"
></div>
<div
class="w-1 h-1 rounded-full bg-gray-500 thinking-animation"
></div>
<div
class="w-1 h-1 rounded-full bg-gray-500 thinking-animation"
></div>
</div>
</div>
`
: html`<span
class="w-2 h-2 rounded-full flex-shrink-0 ${statusDotClass}"
></span>`}
</div>
</div>
</div>
`;
}
}