Skip to content

Commit 7de21f9

Browse files
committed
Add chat panel persistence and improve pin button styling
- Persist chat panel state across page navigation using Astro View Transitions - Add transition:persist wrapper to maintain React component instance - Add subtle cross-fade animation (150ms) with reduced-motion support - Fix pin button active state to match hover colors with deep-thinking bg - Fix pin button icon size to match close button (14x14, strokeWidth 1.5) - Fix border color reset bug when toggling pin state off - Remove deprecated viewTransitions option from astro.config.mjs - Add design document for chat panel persistence feature
1 parent b787019 commit 7de21f9

File tree

5 files changed

+201
-11
lines changed

5 files changed

+201
-11
lines changed

astro.config.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export default defineConfig({
2525
description:
2626
'The Algorand Developer Portal is the go-to resource for developers building on Algorand.',
2727
output: 'static',
28-
viewTransitions: true,
2928
integrations: [
3029
starlight({
3130
title: 'Algorand Developer Portal',
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Chat Panel Persistence Design
2+
3+
This document describes the implementation of persistent chat panel state across page navigation in the Algorand Developer Portal.
4+
5+
## Problem
6+
7+
Currently, when users navigate to a different page (including clicking source links in AI chat responses), the chat panel's React component unmounts and remounts, losing:
8+
9+
- The current conversation with the AI
10+
- Scroll position in the chat
11+
- Any in-progress AI responses
12+
13+
This creates a frustrating experience, especially when the AI provides links to documentation pages that the user wants to explore while maintaining their chat context.
14+
15+
## Solution
16+
17+
Use Astro View Transitions with `transition:persist` to keep the chat panel React component mounted across page navigations.
18+
19+
## Architecture
20+
21+
```
22+
Page A Page B
23+
┌─────────────────────┐ ┌─────────────────────┐
24+
│ ┌───────────────┐ │ │ ┌───────────────┐ │
25+
│ │ Main Content │ │ ────── │ │ Main Content │ │
26+
│ └───────────────┘ │ fade │ └───────────────┘ │
27+
│ ┌───────────────┐ │ │ ┌───────────────┐ │
28+
│ │ Chat Panel │ ═══════════ │ Chat Panel │ │ ← persisted
29+
│ │ (React) │ │ stays │ │ (same inst.) │ │
30+
│ └───────────────┘ │ alive │ └───────────────┘ │
31+
└─────────────────────┘ └─────────────────────┘
32+
```
33+
34+
- Main page content cross-fades between pages (~150ms animation)
35+
- Chat panel wrapper is marked with `transition:persist` - same React instance survives
36+
- Kapa SDK context and conversation state are naturally preserved
37+
- No serialization/deserialization of state needed
38+
39+
## Persistence Level
40+
41+
**Within-session persistence**: The conversation stays alive as long as the browser tab is open. It survives page navigation but not tab close/refresh. This matches user expectations - chat context is maintained while browsing, but a fresh session starts when returning later.
42+
43+
## Files to Modify
44+
45+
| File | Change |
46+
|------|--------|
47+
| `src/components/CustomThemeProvider.astro` | Add `<ViewTransitions />` and wrap chat panel with `transition:persist` |
48+
| `src/styles/global.css` | Add View Transitions timing/easing CSS |
49+
| `astro.config.mjs` | Remove deprecated `viewTransitions: true` option |
50+
51+
## Implementation Details
52+
53+
### CustomThemeProvider.astro
54+
55+
```astro
56+
---
57+
import Default from '@astrojs/starlight/components/ThemeProvider.astro';
58+
import AIChatPanel from './AIChatPanel.tsx';
59+
import { ViewTransitions } from 'astro:transitions';
60+
---
61+
62+
<Default {...Astro.props}><slot /></Default>
63+
64+
<!-- View Transitions enabled site-wide -->
65+
<ViewTransitions />
66+
67+
<!-- Chat panel persists across navigation -->
68+
<div transition:persist="ai-chat-panel">
69+
<AIChatPanel client:only='react' />
70+
</div>
71+
```
72+
73+
### global.css additions
74+
75+
```css
76+
/* ——— View Transitions ——— */
77+
78+
/* Subtle cross-fade for page content */
79+
::view-transition-old(root),
80+
::view-transition-new(root) {
81+
animation-duration: 150ms;
82+
animation-timing-function: ease-out;
83+
}
84+
85+
/* Chat panel doesn't animate during transitions (it persists) */
86+
::view-transition-old(ai-chat-panel),
87+
::view-transition-new(ai-chat-panel) {
88+
animation: none;
89+
}
90+
91+
/* Respect reduced motion preferences */
92+
@media (prefers-reduced-motion: reduce) {
93+
::view-transition-old(root),
94+
::view-transition-new(root) {
95+
animation: none;
96+
}
97+
}
98+
```
99+
100+
### astro.config.mjs cleanup
101+
102+
Remove the deprecated option:
103+
104+
```diff
105+
export default defineConfig({
106+
site: 'https://dev.algorand.co',
107+
output: 'static',
108+
- viewTransitions: true,
109+
integrations: [
110+
```
111+
112+
## Edge Cases & Considerations
113+
114+
### `chat-pinned` class on `<html>`
115+
116+
When View Transitions swap pages, the `<html>` element is replaced. The React component's `useEffect` that manages the `chat-pinned` class will re-run after navigation, so the pinned layout state should self-heal. This needs verification during testing.
117+
118+
### Backdrop behavior during transitions
119+
120+
The backdrop overlay (when chat is open in floating mode) is part of the persisted component, so it should remain visible during transitions. No special handling needed.
121+
122+
### Browser compatibility
123+
124+
View Transitions are supported in:
125+
- Chrome 111+ (March 2023)
126+
- Edge 111+
127+
- Opera 97+
128+
- Safari 18+ (September 2024)
129+
- Firefox: Behind flag, not yet default
130+
131+
For unsupported browsers, navigation falls back to standard full-page loads. The chat state will be lost in these cases, which is acceptable degradation.
132+
133+
## Testing Plan
134+
135+
1. Open chat panel and start a conversation
136+
2. Click a sidebar navigation link → verify conversation persists
137+
3. Click a source link in an AI response → verify conversation persists
138+
4. Test with chat pinned → verify pinned layout persists correctly
139+
5. Test the page transition animation looks smooth
140+
6. Test on mobile viewport
141+
7. Test with `prefers-reduced-motion` enabled
142+
143+
## Rollback Plan
144+
145+
If View Transitions cause issues:
146+
1. Remove `<ViewTransitions />` from CustomThemeProvider.astro
147+
2. Remove the `transition:persist` wrapper
148+
3. Remove View Transitions CSS from global.css
149+
4. Restore `viewTransitions: true` in astro.config.mjs (if needed for other reasons)
150+
151+
The chat panel will revert to its previous behavior (state lost on navigation).

src/components/ChatInterface.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,22 @@ export default function ChatInterface({ canPin = false }: ChatInterfaceProps) {
193193
{canPin && (
194194
<button
195195
onClick={toggleChatPinned}
196-
className='chat-panel-btn'
197-
style={styles.headerBtn}
196+
className={isPinned ? '' : 'chat-panel-btn'}
197+
style={{
198+
...styles.headerBtn,
199+
...(isPinned ? styles.pinBtnActive : {}),
200+
}}
198201
title={isPinned ? 'Unpin panel' : 'Pin panel'}
199202
>
200203
{isPinned ? (
201204
// Unpinned icon (floating mode)
202205
<svg
203-
width='16'
204-
height='16'
206+
width='14'
207+
height='14'
205208
viewBox='0 0 24 24'
206209
fill='none'
207210
stroke='currentColor'
208-
strokeWidth='2'
211+
strokeWidth='1.5'
209212
strokeLinecap='round'
210213
strokeLinejoin='round'
211214
>
@@ -215,12 +218,12 @@ export default function ChatInterface({ canPin = false }: ChatInterfaceProps) {
215218
) : (
216219
// Pinned icon (docked mode)
217220
<svg
218-
width='16'
219-
height='16'
221+
width='14'
222+
height='14'
220223
viewBox='0 0 24 24'
221224
fill='none'
222225
stroke='currentColor'
223-
strokeWidth='2'
226+
strokeWidth='1.5'
224227
strokeLinecap='round'
225228
strokeLinejoin='round'
226229
>
@@ -582,10 +585,16 @@ const styles: Record<string, React.CSSProperties> = {
582585
padding: 0,
583586
background: 'var(--sl-color-gray-6)',
584587
border: '1px solid var(--sl-color-gray-5)',
588+
borderColor: 'var(--sl-color-gray-5)', // Explicit to properly reset after active state
585589
color: 'var(--sl-color-gray-3)',
586590
cursor: 'pointer',
587591
borderRadius: '0.375rem',
588-
transition: 'border-color 0.15s, color 0.15s',
592+
transition: 'border-color 0.15s, color 0.15s, background 0.15s',
593+
},
594+
pinBtnActive: {
595+
color: '#01DC94',
596+
borderColor: '#01DC94',
597+
background: 'rgba(255, 127, 72, 0.15)',
589598
},
590599
messages: {
591600
flex: 1,

src/components/CustomThemeProvider.astro

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import Default from '@astrojs/starlight/components/ThemeProvider.astro';
33
import AIChatPanel from './AIChatPanel.tsx';
4+
import { ViewTransitions } from 'astro:transitions';
45
---
56

67
<script is:inline>
@@ -13,4 +14,11 @@ import AIChatPanel from './AIChatPanel.tsx';
1314
</script>
1415

1516
<Default {...Astro.props}><slot /></Default>
16-
<AIChatPanel client:only='react' />
17+
18+
<!-- View Transitions enabled site-wide for smooth page navigation -->
19+
<ViewTransitions />
20+
21+
<!-- Chat panel persists across navigation to maintain conversation state -->
22+
<div transition:persist="ai-chat-panel">
23+
<AIChatPanel client:only='react' />
24+
</div>

src/styles/global.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,26 @@ html.chat-pinned .main-pane {
418418
display: none !important;
419419
}
420420
}
421+
422+
/* ——— View Transitions ——— */
423+
424+
/* Subtle cross-fade for page content */
425+
::view-transition-old(root),
426+
::view-transition-new(root) {
427+
animation-duration: 150ms;
428+
animation-timing-function: ease-out;
429+
}
430+
431+
/* Chat panel doesn't animate during transitions (it persists) */
432+
::view-transition-old(ai-chat-panel),
433+
::view-transition-new(ai-chat-panel) {
434+
animation: none;
435+
}
436+
437+
/* Respect reduced motion preferences */
438+
@media (prefers-reduced-motion: reduce) {
439+
::view-transition-old(root),
440+
::view-transition-new(root) {
441+
animation: none;
442+
}
443+
}

0 commit comments

Comments
 (0)