feat: standardize UI theme, fix dark mode consistency, and implement editorial tags
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m24s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m24s
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
GitMerge, PlusCircle, Eye, Code, Languages,
|
||||
Presentation, PenTool, ExternalLink, ImagePlus,
|
||||
ChevronRight, MessageSquare, History, Scissors, Zap, Layout, ArrowRightLeft, Copy, CheckCircle,
|
||||
Tag as TagIcon, RefreshCw,
|
||||
} from 'lucide-react'
|
||||
import { motion, AnimatePresence } from 'motion/react'
|
||||
import { exportExcalidrawSceneToPngBlob } from '@/lib/client/excalidraw-export-image'
|
||||
@@ -47,6 +48,7 @@ import {
|
||||
} from '@/components/ui/select'
|
||||
import { getNotebookIcon } from '@/lib/notebook-icon'
|
||||
import { HierarchicalNotebookSelector } from '@/components/hierarchical-notebook-selector'
|
||||
import { AutoLabelSuggestionDialog } from '@/components/auto-label-suggestion-dialog'
|
||||
import { scrapePageText } from '@/app/actions/scrape'
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────────
|
||||
@@ -119,6 +121,10 @@ interface ContextualAIChatProps {
|
||||
diagramInsertFormat?: 'markdown' | 'html'
|
||||
/** Called to trigger AI title generation for the note */
|
||||
onGenerateTitle?: () => void
|
||||
/** Notebook ID for label regeneration */
|
||||
notebookId?: string
|
||||
/** Notebook name for display */
|
||||
notebookName?: string
|
||||
}
|
||||
|
||||
function CopyPreviewButton({ text }: { text: string }) {
|
||||
@@ -170,6 +176,8 @@ export function ContextualAIChat({
|
||||
className,
|
||||
diagramInsertFormat = 'markdown',
|
||||
onGenerateTitle,
|
||||
notebookId,
|
||||
notebookName,
|
||||
}: ContextualAIChatProps) {
|
||||
const { t, language } = useLanguage()
|
||||
const webSearchAvailable = useWebSearchAvailable()
|
||||
@@ -209,6 +217,10 @@ export function ContextualAIChat({
|
||||
// hoveredMsgId: which chat message shows inject actions
|
||||
const [hoveredMsgId, setHoveredMsgId] = useState<string | null>(null)
|
||||
|
||||
// Label regeneration state
|
||||
const [regenerateLabelsLoading, setRegenerateLabelsLoading] = useState(false)
|
||||
const [autoLabelOpen, setAutoLabelOpen] = useState(false)
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const transport = useRef(new DefaultChatTransport({ api: '/api/chat' })).current
|
||||
@@ -513,6 +525,14 @@ export function ContextualAIChat({
|
||||
}
|
||||
}
|
||||
|
||||
const handleRegenerateLabels = () => {
|
||||
if (!notebookId) {
|
||||
mToast.error(t('ai.autoLabels.noNotebook') || 'Aucun carnet sélectionné')
|
||||
return
|
||||
}
|
||||
setAutoLabelOpen(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{expanded && (
|
||||
@@ -522,7 +542,7 @@ export function ContextualAIChat({
|
||||
/>
|
||||
)}
|
||||
<aside className={cn(
|
||||
'border-l border-border bg-background flex flex-col flex-shrink-0 z-10 transition-all duration-300 shadow-2xl',
|
||||
'border-l border-border bg-memento-paper dark:bg-background flex flex-col flex-shrink-0 z-10 transition-all duration-300 shadow-2xl',
|
||||
expanded
|
||||
? 'fixed right-0 top-0 h-screen w-[640px] z-[200]'
|
||||
: 'h-full w-[360px]',
|
||||
@@ -585,7 +605,7 @@ export function ContextualAIChat({
|
||||
|
||||
<div className="flex-1 flex flex-col min-h-0 relative">
|
||||
{actionPreview && (
|
||||
<div className="absolute inset-0 z-20 flex flex-col bg-background/95 backdrop-blur-md animate-in fade-in slide-in-from-top-4 duration-300">
|
||||
<div className="absolute inset-0 z-20 flex flex-col bg-memento-paper/95 dark:bg-background/95 backdrop-blur-md animate-in fade-in slide-in-from-top-4 duration-300">
|
||||
<div className="px-6 py-4 border-b border-border flex items-center justify-between shrink-0">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-memento-blue">{actionPreview.label}</p>
|
||||
<button onClick={handleDiscardPreview} className="text-foreground/40 hover:text-foreground"><X size={18} /></button>
|
||||
@@ -604,7 +624,7 @@ export function ContextualAIChat({
|
||||
)}
|
||||
|
||||
{resourcePreview && (
|
||||
<div className="absolute inset-0 z-20 flex flex-col bg-background/95 backdrop-blur-md animate-in fade-in slide-in-from-top-4 duration-300">
|
||||
<div className="absolute inset-0 z-20 flex flex-col bg-memento-paper/95 dark:bg-background/95 backdrop-blur-md animate-in fade-in slide-in-from-top-4 duration-300">
|
||||
<div className="px-6 py-4 border-b border-border/40 flex items-center justify-between shrink-0">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-memento-blue">
|
||||
{resourcePreview.source === 'chat' ? 'Injecter depuis Discussion' : 'Aperçu IA'}
|
||||
@@ -770,6 +790,28 @@ export function ContextualAIChat({
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="flex flex-col flex-1 overflow-y-auto p-6 space-y-10 custom-scrollbar"
|
||||
>
|
||||
{notebookId && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-px flex-1 bg-border/40" />
|
||||
<h4 className="text-[9px] uppercase tracking-[0.3em] font-bold text-foreground/40 whitespace-nowrap">{t('ai.organization') || 'Organisation'}</h4>
|
||||
<div className="h-px flex-1 bg-border/40" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRegenerateLabels}
|
||||
className="w-full flex items-center gap-3 p-4 bg-card border border-border rounded-xl transition-all hover:border-memento-blue/30 cursor-pointer"
|
||||
>
|
||||
<div className="p-2 bg-card rounded-lg text-memento-blue shrink-0"><TagIcon size={18} /></div>
|
||||
<div className="flex-1 text-left">
|
||||
<h5 className="text-[10px] font-bold text-foreground">{t('ai.autoLabels.regenerate') || 'Labels IA'}</h5>
|
||||
<p className="text-[8px] text-foreground/40 uppercase tracking-tight">{notebookName || ''}</p>
|
||||
</div>
|
||||
<RefreshCw size={14} className="text-memento-blue shrink-0" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-px flex-1 bg-border/40" />
|
||||
@@ -1101,6 +1143,17 @@ export function ContextualAIChat({
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{autoLabelOpen && notebookId && (
|
||||
<AutoLabelSuggestionDialog
|
||||
open={autoLabelOpen}
|
||||
onOpenChange={setAutoLabelOpen}
|
||||
notebookId={notebookId}
|
||||
onLabelsCreated={() => {
|
||||
mToast.success(t('ai.autoLabels.created', { count: 0 }) || 'Labels créés')
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user