@agent-relay/dashboard 2.0.82 → 2.0.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/out/404.html +1 -1
  2. package/out/_next/static/chunks/1028-da5d75e35d1420f1.js +1 -0
  3. package/out/_next/static/chunks/1528-78b17000a7e10bc6.js +2 -0
  4. package/out/_next/static/chunks/1695-4a5d33ba715e09b4.js +1 -0
  5. package/out/_next/static/chunks/1705-36c2180d00a4a569.js +1 -0
  6. package/out/_next/static/chunks/1dd3208c-e1f87c7b3dc1a820.js +1 -0
  7. package/out/_next/static/chunks/3663-47290254b8f6f5dd.js +1 -0
  8. package/out/_next/static/chunks/3677-4b225baf4801d9b9.js +73 -0
  9. package/out/_next/static/chunks/5118-7e8ada2df38eef07.js +1 -0
  10. package/out/_next/static/chunks/5888-15cbe97c90ed5fae.js +1 -0
  11. package/out/_next/static/chunks/6773-a45343a98df3abb5.js +1 -0
  12. package/out/_next/static/chunks/6940-b824612b605e79b3.js +9 -0
  13. package/out/_next/static/chunks/7894-f4a15249082a680d.js +1 -0
  14. package/out/_next/static/chunks/9175-b3617c1e5cbfed0e.js +1 -0
  15. package/out/_next/static/chunks/9372-1a804b8d08c7a236.js +1 -0
  16. package/out/_next/static/chunks/{ab6c8a12-0a58072fbb505134.js → ab6c8a12-91438a812d94ecf0.js} +1 -1
  17. package/out/_next/static/chunks/app/_not-found/page-8e8842f82d204726.js +1 -0
  18. package/out/_next/static/chunks/app/about/page-b78577a7da8fa459.js +1 -0
  19. package/out/_next/static/chunks/app/app/[[...slug]]/page-3dffd65b6344f53e.js +1 -0
  20. package/out/_next/static/chunks/app/app/onboarding/page-b89be9aa6264a5e1.js +1 -0
  21. package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-fbd00893ef69e499.js +1 -0
  22. package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-de2ea13649d0b6d3.js +1 -0
  23. package/out/_next/static/chunks/app/blog/page-a08e263c57a156fa.js +1 -0
  24. package/out/_next/static/chunks/app/careers/page-02228e1d6969b232.js +1 -0
  25. package/out/_next/static/chunks/app/changelog/page-1b5c1d79efc6e53a.js +1 -0
  26. package/out/_next/static/chunks/app/cloud/link/page-99654edffffb3af2.js +1 -0
  27. package/out/_next/static/chunks/app/complete-profile/page-59d146e5ddeafc5c.js +1 -0
  28. package/out/_next/static/chunks/app/connect-repos/page-995e16a976a6632c.js +1 -0
  29. package/out/_next/static/chunks/app/contact/page-273396a5ad57bcee.js +1 -0
  30. package/out/_next/static/chunks/app/dev/cli-tools/page-a71b80dcb2d5fc8d.js +1 -0
  31. package/out/_next/static/chunks/app/dev/log-viewer/page-46a6151ae1be0796.js +1 -0
  32. package/out/_next/static/chunks/app/docs/page-7c7cb603b24b7c40.js +1 -0
  33. package/out/_next/static/chunks/app/history/page-0c5cab1dab4e8886.js +1 -0
  34. package/out/_next/static/chunks/app/layout-96d72ba8ef8a43a0.js +1 -0
  35. package/out/_next/static/chunks/app/login/page-0ccbab34213df842.js +1 -0
  36. package/out/_next/static/chunks/app/metrics/page-8616272aeab9c8b0.js +1 -0
  37. package/out/_next/static/chunks/app/page-09ce10603ad9a251.js +1 -0
  38. package/out/_next/static/chunks/app/pricing/page-91c975079120c941.js +1 -0
  39. package/out/_next/static/chunks/app/privacy/{page-c21d51ac2dee3a88.js → page-a49ab271cc686644.js} +1 -1
  40. package/out/_next/static/chunks/app/providers/{page-59114505f4353512.js → page-d775d6eb5bc29e96.js} +1 -1
  41. package/out/_next/static/chunks/app/providers/setup/[provider]/page-ec4ef3cd80de807e.js +1 -0
  42. package/out/_next/static/chunks/app/security/page-d9da9bd9191e8f95.js +1 -0
  43. package/out/_next/static/chunks/app/signup/page-930eca0bf5fd299d.js +1 -0
  44. package/out/_next/static/chunks/app/terms/page-3e4827620b98613c.js +1 -0
  45. package/out/_next/static/chunks/framework-648e1ae7da590300.js +1 -0
  46. package/out/_next/static/chunks/{main-acb1b24265295d6a.js → main-2b1990080c292d92.js} +1 -1
  47. package/out/_next/static/chunks/main-app-9f6b7ff9e754a8f5.js +1 -0
  48. package/out/_next/static/chunks/pages/_app-a077b72e02273ab1.js +1 -0
  49. package/out/_next/static/chunks/pages/_error-84001666436a04e4.js +1 -0
  50. package/out/_next/static/chunks/{webpack-dd93b81e2659669c.js → webpack-7586035f1585f2db.js} +1 -1
  51. package/out/_next/static/css/eb9fc69d1e3d2bed.css +1 -0
  52. package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_buildManifest.js +1 -1
  53. package/out/about.html +2 -2
  54. package/out/about.txt +2 -2
  55. package/out/app/onboarding.html +1 -1
  56. package/out/app/onboarding.txt +2 -2
  57. package/out/app.html +1 -1
  58. package/out/app.txt +2 -2
  59. package/out/blog/go-to-bed-wake-up-to-a-finished-product.html +3 -3
  60. package/out/blog/go-to-bed-wake-up-to-a-finished-product.txt +1 -1
  61. package/out/blog/let-them-cook-multi-agent-orchestration.html +2 -2
  62. package/out/blog/let-them-cook-multi-agent-orchestration.txt +2 -2
  63. package/out/blog.html +2 -2
  64. package/out/blog.txt +1 -1
  65. package/out/careers.html +2 -2
  66. package/out/careers.txt +2 -2
  67. package/out/changelog.html +2 -2
  68. package/out/changelog.txt +2 -2
  69. package/out/cloud/link.html +1 -1
  70. package/out/cloud/link.txt +2 -2
  71. package/out/complete-profile.html +2 -2
  72. package/out/complete-profile.txt +2 -2
  73. package/out/connect-repos.html +1 -1
  74. package/out/connect-repos.txt +2 -2
  75. package/out/contact.html +2 -2
  76. package/out/contact.txt +2 -2
  77. package/out/dev/cli-tools.html +1 -0
  78. package/out/dev/cli-tools.txt +7 -0
  79. package/out/dev/log-viewer.html +23 -0
  80. package/out/dev/log-viewer.txt +7 -0
  81. package/out/docs.html +2 -2
  82. package/out/docs.txt +2 -2
  83. package/out/history.html +1 -1
  84. package/out/history.txt +2 -2
  85. package/out/index.html +1 -1
  86. package/out/index.txt +2 -2
  87. package/out/login.html +2 -2
  88. package/out/login.txt +2 -2
  89. package/out/metrics.html +1 -1
  90. package/out/metrics.txt +2 -2
  91. package/out/pricing.html +2 -2
  92. package/out/pricing.txt +2 -2
  93. package/out/privacy.html +2 -2
  94. package/out/privacy.txt +2 -2
  95. package/out/providers/setup/claude.html +1 -1
  96. package/out/providers/setup/claude.txt +2 -2
  97. package/out/providers/setup/codex.html +1 -1
  98. package/out/providers/setup/codex.txt +2 -2
  99. package/out/providers/setup/cursor.html +1 -1
  100. package/out/providers/setup/cursor.txt +2 -2
  101. package/out/providers.html +1 -1
  102. package/out/providers.txt +2 -2
  103. package/out/security.html +2 -2
  104. package/out/security.txt +2 -2
  105. package/out/signup.html +2 -2
  106. package/out/signup.txt +2 -2
  107. package/out/terms.html +2 -2
  108. package/out/terms.txt +2 -2
  109. package/package.json +5 -1
  110. package/src/adapters/DashboardConfigProvider.tsx +56 -0
  111. package/src/adapters/cloudFetchAdapter.ts +278 -0
  112. package/src/adapters/index.ts +3 -0
  113. package/src/adapters/types.ts +508 -0
  114. package/src/app/app/[[...slug]]/DashboardPageClient.tsx +67 -18
  115. package/src/app/app/onboarding/page.tsx +870 -170
  116. package/src/app/cloud/link/page.tsx +14 -6
  117. package/src/app/connect-repos/page.tsx +9 -3
  118. package/src/app/dev/cli-tools/page.tsx +130 -0
  119. package/src/app/dev/log-viewer/MockLogViewer.tsx +132 -0
  120. package/src/app/dev/log-viewer/fixtures.ts +110 -0
  121. package/src/app/dev/log-viewer/page.tsx +288 -0
  122. package/src/app/history/page.tsx +28 -12
  123. package/src/app/page.tsx +1 -1
  124. package/src/app/providers/setup/[provider]/ProviderSetupClient.tsx +209 -59
  125. package/src/components/AgentCard.tsx +4 -4
  126. package/src/components/AgentLogPreview.tsx +2 -38
  127. package/src/components/App.tsx +441 -2624
  128. package/src/components/CliToolHarness.test.tsx +83 -0
  129. package/src/components/CliToolHarness.tsx +292 -0
  130. package/src/components/CoordinatorPanel.tsx +13 -6
  131. package/src/components/LogViewer.tsx +2 -42
  132. package/src/components/ProviderAuthFlow.tsx +201 -81
  133. package/src/components/ProvisioningProgress.tsx +1 -1
  134. package/src/components/ReactionChips.tsx +2 -1
  135. package/src/components/SpawnModal.test.tsx +51 -18
  136. package/src/components/SpawnModal.tsx +175 -207
  137. package/src/components/TerminalProviderSetup.tsx +1 -1
  138. package/src/components/ThreadPanel.tsx +2 -0
  139. package/src/components/WorkspaceContext.tsx +7 -19
  140. package/src/components/XTermLogViewer.tsx +190 -27
  141. package/src/components/channels/ChannelMessageList.tsx +94 -4
  142. package/src/components/channels/ChannelViewV1.tsx +35 -11
  143. package/src/components/channels/api.ts +21 -20
  144. package/src/components/channels/types.ts +16 -0
  145. package/src/components/hooks/index.ts +0 -19
  146. package/src/components/hooks/useMessages.test.ts +80 -0
  147. package/src/components/hooks/useMessages.ts +13 -4
  148. package/src/components/hooks/useOrchestrator.ts +1 -1
  149. package/src/components/hooks/usePresence.ts +45 -6
  150. package/src/components/hooks/useThread.ts +83 -46
  151. package/src/components/hooks/useTrajectory.ts +62 -5
  152. package/src/components/hooks/useWebSocket.test.ts +358 -0
  153. package/src/components/hooks/useWebSocket.ts +243 -5
  154. package/src/components/index.ts +2 -14
  155. package/src/components/layout/Header.tsx +9 -15
  156. package/src/components/layout/Sidebar.tsx +1 -8
  157. package/src/components/settings/SettingsPage.tsx +108 -47
  158. package/src/components/settings/index.ts +0 -3
  159. package/src/landing/blogData.ts +1 -1
  160. package/src/lib/agent-merge.test.ts +2 -2
  161. package/src/lib/api.ts +8 -38
  162. package/src/lib/identity.test.ts +139 -0
  163. package/src/lib/identity.ts +48 -0
  164. package/src/lib/relaycastMessageAdapters.test.ts +182 -0
  165. package/src/lib/relaycastMessageAdapters.ts +105 -0
  166. package/src/lib/sanitize-logs.test.ts +227 -0
  167. package/src/lib/sanitize-logs.ts +202 -0
  168. package/src/providers/AgentProvider.tsx +799 -0
  169. package/src/providers/ChannelProvider.tsx +528 -0
  170. package/src/providers/CloudWorkspaceProvider.tsx +402 -0
  171. package/src/providers/MessageProvider.tsx +875 -0
  172. package/src/providers/RelayConfigProvider.tsx +94 -0
  173. package/src/providers/SendProvider.tsx +497 -0
  174. package/src/providers/SettingsProvider.tsx +247 -0
  175. package/src/providers/index.ts +26 -0
  176. package/src/types/index.ts +10 -10
  177. package/out/_next/static/chunks/11-9a2993a37266dcb3.js +0 -9
  178. package/out/_next/static/chunks/118-ae2b650136a5a5fc.js +0 -1
  179. package/out/_next/static/chunks/1dd3208c-40ab0fc0f60392b8.js +0 -1
  180. package/out/_next/static/chunks/202-fc0763dd7488e58f.js +0 -1
  181. package/out/_next/static/chunks/259-83b77fa1b91ba5aa.js +0 -1
  182. package/out/_next/static/chunks/407-0c82986cf79c8ecb.js +0 -1
  183. package/out/_next/static/chunks/528-f5f676996d613c25.js +0 -2
  184. package/out/_next/static/chunks/663-ddb04081febc3678.js +0 -1
  185. package/out/_next/static/chunks/687-88b6b139a6bb0e2e.js +0 -1
  186. package/out/_next/static/chunks/695-51d25b1988644374.js +0 -1
  187. package/out/_next/static/chunks/773-54a2641043c81e55.js +0 -1
  188. package/out/_next/static/chunks/app/_not-found/page-6da9b72091e5b511.js +0 -1
  189. package/out/_next/static/chunks/app/about/page-fff7c6457683f243.js +0 -1
  190. package/out/_next/static/chunks/app/app/[[...slug]]/page-f7eca1b66fb4249b.js +0 -1
  191. package/out/_next/static/chunks/app/app/onboarding/page-129abc5da2e67971.js +0 -1
  192. package/out/_next/static/chunks/app/blog/go-to-bed-wake-up-to-a-finished-product/page-5d5f28fd126b692f.js +0 -1
  193. package/out/_next/static/chunks/app/blog/let-them-cook-multi-agent-orchestration/page-b194f207fbd91862.js +0 -1
  194. package/out/_next/static/chunks/app/blog/page-b9bd9d8703fca76a.js +0 -1
  195. package/out/_next/static/chunks/app/careers/page-a4bd8d5f4de8f4eb.js +0 -1
  196. package/out/_next/static/chunks/app/changelog/page-9a1f6ad1743d63c5.js +0 -1
  197. package/out/_next/static/chunks/app/cloud/link/page-0844c5699b027c3b.js +0 -1
  198. package/out/_next/static/chunks/app/complete-profile/page-39ed5a67916beb87.js +0 -1
  199. package/out/_next/static/chunks/app/connect-repos/page-297eddee0c39f2a3.js +0 -1
  200. package/out/_next/static/chunks/app/contact/page-3c1dd8690217fade.js +0 -1
  201. package/out/_next/static/chunks/app/docs/page-1875e981f2c3fd13.js +0 -1
  202. package/out/_next/static/chunks/app/history/page-2d5c5695c9e8b40c.js +0 -1
  203. package/out/_next/static/chunks/app/layout-0a4b99656da25511.js +0 -1
  204. package/out/_next/static/chunks/app/login/page-f69c076f5a6fc520.js +0 -1
  205. package/out/_next/static/chunks/app/metrics/page-bebbee055669a17e.js +0 -1
  206. package/out/_next/static/chunks/app/page-0ee604f7070d14c0.js +0 -1
  207. package/out/_next/static/chunks/app/pricing/page-eeae7d594af333b6.js +0 -1
  208. package/out/_next/static/chunks/app/providers/setup/[provider]/page-daf9b3e05e77ae19.js +0 -1
  209. package/out/_next/static/chunks/app/security/page-cd562730fe84a0a2.js +0 -1
  210. package/out/_next/static/chunks/app/signup/page-c242ca08101a84ff.js +0 -1
  211. package/out/_next/static/chunks/app/terms/page-c7001720e7941dc6.js +0 -1
  212. package/out/_next/static/chunks/framework-3664cab31236a9fa.js +0 -1
  213. package/out/_next/static/chunks/main-app-7f73a939a312a228.js +0 -1
  214. package/out/_next/static/chunks/pages/_app-10a93ab5b7c32eb3.js +0 -1
  215. package/out/_next/static/chunks/pages/_error-2d792b2a41857be4.js +0 -1
  216. package/out/_next/static/css/8968d98ed4c4d33f.css +0 -1
  217. package/src/components/BillingResult.tsx +0 -447
  218. package/src/components/CloudSessionProvider.tsx +0 -130
  219. package/src/components/SessionExpiredModal.tsx +0 -128
  220. package/src/components/WorkspaceStatusIndicator.tsx +0 -396
  221. package/src/components/hooks/useSession.ts +0 -209
  222. package/src/components/hooks/useWorkspaceMembers.ts +0 -132
  223. package/src/components/hooks/useWorkspaceStatus.ts +0 -237
  224. package/src/components/settings/BillingSettingsPanel.tsx +0 -564
  225. package/src/components/settings/TeamSettingsPanel.tsx +0 -560
  226. package/src/components/settings/WorkspaceSettingsPanel.tsx +0 -1368
  227. package/src/lib/cloudApi.ts +0 -893
  228. /package/out/_next/static/{IxfA6RZu4trcsEMYlkQra → g3G0LMdB7lxcrU5mdM54m}/_ssgManifest.js +0 -0
@@ -0,0 +1,288 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * /dev/log-viewer — Isolated test page for the log viewer components.
5
+ *
6
+ * Renders mock data through both inline (sanitized text) and the production
7
+ * XTermLogViewer (full ANSI) side by side. Supports:
8
+ * - Static edge-case fixtures (always available)
9
+ * - Real worker logs loaded dynamically from /api/logs/:name
10
+ * No WebSocket or auth required — XTermLogViewer runs in mock mode.
11
+ */
12
+
13
+ import React, { useState, useEffect } from 'react';
14
+ import { InlineMockViewer } from './MockLogViewer';
15
+ import { XTermLogViewer } from '../../../components/XTermLogViewer';
16
+ import { STATIC_FIXTURES, rawLogToFixture, type LogFixture } from './fixtures';
17
+
18
+ interface LogAgent {
19
+ name: string;
20
+ loading?: boolean;
21
+ }
22
+
23
+ export default function DevLogViewerPage() {
24
+ const [fixtures, setFixtures] = useState<LogFixture[]>(STATIC_FIXTURES);
25
+ const [selectedFixture, setSelectedFixture] = useState<LogFixture>(STATIC_FIXTURES[0]);
26
+ const [streaming, setStreaming] = useState(false);
27
+ const [reloadKey, setReloadKey] = useState(0);
28
+ const [availableAgents, setAvailableAgents] = useState<LogAgent[]>([]);
29
+ const [loadingAgents, setLoadingAgents] = useState(true);
30
+
31
+ // Resolve API base: check ?api= query param, otherwise try relative /api
32
+ const getApiBase = () => {
33
+ if (typeof window === 'undefined') return '';
34
+ const params = new URLSearchParams(window.location.search);
35
+ return params.get('api') || '';
36
+ };
37
+
38
+ // Fetch available log agents from the server
39
+ useEffect(() => {
40
+ let cancelled = false;
41
+
42
+ (async () => {
43
+ try {
44
+ const apiBase = getApiBase();
45
+ const res = await fetch(`${apiBase}/api/logs`);
46
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
47
+ const data = await res.json();
48
+ if (cancelled) return;
49
+
50
+ // Filter out step-* logs (internal orchestration) — show named agents only
51
+ const agents: string[] = (data.agents || []).filter(
52
+ (name: string) => !name.startsWith('step-')
53
+ );
54
+ setAvailableAgents(agents.map((name) => ({ name })));
55
+ } catch {
56
+ // Server not available — just use static fixtures
57
+ console.warn('Could not fetch log agents from /api/logs — using static fixtures only');
58
+ } finally {
59
+ if (!cancelled) setLoadingAgents(false);
60
+ }
61
+ })();
62
+
63
+ return () => { cancelled = true; };
64
+ }, []);
65
+
66
+ // Load a real log file from the server
67
+ const loadAgentLog = async (agentName: string) => {
68
+ // Check if already loaded
69
+ const existing = fixtures.find((f) => f.name === agentName);
70
+ if (existing) {
71
+ setSelectedFixture(existing);
72
+ setReloadKey((k) => k + 1);
73
+ return;
74
+ }
75
+
76
+ // Mark as loading
77
+ setAvailableAgents((prev) =>
78
+ prev.map((a) => (a.name === agentName ? { ...a, loading: true } : a))
79
+ );
80
+
81
+ try {
82
+ const apiBase = getApiBase();
83
+ const res = await fetch(`${apiBase}/api/logs/${encodeURIComponent(agentName)}`);
84
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
85
+ const data = await res.json();
86
+
87
+ if (!data.found || !data.content) {
88
+ console.warn(`No log content found for ${agentName}`);
89
+ return;
90
+ }
91
+
92
+ const fixture = rawLogToFixture(agentName, data.content);
93
+ setFixtures((prev) => [...prev, fixture]);
94
+ setSelectedFixture(fixture);
95
+ setReloadKey((k) => k + 1);
96
+ } catch (err) {
97
+ console.error(`Failed to load log for ${agentName}:`, err);
98
+ } finally {
99
+ setAvailableAgents((prev) =>
100
+ prev.map((a) => (a.name === agentName ? { ...a, loading: false } : a))
101
+ );
102
+ }
103
+ };
104
+
105
+ const handleFixtureChange = (fixture: LogFixture) => {
106
+ setSelectedFixture(fixture);
107
+ setReloadKey((k) => k + 1);
108
+ };
109
+
110
+ const handleReplay = () => {
111
+ setReloadKey((k) => k + 1);
112
+ };
113
+
114
+ // Convert LogFixtureLine[] to mockData format for XTermLogViewer
115
+ const mockData = selectedFixture.lines.map((line) => ({
116
+ content: line.content,
117
+ delay: line.delay,
118
+ }));
119
+
120
+ return (
121
+ <div className="min-h-screen bg-[#0a0c10] text-[#c9d1d9]">
122
+ {/* Header */}
123
+ <div className="border-b border-[#21262d] bg-[#0d1117]">
124
+ <div className="max-w-[1600px] mx-auto px-6 py-4">
125
+ <div className="flex items-center justify-between">
126
+ <div>
127
+ <h1 className="text-lg font-semibold text-white flex items-center gap-2">
128
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="text-accent-cyan">
129
+ <polyline points="4 17 10 11 4 5" />
130
+ <line x1="12" y1="19" x2="20" y2="19" />
131
+ </svg>
132
+ Log Viewer Test Page
133
+ </h1>
134
+ <p className="text-sm text-[#8b949e] mt-1">
135
+ Isolated rendering tests — static fixtures + real worker logs
136
+ </p>
137
+ </div>
138
+ <span className="px-2 py-1 rounded-md bg-[#d29922]/20 text-[#d29922] text-xs font-medium uppercase tracking-wider">
139
+ Dev Only
140
+ </span>
141
+ </div>
142
+ </div>
143
+ </div>
144
+
145
+ {/* Controls */}
146
+ <div className="border-b border-[#21262d] bg-[#0d1117]/50">
147
+ <div className="max-w-[1600px] mx-auto px-6 py-3">
148
+ <div className="flex items-center gap-4 flex-wrap">
149
+ {/* Static fixture selector */}
150
+ <div className="flex items-center gap-2">
151
+ <span className="text-xs text-[#8b949e] uppercase tracking-wider font-medium">Static:</span>
152
+ <div className="flex gap-1">
153
+ {STATIC_FIXTURES.map((fixture) => (
154
+ <button
155
+ key={fixture.name}
156
+ onClick={() => handleFixtureChange(fixture)}
157
+ className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
158
+ selectedFixture.name === fixture.name
159
+ ? 'bg-accent-cyan/20 text-accent-cyan shadow-[0_0_8px_rgba(0,217,255,0.15)]'
160
+ : 'bg-[#21262d] text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#30363d]'
161
+ }`}
162
+ >
163
+ {fixture.name}
164
+ </button>
165
+ ))}
166
+ </div>
167
+ </div>
168
+
169
+ <div className="w-px h-6 bg-[#30363d]" />
170
+
171
+ {/* Streaming toggle */}
172
+ <label className="flex items-center gap-2 cursor-pointer">
173
+ <input
174
+ type="checkbox"
175
+ checked={streaming}
176
+ onChange={(e) => {
177
+ setStreaming(e.target.checked);
178
+ setReloadKey((k) => k + 1);
179
+ }}
180
+ className="sr-only"
181
+ />
182
+ <div
183
+ className={`w-8 h-4 rounded-full transition-colors ${
184
+ streaming ? 'bg-accent-cyan' : 'bg-[#30363d]'
185
+ }`}
186
+ >
187
+ <div
188
+ className={`w-3 h-3 rounded-full bg-white mt-0.5 transition-transform ${
189
+ streaming ? 'translate-x-4.5 ml-[18px]' : 'translate-x-0.5 ml-[2px]'
190
+ }`}
191
+ />
192
+ </div>
193
+ <span className="text-xs text-[#8b949e]">Streaming</span>
194
+ </label>
195
+
196
+ {/* Replay button */}
197
+ <button
198
+ onClick={handleReplay}
199
+ className="px-3 py-1.5 rounded-lg text-xs font-medium bg-[#21262d] text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#30363d] transition-all flex items-center gap-1.5"
200
+ >
201
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
202
+ <polyline points="1 4 1 10 7 10" />
203
+ <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10" />
204
+ </svg>
205
+ Replay
206
+ </button>
207
+ </div>
208
+
209
+ {/* Real log agents (loaded dynamically) */}
210
+ {availableAgents.length > 0 && (
211
+ <div className="flex items-center gap-2 mt-3">
212
+ <span className="text-xs text-[#8b949e] uppercase tracking-wider font-medium">Real Logs:</span>
213
+ <div className="flex gap-1 flex-wrap">
214
+ {availableAgents.map((agent) => {
215
+ const isLoaded = fixtures.some((f) => f.name === agent.name);
216
+ const isSelected = selectedFixture.name === agent.name;
217
+ return (
218
+ <button
219
+ key={agent.name}
220
+ onClick={() => loadAgentLog(agent.name)}
221
+ disabled={agent.loading}
222
+ className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
223
+ isSelected
224
+ ? 'bg-[#3fb950]/20 text-[#3fb950] shadow-[0_0_8px_rgba(63,185,80,0.15)]'
225
+ : isLoaded
226
+ ? 'bg-[#238636]/10 text-[#3fb950] hover:bg-[#238636]/20'
227
+ : 'bg-[#21262d] text-[#8b949e] hover:text-[#c9d1d9] hover:bg-[#30363d]'
228
+ } ${agent.loading ? 'opacity-50 cursor-wait' : ''}`}
229
+ >
230
+ {agent.loading ? '...' : agent.name}
231
+ </button>
232
+ );
233
+ })}
234
+ </div>
235
+ {loadingAgents && (
236
+ <span className="text-[10px] text-[#6e7681] animate-pulse">Loading agents...</span>
237
+ )}
238
+ </div>
239
+ )}
240
+ {!loadingAgents && availableAgents.length === 0 && (
241
+ <div className="mt-2 text-[10px] text-[#484f58]">
242
+ No real log agents found — start the dashboard server to load worker logs
243
+ </div>
244
+ )}
245
+
246
+ {/* Fixture description */}
247
+ <p className="text-xs text-[#6e7681] mt-2">{selectedFixture.description}</p>
248
+ </div>
249
+ </div>
250
+
251
+ {/* Side-by-side viewers */}
252
+ <div className="max-w-[1600px] mx-auto px-6 py-6">
253
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
254
+ {/* Inline mode */}
255
+ <div>
256
+ <h2 className="text-sm font-medium text-[#8b949e] mb-3 flex items-center gap-2">
257
+ <span className="w-2 h-2 rounded-full bg-[#58a6ff]" />
258
+ Inline Mode
259
+ <span className="text-[10px] text-[#6e7681]">(sanitized text, no ANSI)</span>
260
+ </h2>
261
+ <InlineMockViewer
262
+ key={`inline-${reloadKey}`}
263
+ fixture={selectedFixture}
264
+ streaming={streaming}
265
+ />
266
+ </div>
267
+
268
+ {/* XTerm panel mode — uses real production component in mock mode */}
269
+ <div>
270
+ <h2 className="text-sm font-medium text-[#8b949e] mb-3 flex items-center gap-2">
271
+ <span className="w-2 h-2 rounded-full bg-[#3fb950]" />
272
+ XTerm Panel Mode
273
+ <span className="text-[10px] text-[#6e7681]">(production component, mock data)</span>
274
+ </h2>
275
+ <XTermLogViewer
276
+ key={`xterm-${reloadKey}`}
277
+ agentName={selectedFixture.name}
278
+ mockData={mockData}
279
+ mockStreaming={streaming}
280
+ maxHeight="400px"
281
+ suppressNoisyOutput={true}
282
+ />
283
+ </div>
284
+ </div>
285
+ </div>
286
+ </div>
287
+ );
288
+ }
@@ -21,12 +21,12 @@ import { getAgentColor, getAgentInitials } from '../../lib/colors';
21
21
  type ViewMode = 'conversations' | 'sessions' | 'messages';
22
22
 
23
23
  export default function HistoryPage() {
24
- const [viewMode, setViewMode] = useState<ViewMode>('conversations');
24
+ const [viewMode, setViewMode] = useState<ViewMode | null>(null);
25
25
  const [conversations, setConversations] = useState<Conversation[]>([]);
26
26
  const [sessions, setSessions] = useState<HistorySession[]>([]);
27
27
  const [messages, setMessages] = useState<HistoryMessage[]>([]);
28
28
  const [stats, setStats] = useState<HistoryStats | null>(null);
29
- const [isLoading, setIsLoading] = useState(true);
29
+ const [isLoading, setIsLoading] = useState(false);
30
30
  const [error, setError] = useState<string | null>(null);
31
31
 
32
32
  // Filters
@@ -34,8 +34,12 @@ export default function HistoryPage() {
34
34
  const [agentFilter, setAgentFilter] = useState('');
35
35
  const [selectedConversation, setSelectedConversation] = useState<Conversation | null>(null);
36
36
 
37
- // Fetch stats on mount
37
+ // Fetch stats once when the user first selects a tab
38
+ const [statsLoaded, setStatsLoaded] = useState(false);
38
39
  useEffect(() => {
40
+ if (!viewMode || statsLoaded) return;
41
+
42
+ setStatsLoaded(true);
39
43
  const fetchStats = async () => {
40
44
  const result = await api.getHistoryStats();
41
45
  if (result.success && result.data) {
@@ -43,10 +47,12 @@ export default function HistoryPage() {
43
47
  }
44
48
  };
45
49
  fetchStats();
46
- }, []);
50
+ }, [viewMode, statsLoaded]);
47
51
 
48
- // Fetch data based on view mode
52
+ // Fetch data based on view mode — only when user has selected a tab
49
53
  useEffect(() => {
54
+ if (!viewMode) return;
55
+
50
56
  const fetchData = async () => {
51
57
  setIsLoading(true);
52
58
  setError(null);
@@ -120,6 +126,13 @@ export default function HistoryPage() {
120
126
  setViewMode('conversations');
121
127
  }, []);
122
128
 
129
+ const handleTabClick = useCallback((mode: ViewMode) => {
130
+ if (mode === 'conversations') {
131
+ setSelectedConversation(null);
132
+ }
133
+ setViewMode(mode);
134
+ }, []);
135
+
123
136
  return (
124
137
  <div className="min-h-screen bg-bg-primary text-text-primary font-sans">
125
138
  {/* Header */}
@@ -162,24 +175,21 @@ export default function HistoryPage() {
162
175
  <div className="flex gap-1 bg-bg-secondary rounded-lg p-1 border border-border">
163
176
  <TabButton
164
177
  active={viewMode === 'conversations'}
165
- onClick={() => {
166
- setSelectedConversation(null);
167
- setViewMode('conversations');
168
- }}
178
+ onClick={() => handleTabClick('conversations')}
169
179
  >
170
180
  <ConversationIcon />
171
181
  Conversations
172
182
  </TabButton>
173
183
  <TabButton
174
184
  active={viewMode === 'sessions'}
175
- onClick={() => setViewMode('sessions')}
185
+ onClick={() => handleTabClick('sessions')}
176
186
  >
177
187
  <SessionIcon />
178
188
  Sessions
179
189
  </TabButton>
180
190
  <TabButton
181
191
  active={viewMode === 'messages'}
182
- onClick={() => setViewMode('messages')}
192
+ onClick={() => handleTabClick('messages')}
183
193
  >
184
194
  <MessageIcon />
185
195
  Messages
@@ -231,7 +241,13 @@ export default function HistoryPage() {
231
241
  </div>
232
242
 
233
243
  {/* Content */}
234
- {isLoading ? (
244
+ {!viewMode ? (
245
+ <EmptyState
246
+ icon={<ConversationIcon className="w-12 h-12" />}
247
+ title="Conversation History"
248
+ description="Select a tab above to browse conversations, sessions, or messages."
249
+ />
250
+ ) : isLoading ? (
235
251
  <div className="flex items-center justify-center h-[60vh]">
236
252
  <div className="flex flex-col items-center gap-4">
237
253
  <div className="w-10 h-10 border-2 border-border border-t-accent rounded-full animate-spin" />
package/src/app/page.tsx CHANGED
@@ -55,5 +55,5 @@ export default function HomePage() {
55
55
 
56
56
  // Cloud mode: show landing page at root
57
57
  // Local mode: show dashboard at root
58
- return isCloud ? <LandingPage /> : <App />;
58
+ return isCloud ? <LandingPage /> : <App enableReactions />;
59
59
  }