feat: design system overhaul — sidebar, AI chats, settings, brainstorm, color cleanup
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
- Sidebar: dynamic brand-accent colors, brainstorm section restyled - AI chat general: popup panel with expand/collapse, hides when contextual AI open - AI chat contextual: tabs reordered (Actions first), X close button, height fix - Settings: all tabs restyled, 6 new color presets (sage, terracotta, iron, etc.) - Global color cleanup: emerald/orange hardcoded → brand-accent dynamic - Brainstorm page: orange → brand-accent throughout - PageEntry animation component added to key pages - Floating AI button: bg-brand-accent instead of hardcoded black - i18n: all 15 locales updated with new AI/billing keys - Billing: freemium quota tracking, BYOK, stripe subscription scaffolding - Admin: integrated into new design - AGENTS.md + CLAUDE.md project rules added
This commit is contained in:
@@ -60,7 +60,7 @@ function CursorTrackerEffect({ containerRef, moveCursor }: { containerRef: React
|
||||
|
||||
const WAVE_COLORS: Record<number, { border: string; bg: string; text: string }> = {
|
||||
1: { border: 'border-orange-200', bg: 'bg-orange-50', text: 'text-orange-600' },
|
||||
2: { border: 'border-memento-blue', bg: 'bg-memento-blue', text: 'text-memento-blue' },
|
||||
2: { border: 'border-brand-accent', bg: 'bg-brand-accent', text: 'text-brand-accent' },
|
||||
3: { border: 'border-violet-200', bg: 'bg-violet-50', text: 'text-violet-600' },
|
||||
}
|
||||
|
||||
@@ -224,8 +224,8 @@ export function BrainstormPage() {
|
||||
try {
|
||||
const result = await exportBrainstorm.mutateAsync()
|
||||
if (result?.id) {
|
||||
const notebookName = result._notebookName || 'Brainstorm'
|
||||
setExportToast({ noteTitle: result.title || 'Synthèse', notebookName })
|
||||
const notebookName = result._notebookName || t('brainstorm.exportDefaultNotebookName')
|
||||
setExportToast({ noteTitle: result.title || t('brainstorm.exportDefaultNoteTitle'), notebookName })
|
||||
setTimeout(() => {
|
||||
setExportToast(null)
|
||||
router.push(`/?openNote=${result.id}`)
|
||||
@@ -238,7 +238,7 @@ export function BrainstormPage() {
|
||||
setTimeout(() => setImpactToast(null), 5000)
|
||||
}
|
||||
} catch (err: any) {
|
||||
setExportError(err?.message || 'Export failed')
|
||||
setExportError(err?.message || t('brainstorm.exportFailedMessage'))
|
||||
setTimeout(() => setExportError(null), 4000)
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ export function BrainstormPage() {
|
||||
duration: 20,
|
||||
ease: 'linear',
|
||||
}}
|
||||
className="w-14 h-14 rounded-2xl bg-orange-500 shadow-[0_0_20px_rgba(249,115,22,0.2)] flex items-center justify-center text-white"
|
||||
className="w-14 h-14 rounded-2xl bg-brand-accent shadow-[0_0_20px_rgba(164,113,72,0.2)] flex items-center justify-center text-white"
|
||||
>
|
||||
<Wind size={28} />
|
||||
</motion.div>
|
||||
@@ -292,7 +292,7 @@ export function BrainstormPage() {
|
||||
{t('brainstorm.title') || 'Waves of Thought'}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="w-8 h-px bg-orange-400/40" />
|
||||
<span className="w-8 h-px bg-brand-accent/40" />
|
||||
<p className="text-[10px] text-muted-foreground tracking-[0.3em] uppercase font-bold">
|
||||
{t('brainstorm.subtitle') || 'Unfold dimensions of potentiality'}
|
||||
</p>
|
||||
@@ -304,7 +304,7 @@ export function BrainstormPage() {
|
||||
<button
|
||||
onClick={handleExport}
|
||||
disabled={exportBrainstorm.isPending}
|
||||
className="flex items-center gap-2 px-4 py-2 bg-white dark:bg-white/5 border border-border rounded-xl text-xs font-bold uppercase tracking-widest text-muted-foreground hover:text-orange-500 transition-all shadow-sm disabled:opacity-50"
|
||||
className="flex items-center gap-2 px-4 py-2 bg-white dark:bg-white/5 border border-border rounded-xl text-xs font-bold uppercase tracking-widest text-muted-foreground hover:text-brand-accent transition-all shadow-sm disabled:opacity-50"
|
||||
title={t('brainstorm.export') || 'Export'}
|
||||
>
|
||||
<Download size={14} />
|
||||
@@ -325,18 +325,18 @@ export function BrainstormPage() {
|
||||
<span className="hidden sm:inline">{t('brainstorm.invite') || 'Invite'}</span>
|
||||
</button>
|
||||
<div className="flex items-center px-3 py-1.5 bg-white dark:bg-white/5 border border-border rounded-xl shadow-sm transition-all hover:border-emerald-500/30">
|
||||
<div className="flex items-center gap-2 mr-3" title="Live Collaboration">
|
||||
<div className="flex items-center gap-2 mr-3" title={t('brainstorm.liveCollaborationTitle')}>
|
||||
<div className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.6)]"></span>
|
||||
</div>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-emerald-600 dark:text-emerald-400 hidden sm:inline-block">Live</span>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-emerald-600 dark:text-emerald-400 hidden sm:inline-block">{t('brainstorm.liveStatus')}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center group/avatars">
|
||||
{authSession?.user?.name && (
|
||||
<div
|
||||
className="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white ring-2 ring-white dark:ring-[#1A1A1A] shadow-sm bg-memento-blue z-10 transition-transform hover:scale-110 hover:z-20"
|
||||
className="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold text-white ring-2 ring-white dark:ring-[#1A1A1A] shadow-sm bg-brand-accent z-10 transition-transform hover:scale-110 hover:z-20"
|
||||
title={`${authSession.user.name} (You)`}
|
||||
>
|
||||
{authSession.user.name.charAt(0).toUpperCase()}
|
||||
@@ -368,19 +368,19 @@ export function BrainstormPage() {
|
||||
</div>
|
||||
|
||||
<div className="relative group">
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-orange-500/20 to-memento-blue/20 rounded-[28px] blur-xl opacity-0 group-focus-within:opacity-100 transition-opacity duration-700" />
|
||||
<div className="absolute -inset-1 bg-gradient-to-r from-brand-accent/20 to-brand-accent/10 rounded-[28px] blur-xl opacity-0 group-focus-within:opacity-100 transition-opacity duration-700" />
|
||||
<input
|
||||
type="text"
|
||||
value={seedInput}
|
||||
onChange={(e) => setSeedInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleStartBrainstorm()}
|
||||
placeholder={t('brainstorm.placeholder') || 'Enter a concept to unfold...'}
|
||||
className="w-full relative bg-white dark:bg-[#1A1A1A] border-2 rounded-2xl px-8 py-7 pr-20 outline-none transition-all text-2xl font-serif italic text-foreground shadow-sm group-hover:shadow-md border-border/40 focus:border-orange-400/40 focus:ring-4 focus:ring-orange-500/5"
|
||||
className="w-full relative bg-white dark:bg-[#1A1A1A] border-2 rounded-2xl px-8 py-7 pr-20 outline-none transition-all text-2xl font-serif italic text-foreground shadow-sm group-hover:shadow-md border-border/40 focus:border-brand-accent/40 focus:ring-4 focus:ring-brand-accent/5"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleStartBrainstorm()}
|
||||
disabled={isGenerating || !seedInput.trim()}
|
||||
className="absolute right-4 top-4 bottom-4 px-6 bg-foreground dark:bg-orange-500 text-background rounded-xl disabled:opacity-50 transition-all hover:scale-[1.02] active:scale-[0.98] flex items-center justify-center gap-2 min-w-[70px] shadow-lg"
|
||||
className="absolute right-4 top-4 bottom-4 px-6 bg-foreground dark:bg-brand-accent text-background rounded-xl disabled:opacity-50 transition-all hover:scale-[1.02] active:scale-[0.98] flex items-center justify-center gap-2 min-w-[70px] shadow-lg"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<div className="w-6 h-6 border-3 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
@@ -395,7 +395,7 @@ export function BrainstormPage() {
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="mt-6 flex items-center gap-4 text-orange-500/80 italic font-serif"
|
||||
className="mt-6 flex items-center gap-4 text-brand-accent/80 italic font-serif"
|
||||
>
|
||||
<div className="flex gap-1.5">
|
||||
{[0.2, 0.4, 0.6].map((d, i) => (
|
||||
@@ -403,7 +403,7 @@ export function BrainstormPage() {
|
||||
key={i}
|
||||
animate={{ scale: [1, 1.5, 1], opacity: [0.3, 1, 0.3] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity, delay: d }}
|
||||
className="w-1.5 h-1.5 rounded-full bg-orange-500"
|
||||
className="w-1.5 h-1.5 rounded-full bg-brand-accent"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -444,7 +444,7 @@ export function BrainstormPage() {
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none opacity-20 flex-col gap-6">
|
||||
<Wind size={120} strokeWidth={1} className="text-muted-foreground animate-pulse" />
|
||||
<p className="text-xl font-serif italic text-muted-foreground">
|
||||
The canvas is waiting for your spark...
|
||||
{t('brainstorm.canvasWaitingHint')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -467,7 +467,7 @@ export function BrainstormPage() {
|
||||
}}
|
||||
/>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground">
|
||||
Wave {w}
|
||||
{t('brainstorm.waveBadge', { wave: w })}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -479,7 +479,7 @@ export function BrainstormPage() {
|
||||
className="px-6 py-3 bg-[#F4F1EA] dark:bg-black/60 backdrop-blur-xl border border-border shadow-xl rounded-full flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest text-muted-foreground hover:bg-foreground hover:text-background transition-all"
|
||||
>
|
||||
<Plus size={14} />
|
||||
{t('brainstorm.addManualIdea') || 'Add Manual Idea'}
|
||||
{t('brainstorm.addIdea')}
|
||||
</button>
|
||||
)}
|
||||
</motion.div>
|
||||
@@ -519,7 +519,7 @@ export function BrainstormPage() {
|
||||
selectedIdea.waveNumber === 1
|
||||
? 'border-orange-300 dark:border-orange-700 bg-orange-50 dark:bg-orange-500/15 text-orange-600 dark:text-orange-400'
|
||||
: selectedIdea.waveNumber === 2
|
||||
? 'border-memento-blue dark:border-blue-700 bg-memento-blue dark:bg-memento-blue/15 text-memento-blue dark:text-memento-blue'
|
||||
? 'border-brand-accent dark:border-blue-700 bg-brand-accent dark:bg-brand-accent/15 text-brand-accent dark:text-brand-accent'
|
||||
: 'border-violet-300 dark:border-violet-700 bg-violet-50 dark:bg-violet-500/15 text-violet-600 dark:text-violet-400'
|
||||
}`}
|
||||
>
|
||||
@@ -546,18 +546,18 @@ export function BrainstormPage() {
|
||||
|
||||
<div className="flex items-center gap-4 mb-8">
|
||||
<div className="flex items-center gap-1">
|
||||
<Zap size={14} className="text-orange-500" />
|
||||
<Zap size={14} className="text-brand-accent" />
|
||||
<span className="text-xs font-bold text-muted-foreground">
|
||||
{t('brainstorm.novelty') || 'Novelty'}: {selectedIdea.noveltyScore || 'N/A'}/10
|
||||
{t('brainstorm.novelty')}: {selectedIdea.noveltyScore ?? t('common.notAvailable')}/10
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
{selectedIdea.createdByType === 'human' ? (
|
||||
<>
|
||||
<div className="w-4 h-4 rounded-full bg-memento-blue flex items-center justify-center text-[8px] font-bold text-white">
|
||||
<div className="w-4 h-4 rounded-full bg-brand-accent flex items-center justify-center text-[8px] font-bold text-white">
|
||||
{(selectedIdea as any).creator?.name?.charAt(0)?.toUpperCase() || 'U'}
|
||||
</div>
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-memento-blue">
|
||||
<span className="text-[10px] font-bold uppercase tracking-widest text-brand-accent">
|
||||
{t('brainstorm.humanIdea') || 'Human'}
|
||||
</span>
|
||||
</>
|
||||
@@ -615,13 +615,13 @@ export function BrainstormPage() {
|
||||
</span>
|
||||
{isRestricted && !isGuest && (
|
||||
<span className="shrink-0 px-1.5 py-0.5 rounded-full text-[8px] font-bold uppercase tracking-wider border border-amber-200 dark:border-amber-700 bg-amber-500/10 text-amber-600 dark:text-amber-400 flex items-center gap-0.5">
|
||||
<Lock size={8} /> Owner
|
||||
<Lock size={8} /> {t('brainstorm.ownerBadge')}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
{ref.note ? (
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
{ref.note.title || 'Untitled'}
|
||||
{ref.note.title || t('notes.untitled')}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-sm italic text-muted-foreground">
|
||||
@@ -653,9 +653,9 @@ export function BrainstormPage() {
|
||||
<button
|
||||
onClick={() => handleDeepen(selectedIdea)}
|
||||
disabled={expandIdea.isPending}
|
||||
className="flex flex-col items-center justify-center p-6 border-2 border-dashed border-border rounded-2xl hover:border-orange-400/40 hover:bg-orange-500/5 transition-all group disabled:opacity-50"
|
||||
className="flex flex-col items-center justify-center p-6 border-2 border-dashed border-border rounded-2xl hover:border-brand-accent/40 hover:bg-brand-accent/5 transition-all group disabled:opacity-50"
|
||||
>
|
||||
<Wind size={24} className="text-muted-foreground group-hover:text-orange-500 mb-2" />
|
||||
<Wind size={24} className="text-muted-foreground group-hover:text-brand-accent mb-2" />
|
||||
<span className="text-[11px] font-bold uppercase tracking-widest text-muted-foreground group-hover:text-foreground">
|
||||
{expandIdea.isPending ? (t('brainstorm.deepening') || 'Generating...') : (t('brainstorm.deepen') || 'Deepen')}
|
||||
</span>
|
||||
@@ -682,10 +682,10 @@ export function BrainstormPage() {
|
||||
)}
|
||||
|
||||
{isGuest && (
|
||||
<div className="mt-6 px-4 py-3 bg-orange-500/5 border border-orange-500/10 rounded-xl flex items-center gap-2">
|
||||
<Globe size={14} className="text-orange-500 shrink-0" />
|
||||
<span className="text-[11px] text-orange-600 dark:text-orange-400">
|
||||
Vous consultez ce brainstorm en tant qu'invité. Connectez-vous pour modifier.
|
||||
<div className="mt-6 px-4 py-3 bg-brand-accent/5 border border-brand-accent/10 rounded-xl flex items-center gap-2">
|
||||
<Globe size={14} className="text-brand-accent shrink-0" />
|
||||
<span className="text-[11px] text-brand-accent">
|
||||
{t('brainstorm.guestReadOnlyNotice')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -706,7 +706,7 @@ export function BrainstormPage() {
|
||||
activeSessionId === s.id
|
||||
? 'bg-foreground text-background scale-110 shadow-lg'
|
||||
: (s as any)._owned === false
|
||||
? 'bg-memento-blue dark:bg-memento-blue/10 text-memento-blue hover:bg-blue-100 hover:text-memento-blue'
|
||||
? 'bg-brand-accent dark:bg-brand-accent/10 text-brand-accent hover:bg-blue-100 hover:text-brand-accent'
|
||||
: 'bg-white dark:bg-white/10 text-muted-foreground hover:bg-foreground/5 hover:text-foreground'
|
||||
}`}
|
||||
title={s.seedIdea}
|
||||
@@ -740,11 +740,11 @@ export function BrainstormPage() {
|
||||
>
|
||||
<span>
|
||||
{impactToast.notesEnriched === 0 && impactToast.notesMarkedDry === 0
|
||||
? (t('brainstorm.linkCopied') || 'Invite link copied!')
|
||||
? t('brainstorm.linkCopied')
|
||||
: <>
|
||||
{impactToast.notesEnriched > 0 && `${impactToast.notesEnriched} note(s) enriched`}
|
||||
{impactToast.notesEnriched > 0 && impactToast.notesMarkedDry > 0 && ' · '}
|
||||
{impactToast.notesMarkedDry > 0 && `${impactToast.notesMarkedDry} note(s) marked dry`}
|
||||
{impactToast.notesEnriched > 0 && t('brainstorm.impactNotesEnriched', { count: impactToast.notesEnriched })}
|
||||
{impactToast.notesEnriched > 0 && impactToast.notesMarkedDry > 0 && t('brainstorm.impactSeparator')}
|
||||
{impactToast.notesMarkedDry > 0 && t('brainstorm.impactNotesMarkedDry', { count: impactToast.notesMarkedDry })}
|
||||
</>
|
||||
}
|
||||
</span>
|
||||
@@ -782,11 +782,13 @@ export function BrainstormPage() {
|
||||
<Wind size={18} />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-bold">{exportToast.noteTitle}</span>
|
||||
<span className="text-[11px] text-emerald-100">Carnet : {exportToast.notebookName}</span>
|
||||
<span className="text-[11px] text-emerald-100">
|
||||
{t('brainstorm.exportNotebookPrefix')} {exportToast.notebookName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="ml-3 flex items-center gap-1.5 text-emerald-200 text-[10px]">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-300 animate-pulse" />
|
||||
Ouverture…
|
||||
{t('brainstorm.exportOpening')}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
@@ -807,7 +809,7 @@ export function BrainstormPage() {
|
||||
</div>
|
||||
<div className="ml-3 flex items-center gap-1.5 text-emerald-200 text-[10px]">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-emerald-300 animate-pulse" />
|
||||
Ouverture…
|
||||
{t('brainstorm.exportOpening')}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user