cleanup: remove unused reference folders and untracked scratch directories
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 2m1s

This commit is contained in:
Antigravity
2026-05-10 13:25:29 +00:00
parent 280852914e
commit b31efac8ba
185 changed files with 0 additions and 20885 deletions

View File

@@ -1,9 +0,0 @@
# GEMINI_API_KEY: Required for Gemini AI API calls.
# AI Studio automatically injects this at runtime from user secrets.
# Users configure this via the Secrets panel in the AI Studio UI.
GEMINI_API_KEY="MY_GEMINI_API_KEY"
# APP_URL: The URL where this applet is hosted.
# AI Studio automatically injects this at runtime with the Cloud Run service URL.
# Used for self-referential links, OAuth callbacks, and API endpoints.
APP_URL="MY_APP_URL"

View File

@@ -1,8 +0,0 @@
node_modules/
build/
dist/
coverage/
.DS_Store
*.log
.env*
!.env.example

View File

@@ -1,20 +0,0 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/b7b577c6-4d9f-44ac-8fe1-85bc3c6d6e66
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

View File

@@ -1,13 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Google AI Studio App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -1,6 +0,0 @@
{
"name": "Architectural Grid",
"description": "A minimalist notebook for architectural research and conceptual sketches.",
"requestFramePermissions": [],
"majorCapabilities": []
}

View File

@@ -1,34 +0,0 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"lucide-react": "^0.546.0",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"vite": "^6.2.3",
"express": "^4.21.2",
"dotenv": "^17.2.3",
"motion": "^12.23.24"
},
"devDependencies": {
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3",
"@types/express": "^4.17.21"
}
}

View File

@@ -1,317 +0,0 @@
/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import React, { useState, useMemo } from 'react';
import { motion, AnimatePresence } from 'motion/react';
// Components
import { Sidebar } from './components/Sidebar';
import { NotebooksView } from './components/NotebooksView';
import { AgentsView } from './components/AgentsView';
import { SettingsView } from './components/SettingsView';
import { AISidebar } from './components/AISidebar';
// Data & Types
import { CARNETS, ALL_NOTES } from './constants';
import { NavigationView, SettingsTab, AITab, AITone, Carnet, Note } from './types';
export default function App() {
const [activeView, setActiveView] = useState<NavigationView>('notebooks');
const [activeSettingsTab, setActiveSettingsTab] = useState<SettingsTab>('general');
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null);
const [isDarkMode, setIsDarkMode] = useState(false);
const [carnets, setCarnets] = useState<Carnet[]>(CARNETS);
const [notes, setNotes] = useState<Note[]>(ALL_NOTES);
const [activeCarnetId, setActiveCarnetId] = useState('4');
const [activeNoteId, setActiveNoteId] = useState<string | null>(null);
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
const [isAISidebarOpen, setIsAISidebarOpen] = useState(false);
const [aiTab, setAiTab] = useState<AITab>('discussion');
const [selectedTone, setSelectedTone] = useState<AITone>('Professional');
// Modal States
const [showNewCarnetModal, setShowNewCarnetModal] = useState<{ isOpen: boolean; parentId?: string }>({ isOpen: false });
const [showNewNoteModal, setShowNewNoteModal] = useState(false);
// Form States
const [newCarnetName, setNewCarnetName] = useState('');
const [newNoteTitle, setNewNoteTitle] = useState('');
const [newNoteContent, setNewNoteContent] = useState('');
const togglePin = (noteId: string) => {
setNotes(notes.map(n => n.id === noteId ? { ...n, isPinned: !n.isPinned } : n));
};
const filteredNotes = useMemo(() => {
let result = notes.filter(n => n.carnetId === activeCarnetId);
if (selectedTagIds.length > 0) {
result = result.filter(note =>
selectedTagIds.every(tagId => note.tags?.some(tag => tag.id === tagId))
);
}
return [...result].sort((a, b) => {
if (a.isPinned && !b.isPinned) return -1;
if (!a.isPinned && b.isPinned) return 1;
return 0;
});
}, [activeCarnetId, notes]);
const activeNote = useMemo(() =>
notes.find(n => n.id === activeNoteId),
[activeNoteId, notes]);
const activeCarnet = useMemo(() =>
carnets.find(c => c.id === activeCarnetId),
[activeCarnetId, carnets]);
const handleAddCarnet = (e: React.FormEvent) => {
e.preventDefault();
if (!newCarnetName.trim()) return;
const newCarnet: Carnet = {
id: Date.now().toString(),
name: newCarnetName,
initial: newCarnetName.charAt(0).toUpperCase(),
type: 'Project',
parentId: showNewCarnetModal.parentId
};
setCarnets([...carnets, newCarnet]);
setNewCarnetName('');
setShowNewCarnetModal({ isOpen: false });
setActiveCarnetId(newCarnet.id);
};
const handleAddNote = (e: React.FormEvent) => {
e.preventDefault();
if (!newNoteTitle.trim() || !newNoteContent.trim()) return;
const newNote: Note = {
id: `n-${Date.now()}`,
carnetId: activeCarnetId,
title: newNoteTitle,
date: new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' }).format(new Date()),
content: newNoteContent,
imageUrl: 'https://images.unsplash.com/photo-1487958449943-2429e8be8625?auto=format&fit=crop&q=80&w=800&h=600',
tags: []
};
setNotes([newNote, ...notes]);
setNewNoteTitle('');
setNewNoteContent('');
setShowNewNoteModal(false);
setActiveNoteId(newNote.id);
};
return (
<div className={`h-screen flex bg-paper transition-colors duration-500 overflow-hidden font-sans ${isDarkMode ? 'dark' : ''}`}>
<Sidebar
activeView={activeView}
isDarkMode={isDarkMode}
setIsDarkMode={setIsDarkMode}
setActiveView={setActiveView}
carnets={carnets}
notes={notes}
activeCarnetId={activeCarnetId}
activeNoteId={activeNoteId}
setActiveCarnetId={setActiveCarnetId}
setActiveNoteId={setActiveNoteId}
setShowNewCarnetModal={(show, parentId) => setShowNewCarnetModal({ isOpen: show, parentId })}
/>
<main className="flex-1 relative overflow-hidden flex bg-paper dark:bg-dark-paper transition-colors duration-500">
<AnimatePresence mode="wait">
{activeView === 'notebooks' && (
<motion.div
key="notebooks"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
className="h-full w-full"
>
<NotebooksView
activeNoteId={activeNoteId}
activeCarnet={activeCarnet}
filteredNotes={filteredNotes}
activeNote={activeNote}
setActiveNoteId={setActiveNoteId}
togglePin={togglePin}
setShowNewNoteModal={setShowNewNoteModal}
isAISidebarOpen={isAISidebarOpen}
setIsAISidebarOpen={setIsAISidebarOpen}
selectedTagIds={selectedTagIds}
setSelectedTagIds={setSelectedTagIds}
allNotes={notes}
activeCarnetId={activeCarnetId}
setShowNewCarnetModal={(show, parentId) => setShowNewCarnetModal({ isOpen: show, parentId })}
/>
</motion.div>
)}
{activeView === 'agents' && (
<motion.div
key="agents"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
className="h-full w-full"
>
<AgentsView
selectedAgentId={selectedAgentId}
setSelectedAgentId={setSelectedAgentId}
/>
</motion.div>
)}
{activeView === 'settings' && (
<motion.div
key="settings"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
className="h-full w-full"
>
<SettingsView
activeSettingsTab={activeSettingsTab}
setActiveSettingsTab={setActiveSettingsTab}
/>
</motion.div>
)}
</AnimatePresence>
<AISidebar
isOpen={isAISidebarOpen}
setIsOpen={setIsAISidebarOpen}
activeNote={activeNote}
aiTab={aiTab}
setAiTab={setAiTab}
selectedTone={selectedTone}
setSelectedTone={setSelectedTone}
/>
</main>
{/* Modals */}
<AnimatePresence>
{showNewCarnetModal.isOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setShowNewCarnetModal({ isOpen: false })}
className="absolute inset-0 bg-ink/40 backdrop-blur-sm"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="relative w-full max-w-md bg-paper dark:bg-dark-paper border border-border shadow-2xl rounded-2xl p-8"
>
<h3 className="text-2xl font-serif font-medium text-ink dark:text-dark-ink mb-2">
{showNewCarnetModal.parentId ? 'Create Sub-Carnet' : 'Create New Carnet'}
</h3>
{showNewCarnetModal.parentId && (
<p className="text-[10px] text-concrete uppercase tracking-widest font-bold mb-6">
Inside: {carnets.find(c => c.id === showNewCarnetModal.parentId)?.name}
</p>
)}
<form onSubmit={handleAddCarnet} className="space-y-6">
<div>
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink mb-2">Notebook Name</label>
<input
autoFocus
type="text"
value={newCarnetName}
onChange={(e) => setNewCarnetName(e.target.value)}
placeholder="E.g., Sustainable Patterns"
className="w-full bg-white dark:bg-[#2A2A2A] border border-border rounded-lg px-4 py-3 outline-none focus:border-ink transition-colors font-serif italic text-lg text-ink dark:text-dark-ink"
/>
</div>
<div className="flex gap-4 pt-4">
<button
type="button"
onClick={() => setShowNewCarnetModal({ isOpen: false })}
className="flex-1 py-3 border border-border rounded-lg text-sm font-medium hover:bg-slate-50 dark:hover:bg-white/5 transition-colors text-ink dark:text-dark-ink"
>
Cancel
</button>
<button
type="submit"
className="flex-1 py-3 bg-ink dark:bg-ochre text-paper rounded-lg text-sm font-medium hover:opacity-90 transition-opacity"
>
Create Notebook
</button>
</div>
</form>
</motion.div>
</div>
)}
{showNewNoteModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setShowNewNoteModal(false)}
className="absolute inset-0 bg-ink/40 backdrop-blur-sm"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="relative w-full max-w-2xl bg-paper dark:bg-dark-paper border border-border shadow-2xl rounded-2xl p-10"
>
<h3 className="text-3xl font-serif font-medium text-ink dark:text-dark-ink mb-8">Add Architectural Note</h3>
<form onSubmit={handleAddNote} className="space-y-8">
<div>
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink mb-2">Concept Title</label>
<input
autoFocus
type="text"
value={newNoteTitle}
onChange={(e) => setNewNoteTitle(e.target.value)}
placeholder="Enter the title of your study..."
className="w-full bg-white dark:bg-[#2A2A2A] border border-border rounded-lg px-5 py-4 outline-none focus:border-ink transition-colors font-serif text-2xl text-ink dark:text-dark-ink"
/>
</div>
<div>
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink mb-2">Observations & Analysis</label>
<textarea
value={newNoteContent}
onChange={(e) => setNewNoteContent(e.target.value)}
placeholder="Describe the spatial logic, materiality, and light interactions..."
rows={6}
className="w-full bg-white dark:bg-[#2A2A2A] border border-border rounded-lg px-5 py-4 outline-none focus:border-ink transition-colors font-light leading-relaxed resize-none text-ink dark:text-dark-ink"
/>
</div>
<div className="flex gap-4 pt-4">
<button
type="button"
onClick={() => setShowNewNoteModal(false)}
className="flex-1 py-4 border border-border rounded-lg text-sm font-medium hover:bg-slate-50 dark:hover:bg-white/5 transition-colors text-ink dark:text-dark-ink"
>
Cancel
</button>
<button
type="submit"
className="flex-1 py-4 bg-ink dark:bg-ochre text-paper rounded-lg text-sm font-medium hover:opacity-90 transition-opacity"
>
Save Note
</button>
</div>
</form>
</motion.div>
</div>
)}
</AnimatePresence>
</div>
);
}

View File

@@ -1,357 +0,0 @@
import React from 'react';
import {
Sparkles,
ChevronRight,
MessageSquare,
FileCode,
Globe,
Send,
Scissors,
Zap,
Languages,
Layout,
ArrowRightLeft,
BookOpen,
History
} from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { AITab, AITone, Note } from '../types';
interface AISidebarProps {
isOpen: boolean;
setIsOpen: (open: boolean) => void;
activeNote: Note | undefined;
aiTab: AITab;
setAiTab: (tab: AITab) => void;
selectedTone: AITone;
setSelectedTone: (tone: AITone) => void;
}
export const AISidebar: React.FC<AISidebarProps> = ({
isOpen,
setIsOpen,
activeNote,
aiTab,
setAiTab,
selectedTone,
setSelectedTone
}) => {
return (
<AnimatePresence>
{isOpen && (
<motion.aside
initial={{ x: 400, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: 400, opacity: 0 }}
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
className="w-[400px] border-l border-border bg-white shadow-2xl flex flex-col z-50 shrink-0 relative"
>
<div className="p-6 border-b border-border space-y-2">
<div className="flex items-center justify-between">
<h3 className="flex items-center gap-2 font-serif text-xl font-medium text-ink">
<Sparkles size={18} className="text-ochre" />
IA Assistant
</h3>
<button
onClick={() => setIsOpen(false)}
className="p-1 hover:bg-slate-100 rounded-full transition-colors text-muted-ink"
>
<ChevronRight size={20} />
</button>
</div>
<p className="text-[11px] text-muted-ink uppercase tracking-wider font-medium opacity-60 truncate">
"{activeNote?.title}"
</p>
</div>
<div className="flex border-b border-border px-2">
{(['discussion', 'actions', 'resources'] as AITab[]).map((tab) => (
<button
key={tab}
onClick={() => setAiTab(tab)}
className={`flex-1 py-3 text-[10px] uppercase tracking-[0.2em] font-bold transition-all relative
${aiTab === tab ? 'text-manganese' : 'text-muted-ink hover:text-ink/60'}`}
>
{tab}
{aiTab === tab && (
<motion.div
layoutId="activeTab"
className="absolute bottom-0 left-0 right-0 h-0.5 bg-ochre"
/>
)}
</button>
))}
</div>
<div className="flex-1 overflow-y-auto p-6 custom-scrollbar">
<AnimatePresence mode="wait">
{aiTab === 'discussion' && (
<motion.div
key="discussion"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="space-y-8"
>
<div className="h-64 flex flex-col items-center justify-center text-center space-y-4 text-muted-ink/40">
<div className="w-16 h-16 rounded-full border border-dashed border-muted-ink/20 flex items-center justify-center">
<MessageSquare size={24} />
</div>
<p className="text-xs font-serif italic leading-relaxed px-8">Posez une question à l'Assistant pour commencer.</p>
</div>
<div className="space-y-4">
<div className="space-y-3">
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted-ink">Contexte</label>
<div className="w-full p-3 bg-glass border border-border rounded-lg text-xs flex items-center justify-between cursor-pointer hover:bg-white/40 transition-colors backdrop-blur-sm">
<div className="flex items-center gap-2">
<FileCode size={14} className="text-muted-ink" />
<span>Cette note</span>
</div>
<ChevronRight size={14} className="rotate-90 text-muted-ink" />
</div>
</div>
<div className="space-y-3">
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted-ink">Ton d'écriture</label>
<div className="grid grid-cols-2 gap-2">
{(['Professional', 'Creative', 'Academic', 'Casual'] as AITone[]).map((tone) => (
<button
key={tone}
onClick={() => setSelectedTone(tone)}
className={`p-3 rounded-xl border text-[11px] font-medium transition-all
${selectedTone === tone ? 'bg-manganese text-paper border-manganese shadow-lg shadow-manganese/10' : 'bg-glass border-border text-muted-ink hover:border-ink/20'}`}
>
{tone.toUpperCase().substring(0, 3)}
</button>
))}
</div>
</div>
</div>
</motion.div>
)}
{aiTab === 'actions' && (
<motion.div
key="actions"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="space-y-8"
>
<div className="space-y-8">
<div>
<div className="flex items-center gap-2 mb-4">
<div className="h-px flex-1 bg-border/40" />
<h4 className="text-[10px] uppercase tracking-[0.25em] font-bold text-muted-ink whitespace-nowrap">Transformations</h4>
<div className="h-px flex-1 bg-border/40" />
</div>
<div className="grid grid-cols-2 gap-2">
{[
{ icon: <Sparkles size={14} />, label: 'Clarifier', color: 'ochre' },
{ icon: <Scissors size={14} />, label: 'Raccourcir', color: 'rust' },
{ icon: <Zap size={14} />, label: 'Améliorer', color: 'sage' },
{ icon: <Languages size={14} />, label: 'Traduire', color: 'slate' },
].map((action, i) => (
<button
key={i}
className="flex flex-col items-center gap-3 p-4 bg-glass border border-border rounded-xl transition-all group hover:border-ink/20"
>
<div className={`p-2 rounded-lg bg-slate-50 dark:bg-white/10 transition-colors group-hover:bg-manganese group-hover:text-paper shadow-sm text-ink/60`}>
{action.icon}
</div>
<span className="text-[10px] font-bold text-ink/80 uppercase tracking-widest">{action.label}</span>
</button>
))}
<button className="col-span-2 flex items-center justify-center gap-3 py-3 px-4 bg-glass border border-border rounded-xl text-[10px] font-bold text-ink/80 hover:bg-slate-50 dark:hover:bg-white/10 transition-colors hover:border-ink/20 uppercase tracking-widest">
<FileCode size={14} className="text-muted-ink" />
Convertir en Markdown
</button>
</div>
</div>
<div className="space-y-4">
<div className="flex items-center gap-2 mb-2">
<div className="h-px flex-1 bg-border/40" />
<h4 className="text-[10px] uppercase tracking-[0.25em] font-bold text-muted-ink whitespace-nowrap">Generation Tools</h4>
<div className="h-px flex-1 bg-border/40" />
</div>
<div className="group relative p-6 rounded-2xl bg-white border border-border hover:border-blueprint/30 transition-all duration-500 overflow-hidden">
<div className="absolute top-0 right-0 p-4 opacity-5 group-hover:opacity-10 transition-opacity">
<Layout size={80} className="text-blueprint" />
</div>
<div className="relative space-y-5">
<div className="flex items-center gap-3">
<div className="p-2 bg-slate-50 rounded-lg text-blueprint">
<Layout size={18} />
</div>
<div className="space-y-0.5">
<h5 className="text-sm font-bold text-ink leading-none">Présentation</h5>
<p className="text-[10px] text-muted-ink uppercase tracking-tight">Convertir en slides interactives</p>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<span className="text-[9px] uppercase tracking-[0.2em] font-bold text-muted-ink/60 px-1">Thème</span>
<select className="w-full bg-glass dark:bg-black/20 border border-border rounded-lg px-2 py-2 text-xs outline-none focus:ring-1 ring-blueprint/10 transition-all cursor-pointer">
<option>Architectural Mono</option>
<option>Vibrant Tech</option>
<option>Minimal Silk</option>
</select>
</div>
<div className="space-y-1.5">
<span className="text-[9px] uppercase tracking-[0.2em] font-bold text-muted-ink/60 px-1">Style</span>
<select className="w-full bg-glass dark:bg-black/20 border border-border rounded-lg px-2 py-2 text-xs outline-none focus:ring-1 ring-blueprint/10 transition-all cursor-pointer">
<option>Professional</option>
<option>Creative</option>
<option>Brutalist</option>
</select>
</div>
</div>
<button className="w-full py-3.5 bg-blueprint text-paper rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:opacity-90 transition-all shadow-lg shadow-blueprint/20 uppercase tracking-[0.2em]">
Générer
<ArrowRightLeft size={14} className="opacity-60" />
</button>
</div>
</div>
<div className="group relative p-6 rounded-2xl bg-white border border-border hover:border-sage/30 transition-all duration-500 overflow-hidden">
<div className="absolute top-0 right-0 p-4 opacity-5 group-hover:opacity-10 transition-opacity">
<BookOpen size={80} className="text-sage" />
</div>
<div className="relative space-y-5">
<div className="flex items-center gap-3">
<div className="p-2 bg-slate-50 rounded-lg text-sage">
<BookOpen size={18} />
</div>
<div className="space-y-0.5">
<h5 className="text-sm font-bold text-ink leading-none">Diagramme</h5>
<p className="text-[10px] text-muted-ink uppercase tracking-tight">Visualisation de structure</p>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<span className="text-[9px] uppercase tracking-[0.2em] font-bold text-muted-ink/60 px-1">Type</span>
<select className="w-full bg-glass dark:bg-black/20 border border-border rounded-lg px-2 py-2 text-xs outline-none focus:ring-1 ring-sage/10 transition-all cursor-pointer">
<option>Logic Flow</option>
<option>Mind Map</option>
<option>Hierarchy</option>
</select>
</div>
<div className="space-y-1.5">
<span className="text-[9px] uppercase tracking-[0.2em] font-bold text-muted-ink/60 px-1">Style</span>
<select className="w-full bg-glass dark:bg-black/20 border border-border rounded-lg px-2 py-2 text-xs outline-none focus:ring-1 ring-sage/10 transition-all cursor-pointer">
<option>Draft</option>
<option>Polished</option>
<option>Handwritten</option>
</select>
</div>
</div>
<button className="w-full py-3.5 bg-sage text-paper rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:opacity-90 transition-all shadow-lg shadow-sage/20 uppercase tracking-[0.2em]">
Tracer
<ArrowRightLeft size={14} className="opacity-60" />
</button>
</div>
</div>
</div>
<div className="flex flex-col items-center gap-2 opacity-20 py-4">
<History size={16} />
<span className="text-[10px] font-bold uppercase tracking-widest whitespace-nowrap">Auto-Save Enabled</span>
</div>
</div>
</motion.div>
)}
{aiTab === 'resources' && (
<motion.div
key="resources"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="space-y-8"
>
<div className="space-y-6">
<div className="space-y-2">
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted-ink">URL (Optionnel)</label>
<div className="relative">
<input type="text" placeholder="https://..." className="w-full bg-glass border border-border rounded-lg pl-3 pr-10 py-3 text-xs outline-none focus:border-blueprint transition-colors" />
<Globe size={14} className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-ink/40" />
</div>
</div>
<div className="space-y-2">
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted-ink">Texte de la ressource</label>
<textarea
rows={8}
placeholder="Collez votre texte ici (markdown, HTML, texte brut...)"
className="w-full bg-glass border border-border rounded-lg p-4 text-xs outline-none focus:border-blueprint transition-colors resize-none leading-relaxed"
/>
</div>
<div className="space-y-3">
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-muted-ink">Mode d'intégration</label>
<div className="grid grid-cols-3 gap-2">
{[
{ id: 'replace', label: 'Remplacer', sub: 'Direct, sans IA' },
{ id: 'append', label: 'Compléter', sub: 'Ajoute sans réécrire' },
{ id: 'merge', label: 'Fusionner', sub: 'Réécrit et intègre' },
].map((mode) => (
<button key={mode.id} className={`flex flex-col items-center justify-center p-3 rounded-lg border transition-all text-center ${mode.id === 'append' ? 'bg-sage/10 border-sage/50 ring-1 ring-sage/10' : 'bg-white border-border hover:bg-slate-50'}`}>
<span className={`text-[11px] font-bold ${mode.id === 'append' ? 'text-sage' : 'text-ink'}`}>{mode.label}</span>
<span className="text-[8px] text-muted-ink opacity-60 leading-tight mt-1 font-medium">{mode.sub}</span>
</button>
))}
</div>
</div>
<button className="w-full py-4 bg-blueprint text-white rounded-xl text-sm font-bold flex items-center justify-center gap-3 hover:opacity-90 transition-opacity shadow-lg shadow-blueprint/20">
<Sparkles size={18} />
Générer l'aperçu
</button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
<AnimatePresence>
{aiTab === 'discussion' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 20 }}
className="p-6 bg-white border-t border-border"
>
<div className="relative">
<textarea
rows={3}
placeholder="Posez une question sur cette note..."
className="w-full bg-glass backdrop-blur-sm border border-border rounded-2xl p-4 pr-12 text-sm outline-none focus:border-blueprint transition-colors resize-none leading-relaxed font-light"
/>
<div className="absolute right-3 bottom-3 flex gap-2">
<button className="p-2 text-muted-ink hover:text-ink rounded-lg transition-colors">
<Globe size={16} />
</button>
<button className="p-2 bg-blueprint text-white rounded-lg transition-transform hover:scale-105 active:scale-95 shadow-lg shadow-blueprint/10">
<Send size={16} />
</button>
</div>
</div>
<p className="text-[9px] text-muted-ink text-center mt-3 uppercase tracking-widest font-bold opacity-30 italic">Maj+Entrée = nouvelle ligne</p>
</motion.div>
)}
</AnimatePresence>
</motion.aside>
)}
</AnimatePresence>
);
};

View File

@@ -1,347 +0,0 @@
import React from 'react';
import {
Plus,
ArrowLeft,
Clock,
Activity,
Trash2,
Edit3,
Play,
Eye,
Microscope,
Globe,
Layers,
Zap,
BookOpen,
Sparkles
} from 'lucide-react';
import { motion } from 'motion/react';
interface AgentsViewProps {
selectedAgentId: string | null;
setSelectedAgentId: (id: string | null) => void;
}
export const AgentsView: React.FC<AgentsViewProps> = ({
selectedAgentId,
setSelectedAgentId
}) => {
return (
<div className="h-full flex flex-col overflow-y-auto custom-scrollbar">
{!selectedAgentId ? (
<>
<header className="px-12 pt-12 pb-8 flex flex-col gap-6 sticky top-0 bg-paper/80 backdrop-blur-md z-30">
<div className="flex justify-between items-end">
<div className="space-y-1">
<h1 className="text-4xl font-serif font-medium tracking-tight text-ink">Mes Agents</h1>
<p className="text-sm text-muted-ink font-light">Automatisez vos tâches de veille et de recherche.</p>
</div>
<button className="px-6 py-2.5 bg-ink text-paper text-sm font-medium rounded-xl hover:opacity-90 transition-all flex items-center gap-3 shadow-lg shadow-ink/10">
<Plus size={18} />
Nouvel Agent
</button>
</div>
<div className="flex items-center gap-8 border-b border-ink/5 pt-4">
{['Tous', 'Veilleur', 'Chercheur', 'Surveillant', 'Personnalisé'].map((tag, i) => (
<button key={i} className={`pb-4 text-xs font-bold uppercase tracking-widest transition-all relative ${i === 0 ? 'text-ink' : 'text-muted-ink hover:text-ink/60'}`}>
{tag}
{i === 0 && <motion.div layoutId="activeAgentTag" className="absolute bottom-0 left-0 right-0 h-0.5 bg-ink" />}
</button>
))}
</div>
</header>
<div className="px-12 flex-1 pb-20 space-y-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[
{ id: 'a1', icon: <Eye size={20} className="text-amber-600" />, title: 'Surveillant de Notes', status: 'Réussi', type: 'SURVEILLANT', meta: 'Hebdomadaire • 6 exéc.', desc: 'Analyse les notes récentes dun carnet et suggère des compléments, références et liens.' },
{ id: 'a2', icon: <Microscope size={20} className="text-indigo-600" />, title: 'Chercheur de Sujet', status: 'Réussi', type: 'CHERCHEUR', meta: 'Hebdomadaire • 14 exéc.', desc: 'Recherche des informations approfondies sur les derniers modèles de Deepseek et voir lavis des utilisateurs.' },
{ id: 'a3', icon: <Globe size={20} className="text-emerald-600" />, title: 'Veille IA', status: 'Réussi', type: 'VEILLEUR', meta: 'Quotidien • 20 exéc.', desc: 'Scrape les flux RSS de 6 sites IA (The Verge, TechCrunch...) et génère un résumé.' },
].map((agent, i) => (
<div
key={i}
onClick={() => setSelectedAgentId(agent.id)}
className="bg-white dark:bg-white/5 border border-border rounded-2xl p-6 space-y-6 hover:border-ink/20 transition-all group cursor-pointer shadow-sm relative overflow-hidden"
>
<div className="flex items-start justify-between">
<div className="flex items-center gap-4">
<div className="p-3 bg-slate-50 dark:bg-white/10 rounded-xl group-hover:bg-ink group-hover:text-paper transition-all">
{agent.icon}
</div>
<div className="space-y-1">
<h4 className="text-[13px] font-bold text-ink">{agent.title}</h4>
<p className="text-[10px] font-bold uppercase tracking-widest text-muted-ink opacity-60">{agent.type}</p>
</div>
</div>
<div className="flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
<label className="relative inline-flex items-center cursor-pointer">
<input type="checkbox" className="sr-only peer" defaultChecked />
<div className="w-8 h-4 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-3 after:w-3 after:transition-all peer-checked:bg-emerald-500"></div>
</label>
</div>
</div>
<p className="text-xs text-muted-ink leading-relaxed line-clamp-3">
{agent.desc}
</p>
<div className="space-y-3">
<div className="flex items-center justify-between text-[10px] text-muted-ink font-medium">
<div className="flex items-center gap-4">
<span className="flex items-center gap-1"><Clock size={10} /> {agent.meta.split('•')[0]}</span>
<span>{agent.meta.split('•')[1]}</span>
</div>
</div>
<div className="flex items-center justify-between text-[10px] text-muted-ink font-medium">
<div className="flex items-center gap-2">
<span className="uppercase tracking-tight">Prochaine exécution</span>
<span className="text-ink">Hebdomadaire</span>
</div>
<div className="flex items-center gap-2">
<span className="uppercase tracking-tight">Dernier statut</span>
<span className="text-emerald-600 flex items-center gap-1"><Activity size={8} /> {agent.status}</span>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-2 border-t border-border pt-4">
<button className="py-2 border border-border rounded-lg hover:bg-slate-50 dark:hover:bg-white/5 flex items-center justify-center transition-colors text-muted-ink hover:text-ink"><Edit3 size={14} /> <span className="ml-2 text-[10px] font-bold uppercase">Modifier</span></button>
<button
onClick={(e) => { e.stopPropagation(); }}
className="py-2 border border-border rounded-lg hover:bg-slate-50 dark:hover:bg-white/5 flex items-center justify-center transition-colors text-muted-ink hover:text-ink"
>
<Play size={14} className="fill-current" />
</button>
<button
onClick={(e) => { e.stopPropagation(); }}
className="py-2 border border-border rounded-lg hover:bg-rose-50 hover:text-rose-600 hover:border-rose-100 flex items-center justify-center transition-colors text-muted-ink"
>
<Trash2 size={14} />
</button>
</div>
</div>
))}
</div>
<div className="space-y-8">
<div className="flex items-center gap-4">
<h5 className="text-[10px] font-bold uppercase tracking-[0.3em] text-muted-ink whitespace-nowrap">Modèles</h5>
<div className="h-px w-full bg-border/40" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[
{ title: 'Veille IA', desc: 'Scrape les flux RSS de 6 sites IA et génère un résumé hebdomadaire.', icon: <Globe size={18} /> },
{ title: 'Veille Tech', desc: 'Crée un résumé quotidien des news Hacker News et Product Hunt.', icon: <Zap size={18} /> },
{ title: 'Veille Dev', desc: 'Surveille les repos GitHub pour détecter les nouvelles releases.', icon: <Layers size={18} /> },
].map((model, i) => (
<div key={i} className="bg-white/40 dark:bg-white/5 border border-dashed border-border rounded-2xl p-6 group cursor-pointer hover:bg-white dark:hover:bg-white/10 hover:border-ink/20 transition-all">
<div className="w-8 h-8 rounded-lg bg-slate-50 dark:bg-white/10 flex items-center justify-center text-muted-ink group-hover:bg-ink group-hover:text-paper mb-4 transition-all">
{model.icon}
</div>
<h4 className="text-[13px] font-bold text-ink mb-2">{model.title}</h4>
<p className="text-xs text-muted-ink leading-relaxed mb-4">{model.desc}</p>
<button className="text-[11px] font-bold uppercase tracking-widest text-ink hover:opacity-60 transition-opacity flex items-center gap-2">
<Plus size={14} /> Installer
</button>
</div>
))}
</div>
</div>
</div>
</>
) : (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="flex-1 flex flex-col"
>
<header className="px-12 py-10 border-b border-border bg-white dark:bg-paper backdrop-blur-md sticky top-0 z-30">
<div className="flex items-center justify-between max-w-5xl mx-auto">
<button
onClick={() => setSelectedAgentId(null)}
className="flex items-center gap-3 text-xs font-bold uppercase tracking-widest text-muted-ink hover:text-ink transition-colors"
>
<ArrowLeft size={16} />
Retour
</button>
<div className="flex items-center gap-4">
<button className="px-5 py-2 text-xs font-bold uppercase tracking-widest border border-border rounded-xl hover:bg-slate-50 dark:hover:bg-white/5 transition-all">
Logs
</button>
<button className="px-6 py-2 bg-ink text-paper text-xs font-bold uppercase tracking-widest rounded-xl hover:opacity-90 transition-all shadow-lg shadow-ink/10">
Enregistrer
</button>
</div>
</div>
</header>
<div className="flex-1 px-12 py-16 max-w-5xl mx-auto w-full space-y-16">
<section className="space-y-8">
<div className="flex items-end justify-between">
<div className="space-y-4">
<div className="flex items-center gap-3">
<div className="p-4 bg-ink text-paper rounded-2xl shadow-xl shadow-ink/10">
{selectedAgentId === 'a1' ? <Eye size={32} /> : selectedAgentId === 'a2' ? <Microscope size={32} /> : <Globe size={32} />}
</div>
<div className="space-y-1">
<h2 className="text-3xl font-serif font-medium text-ink">
{selectedAgentId === 'a1' ? 'Surveillant de Notes' : selectedAgentId === 'a2' ? 'Chercheur de Sujet' : 'Veille IA'}
</h2>
<div className="flex items-center gap-3">
<span className="text-[10px] font-bold uppercase tracking-widest px-2 py-1 bg-slate-100 dark:bg-white/10 text-muted-ink rounded-md">ID: {selectedAgentId}</span>
<span className="text-[10px] font-bold uppercase tracking-widest px-2 py-1 bg-emerald-50 dark:bg-emerald-500/10 text-emerald-600 rounded-md">Actif</span>
</div>
</div>
</div>
</div>
<div className="flex items-center gap-8 text-[12px] uppercase tracking-[0.2em] font-bold text-muted-ink">
<div className="flex flex-col items-end gap-1">
<span className="opacity-40">Dernière exéc.</span>
<span className="text-ink">Il y a 2h</span>
</div>
<div className="flex flex-col items-end gap-1">
<span className="opacity-40">Succès</span>
<span className="text-emerald-600 tracking-normal">98.4%</span>
</div>
</div>
</div>
</section>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
<div className="lg:col-span-2 space-y-12">
<section className="space-y-6">
<h3 className="text-xs font-bold uppercase tracking-[0.3em] text-muted-ink">Configuration Logique</h3>
<div className="bg-white dark:bg-white/5 border border-border rounded-3xl overflow-hidden shadow-sm">
<div className="p-8 space-y-8">
<div className="space-y-4">
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink">Source de données</label>
<div className="grid grid-cols-2 gap-4">
<div className="p-4 rounded-2xl border-2 border-ink bg-slate-50 dark:bg-white/10 flex items-center justify-between group cursor-pointer transition-all">
<div className="flex items-center gap-3">
<BookOpen size={18} className="text-ink" />
<span className="text-sm font-medium text-ink">Carnets Externes</span>
</div>
<div className="w-5 h-5 rounded-full border-4 border-ink flex items-center justify-center">
<div className="w-2 h-2 bg-ink rounded-full" />
</div>
</div>
<div className="p-4 rounded-2xl border border-border hover:border-ink/20 flex items-center justify-between group cursor-pointer transition-all">
<div className="flex items-center gap-3">
<Globe size={18} className="text-muted-ink" />
<span className="text-sm font-medium text-muted-ink">Web (Flux RSS/SEO)</span>
</div>
<div className="w-5 h-5 rounded-full border border-border" />
</div>
</div>
</div>
<div className="space-y-4">
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink">Modèle d'Intelligence</label>
<select className="w-full bg-slate-50 dark:bg-black/20 border border-border rounded-xl px-4 py-4 text-sm outline-none focus:ring-2 ring-ink/5 transition-all cursor-pointer font-medium text-ink">
<option>Gemini 2.0 Flash (Optimisé)</option>
<option>Gemini 1.5 Pro (Analytique)</option>
<option>Claude 3.5 Sonnet (Via API)</option>
</select>
</div>
<div className="space-y-4">
<label className="block text-[11px] uppercase tracking-widest font-bold text-muted-ink">Instruction Système (Prompt)</label>
<textarea
rows={6}
className="w-full bg-slate-50 dark:bg-black/20 border border-border rounded-2xl p-6 text-sm outline-none focus:ring-2 ring-ink/5 transition-all font-light leading-relaxed resize-none text-ink"
defaultValue="Tu es un assistant spécialisé dans l'analyse de notes architecturales. Ta mission est de scanner les nouveaux carnets, extraire les concepts de matérialité et suggérer des références historiques pertinentes."
/>
</div>
</div>
</div>
</section>
<section className="space-y-6">
<h3 className="text-xs font-bold uppercase tracking-[0.3em] text-muted-ink">Actions de Sortie</h3>
<div className="grid grid-cols-2 gap-4">
<div className="p-6 bg-white dark:bg-white/5 border border-border rounded-2xl space-y-4 hover:border-ink/20 transition-all cursor-pointer group">
<div className="w-10 h-10 rounded-xl bg-slate-50 dark:bg-white/10 flex items-center justify-center text-muted-ink group-hover:bg-ink group-hover:text-paper transition-all">
<Plus size={18} />
</div>
<div className="space-y-1">
<h4 className="text-sm font-bold text-ink leading-tight">Nouvelle Note</h4>
<p className="text-[10px] text-muted-ink font-medium uppercase tracking-tight">Créer un brouillon auto.</p>
</div>
</div>
<div className="p-6 bg-white dark:bg-white/5 border border-border rounded-2xl space-y-4 hover:border-ink/20 transition-all cursor-pointer group opacity-40 border-dashed">
<div className="w-10 h-10 rounded-xl border border-dashed border-border flex items-center justify-center text-muted-ink group-hover:bg-ink group-hover:text-paper transition-all">
<Sparkles size={18} />
</div>
<div className="space-y-1">
<h4 className="text-sm font-bold text-ink leading-tight">Webhook API</h4>
<p className="text-[10px] text-muted-ink font-medium uppercase tracking-tight">Ajouter une destination</p>
</div>
</div>
</div>
</section>
</div>
<div className="space-y-10">
<section className="space-y-6">
<h3 className="text-xs font-bold uppercase tracking-[0.3em] text-muted-ink">Planification</h3>
<div className="bg-ink rounded-3xl p-8 space-y-8 text-paper shadow-2xl shadow-ink/20 relative overflow-hidden">
<div className="absolute top-0 right-0 p-4 opacity-10">
<Clock size={100} />
</div>
<div className="relative space-y-6">
<div className="space-y-2">
<span className="text-[10px] uppercase tracking-widest font-bold opacity-60">Fréquence</span>
<div className="text-2xl font-serif italic text-paper">Hebdomadaire</div>
</div>
<div className="h-px bg-white/10" />
<div className="space-y-4">
<div className="flex items-center justify-between text-xs">
<span className="opacity-60">Lundi</span>
<span className="font-bold">09:00</span>
</div>
<div className="flex items-center justify-between text-xs">
<span className="opacity-60">Jeudi</span>
<span className="font-bold">14:30</span>
</div>
</div>
<button className="w-full py-3 bg-white text-ink text-xs font-bold uppercase tracking-widest rounded-xl hover:opacity-90 transition-all">
Modifier le planning
</button>
</div>
</div>
</section>
<section className="space-y-6">
<h3 className="text-xs font-bold uppercase tracking-[0.3em] text-muted-ink">Notifications</h3>
<div className="space-y-3">
{[
{ label: 'Rapport d\'exécution', enabled: true },
{ label: 'Erreurs critiques', enabled: true },
{ label: 'Modifications de notes', enabled: false },
].map((item, i) => (
<div key={i} className="flex items-center justify-between p-4 bg-slate-50 dark:bg-white/10 rounded-2xl border border-border/50">
<span className="text-xs font-medium text-ink">{item.label}</span>
<label className="relative inline-flex items-center cursor-pointer">
<input type="checkbox" className="sr-only peer" defaultChecked={item.enabled} />
<div className="w-8 h-4 bg-gray-200 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-3 after:w-3 after:transition-all peer-checked:bg-[#75B2D6]"></div>
</label>
</div>
))}
</div>
</section>
<section className="pt-6">
<button className="w-full py-4 border border-rose-200 text-rose-600 bg-rose-50/50 dark:bg-rose-500/10 rounded-2xl text-xs font-bold uppercase tracking-widest hover:bg-rose-100 transition-colors flex items-center justify-center gap-3">
<Trash2 size={16} />
Supprimer l'agent
</button>
</section>
</div>
</div>
</div>
</motion.div>
)}
</div>
);
};

View File

@@ -1,388 +0,0 @@
import React from 'react';
import {
Plus,
Search,
Share2,
Pin,
ChevronRight,
ArrowLeft,
MoreVertical,
Sparkles,
Tag as TagIcon,
X,
BookOpen
} from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { Note, Carnet, Tag } from '../types';
interface NotebooksViewProps {
activeNoteId: string | null;
activeCarnet: Carnet | undefined;
filteredNotes: Note[];
activeNote: Note | undefined;
setActiveNoteId: (id: string | null) => void;
togglePin: (id: string) => void;
setShowNewNoteModal: (show: boolean) => void;
isAISidebarOpen: boolean;
setIsAISidebarOpen: (open: boolean) => void;
selectedTagIds: string[];
setSelectedTagIds: (ids: string[]) => void;
allNotes: Note[];
activeCarnetId: string;
setShowNewCarnetModal: (show: boolean, parentId?: string) => void;
}
export const NotebooksView: React.FC<NotebooksViewProps> = ({
activeNoteId,
activeCarnet,
filteredNotes,
activeNote,
setActiveNoteId,
togglePin,
setShowNewNoteModal,
isAISidebarOpen,
setIsAISidebarOpen,
selectedTagIds,
setSelectedTagIds,
allNotes,
activeCarnetId,
setShowNewCarnetModal
}) => {
const [isTagsExpanded, setIsTagsExpanded] = React.useState(false);
const [tagSearchQuery, setTagSearchQuery] = React.useState('');
const availableTags = React.useMemo(() => {
const carnetNotes = allNotes.filter(n => n.carnetId === activeCarnetId);
const tagsMap = new Map<string, Tag>();
carnetNotes.forEach(note => {
note.tags?.forEach(tag => {
tagsMap.set(tag.id, tag);
});
});
return Array.from(tagsMap.values()).sort((a, b) => {
// AI tags first, then alphabetical
if (a.type === 'ai' && b.type !== 'ai') return -1;
if (a.type !== 'ai' && b.type === 'ai') return 1;
return a.label.localeCompare(b.label);
});
}, [allNotes, activeCarnetId]);
const visibleTags = React.useMemo(() => {
let filtered = availableTags;
if (tagSearchQuery) {
filtered = availableTags.filter(t =>
t.label.toLowerCase().includes(tagSearchQuery.toLowerCase())
);
} else if (!isTagsExpanded) {
filtered = availableTags.slice(0, 10);
// Ensure selected tags are always visible even if not in the first 10
selectedTagIds.forEach(id => {
if (!filtered.find(t => t.id === id)) {
const tag = availableTags.find(t => t.id === id);
if (tag) filtered.push(tag);
}
});
}
return filtered;
}, [availableTags, isTagsExpanded, tagSearchQuery, selectedTagIds]);
const toggleTag = (tagId: string) => {
if (selectedTagIds.includes(tagId)) {
setSelectedTagIds(selectedTagIds.filter(id => id !== tagId));
} else {
setSelectedTagIds([...selectedTagIds, tagId]);
}
};
if (!activeNoteId) {
return (
<div className="h-full flex flex-col overflow-y-auto">
<header className="px-12 pt-12 pb-8 flex flex-col gap-6 sticky top-0 bg-paper/80 backdrop-blur-md z-30">
<div className="flex justify-between items-start">
<h1 className="text-4xl font-serif font-medium tracking-tight text-ink leading-tight pr-12">
{activeCarnet?.name} {filteredNotes[0]?.date || 'Oct 26'}
</h1>
</div>
<div className="flex items-center justify-between border-b border-ink/5 pb-4">
<div className="flex items-center gap-6">
<button
onClick={() => setShowNewNoteModal(true)}
className="flex items-center gap-2 text-[13px] text-ink font-medium hover:opacity-70 transition-opacity"
>
<Plus size={16} />
<span>Add Note</span>
</button>
<button
onClick={() => setShowNewCarnetModal(true, activeCarnetId)}
className="flex items-center gap-2 text-[13px] text-concrete font-medium hover:text-ink transition-all"
>
<BookOpen size={16} />
<span>New Sub-Carnet</span>
</button>
<button className="flex items-center gap-2 text-[13px] text-ink font-medium hover:opacity-70 transition-opacity">
<Search size={16} />
<span>Search</span>
</button>
</div>
<button className="flex items-center gap-2 text-[13px] text-ink font-medium hover:opacity-70 transition-opacity">
<Share2 size={16} />
<span>Share</span>
</button>
</div>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3 text-[10px] font-bold uppercase tracking-[0.2em] text-concrete">
<TagIcon size={12} />
<span>Filter by Tags</span>
{selectedTagIds.length > 0 && (
<span className="bg-blueprint/10 text-blueprint px-2 py-0.5 rounded-full text-[9px] lowercase tracking-normal">
{selectedTagIds.length} active
</span>
)}
</div>
{availableTags.length > 10 && (
<div className="relative group">
<input
type="text"
placeholder="Search tags..."
className="bg-transparent border-b border-border/40 text-[10px] outline-none focus:border-blueprint/40 py-1 px-2 w-32 transition-all focus:w-48 placeholder:text-concrete/40"
onChange={(e) => setTagSearchQuery(e.target.value)}
/>
</div>
)}
</div>
<div className="flex flex-wrap gap-2 items-center min-h-[32px]">
<AnimatePresence mode="popLayout">
{visibleTags.map(tag => {
const isActive = selectedTagIds.includes(tag.id);
return (
<motion.button
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
key={tag.id}
onClick={() => toggleTag(tag.id)}
className={`px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-wider transition-all border flex items-center gap-2
${isActive
? 'bg-ink text-paper border-ink shadow-lg shadow-ink/10'
: 'bg-white/40 border-border text-concrete hover:border-concrete/40 hover:bg-white/60'}`}
>
{tag.type === 'ai' && (
<Sparkles
size={10}
className={isActive ? 'text-blueprint' : 'text-blueprint/60'}
/>
)}
{tag.label}
{isActive && <X size={10} />}
</motion.button>
);
})}
</AnimatePresence>
{availableTags.length > 10 && !tagSearchQuery && (
<button
onClick={() => setIsTagsExpanded(!isTagsExpanded)}
className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-concrete/60 hover:text-ink transition-colors border border-dashed border-border rounded-full"
>
{isTagsExpanded ? 'Show less' : `+ ${availableTags.length - 10} more`}
</button>
)}
{selectedTagIds.length > 0 && (
<button
onClick={() => setSelectedTagIds([])}
className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-rust hover:underline ml-auto"
>
Clear all
</button>
)}
</div>
</div>
</header>
<div className="px-12 flex-1 pb-20">
<div className="max-w-3xl space-y-16">
{filteredNotes.map((note, index) => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 * index, duration: 0.8 }}
key={note.id}
className="space-y-4 group cursor-pointer relative"
onClick={() => setActiveNoteId(note.id)}
>
<h2 className="text-2xl font-serif font-medium text-ink flex items-center justify-between">
<span className="flex items-center gap-3">
{note.isPinned && <Pin size={18} className="text-amber-500 fill-amber-500" />}
{note.title}
</span>
<div className="flex items-center gap-2">
<button
onClick={(e) => {
e.stopPropagation();
togglePin(note.id);
}}
className={`p-2 rounded-full transition-all ${note.isPinned ? 'text-amber-600 bg-amber-50' : 'opacity-0 group-hover:opacity-40 hover:bg-slate-100 text-ink'}`}
>
<Pin size={16} />
</button>
<button className="opacity-0 group-hover:opacity-40 transition-opacity">
<ChevronRight size={20} />
</button>
</div>
</h2>
<div className="flex flex-col md:flex-row gap-8 items-start">
<div className="w-full md:w-56 aspect-[4/3] bg-white/50 dark:bg-white/5 border border-border overflow-hidden rounded shadow-sm flex-shrink-0">
<img
src={note.imageUrl}
alt={note.title}
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"
referrerPolicy="no-referrer"
/>
</div>
<div className="space-y-3">
<div className="flex flex-wrap gap-2 mb-2">
{note.tags?.map(tag => (
<div
key={tag.id}
className={`px-2 py-0.5 rounded text-[9px] font-bold uppercase tracking-wider border flex items-center gap-1.5
${tag.type === 'ai'
? 'bg-blueprint/5 border-blueprint/20 text-blueprint'
: 'bg-concrete/5 border-border text-concrete'}`}
>
{tag.type === 'ai' && <Sparkles size={8} />}
{tag.label}
</div>
))}
</div>
<p className="text-[14px] leading-relaxed text-ink/80 font-light max-w-lg line-clamp-4">
{note.content}
</p>
<span className="text-[11px] text-muted-ink uppercase tracking-widest font-medium">Read more</span>
</div>
</div>
</motion.div>
))}
{filteredNotes.length === 0 && (
<div className="h-64 flex flex-col items-center justify-center text-center space-y-4">
<p className="font-serif text-xl italic text-muted-ink">This notebook is waiting for its first vision.</p>
<button
onClick={() => setShowNewNoteModal(true)}
className="px-6 py-2 border border-ink text-[13px] uppercase tracking-[0.2em] hover:bg-ink hover:text-paper transition-all"
>
Begin Drawing
</button>
</div>
)}
</div>
</div>
<footer className="px-12 py-6 border-t border-ink/5 text-center mt-auto">
<p className="text-[11px] text-muted-ink uppercase tracking-[0.2em] font-medium">
&copy; 2024 Architectural Grid. All rights reserved.
</p>
</footer>
</div>
);
}
return (
<div className="h-full flex overflow-hidden transition-all duration-500">
<div className="flex-1 flex flex-col overflow-y-auto bg-white dark:bg-paper">
<div className="px-12 py-8 flex items-center justify-between sticky top-0 bg-white/90 dark:bg-paper/90 backdrop-blur-sm z-40 border-b border-border">
<button
onClick={() => setActiveNoteId(null)}
className="flex items-center gap-2 text-ink hover:opacity-60 transition-opacity"
>
<ArrowLeft size={18} />
<span className="text-sm font-medium">Back to collection</span>
</button>
<div className="flex items-center gap-4">
<button
onClick={() => togglePin(activeNoteId!)}
className={`p-2 rounded-full transition-all ${activeNote?.isPinned ? 'text-amber-600 bg-amber-50 dark:bg-ochre/10' : 'text-muted-ink hover:text-ink'}`}
title={activeNote?.isPinned ? "Unpin note" : "Pin note"}
>
<Pin size={18} className={activeNote?.isPinned ? 'fill-amber-600' : ''} />
</button>
<button
onClick={() => setIsAISidebarOpen(!isAISidebarOpen)}
className={`flex items-center gap-2 px-3 py-1.5 rounded-full border transition-all duration-300
${isAISidebarOpen ? 'bg-ink text-paper border-ink' : 'border-border text-ink hover:bg-white/50 dark:hover:bg-white/5'}`}
>
<Sparkles size={16} />
<span className="text-xs font-medium">AI Assistant</span>
</button>
<button className="p-2 text-muted-ink hover:text-ink transition-colors">
<Share2 size={18} />
</button>
<button className="p-2 text-muted-ink hover:text-ink transition-colors">
<MoreVertical size={18} />
</button>
</div>
</div>
<div className="max-w-4xl mx-auto w-full px-12 py-16 space-y-12">
<div className="space-y-4">
<div className="flex items-center gap-3 text-[12px] text-muted-ink uppercase tracking-[.25em] font-bold">
<span>{activeCarnet?.name}</span>
<ChevronRight size={10} />
<span>{activeNote?.date}</span>
</div>
<h1 className="text-5xl md:text-6xl font-serif font-bold text-ink leading-tight">
{activeNote?.title}
</h1>
<div className="flex flex-wrap gap-2 pt-2">
{activeNote?.tags?.map(tag => (
<div
key={tag.id}
className={`px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest border flex items-center gap-2
${tag.type === 'ai'
? 'bg-blueprint/5 border-blueprint/20 text-blueprint'
: 'bg-paper border-border text-concrete'}`}
>
{tag.type === 'ai' && <Sparkles size={12} />}
{tag.label}
{tag.type === 'ai' && (
<div className="w-1.5 h-1.5 rounded-full bg-blueprint animate-pulse" />
)}
</div>
))}
</div>
</div>
<div className="aspect-[16/9] w-full bg-slate-100 dark:bg-white/5 rounded-xl overflow-hidden shadow-xl">
<img
src={activeNote?.imageUrl}
alt={activeNote?.title}
className="w-full h-full object-cover grayscale contrast-110"
referrerPolicy="no-referrer"
/>
</div>
<div className="max-w-2xl mx-auto space-y-8 pb-32">
<p className="text-xl md:text-2xl font-serif leading-relaxed text-ink italic">
{activeNote?.content.split('.')[0]}.
</p>
<div className="h-px bg-border w-32" />
<p className="text-lg leading-relaxed text-ink/80 font-light space-y-4 text-justify whitespace-pre-line">
{activeNote?.content}
{activeNote?.id.startsWith('n-') && (
<>
<br /><br />
Architectural grids serve as the invisible scaffolding upon which spatial experiences are constructed. Beyond mere structural repetition, they facilitate a rhythmic dialogue between materiality and void. In this exploration, we examine how light fractures these rigid boundaries, creating a dynamic interplay that evolves with the passage of time.
</>
)}
</p>
</div>
</div>
</div>
</div>
);
};

View File

@@ -1,66 +0,0 @@
import React from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { SettingsTab } from '../types';
import { SettingsHeader } from './settings/SettingsHeader';
import { GeneralTab } from './settings/GeneralTab';
import { AITab } from './settings/AITab';
import { AppearanceTab } from './settings/AppearanceTab';
interface SettingsViewProps {
activeSettingsTab: SettingsTab;
setActiveSettingsTab: (tab: SettingsTab) => void;
}
export const SettingsView: React.FC<SettingsViewProps> = ({
activeSettingsTab,
setActiveSettingsTab
}) => {
return (
<div className="h-full flex flex-col bg-paper dark:bg-dark-paper overflow-y-auto custom-scrollbar relative font-sans">
<div className="absolute inset-0 opacity-[0.04] pointer-events-none grainy-bg mix-blend-multiply dark:mix-blend-overlay" />
<div className="relative z-10 flex flex-col min-h-full">
<SettingsHeader
activeTab={activeSettingsTab}
setActiveTab={setActiveSettingsTab}
/>
<div className="flex-1 px-12 pb-24 h-full">
<div className="max-w-6xl mx-auto">
<AnimatePresence mode="wait">
{activeSettingsTab === 'general' && (
<GeneralTab key="general" />
)}
{activeSettingsTab === 'ai' && (
<AITab key="ai" />
)}
{activeSettingsTab === 'appearance' && (
<AppearanceTab key="appearance" />
)}
{['profile', 'data', 'mcp', 'about'].includes(activeSettingsTab) && (
<motion.div
key="placeholder"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="h-[50vh] flex flex-col items-center justify-center border border-dashed border-border rounded-[32px] space-y-6 bg-white/20 dark:bg-white/5"
>
<div className="w-16 h-16 rounded-3xl border border-dashed border-concrete/20 flex items-center justify-center text-concrete/40 bg-paper/50">
<span className="text-2xl font-serif italic text-concrete">?</span>
</div>
<div className="text-center space-y-1">
<p className="text-ink font-bold text-sm tracking-tight">Section en développement</p>
<p className="text-concrete italic text-[11px] font-light">Le module {activeSettingsTab} sera disponible prochainement.</p>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
</div>
</div>
</div>
);
};

View File

@@ -1,350 +0,0 @@
import React from 'react';
import {
Plus,
Archive,
Settings,
ChevronRight,
BookOpen,
Bot,
Microscope,
Activity,
Pin,
Moon,
Sun,
Bell,
Lock
} from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { NavigationView, Carnet, Note } from '../types';
interface NoteLinkProps {
note: Note;
isActive: boolean;
onClick: () => void;
}
const NoteLink: React.FC<NoteLinkProps> = ({ note, isActive, onClick }) => (
<motion.button
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
onClick={onClick}
className={`w-full flex items-center gap-2 pl-12 pr-4 py-2 text-[12px] transition-colors rounded-lg
${isActive ? 'bg-white/50 dark:bg-white/10 text-ink font-medium' : 'text-muted-ink hover:text-ink hover:bg-white/30'}`}
>
<div className="flex items-center gap-2 flex-1 truncate">
<div className={`w-1.5 h-1.5 rounded-full shrink-0 ${isActive ? 'bg-ink' : 'bg-transparent border border-muted-ink/30'}`} />
<span className="truncate">{note.title}</span>
</div>
{note.isPinned && <Pin size={10} className="text-amber-500 shrink-0" />}
</motion.button>
);
interface SidebarItemProps {
carnet: Carnet;
isActive: boolean;
notes: Note[];
activeNoteId: string | null;
onCarnetClick: () => void;
onNoteClick: (noteId: string) => void;
onAddSubCarnet: () => void;
children?: React.ReactNode;
level: number;
isExpanded: boolean;
toggleExpand: () => void;
}
const SidebarItem: React.FC<SidebarItemProps> = ({
carnet,
isActive,
notes,
activeNoteId,
onCarnetClick,
onNoteClick,
onAddSubCarnet,
children,
level,
isExpanded,
toggleExpand
}) => {
const hasChildren = React.Children.count(children) > 0;
return (
<div className="space-y-0.5">
<div
className="flex items-center group relative h-10"
style={{ paddingLeft: `${level * 12}px` }}
>
{/* Hierarchy Guide Line */}
{level > 0 && (
<div className="absolute left-[8px] top-[-10px] bottom-1/2 w-px bg-border/40" />
)}
{level > 0 && (
<div className="absolute left-[8px] top-1/2 w-[8px] h-px bg-border/40" />
)}
<div className="flex-1 flex items-center gap-1">
{hasChildren ? (
<button
onClick={(e) => {
e.stopPropagation();
toggleExpand();
}}
className="p-1 hover:bg-ink/5 dark:hover:bg-white/5 rounded-md transition-colors text-muted-ink"
>
<motion.div animate={{ rotate: isExpanded ? 90 : 0 }} transition={{ duration: 0.2 }}>
<ChevronRight size={14} />
</motion.div>
</button>
) : (
<div className="w-6" /> // Spacer for alignment
)}
<motion.button
whileHover={{ x: 2 }}
onClick={onCarnetClick}
className={`flex-1 flex items-center gap-2.5 px-2 py-1.5 rounded-lg transition-all duration-300
${isActive ? 'bg-white shadow-sm border border-border/40 dark:bg-white/10' : 'hover:bg-white/40 dark:hover:bg-white/5'}`}
>
<div className={`w-6 h-6 rounded-md flex items-center justify-center text-[10px] font-bold border transition-all
${isActive ? 'bg-ink text-paper border-ink' : 'bg-paper dark:bg-white/10 text-concrete border-border dark:border-white/10'}`}>
{carnet.initial}
</div>
<div className="flex-1 text-left flex items-center gap-2">
<span className={`text-[12px] font-medium transition-colors truncate ${isActive ? 'text-ink' : 'text-muted-ink hover:text-ink'}`}>
{carnet.name}
</span>
{carnet.isPrivate && <Lock size={10} className="text-concrete/60 shrink-0" />}
</div>
<div className="flex items-center gap-1.5">
<button
onClick={(e) => {
e.stopPropagation();
onAddSubCarnet();
}}
className="opacity-0 group-hover:opacity-100 p-1 hover:bg-ink/10 dark:hover:bg-white/10 rounded-md transition-all text-concrete hover:text-ink"
title="Add sub-carnet"
>
<Plus size={10} />
</button>
{notes.length > 0 && (
<span className="text-[9px] font-bold text-concrete/40 px-1.5 border border-border/40 rounded-full group-hover:text-concrete transition-colors">
{notes.length}
</span>
)}
</div>
</motion.button>
</div>
</div>
<AnimatePresence initial={false}>
{(isExpanded || (isActive && !hasChildren)) && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.23, 1, 0.32, 1] }}
className="overflow-hidden"
>
<div className="relative" style={{ marginLeft: `${(level + 1) * 12 + 10}px` }}>
{/* Vertical line for nested content */}
<div className="absolute left-[-6px] top-0 bottom-4 w-px bg-border/30" />
<div className="space-y-1 py-1">
{children}
{isActive && !hasChildren && notes.map(note => (
<NoteLink
key={note.id}
note={note}
isActive={activeNoteId === note.id}
onClick={() => onNoteClick(note.id)}
/>
))}
{isActive && !hasChildren && notes.length === 0 && (
<p className="pl-8 py-2 text-[10px] italic text-concrete/40 font-light">
No notes found
</p>
)}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
};
interface SidebarProps {
activeView: NavigationView;
isDarkMode: boolean;
setIsDarkMode: (val: boolean) => void;
setActiveView: (view: NavigationView) => void;
carnets: Carnet[];
notes: Note[];
activeCarnetId: string;
activeNoteId: string | null;
setActiveCarnetId: (id: string) => void;
setActiveNoteId: (id: string | null) => void;
setShowNewCarnetModal: (show: boolean, parentId?: string) => void;
}
export const Sidebar: React.FC<SidebarProps> = ({
activeView,
isDarkMode,
setIsDarkMode,
setActiveView,
carnets,
notes,
activeCarnetId,
activeNoteId,
setActiveCarnetId,
setActiveNoteId,
setShowNewCarnetModal
}) => {
const [expandedIds, setExpandedIds] = React.useState<Set<string>>(new Set(['4'])); // Default expand Research
const toggleExpand = (id: string) => {
const newSet = new Set(expandedIds);
if (newSet.has(id)) newSet.delete(id);
else newSet.add(id);
setExpandedIds(newSet);
};
const renderCarnetTree = (parentId: string | undefined = undefined, level: number = 0) => {
return carnets
.filter(c => c.parentId === parentId)
.map(carnet => (
<SidebarItem
key={carnet.id}
carnet={carnet}
isActive={activeCarnetId === carnet.id}
notes={notes.filter(n => n.carnetId === carnet.id)}
activeNoteId={activeNoteId}
level={level}
isExpanded={expandedIds.has(carnet.id)}
toggleExpand={() => toggleExpand(carnet.id)}
onAddSubCarnet={() => {
if (!expandedIds.has(carnet.id)) toggleExpand(carnet.id);
setShowNewCarnetModal(true, carnet.id);
}}
onCarnetClick={() => {
setActiveCarnetId(carnet.id);
setActiveNoteId(null);
// Auto expand when clicking
if (!expandedIds.has(carnet.id)) toggleExpand(carnet.id);
}}
onNoteClick={(id) => {
setActiveCarnetId(carnet.id);
setActiveNoteId(id);
}}
>
{renderCarnetTree(carnet.id, level + 1)}
</SidebarItem>
));
};
return (
<aside className="w-80 bg-white/30 dark:bg-[#151515] backdrop-blur-md border-r border-border p-6 flex flex-col z-20 shrink-0 transition-colors duration-500">
<div className="mb-10 flex items-center justify-between">
<div className="w-10 h-10 rounded-full bg-slate-200 dark:bg-white/10 border border-border flex items-center justify-center text-ink font-serif text-lg shadow-sm">
A
</div>
<div className="flex items-center gap-2">
<button
onClick={() => setIsDarkMode(!isDarkMode)}
className="p-2 text-muted-ink hover:text-ink transition-all bg-white/50 dark:bg-white/10 rounded-full border border-border dark:border-white/10"
>
{isDarkMode ? <Sun size={14} /> : <Moon size={14} />}
</button>
<button className="p-2 text-muted-ink hover:text-ink transition-all relative group bg-white/50 dark:bg-white/10 rounded-full border border-border dark:border-white/10">
<Bell size={14} />
<span className="absolute -top-1 -right-1 w-4 h-4 bg-rose-500 text-white text-[9px] font-bold flex items-center justify-center rounded-full border border-white shadow-sm">
3
</span>
</button>
<div className="flex bg-white/50 dark:bg-white/10 p-1 rounded-full border border-border dark:border-white/10 transition-all">
<button
onClick={() => setActiveView('notebooks')}
className={`p-1.5 rounded-full transition-all ${activeView === 'notebooks' ? 'bg-ink text-paper shadow-sm' : 'text-muted-ink hover:text-ink'}`}
title="Carnets"
>
<BookOpen size={14} />
</button>
<button
onClick={() => setActiveView('agents')}
className={`p-1.5 rounded-full transition-all ${activeView === 'agents' ? 'bg-ink text-paper shadow-sm' : 'text-muted-ink hover:text-ink'}`}
title="Agents"
>
<Bot size={14} />
</button>
</div>
</div>
</div>
<div className="flex-1 overflow-y-auto space-y-8 -mx-2 px-2 py-4 custom-scrollbar">
{activeView === 'notebooks' ? (
<div className="space-y-6">
<div className="flex items-center justify-between px-4">
<p className="text-[10px] font-bold text-concrete tracking-[0.2em] uppercase">
Architecture Grid
</p>
<button
onClick={() => setShowNewCarnetModal(true)}
className="p-1 hover:bg-paper dark:hover:bg-white/5 rounded-md text-concrete hover:text-ink transition-colors"
title="New Carnet"
>
<Plus size={14} />
</button>
</div>
<nav className="space-y-0.5">
{renderCarnetTree()}
</nav>
</div>
) : activeView === 'agents' ? (
<div>
<p className="text-[10px] font-bold text-muted-ink tracking-widest uppercase mb-4 px-4">
Intelligence OS
</p>
<div className="space-y-1">
{[
{ id: 'a1', name: 'Mes Agents', icon: <Bot size={16} /> },
{ id: 'a2', name: 'Le Lab AI', icon: <Microscope size={16} /> },
{ id: 'a3', name: 'Activités', icon: <Activity size={16} /> },
].map(item => (
<button
key={item.id}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl transition-all duration-300 group
${item.id === 'a1' ? 'active-nav-item' : 'text-muted-ink hover:bg-white/40 dark:hover:bg-white/5 hover:text-ink'}`}
>
<div className={`w-8 h-8 rounded-full flex items-center justify-center border transition-colors
${item.id === 'a1' ? 'bg-ink text-paper border-ink' : 'bg-white/60 dark:bg-white/10 border-border dark:border-white/10 group-hover:border-ink/20'}`}>
{item.icon}
</div>
<span className="text-[13px] font-medium">{item.name}</span>
</button>
))}
</div>
</div>
) : null}
</div>
<div className="pt-6 border-t border-border space-y-4">
<button className="flex items-center gap-3 px-4 text-[13px] text-muted-ink hover:text-ink transition-colors font-medium group">
<Archive size={16} className="text-muted-ink group-hover:text-ink" />
<span>Archive</span>
</button>
<button
onClick={() => setActiveView('settings')}
className={`flex items-center gap-3 px-4 text-[13px] transition-colors font-medium group ${activeView === 'settings' ? 'text-ink' : 'text-muted-ink hover:text-ink'}`}
>
<Settings size={16} className={activeView === 'settings' ? 'text-ink' : 'text-muted-ink group-hover:text-ink'} />
<span>Settings</span>
</button>
</div>
</aside>
);
};

View File

@@ -1,152 +0,0 @@
import React from 'react';
import { Sparkles, Edit3, MessageCircle, Languages, Tag, History, FlaskConical } from 'lucide-react';
import { motion } from 'motion/react';
const AISettingCard = ({ icon, title, description, defaultChecked = false }: any) => (
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-2xl p-6 flex items-center justify-between group hover:shadow-xl hover:shadow-blueprint/5 transition-all duration-300">
<div className="flex items-center gap-5">
<div className="p-3 bg-paper dark:bg-blueprint/10 rounded-2xl text-blueprint group-hover:bg-blueprint group-hover:text-white group-hover:scale-110 transition-all duration-300 border border-blueprint/20">
{icon}
</div>
<div className="space-y-1">
<h4 className="text-[13px] font-bold text-ink">{title}</h4>
<p className="text-[10px] text-muted-ink leading-relaxed pr-4 line-clamp-2">{description}</p>
</div>
</div>
<label className="relative inline-flex items-center cursor-pointer shrink-0 ml-4">
<input type="checkbox" className="sr-only peer" defaultChecked={defaultChecked} />
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] peer-checked:after:border-white after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all duration-300 ease-in-out peer-checked:bg-blueprint"></div>
</label>
</div>
);
export const AITab: React.FC = () => {
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-16 pb-20"
>
<div className="space-y-10">
<h3 className="text-[10px] font-bold uppercase tracking-[0.4em] text-muted-ink opacity-60">Configurez vos fonctionnalités IA et préférences</h3>
<div className="space-y-6">
<h4 className="text-sm font-bold text-ink border-b border-border/40 pb-4">Fonctionnalités IA</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<AISettingCard
icon={<Edit3 size={18} />}
title="Suggestions de titre"
description="Suggérer des titres pour les notes sans titre après 50+ mots"
defaultChecked
/>
<AISettingCard
icon={<Sparkles size={18} />}
title="IA Note"
description="Active le bouton de chat IA et les outils d'amélioration du texte"
defaultChecked
/>
<AISettingCard
icon={<MessageCircle size={18} />}
title="💡 J'ai remarqué quelque chose..."
description="Aperçu quotidien de vos notes"
defaultChecked
/>
<AISettingCard
icon={<Languages size={18} />}
title="Détection de langue"
description="Détecte automatiquement la langue de vos notes"
defaultChecked
/>
<AISettingCard
icon={<Tag size={18} />}
title="Suggestion des labels"
description="Suggère et applique des étiquettes automatiquement à vos notes"
defaultChecked
/>
<AISettingCard
icon={<History size={18} />}
title="Historique des notes"
description="Active les snapshots de versions et la restauration depuis History"
defaultChecked
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 pt-6">
{/* Fréquence */}
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-2xl p-8 space-y-10">
<div className="space-y-1.5 text-left text-blueprint">
<h4 className="text-sm font-bold">Fréquence</h4>
<p className="text-[10px] opacity-60 uppercase tracking-wider font-semibold">Fréquence d'analyse des connexions</p>
</div>
<div className="space-y-6">
<label className="flex items-center gap-4 cursor-pointer group">
<input type="radio" name="freq" className="sr-only peer" defaultChecked />
<div className="w-5 h-5 rounded-full border-2 border-border peer-checked:border-blueprint flex items-center justify-center p-0.5 transition-all">
<div className="w-full h-full rounded-full bg-transparent peer-checked:bg-blueprint" />
</div>
<span className="text-sm font-medium text-ink group-hover:opacity-70 transition-opacity">Quotidienne</span>
</label>
<label className="flex items-center gap-4 cursor-pointer group">
<input type="radio" name="freq" className="sr-only peer" />
<div className="w-5 h-5 rounded-full border-2 border-border peer-checked:border-blueprint flex items-center justify-center p-0.5 transition-all">
<div className="w-full h-full rounded-full bg-transparent peer-checked:bg-blueprint" />
</div>
<span className="text-sm font-medium text-ink group-hover:opacity-70 transition-opacity">Hebdomadaire</span>
</label>
</div>
</div>
{/* Mode d'historique */}
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-2xl p-8 space-y-10">
<div className="space-y-1.5 text-left text-blueprint">
<h4 className="text-sm font-bold">Mode d'historique</h4>
<p className="text-[10px] opacity-60 uppercase tracking-wider font-semibold">Gestion des snapshots</p>
</div>
<div className="space-y-6">
<label className="flex items-center gap-4 cursor-pointer group">
<input type="radio" name="hist" className="sr-only peer" defaultChecked />
<div className="w-5 h-5 rounded-full border-2 border-border peer-checked:border-blueprint flex items-center justify-center p-0.5 transition-all">
<div className="w-full h-full rounded-full bg-transparent peer-checked:bg-blueprint" />
</div>
<div className="space-y-0.5">
<p className="text-sm font-bold text-ink">Manuel (bouton commit)</p>
<p className="text-[10px] text-muted-ink">Créer des snapshots manuellement</p>
</div>
</label>
<label className="flex items-center gap-4 cursor-pointer group">
<input type="radio" name="hist" className="sr-only peer" />
<div className="w-5 h-5 rounded-full border-2 border-border peer-checked:border-blueprint flex items-center justify-center p-0.5 transition-all">
<div className="w-full h-full rounded-full bg-transparent peer-checked:bg-blueprint" />
</div>
<div className="space-y-0.5">
<p className="text-sm font-bold text-ink">Automatique (intelligent)</p>
<p className="text-[10px] text-muted-ink">Snapshots automatiques avec détection</p>
</div>
</label>
</div>
</div>
</div>
{/* Mode Démo */}
<div className="bg-ochre/5 dark:bg-ochre/10 border border-ochre/20 rounded-2xl p-8 flex items-center justify-between group transition-all duration-300 hover:bg-ochre/10">
<div className="flex items-center gap-6">
<div className="p-3 bg-paper dark:bg-ochre/20 rounded-2xl text-ochre border border-ochre/30">
<FlaskConical size={20} />
</div>
<div className="space-y-1.5 text-left">
<h4 className="text-sm font-bold text-ink flex items-center gap-3">
🧪 Mode Démo
</h4>
<p className="text-[11px] text-muted-ink leading-relaxed font-medium">Accélère Memory Echo pour les tests. Les connexions apparaissent instantanément.</p>
</div>
</div>
<label className="relative inline-flex items-center cursor-pointer shrink-0 ml-4">
<input type="checkbox" className="sr-only peer" />
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] peer-checked:after:border-white after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all duration-300 ease-in-out peer-checked:bg-ochre"></div>
</label>
</div>
</div>
</motion.div>
);
};

View File

@@ -1,85 +0,0 @@
import React from 'react';
import { Palette, Type, LayoutGrid, Maximize } from 'lucide-react';
import { motion } from 'motion/react';
const AppearanceSelect = ({ icon, title, description, options, defaultValue }: any) => (
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-2xl p-8 space-y-8 group transition-all duration-300 hover:shadow-xl hover:shadow-slate/5">
<div className="flex items-center gap-5">
<div className="p-3 bg-paper dark:bg-white/10 rounded-2xl text-slate border border-border group-hover:scale-110 transition-transform duration-300">
{icon}
</div>
<div className="space-y-0.5 text-left">
<h4 className="text-base font-bold text-ink">{title}</h4>
<p className="text-[11px] text-concrete leading-tight">{description}</p>
</div>
</div>
<div className="relative group/select">
<select
defaultValue={defaultValue}
className="w-full bg-white/50 dark:bg-black/40 border border-border rounded-xl px-5 py-4 text-sm outline-none focus:ring-1 ring-slate/20 appearance-none cursor-pointer text-ink font-bold transition-all hover:bg-white dark:hover:bg-black/60"
>
{options.map((opt: string) => (
<option key={opt}>{opt}</option>
))}
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none text-concrete group-hover/select:text-slate transition-colors">
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L5 5L9 1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
</div>
</div>
);
export const AppearanceTab: React.FC = () => {
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-16 pb-20"
>
<div className="space-y-10">
<h3 className="text-[10px] font-bold uppercase tracking-[0.4em] text-concrete opacity-60">Personnaliser l'apparence de l'application</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<AppearanceSelect
icon={<Palette size={20} />}
title="Thème"
description="Sélectionner le mode visuel"
options={['Clair', 'Sombre', 'Système']}
defaultValue="Clair"
/>
<AppearanceSelect
icon={<Type size={20} />}
title="Taille de la police"
description="Ajustez la lisibilité globale de l'interface"
options={['Petite', 'Moyenne', 'Grande']}
defaultValue="Moyenne"
/>
<AppearanceSelect
icon={<Type size={20} />}
title="Famille de polices"
description="La typographie définit l'âme de l'application"
options={['Inter', 'JetBrains Mono', 'Public Sans', 'Outfit']}
defaultValue="JetBrains Mono"
/>
<AppearanceSelect
icon={<LayoutGrid size={20} />}
title="Affichage des notes"
description="Gestion visuelle de la grille de composition"
options={['Cartes (grille)', 'Liste', 'Tableau']}
defaultValue="Cartes (grille)"
/>
<AppearanceSelect
icon={<Maximize size={20} />}
title="Taille des notes"
description="Structure de la mise en page des éléments"
options={['Taille uniforme', 'Variable (Masonry)']}
defaultValue="Taille uniforme"
/>
</div>
</div>
</motion.div>
);
};

View File

@@ -1,82 +0,0 @@
import React from 'react';
import { Globe, Bell } from 'lucide-react';
import { motion } from 'motion/react';
export const GeneralTab: React.FC = () => {
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="space-y-12"
>
<div className="space-y-4">
<h3 className="text-[10px] font-bold uppercase tracking-[0.3em] text-concrete">Paramètres généraux de l'application</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Langue */}
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-xl p-8 space-y-6">
<div className="flex items-center gap-5">
<div className="p-3 bg-paper dark:bg-white/10 rounded-2xl text-slate border border-border">
<Globe size={18} />
</div>
<div className="space-y-0.5">
<h4 className="text-base font-bold text-ink">Langue</h4>
<p className="text-[11px] text-concrete">Sélectionner une langue</p>
</div>
</div>
<div className="relative group">
<select className="w-full bg-white/50 dark:bg-black/40 border border-border rounded-xl px-5 py-3.5 text-sm outline-none focus:ring-1 ring-blueprint/20 appearance-none cursor-pointer transition-all hover:bg-white dark:hover:bg-black/60 text-ink font-medium">
<option>Français</option>
<option>English</option>
<option>Español</option>
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none opacity-40 text-concrete">
<svg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L5 5L9 1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
</svg>
</div>
</div>
</div>
{/* Notifications */}
<div className="bg-white/40 dark:bg-white/5 border border-border rounded-xl p-8 space-y-6">
<div className="flex items-center gap-5">
<div className="p-3 bg-paper dark:bg-white/10 rounded-2xl text-slate border border-border">
<Bell size={18} />
</div>
<div className="space-y-0.5">
<h4 className="text-base font-bold text-ink">Notifications</h4>
<p className="text-[11px] text-concrete">Gérez vos préférences de notifications</p>
</div>
</div>
<div className="space-y-6 divide-y divide-border/40 text-left">
<div className="flex items-center justify-between pt-0">
<div className="space-y-1">
<p className="text-xs font-bold text-ink">Notifications par email</p>
<p className="text-[10px] text-concrete leading-relaxed">Recevoir des notifications importantes par email</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input type="checkbox" className="sr-only peer" />
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] peer-checked:after:border-white after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all duration-300 ease-in-out peer-checked:bg-slate"></div>
</label>
</div>
<div className="flex items-center justify-between pt-6">
<div className="space-y-1">
<p className="text-xs font-bold text-ink">Notifications bureau</p>
<p className="text-[10px] text-concrete leading-relaxed">Recevoir des notifications dans votre navigateur</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input type="checkbox" className="sr-only peer" defaultChecked />
<div className="w-11 h-6 bg-gray-200 dark:bg-white/10 rounded-full peer peer-checked:after:translate-x-[20px] peer-checked:after:border-white after:content-[''] after:absolute after:top-[4px] after:left-[4px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all duration-300 ease-in-out peer-checked:bg-slate"></div>
</label>
</div>
</div>
</div>
</div>
</div>
</motion.div>
);
};

View File

@@ -1,51 +0,0 @@
import React from 'react';
import { Settings, Sparkles, Palette, User, Database, Code, Info } from 'lucide-react';
import { motion } from 'motion/react';
import { SettingsTab } from '../../types';
interface SettingsHeaderProps {
activeTab: SettingsTab;
setActiveTab: (tab: SettingsTab) => void;
}
export const SettingsHeader: React.FC<SettingsHeaderProps> = ({ activeTab, setActiveTab }) => {
const tabs = [
{ id: 'general', label: 'Paramètres généraux', icon: <Settings size={14} /> },
{ id: 'ai', label: 'Paramètres IA', icon: <Sparkles size={14} /> },
{ id: 'appearance', label: 'Apparence', icon: <Palette size={14} /> },
{ id: 'profile', label: 'Profil', icon: <User size={14} /> },
{ id: 'data', label: 'Gestion des données', icon: <Database size={14} /> },
{ id: 'mcp', label: 'Paramètres MCP', icon: <Code size={14} /> },
{ id: 'about', label: 'À propos', icon: <Info size={14} /> },
];
return (
<header className="px-12 pt-20 pb-16 space-y-12">
<div className="space-y-4">
<h1 className="text-[64px] font-serif text-ink tracking-tight leading-none italic font-medium">Paramètres</h1>
<p className="text-[10px] font-bold uppercase tracking-[0.4em] text-concrete opacity-60">Configuration & Préférences</p>
</div>
<nav className="flex items-center gap-1 border-b border-border/40 pb-px">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id as SettingsTab)}
className={`flex items-center gap-2.5 px-6 py-5 text-[10px] font-bold uppercase tracking-[0.18em] transition-all relative whitespace-nowrap
${activeTab === tab.id ? 'text-ink' : 'text-concrete hover:text-ink/60'}`}
>
<span className={activeTab === tab.id ? 'text-ink' : 'text-concrete'}>{tab.icon}</span>
{tab.label}
{activeTab === tab.id && (
<motion.div
layoutId="activeSettingsTabLine"
className="absolute bottom-0 left-0 right-0 h-0.5 bg-ink"
transition={{ type: 'spring', bounce: 0.1, duration: 0.8 }}
/>
)}
</button>
))}
</nav>
</header>
);
};

View File

@@ -1,62 +0,0 @@
import { Carnet, Note } from './types';
export const CARNETS: Carnet[] = [
{ id: '1', name: 'Daily Notes', initial: 'D', type: 'Private', isPrivate: true },
{ id: '2', name: 'Project: Neo', initial: 'P', type: 'Project' },
{ id: '3', name: 'Shared Docs', initial: 'S', type: 'Shared' },
{ id: '4', name: 'Architecture Research', initial: 'A', type: 'Project' },
{ id: '5', name: 'History of Architecture', initial: 'H', type: 'Project', parentId: '4' },
{ id: '6', name: 'Modernism', initial: 'M', type: 'Project', parentId: '5' },
{ id: '7', name: 'Sustainable Design', initial: 'S', type: 'Project', parentId: '4' },
];
export const ALL_NOTES: Note[] = [
{
id: 'n1',
carnetId: '4',
title: 'Grid Systems',
date: 'Oct 26, 2024',
content: 'Grid Systems is streathen in ognitiacs clesign and simulhere desipmalt: complded structurer and manamateriai-s: ci arevenuatingly used, asiller straterty of insaee to the tmn and usaes of disrension, architecture of emiornabious tracious structures.',
imageUrl: 'https://images.unsplash.com/photo-1503387762-592dea58ef23?auto=format&fit=crop&q=80&w=800&h=600',
tags: [
{ id: 't1', label: 'Architecture', type: 'user' },
{ id: 't2', label: 'Systems', type: 'ai' }
]
},
{
id: 'n2',
carnetId: '4',
title: 'Materiality',
date: 'Oct 24, 2024',
content: 'Materiality is combinated by relliaitic structureirs measure of plastics, natural, materials and priotical structures. Materialed coasts erabiocera alann light spaces and octicm employed design on thodolen of materiality, and tohlite tersev/ used in the gridin structures en obain materials, coms pathetic structure.',
imageUrl: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80&w=800&h=600',
tags: [
{ id: 't3', label: 'Materials', type: 'user' },
{ id: 't4', label: 'Sustainabilty', type: 'ai' }
]
},
{
id: 'n3',
carnetId: '4',
title: 'Light & Space',
date: 'Oct 22, 2024',
content: 'Light & Space is a creaivity of light & Space inralicated in sizazant or dark crotrcning and netrescenations of avant trurme sivonpaltures for in inncr-en allimativefiting is cerriadating and sityle.',
imageUrl: 'https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=800&h=600',
tags: [
{ id: 't5', label: 'Lighting', type: 'user' },
{ id: 't6', label: 'Atmosphere', type: 'ai' }
]
},
{
id: 'n4',
carnetId: '2',
title: 'Neo-Brutalism study',
date: 'Sep 12, 2024',
content: 'Exploring the raw aesthetic of neo-brutalism in urban environments. Focus on concrete textures and massive forms.',
imageUrl: 'https://images.unsplash.com/photo-1518005020951-eccb494ad742?auto=format&fit=crop&q=80&w=800&h=600',
tags: [
{ id: 't7', label: 'Brutalism', type: 'user' },
{ id: 't8', label: 'Urban', type: 'ai' }
]
}
];

View File

@@ -1,98 +0,0 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Playfair+Display:ital,wght@0,400;0,700;1,400&family=JetBrains+Mono:wght@400;500&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-serif: "Playfair Display", serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
/* Foundation */
--color-paper: #F2F0E9;
--color-ink: #1C1C1C;
--color-muted-ink: rgba(28, 28, 28, 0.6);
--color-border: rgba(28, 28, 28, 0.1);
--color-concrete: #8D8D8D;
/* Architectural Accents */
--color-blueprint: #75B2D6;
--color-slate: #4A4E69;
--color-ochre: #D4A373;
--color-sage: #A3B18A;
--color-rust: #9B2226;
--color-glass: rgba(255, 255, 255, 0.4);
/* Dark Theme Aliases */
--color-dark-paper: #0D0D0D;
--color-dark-ink: #EAEAEA;
--color-dark-muted: rgba(234, 234, 234, 0.5);
--color-dark-border: rgba(234, 234, 234, 0.1);
}
@layer base {
body {
@apply bg-paper text-ink font-sans antialiased transition-colors duration-500;
}
.dark body {
@apply bg-dark-paper;
}
.dark {
--color-paper: #121212;
--color-ink: #EAEAEA;
--color-muted-ink: rgba(234, 234, 234, 0.6);
--color-border: rgba(255, 255, 255, 0.08);
--color-glass: rgba(0, 0, 0, 0.4);
--color-concrete: #555555;
}
}
.paper-texture {
background-color: var(--color-paper);
background-image: url("https://www.transparenttextures.com/patterns/natural-paper.png");
}
/* Custom Scrollbar - Architectural Minimalist */
.custom-scrollbar::-webkit-scrollbar {
width: 3px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: rgba(28, 28, 28, 0.08);
border-radius: 10px;
}
.custom-scrollbar:hover::-webkit-scrollbar-thumb {
background: rgba(28, 28, 28, 0.2);
}
.ai-glass {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.5);
}
.dark .ai-glass {
background: rgba(30, 30, 30, 0.85);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.sidebar-shadow {
box-shadow: 1px 0 10px rgba(0, 0, 0, 0.05);
}
.active-nav-item {
background: linear-gradient(to right, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.4));
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
border: 1px solid rgba(255, 255, 255, 0.5);
}
.dark .active-nav-item {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

View File

@@ -1,10 +0,0 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View File

@@ -1,30 +0,0 @@
export type NavigationView = 'notebooks' | 'agents' | 'settings';
export type AITone = 'Professional' | 'Creative' | 'Academic' | 'Casual';
export type AITab = 'discussion' | 'actions' | 'resources';
export type SettingsTab = 'general' | 'ai' | 'appearance' | 'profile' | 'data' | 'mcp' | 'about';
export interface Tag {
id: string;
label: string;
type: 'ai' | 'user';
}
export interface Note {
id: string;
carnetId: string;
title: string;
content: string;
imageUrl: string;
date: string;
tags: Tag[];
isPinned?: boolean;
}
export interface Carnet {
id: string;
name: string;
initial: string;
type: 'Private' | 'Project' | 'Shared';
isPrivate?: boolean;
parentId?: string;
}

View File

@@ -1,26 +0,0 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View File

@@ -1,24 +0,0 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});