fix: restore Expand/Minimize button in AI panel + dynamic width, note thumbnail SVG placeholder with emoji/letter
This commit is contained in:
@@ -516,7 +516,8 @@ export function ContextualAIChat({
|
||||
|
||||
return (
|
||||
<aside className={cn(
|
||||
'border-l border-border/40 bg-background flex flex-col h-full w-full flex-shrink-0 z-10 transition-all duration-300',
|
||||
'border-l border-border/40 bg-background flex flex-col h-full flex-shrink-0 z-10 transition-all duration-300',
|
||||
expanded ? 'w-[560px]' : 'w-[360px]',
|
||||
className,
|
||||
)}>
|
||||
|
||||
@@ -553,6 +554,16 @@ export function ContextualAIChat({
|
||||
<Wand2 className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost" size="icon"
|
||||
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => setExpanded(e => !e)}
|
||||
title={expanded ? t('ai.shrinkPanel') : t('ai.expandPanel')}
|
||||
>
|
||||
{expanded
|
||||
? <Minimize2 className="h-3.5 w-3.5" />
|
||||
: <Maximize2 className="h-3.5 w-3.5" />}
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" onClick={onClose} className="h-7 w-7 text-muted-foreground hover:text-foreground">
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
|
||||
@@ -899,7 +899,7 @@ export function NoteEditor({ note, readOnly = false, onClose, fullPage = false }
|
||||
|
||||
{/* ── Side panel: AI Chat ── */}
|
||||
{aiOpen && (
|
||||
<div className="w-[400px] h-full self-stretch border-l border-black/10 dark:border-white/10 bg-background flex flex-col z-50 shrink-0">
|
||||
<div className="h-full self-stretch bg-background flex flex-col z-50 shrink-0">
|
||||
<ContextualAIChat
|
||||
onClose={() => setAiOpen(false)}
|
||||
noteTitle={title}
|
||||
|
||||
@@ -116,6 +116,63 @@ function EditorialNoteMenu({ note, onOpen, onOpenHistory }: {
|
||||
)
|
||||
}
|
||||
|
||||
/** Deterministic hue from a string — consistent per note */
|
||||
function stringToHue(s: string): number {
|
||||
let h = 0
|
||||
for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) & 0xffff
|
||||
return h % 360
|
||||
}
|
||||
|
||||
/** SVG thumbnail for notes without an image */
|
||||
function NoteThumbnailPlaceholder({ title, noteId }: { title: string; noteId: string }) {
|
||||
// Try to extract the first emoji from the title
|
||||
const emojiMatch = title.match(/\p{Emoji_Presentation}|\p{Emoji}\uFE0F/u)
|
||||
const emoji = emojiMatch?.[0]
|
||||
const letter = title.replace(/\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu, '').trim()[0]?.toUpperCase() || '?'
|
||||
const hue = stringToHue(noteId)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full flex items-center justify-center relative overflow-hidden"
|
||||
style={{ background: `linear-gradient(145deg, hsl(${hue} 25% 94%) 0%, hsl(${hue} 18% 87%) 100%)` }}
|
||||
>
|
||||
{/* Decorative concentric circles */}
|
||||
<svg
|
||||
className="absolute inset-0 w-full h-full"
|
||||
viewBox="0 0 224 168"
|
||||
fill="none"
|
||||
aria-hidden
|
||||
style={{ color: `hsl(${hue} 30% 60%)` }}
|
||||
>
|
||||
<circle cx="112" cy="84" r="90" stroke="currentColor" strokeWidth="0.6" opacity="0.25" />
|
||||
<circle cx="112" cy="84" r="64" stroke="currentColor" strokeWidth="0.6" opacity="0.2" />
|
||||
<circle cx="112" cy="84" r="38" stroke="currentColor" strokeWidth="0.6" opacity="0.15" />
|
||||
<line x1="22" y1="84" x2="202" y2="84" stroke="currentColor" strokeWidth="0.4" opacity="0.15" />
|
||||
<line x1="112" y1="4" x2="112" y2="164" stroke="currentColor" strokeWidth="0.4" opacity="0.15" />
|
||||
</svg>
|
||||
{emoji ? (
|
||||
<span
|
||||
className="relative text-5xl leading-none select-none"
|
||||
style={{ filter: `drop-shadow(0 2px 8px hsl(${hue} 40% 40% / 0.2))` }}
|
||||
>
|
||||
{emoji}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="relative font-memento-serif font-bold select-none leading-none"
|
||||
style={{
|
||||
fontSize: '4.5rem',
|
||||
color: `hsl(${hue} 35% 35%)`,
|
||||
opacity: 0.35,
|
||||
}}
|
||||
>
|
||||
{letter}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function NotesEditorialView({
|
||||
notes,
|
||||
onOpen,
|
||||
@@ -166,7 +223,7 @@ export function NotesEditorialView({
|
||||
className="w-full h-full object-cover mix-blend-multiply opacity-80 grayscale contrast-125 hover:grayscale-0 hover:opacity-100 transition-all duration-500"
|
||||
/>
|
||||
) : (
|
||||
<div className="h-full w-full bg-gradient-to-br from-muted/40 to-muted/10" aria-hidden />
|
||||
<NoteThumbnailPlaceholder title={title} noteId={note.id} />
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-3 flex-1">
|
||||
|
||||
Reference in New Issue
Block a user