perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot - #38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov

Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov
tyulyukov merged commit 0802522 into mainApr 10, 2026
4 of 5 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@tyulyukov