Conversation
…n locks\n\nfix(web/chat): stream by assistant id, single-source history, refetch-and-merge on done; stop overwriting prior turns\n\ntest(chat): add mock stream and multi-turn persistence test\n\nchore(scripts): run-web supports --mock-chat
…hange; include id\n\ntest(realtime): validate coalescing and multi-turn persistence
| * { box-sizing: border-box; } | ||
|
|
||
| .focus-ring { | ||
| @apply focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[color:var(--accent-500)] focus:ring-offset-[color:var(--bg)]; |
There was a problem hiding this comment.
Invalid Tailwind utilities in @apply: ring-[color:…] and ring-offset-[color:…] are not recognized and can fail @apply. Use a valid ring color utility or an arbitrary value without the 'color:' prefix.
Prompt for AI agents
Address the following comment on web/styles/globals.css at line 43:
<comment>Invalid Tailwind utilities in @apply: ring-[color:…] and ring-offset-[color:…] are not recognized and can fail @apply. Use a valid ring color utility or an arbitrary value without the 'color:' prefix.</comment>
<file context>
@@ -0,0 +1,56 @@
+* { box-sizing: border-box; }
+
+.focus-ring {
+ @apply focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[color:var(--accent-500)] focus:ring-offset-[color:var(--bg)];
+}
+
</file context>
|
|
||
| function Switch({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) { | ||
| return ( | ||
| <button |
There was a problem hiding this comment.
Specify button type to avoid unintended form submissions if this component is ever used inside a form.
Prompt for AI agents
Address the following comment on web/app/settings/page.tsx at line 69:
<comment>Specify button type to avoid unintended form submissions if this component is ever used inside a form.</comment>
<file context>
@@ -0,0 +1,79 @@
+
+function Switch({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
+ return (
+ <button
+ role="switch"
+ aria-checked={checked}
</file context>
|
|
||
| export default { | ||
| darkMode: ['class', '[data-theme="dark"]'], | ||
| content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'], |
There was a problem hiding this comment.
Tailwind content paths exclude web/lib where classes are used, which can purge required styles (e.g., Toaster). Include lib in content globs.
Prompt for AI agents
Address the following comment on web/tailwind.config.ts at line 5:
<comment>Tailwind content paths exclude web/lib where classes are used, which can purge required styles (e.g., Toaster). Include lib in content globs.</comment>
<file context>
@@ -0,0 +1,41 @@
+
+export default {
+ darkMode: ['class', '[data-theme="dark"]'],
+ content: ['./app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}'],
+ theme: {
+ extend: {
</file context>
| --core-release) CORE_RELEASE=1; shift ;; | ||
| --mock-chat) MOCK_CHAT=1; shift ;; | ||
| -h|--help) | ||
| echo "Usage: $0 [--restart-core|-r] [--core-release]"; exit 0 ;; |
There was a problem hiding this comment.
Help usage message omits the --mock-chat flag that the script supports, which may confuse users.
Prompt for AI agents
Address the following comment on scripts/run-web.sh at line 20:
<comment>Help usage message omits the --mock-chat flag that the script supports, which may confuse users.</comment>
<file context>
@@ -0,0 +1,95 @@
+ --core-release) CORE_RELEASE=1; shift ;;
+ --mock-chat) MOCK_CHAT=1; shift ;;
+ -h|--help)
+ echo "Usage: $0 [--restart-core|-r] [--core-release]"; exit 0 ;;
+ *) echo "[hub-web] Unknown option: $1"; shift ;;
+ esac
</file context>
| @@ -0,0 +1 @@ | |||
| NEXT_PUBLIC_API_BASE=http://127.0.0.1:6061 | |||
There was a problem hiding this comment.
Avoid committing .env.local with real values; keep only an example file and gitignore .env.local to prevent environment-specific config from leaking into the repo.
Prompt for AI agents
Address the following comment on web/.env.local at line 1:
<comment>Avoid committing .env.local with real values; keep only an example file and gitignore .env.local to prevent environment-specific config from leaking into the repo.</comment>
<file context>
@@ -0,0 +1 @@
+NEXT_PUBLIC_API_BASE=http://127.0.0.1:6061
</file context>
| <div className="flex h-full flex-col gap-2"> | ||
| <button | ||
| className="rounded-md bg-accent-500 px-3 py-2 text-sm text-black hover:bg-accent-600" | ||
| onClick={async () => { |
There was a problem hiding this comment.
Async click handler lacks error handling; failures from createSession will surface with no user feedback.
Prompt for AI agents
Address the following comment on web/components/chat/SessionList.tsx at line 13:
<comment>Async click handler lacks error handling; failures from createSession will surface with no user feedback.</comment>
<file context>
@@ -0,0 +1,41 @@
+ <div className="flex h-full flex-col gap-2">
+ <button
+ className="rounded-md bg-accent-500 px-3 py-2 text-sm text-black hover:bg-accent-600"
+ onClick={async () => {
+ const s = await api.createSession();
+ await qc.invalidateQueries({ queryKey: ['sessions'] });
</file context>
| onClick={() => onSelect(s.id)} | ||
| > | ||
| <div className="truncate text-text">{s.title || s.id}</div> | ||
| <div className="text-2xs truncate text-text-dim">{new Date(s.updated_at).toLocaleString()}</div> |
There was a problem hiding this comment.
Uses undefined Tailwind utility 'text-2xs'; without a fontSize extension this class won’t apply.
Prompt for AI agents
Address the following comment on web/components/chat/SessionList.tsx at line 33:
<comment>Uses undefined Tailwind utility 'text-2xs'; without a fontSize extension this class won’t apply.</comment>
<file context>
@@ -0,0 +1,41 @@
+ onClick={() => onSelect(s.id)}
+ >
+ <div className="truncate text-text">{s.title || s.id}</div>
+ <div className="text-2xs truncate text-text-dim">{new Date(s.updated_at).toLocaleString()}</div>
+ </button>
+ </li>
</file context>
| <div className="text-2xs truncate text-text-dim">{new Date(s.updated_at).toLocaleString()}</div> | |
| <div className="text-xs truncate text-text-dim">{new Date(s.updated_at).toLocaleString()}</div> |
| return defaultSettings; | ||
| }); | ||
|
|
||
| useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } catch {} }, [settings]); |
There was a problem hiding this comment.
Empty catch block hides errors during persistence; add a brief comment or handle specific cases to clarify intentional suppression.
Prompt for AI agents
Address the following comment on web/components/app/ThemeProvider.tsx at line 42:
<comment>Empty catch block hides errors during persistence; add a brief comment or handle specific cases to clarify intentional suppression.</comment>
<file context>
@@ -0,0 +1,70 @@
+ return defaultSettings;
+ });
+
+ useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } catch {} }, [settings]);
+ useEffect(() => { applyTheme(settings); }, [settings]);
+
</file context>
| }); | ||
|
|
||
| useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } catch {} }, [settings]); | ||
| useEffect(() => { applyTheme(settings); }, [settings]); |
There was a problem hiding this comment.
Applying theme changes in useEffect can cause a flash of unstyled/incorrect theme on first paint; consider useLayoutEffect to apply before paint.
Prompt for AI agents
Address the following comment on web/components/app/ThemeProvider.tsx at line 43:
<comment>Applying theme changes in useEffect can cause a flash of unstyled/incorrect theme on first paint; consider useLayoutEffect to apply before paint.</comment>
<file context>
@@ -0,0 +1,70 @@
+ });
+
+ useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(settings)); } catch {} }, [settings]);
+ useEffect(() => { applyTheme(settings); }, [settings]);
+
+ const api = useMemo<Ctx>(() => ({
</file context>
| const api = useMemo<Ctx>(() => ({ | ||
| settings, | ||
| set: (k, v) => setSettings((s) => ({ ...s, [k]: v })), | ||
| reset: () => setSettings(defaultSettings), |
There was a problem hiding this comment.
Using a shared constant for state reset can result in no re-render if the current state is the same reference; create a fresh object when resetting.
Prompt for AI agents
Address the following comment on web/components/app/ThemeProvider.tsx at line 48:
<comment>Using a shared constant for state reset can result in no re-render if the current state is the same reference; create a fresh object when resetting.</comment>
<file context>
@@ -0,0 +1,70 @@
+ const api = useMemo<Ctx>(() => ({
+ settings,
+ set: (k, v) => setSettings((s) => ({ ...s, [k]: v })),
+ reset: () => setSettings(defaultSettings),
+ }), [settings]);
+
</file context>
Summary by cubic
Make chat streaming persistent and race-safe by moving assistant message creation to the server, introducing server-generated message IDs, and finalizing on stream completion. Adds a lightweight web app and tooling to exercise SSE chat end to end.
New Features
Bug Fixes