add /dist/messages-viewer.js bundle output
diff --git a/webui/esbuild.go b/webui/esbuild.go
index d3193d5..f8f010d 100644
--- a/webui/esbuild.go
+++ b/webui/esbuild.go
@@ -207,6 +207,7 @@
 		"src/web-components/sketch-app-shell.ts",
 		"src/web-components/mobile-app-shell.ts",
 		"src/web-components/sketch-monaco-view.ts",
+		"src/messages-viewer.ts",
 		"node_modules/monaco-editor/esm/vs/editor/editor.worker.js",
 		"node_modules/monaco-editor/esm/vs/language/typescript/ts.worker.js",
 		"node_modules/monaco-editor/esm/vs/language/html/html.worker.js",
@@ -438,6 +439,7 @@
 		"src/web-components/sketch-app-shell.ts",
 		"src/web-components/mobile-app-shell.ts",
 		"src/web-components/sketch-monaco-view.ts",
+		"src/messages-viewer.ts",
 	}
 	metafiles := make([]string, len(bundleTs))
 
diff --git a/webui/package.json b/webui/package.json
index f96f86d..027bf68 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -7,6 +7,7 @@
   "main": "dist/index.js",
   "exports": {
     ".": "./dist/index.js",
+    "./messages-viewer.js": "./dist/messages-viewer.js",
     "./sketch-app-shell.js": "./dist/sketch-app-shell.js",
     "./web-components/*": "./dist/web-components/*.js"
   },
diff --git a/webui/src/messages-viewer.ts b/webui/src/messages-viewer.ts
new file mode 100644
index 0000000..9a08da3
--- /dev/null
+++ b/webui/src/messages-viewer.ts
@@ -0,0 +1,23 @@
+import { SketchTimeline } from "./web-components/sketch-timeline";
+import { aggregateAgentMessages } from "./web-components/aggregateAgentMessages";
+
+// Ensure this dependency ends up in the bundle (the "as SketchTimeline" reference below
+// is insufficient for the bundler to include it).
+SketchTimeline;
+
+export function renderMessagesViewer(viewData: any, container: HTMLDivElement) {
+  const timelineEl = document.createElement(
+    "sketch-timeline",
+  ) as SketchTimeline;
+  // Filter out hidden messages at the display level (matches sketch behavior)
+  const visibleMessages = viewData.Messages.filter(
+    (msg: any) => !msg.hide_output,
+  );
+  const messages = aggregateAgentMessages(visibleMessages, []);
+  timelineEl.messages = messages;
+  timelineEl.toolCalls = viewData.ToolResults;
+  //timelineEl.scrollContainer = window.document;
+  container.replaceWith(timelineEl);
+}
+
+window.globalThis.renderMessagesViewer = renderMessagesViewer;