webui: migrate mobile components to SketchTailwindElement
Complete migration of all mobile web components from LitElement to
SketchTailwindElement base class with Tailwind CSS styling:
Components migrated:
- mobile-chat-input.ts: Chat input with file upload, textarea auto-resize
- mobile-chat.ts: Message display with markdown rendering and tool calls
- mobile-diff.ts: Git diff viewer with Monaco editor integration
- mobile-shell.ts: Main container coordinating mobile UI layout
- mobile-title.ts: Header with connection status and view switching
Key changes:
- Replaced LitElement inheritance with SketchTailwindElement
- Converted all CSS-in-JS styles to Tailwind utility classes
- Removed static styles blocks and shadow DOM styling
- Added custom animations via document.head for non-Tailwind effects
- Preserved all existing functionality and component interactions
Technical improvements:
- Consistent iOS safe area support with env() CSS functions
- Proper flexbox layouts for mobile responsive design
- Maintained accessibility with proper ARIA labels and focus states
- Enhanced hover and active states using Tailwind modifiers
- Optimized touch interactions with -webkit-overflow-scrolling
The mobile components now follow the established SketchTailwindElement
pattern while maintaining full feature parity with the original
shadow DOM implementations.
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s21f840091392b02ek
diff --git a/webui/src/web-components/mobile-shell.ts b/webui/src/web-components/mobile-shell.ts
index dd59ad8..5672be9 100644
--- a/webui/src/web-components/mobile-shell.ts
+++ b/webui/src/web-components/mobile-shell.ts
@@ -1,8 +1,9 @@
-import { css, html, LitElement } from "lit";
+import { html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { ConnectionStatus, DataManager } from "../data";
import { AgentMessage, State } from "../types";
import { aggregateAgentMessages } from "./aggregateAgentMessages";
+import { SketchTailwindElement } from "./sketch-tailwind-element";
import "./mobile-title";
import "./mobile-chat";
@@ -10,7 +11,7 @@
import "./mobile-diff";
@customElement("mobile-shell")
-export class MobileShell extends LitElement {
+export class MobileShell extends SketchTailwindElement {
private dataManager = new DataManager();
@state()
@@ -25,55 +26,6 @@
@state()
currentView: "chat" | "diff" = "chat";
- static styles = css`
- :host {
- display: flex;
- flex-direction: column;
- /* Use dynamic viewport height for better iOS support */
- height: 100dvh;
- /* Fallback for browsers that don't support dvh */
- height: 100vh;
- /* iOS Safari custom property fallback */
- height: calc(var(--vh, 1vh) * 100);
- /* Additional iOS Safari fix */
- min-height: 100vh;
- min-height: -webkit-fill-available;
- width: 100vw;
- background-color: #ffffff;
- font-family:
- -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif;
- }
-
- .mobile-container {
- display: flex;
- flex-direction: column;
- height: 100%;
- overflow: hidden;
- }
-
- mobile-title {
- flex-shrink: 0;
- }
-
- mobile-chat {
- flex: 1;
- overflow: hidden;
- min-height: 0;
- }
-
- mobile-diff {
- flex: 1;
- overflow: hidden;
- min-height: 0;
- }
-
- mobile-chat-input {
- flex-shrink: 0;
- /* Ensure proper height calculation */
- min-height: 64px;
- }
- `;
-
connectedCallback() {
super.connectedCallback();
this.setupDataManager();
@@ -166,8 +118,12 @@
(this.state?.outstanding_tool_calls?.length ?? 0) > 0;
return html`
- <div class="mobile-container">
+ <div
+ class="flex flex-col bg-white font-sans w-screen overflow-hidden"
+ style="height: 100dvh; height: 100vh; height: calc(var(--vh, 1vh) * 100); min-height: 100vh; min-height: -webkit-fill-available;"
+ >
<mobile-title
+ class="flex-shrink-0"
.connectionStatus=${this.connectionStatus}
.isThinking=${isThinking}
.skabandAddr=${this.state?.skaband_addr}
@@ -179,13 +135,17 @@
${this.currentView === "chat"
? html`
<mobile-chat
+ class="flex-1 overflow-hidden min-h-0"
.messages=${this.messages}
.isThinking=${isThinking}
></mobile-chat>
`
- : html` <mobile-diff></mobile-diff> `}
+ : html`<mobile-diff
+ class="flex-1 overflow-hidden min-h-0"
+ ></mobile-diff>`}
<mobile-chat-input
+ class="flex-shrink-0 min-h-[64px]"
.disabled=${this.connectionStatus !== "connected"}
@send-message=${this.handleSendMessage}
></mobile-chat-input>