webui: add ESLint and eslint-typescript

I've historically found this stuff worthwhile, so let's swallow the pill.

Fortunately, sketch was happy to oblige.

- Install ESLint, @eslint/js, and typescript-eslint packages
- Configure eslint.config.mjs with recommended TypeScript ESLint rules
- Add browser globals support for DOM and web APIs
- Integrate ESLint into npm test workflow via package.json scripts
- Update Makefile to run lint checks as part of test suite
- Fix 249+ linting issues including:
  * Remove unused imports and variables (with _ prefix convention)
  * Fix case declaration issues with eslint-disable blocks
  * Remove unnecessary escape characters
  * Address prefer-const violations
  * Handle unused function parameters appropriately
- Configure ignore patterns to exclude dist/ and node_modules/
- Set rules to allow explicit 'any' types temporarily
- Convert warnings for ts-ignore, async promise executors, and unsafe optional chaining

Results:
- ESLint now runs successfully with 0 errors and only 6 warnings
- TypeScript compilation continues to work correctly
- Linting integrated into test workflow for continuous quality enforcement
- Codebase follows consistent ESLint TypeScript recommended practices

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: sd7b538be0a28d294k
diff --git a/webui/src/web-components/sketch-app-shell-base.ts b/webui/src/web-components/sketch-app-shell-base.ts
index 502b019..070ed21 100644
--- a/webui/src/web-components/sketch-app-shell-base.ts
+++ b/webui/src/web-components/sketch-app-shell-base.ts
@@ -1,7 +1,8 @@
-import { css, html, LitElement } from "lit";
-import { customElement, property, state } from "lit/decorators.js";
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { html } from "lit";
+import { property, state } from "lit/decorators.js";
 import { ConnectionStatus, DataManager } from "../data";
-import { AgentMessage, GitLogEntry, State } from "../types";
+import { AgentMessage, State } from "../types";
 import { aggregateAgentMessages } from "./aggregateAgentMessages";
 import { SketchTailwindElement } from "./sketch-tailwind-element";
 
@@ -19,7 +20,7 @@
 import "./sketch-view-mode-select";
 import "./sketch-todo-panel";
 
-import { createRef, ref } from "lit/directives/ref.js";
+import { createRef } from "lit/directives/ref.js";
 import { SketchChatInput } from "./sketch-chat-input";
 
 type ViewMode = "chat" | "diff2" | "terminal";
@@ -322,12 +323,7 @@
     }
   }
 
-  private _handleMultipleChoice(event: CustomEvent) {
-    window.console.log("_handleMultipleChoice", event);
-    this._sendChat;
-  }
-
-  private _handleDiffComment(event: CustomEvent) {
+  private _handleDiffComment(_event: CustomEvent) {
     // Empty stub required by the event binding in the template
     // Actual handling occurs at global level in sketch-chat-input component
   }
@@ -567,7 +563,7 @@
       try {
         const todoData = JSON.parse(latestTodoContent);
         hasTodos = todoData.items && todoData.items.length > 0;
-      } catch (error) {
+      } catch {
         // Invalid JSON, treat as no todos
         hasTodos = false;
       }
@@ -1102,12 +1098,6 @@
       }
     });
 
-    // Setup end button
-    const endButton = this.renderRoot?.querySelector(
-      "#endButton",
-    ) as HTMLButtonElement;
-    // We're already using the @click binding in the HTML, so manual event listener not needed here
-
     // Process any existing messages to find commit information
     if (this.messages && this.messages.length > 0) {
       // Update last commit info via container status component