mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Thème Nocturne clair (Claude Design) à la place du néo-brutaliste
Reprend les jetons de « Loki App.dc.html » : fond gris froid #f7f8fa, surfaces blanches, accent bleu ardoise #2f4a7a (au lieu de l'orange), filets 1 px #e3e6eb (au lieu des bordures noires 3 px) et ombres douces diffuses (au lieu des ombres dures décalées). - tailwind.config.ts : les NOMS de jetons sont conservés, seules leurs valeurs basculent — toute l'interface suit sans réécrire les composants. Les jetons historiquement sombres (card-deep, on-dark…) deviennent la surface sélectionnée claire et son encre. - Police unique Inter ; l'ex-« font-pixel » devient le kicker de la maquette (petites capitales espacées de 0.14em), donc les libellés existants suivent sans modification. - Conversion mécanique des filets 3 px / 2 px vers 1 px, séparateurs épais affinés, logo et avatar refaits selon la maquette (carré teinté à filet accent), couleurs « papier » de l'aperçu passées en jetons. - Texte blanc corrigé partout où il reposait sur une surface devenue claire ; conservé sur les fonds accent/warn où il reste lisible. Vérifié au rendu : body #f7f8fa, encre #1c2536, surface #ffffff, Inter ; plus aucune trace d'orange, de noir #18181b ni d'ombre dure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
This commit is contained in:
16 files changed
+254
-215
No files matched your search
+1
-1
@@ -7,7 +7,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Press+Start+2P&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>Loki - Agent local</title>
|
||||
|
||||
@@ -16,10 +16,10 @@ const items: { id: View; label: string; icon: React.ReactNode }[] = [
|
||||
];
|
||||
|
||||
const btn = (on: boolean) =>
|
||||
`flex h-10 w-10 items-center justify-center border-[3px] ${
|
||||
`flex h-10 w-10 items-center justify-center border ${
|
||||
on
|
||||
? "border-white bg-accent text-white shadow-accent-soft"
|
||||
: "border-chrome-3 text-on-dark-2 hover:text-white"
|
||||
: "border-chrome-3 text-on-dark-2 hover:text-ink"
|
||||
}`;
|
||||
|
||||
/** Barre d'activité verticale (60px) sombre à gauche. */
|
||||
@@ -31,7 +31,7 @@ export function ActivityBar({
|
||||
onChange: (v: View) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex w-[60px] flex-none flex-col items-center gap-2 border-r-[3px] border-line bg-bar py-3">
|
||||
<div className="flex w-[60px] flex-none flex-col items-center gap-2 border-r border-line bg-bar py-3">
|
||||
{items.map((it) => (
|
||||
<button
|
||||
key={it.id}
|
||||
@@ -55,7 +55,7 @@ export function ActivityBar({
|
||||
<SettingsIcon />
|
||||
</button>
|
||||
|
||||
<div className="font-pixel flex h-[34px] w-[34px] items-center justify-center border-[3px] border-white bg-white text-[11px] text-line">
|
||||
<div className="font-pixel flex h-[34px] w-[34px] items-center justify-center border border-line bg-line-soft text-[11px] text-ink-3">
|
||||
M
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -104,20 +104,23 @@ export const CheckIcon = ({ size = 14, className }: Props) => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
/** Logo Loki : carré orange à bordure blanche + carré interne (néo-brutaliste). */
|
||||
/** Logo Loki : carré teinté à filet accent + petit carré accent (maquette). */
|
||||
export const LokiMark = ({ size = 30 }: { size?: number }) => (
|
||||
<div
|
||||
className="flex items-center justify-center bg-accent"
|
||||
className="grid place-items-center border border-accent bg-accent-ghost"
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
border: "3px solid #fff",
|
||||
boxShadow: "3px 3px 0 #ff5436",
|
||||
borderRadius: 7,
|
||||
borderRadius: Math.round(size * 0.27),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{ width: size * 0.3, height: size * 0.3, background: "#fff" }}
|
||||
className="bg-accent"
|
||||
style={{
|
||||
width: size * 0.3,
|
||||
height: size * 0.3,
|
||||
borderRadius: 2,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -45,15 +45,15 @@ function CodeBlock({ lang, code }: { lang: string; code: string }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-2 overflow-hidden border-[3px] border-line bg-card-deep shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<div className="flex items-center justify-between border-b-2 border-chrome-2 px-3 py-1.5">
|
||||
<div className="my-2 overflow-hidden border border-line bg-card-deep shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<div className="flex items-center justify-between border-b border-chrome-2 px-3 py-1.5">
|
||||
<span className="font-mono text-[10.5px] uppercase tracking-wide text-on-dark-2">
|
||||
{lang || "code"}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={copy}
|
||||
className="flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] text-on-dark-2 transition-colors hover:text-white"
|
||||
className="flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] text-on-dark-2 transition-colors hover:text-ink"
|
||||
>
|
||||
{copied ? <CheckIcon size={13} /> : <CopyIcon size={13} />}
|
||||
{failed ? "Erreur" : copied ? "Copié" : "Copier"}
|
||||
@@ -122,7 +122,7 @@ const components: Components = {
|
||||
),
|
||||
em: ({ children }) => <em className="italic">{children}</em>,
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="my-2 border-l-2 border-line-strong pl-3 text-muted">
|
||||
<blockquote className="my-2 border-l border-line-strong pl-3 text-muted">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
|
||||
@@ -40,14 +40,14 @@ export function ModelSelector({
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className={
|
||||
variant === "composer"
|
||||
? "flex h-8 min-w-0 max-w-[220px] items-center gap-1.5 border-2 border-line bg-accent px-2.5 text-[13px] text-white"
|
||||
: "flex h-[34px] min-w-0 max-w-[260px] items-center gap-2 border-[3px] border-white bg-accent px-3 text-white shadow-accent-soft"
|
||||
? "flex h-8 min-w-0 max-w-[220px] items-center gap-1.5 border border-line bg-accent px-2.5 text-[13px] text-white"
|
||||
: "flex h-[34px] min-w-0 max-w-[260px] items-center gap-2 border border-white bg-accent px-3 text-white shadow-accent-soft"
|
||||
}
|
||||
title={selectedModel || undefined}
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
<span
|
||||
className={`h-2.5 w-2.5 border-2 border-line ${
|
||||
className={`h-2.5 w-2.5 border border-line ${
|
||||
warmingModel === selectedModel
|
||||
? "bg-accent animate-pulse"
|
||||
: selectedLoaded
|
||||
@@ -74,7 +74,7 @@ export function ModelSelector({
|
||||
|
||||
{open && (
|
||||
<div
|
||||
className={`absolute z-20 w-64 border-[3px] border-line bg-card p-1 shadow-hard ${
|
||||
className={`absolute z-20 w-64 border border-line bg-card p-1 shadow-hard ${
|
||||
variant === "composer"
|
||||
? "bottom-[calc(100%+6px)] left-0"
|
||||
: "right-0 top-11"
|
||||
@@ -97,7 +97,7 @@ export function ModelSelector({
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`h-2 w-2 border-2 border-line ${
|
||||
className={`h-2 w-2 border border-line ${
|
||||
m.name === selectedModel ? "bg-accent" : "bg-card"
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -52,7 +52,7 @@ export function ProjectChip() {
|
||||
<div ref={rootRef} className="relative">
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="flex h-8 items-center gap-1.5 border-2 border-line bg-base px-2.5 text-[12px] text-ink-2"
|
||||
className="flex h-8 items-center gap-1.5 border border-line bg-base px-2.5 text-[12px] text-ink-2"
|
||||
title="Projet de travail de cette session"
|
||||
>
|
||||
📁 <span className="max-w-[140px] truncate">{active ?? "workspace"}</span>
|
||||
@@ -60,7 +60,7 @@ export function ProjectChip() {
|
||||
|
||||
{open && (
|
||||
<div
|
||||
className="absolute bottom-[calc(100%+6px)] left-0 z-30 w-[240px] border-[3px] border-line bg-card shadow-hard"
|
||||
className="absolute bottom-[calc(100%+6px)] left-0 z-30 w-[240px] border border-line bg-card shadow-hard"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
<button
|
||||
@@ -75,7 +75,7 @@ export function ProjectChip() {
|
||||
<button
|
||||
key={p.name}
|
||||
onClick={() => choose(p.name)}
|
||||
className={`block w-full border-t-2 border-line-soft px-3 py-2 text-left text-[13px] text-ink-2 hover:bg-base ${
|
||||
className={`block w-full border-t border-line-soft px-3 py-2 text-left text-[13px] text-ink-2 hover:bg-base ${
|
||||
active === p.name ? "bg-base font-bold" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -85,7 +85,7 @@ export function ProjectChip() {
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
<div className="border-t-2 border-line-soft p-2">
|
||||
<div className="border-t border-line-soft p-2">
|
||||
{creating ? (
|
||||
<input
|
||||
autoFocus
|
||||
@@ -99,7 +99,7 @@ export function ProjectChip() {
|
||||
}
|
||||
}}
|
||||
placeholder="nom-du-projet"
|
||||
className="w-full border-2 border-line bg-base px-2 py-1 text-[12px] text-ink outline-none"
|
||||
className="w-full border border-line bg-base px-2 py-1 text-[12px] text-ink outline-none"
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
|
||||
@@ -55,7 +55,7 @@ export function SessionMenu() {
|
||||
<div ref={rootRef} className="relative">
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="flex cursor-pointer items-center gap-2 text-[13px] text-on-dark hover:text-white"
|
||||
className="flex cursor-pointer items-center gap-2 text-[13px] text-on-dark hover:text-ink"
|
||||
title="Sessions"
|
||||
>
|
||||
<span className="max-w-[220px] truncate">
|
||||
@@ -66,7 +66,7 @@ export function SessionMenu() {
|
||||
|
||||
{open && (
|
||||
<div
|
||||
className="absolute left-0 top-[calc(100%+9px)] z-30 w-[300px] border-[3px] border-line bg-chrome-2 shadow-hard"
|
||||
className="absolute left-0 top-[calc(100%+9px)] z-30 w-[300px] border border-line bg-chrome-2 shadow-hard"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
<button
|
||||
@@ -74,7 +74,7 @@ export function SessionMenu() {
|
||||
setOpen(false);
|
||||
await newSession();
|
||||
}}
|
||||
className="flex w-full cursor-pointer items-center gap-2 border-b-[3px] border-chrome-3 px-3 py-2.5 text-[13px] font-bold text-accent hover:bg-chrome-3"
|
||||
className="flex w-full cursor-pointer items-center gap-2 border-b border-chrome-3 px-3 py-2.5 text-[13px] font-bold text-accent hover:bg-chrome-3"
|
||||
>
|
||||
<PlusIcon />
|
||||
Nouvelle session
|
||||
@@ -97,7 +97,7 @@ export function SessionMenu() {
|
||||
setOpen(false);
|
||||
void openSession(s.id);
|
||||
}}
|
||||
className={`group cursor-pointer border-b-2 border-chrome-3 px-3 py-2 last:border-b-0 ${
|
||||
className={`group cursor-pointer border-b border-chrome-3 px-3 py-2 last:border-b-0 ${
|
||||
active ? "bg-chrome-3" : "hover:bg-chrome-3"
|
||||
}`}
|
||||
>
|
||||
@@ -116,12 +116,12 @@ export function SessionMenu() {
|
||||
}
|
||||
}}
|
||||
onBlur={() => setEditingId(null)}
|
||||
className="min-w-0 flex-1 border-2 border-line bg-card-deep px-1.5 py-0.5 text-[13px] text-white outline-none"
|
||||
className="min-w-0 flex-1 border border-line bg-card-deep px-1.5 py-0.5 text-[13px] text-ink outline-none"
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
className={`min-w-0 flex-1 truncate text-[13px] ${
|
||||
active ? "text-white" : "text-on-dark"
|
||||
active ? "text-ink" : "text-ink-3"
|
||||
}`}
|
||||
>
|
||||
{s.title}
|
||||
@@ -129,7 +129,7 @@ export function SessionMenu() {
|
||||
)}
|
||||
{working && (
|
||||
<span
|
||||
className="flex-none border-2 border-line bg-accent px-1 py-0.5 text-[9px] leading-none text-white"
|
||||
className="flex-none border border-line bg-accent px-1 py-0.5 text-[9px] leading-none text-white"
|
||||
title="Session en cours de travail"
|
||||
>
|
||||
EN COURS
|
||||
@@ -141,7 +141,7 @@ export function SessionMenu() {
|
||||
setEditingId(s.id);
|
||||
setDraft(s.title);
|
||||
}}
|
||||
className="hidden flex-none text-[11px] text-on-dark-3 hover:text-white group-hover:block"
|
||||
className="hidden flex-none text-[11px] text-on-dark-3 hover:text-ink group-hover:block"
|
||||
title="Renommer la session"
|
||||
>
|
||||
✎
|
||||
|
||||
@@ -23,11 +23,11 @@ export function ToolCard({ call }: { call: ToolCall }) {
|
||||
const write = call.name === "write_file" || call.name === "edit_file";
|
||||
|
||||
return (
|
||||
<div className="mb-[11px] overflow-hidden border-[3px] border-line bg-card shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<div className="mb-[11px] overflow-hidden border border-line bg-card shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<div className="flex items-center gap-2.5 px-3 py-2.5">
|
||||
<span
|
||||
className={`flex h-[26px] w-[26px] items-center justify-center border-2 border-line ${
|
||||
write ? "bg-accent text-white" : "bg-card-deep text-white"
|
||||
className={`flex h-[26px] w-[26px] items-center justify-center border border-line ${
|
||||
write ? "bg-accent text-white" : "bg-card-deep text-ink"
|
||||
}`}
|
||||
>
|
||||
<ToolGlyph name={call.name} />
|
||||
@@ -37,7 +37,7 @@ export function ToolCard({ call }: { call: ToolCall }) {
|
||||
<div className="flex-1" />
|
||||
{running ? (
|
||||
<span className="flex items-center gap-1.5 text-[13px] text-muted-2">
|
||||
<span className="h-2.5 w-2.5 animate-spin rounded-full border-2 border-muted-3 border-t-accent" />
|
||||
<span className="h-2.5 w-2.5 animate-spin rounded-full border border-muted-3 border-t-accent" />
|
||||
en cours
|
||||
</span>
|
||||
) : pending ? (
|
||||
@@ -53,7 +53,7 @@ export function ToolCard({ call }: { call: ToolCall }) {
|
||||
)}
|
||||
</div>
|
||||
{call.summary && !running && (
|
||||
<div className="border-t-2 border-line-soft px-3 py-2 text-[13px] text-muted-2">
|
||||
<div className="border-t border-line-soft px-3 py-2 text-[13px] text-muted-2">
|
||||
→ {call.summary}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -16,10 +16,10 @@ export function TopBar() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex h-[54px] flex-none items-center gap-3.5 border-b-[3px] border-line bg-bar px-4">
|
||||
<div className="flex h-[54px] flex-none items-center gap-3.5 border-b border-line bg-bar px-4">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<LokiMark />
|
||||
<div className="font-pixel text-[14px] text-white">LOKI</div>
|
||||
<div className="font-pixel text-[14px] text-ink">LOKI</div>
|
||||
<div
|
||||
className="text-[13px] leading-none text-on-dark-2"
|
||||
title={version ? `build ${version}` : undefined}
|
||||
@@ -39,7 +39,7 @@ export function TopBar() {
|
||||
|
||||
{/* Stats système temps réel : CPU, RAM, GPU/VRAM */}
|
||||
{stats && (
|
||||
<div className="flex h-8 items-center gap-2.5 border-[3px] border-chrome-3 bg-chrome-2 px-[11px] text-[13px] text-on-dark">
|
||||
<div className="flex h-8 items-center gap-2.5 border border-chrome-3 bg-chrome-2 px-[11px] text-[13px] text-on-dark">
|
||||
<span>CPU {stats.cpu_pct.toFixed(0)}%</span>
|
||||
<span className="text-on-dark-3">·</span>
|
||||
<span>RAM {stats.ram_pct.toFixed(0)}%</span>
|
||||
@@ -58,11 +58,11 @@ export function TopBar() {
|
||||
|
||||
{/* Statut Ollama */}
|
||||
<div
|
||||
className="flex h-8 items-center gap-2 border-[3px] border-chrome-3 bg-chrome-2 px-[11px]"
|
||||
className="flex h-8 items-center gap-2 border border-chrome-3 bg-chrome-2 px-[11px]"
|
||||
title={connected ? status?.host : status?.error}
|
||||
>
|
||||
<span
|
||||
className={`h-[11px] w-[11px] border-2 border-line ${
|
||||
className={`h-[11px] w-[11px] border border-line ${
|
||||
connected ? "bg-ok" : "bg-warn"
|
||||
}`}
|
||||
/>
|
||||
|
||||
+33
-13
@@ -10,27 +10,47 @@ body,
|
||||
}
|
||||
|
||||
body {
|
||||
background: #ececef;
|
||||
color: #18181b;
|
||||
font-family: "DM Mono", ui-monospace, monospace;
|
||||
letter-spacing: -0.01em;
|
||||
background: #f7f8fa;
|
||||
color: #1c2536;
|
||||
font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Police pixel pour les libellés (utilitaire .font-pixel via Tailwind aussi) */
|
||||
.font-pixel {
|
||||
font-family: "Press Start 2P", ui-monospace, monospace;
|
||||
/* Titres : léger resserrement, comme dans la maquette Nocturne. */
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
/* Barres de défilement néo-brutalistes */
|
||||
/* Ex-police pixel : « kicker » de la maquette (petites capitales espacées). */
|
||||
.font-pixel {
|
||||
font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* Barres de défilement discrètes (règles .scr de la maquette) */
|
||||
.scr::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
.scr::-webkit-scrollbar-thumb {
|
||||
background: #18181b;
|
||||
border: 2px solid #ececef;
|
||||
background: #c3c9d2;
|
||||
border-radius: 6px;
|
||||
border: 3px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
.scr::-webkit-scrollbar-track {
|
||||
background: #e4e4e7;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Focus visible accentué, sélection teintée — repris de la maquette. */
|
||||
:focus-visible {
|
||||
outline: 2px solid #2f4a7a;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
::selection {
|
||||
background: rgba(47, 74, 122, 0.32);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export function HistoryView({ onOpen }: { onOpen: () => void }) {
|
||||
<Page title="Historique" subtitle="Toutes les conversations enregistrées localement.">
|
||||
<div className="grid grid-cols-1 gap-3 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{sessions.map((session) => (
|
||||
<div key={session.id} className="border-[3px] border-line bg-card p-4 shadow-hard">
|
||||
<div key={session.id} className="border border-line bg-card p-4 shadow-hard">
|
||||
<div className="truncate text-[15px] font-semibold text-ink">{session.title}</div>
|
||||
<div className="mt-1 text-[12px] text-muted-2">
|
||||
{session.message_count ?? 0} message(s) · {new Date(session.updated_at * 1000).toLocaleString("fr-FR")}
|
||||
@@ -25,13 +25,13 @@ export function HistoryView({ onOpen }: { onOpen: () => void }) {
|
||||
await openSession(session.id);
|
||||
onOpen();
|
||||
}}
|
||||
className="border-2 border-line bg-accent px-3 py-1.5 text-[12px] font-bold text-white"
|
||||
className="border border-line bg-accent px-3 py-1.5 text-[12px] font-bold text-white"
|
||||
>
|
||||
Ouvrir
|
||||
</button>
|
||||
<button
|
||||
onClick={() => removeSession(session.id)}
|
||||
className="border-2 border-line bg-base px-3 py-1.5 text-[12px] text-warn"
|
||||
className="border border-line bg-base px-3 py-1.5 text-[12px] text-warn"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
@@ -53,7 +53,7 @@ export function FilesView() {
|
||||
|
||||
return (
|
||||
<Page title="Fichiers" subtitle="Workspace créé et modifié par l’agent.">
|
||||
<div className="border-[3px] border-line bg-panel p-3 shadow-hard">
|
||||
<div className="border border-line bg-panel p-3 shadow-hard">
|
||||
{fileTree.length ? <WorkspaceTree nodes={fileTree} depth={0} /> : <Empty text="Le workspace est vide." />}
|
||||
</div>
|
||||
</Page>
|
||||
@@ -74,9 +74,9 @@ function WorkspaceTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
|
||||
{nodes.map((node) => (
|
||||
<div key={node.path}>
|
||||
<div
|
||||
className={`mb-1 flex items-center gap-2 border-2 px-3 py-2 ${
|
||||
className={`mb-1 flex items-center gap-2 border px-3 py-2 ${
|
||||
node.path === previewPath
|
||||
? "border-accent bg-card-deep text-white"
|
||||
? "border-accent bg-card-deep text-ink"
|
||||
: "border-line bg-card text-ink-2"
|
||||
}`}
|
||||
style={{ marginLeft: depth * 18 }}
|
||||
@@ -94,7 +94,7 @@ function WorkspaceTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
|
||||
{node.type === "file" && (
|
||||
<button
|
||||
onClick={() => downloadFile(node.path, currentProject())}
|
||||
className="flex items-center gap-1 border-2 border-line bg-base px-2 py-1 text-[11px] text-accent"
|
||||
className="flex items-center gap-1 border border-line bg-base px-2 py-1 text-[11px] text-accent"
|
||||
title={`Télécharger ${node.name}`}
|
||||
>
|
||||
<DownloadIcon size={12} /> Télécharger
|
||||
@@ -102,7 +102,7 @@ function WorkspaceTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
|
||||
)}
|
||||
<button
|
||||
onClick={() => confirmDelete(node)}
|
||||
className="flex items-center gap-1 border-2 border-line bg-base px-2 py-1 text-[11px] text-warn"
|
||||
className="flex items-center gap-1 border border-line bg-base px-2 py-1 text-[11px] text-warn"
|
||||
title={`Supprimer ${node.name}`}
|
||||
>
|
||||
<TrashIcon size={12} /> Supprimer
|
||||
@@ -128,10 +128,10 @@ export function ToolsView({ onSettings }: { onSettings: () => void }) {
|
||||
{availableTools.map((name) => {
|
||||
const enabled = config?.tools[name] ?? false;
|
||||
return (
|
||||
<div key={name} className="border-[3px] border-line bg-card p-4 shadow-hard">
|
||||
<div key={name} className="border border-line bg-card p-4 shadow-hard">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="font-mono text-[14px] text-ink">{name}</span>
|
||||
<span className={`border-2 border-line px-2 py-1 text-[10px] ${enabled ? "bg-ok text-white" : "bg-base text-muted-2"}`}>
|
||||
<span className={`border border-line px-2 py-1 text-[10px] ${enabled ? "bg-ok text-white" : "bg-base text-muted-2"}`}>
|
||||
{enabled ? "ACTIF" : "INACTIF"}
|
||||
</span>
|
||||
</div>
|
||||
@@ -141,7 +141,7 @@ export function ToolsView({ onSettings }: { onSettings: () => void }) {
|
||||
</div>
|
||||
<button
|
||||
onClick={onSettings}
|
||||
className="mt-5 border-[3px] border-line bg-accent px-4 py-2 text-[13px] font-bold text-white shadow-hard"
|
||||
className="mt-5 border border-line bg-accent px-4 py-2 text-[13px] font-bold text-white shadow-hard"
|
||||
>
|
||||
Configurer les outils
|
||||
</button>
|
||||
@@ -162,7 +162,7 @@ function Page({ title, subtitle, children }: { title: string; subtitle: string;
|
||||
}
|
||||
|
||||
function Empty({ text }: { text: string }) {
|
||||
return <div className="border-2 border-line bg-card p-8 text-center text-muted-2">{text}</div>;
|
||||
return <div className="border border-line bg-card p-8 text-center text-muted-2">{text}</div>;
|
||||
}
|
||||
|
||||
function formatSize(bytes: number): string {
|
||||
|
||||
@@ -63,10 +63,10 @@ export function ChatPanel() {
|
||||
return (
|
||||
<div className="flex min-w-0 flex-1 flex-col bg-base">
|
||||
{/* Barre de contexte */}
|
||||
<div className="flex h-[46px] flex-none items-center gap-2 border-b-[3px] border-line bg-panel px-4">
|
||||
<div className="flex h-[46px] flex-none items-center gap-2 border-b border-line bg-panel px-4">
|
||||
<Chip>⚑ Invite système</Chip>
|
||||
<Chip>
|
||||
<span className="h-2 w-2 border-2 border-line bg-ok" />
|
||||
<span className="h-2 w-2 border border-line bg-ok" />
|
||||
{activeTools} outil{activeTools > 1 ? "s" : ""}
|
||||
</Chip>
|
||||
<Chip>
|
||||
@@ -130,9 +130,9 @@ export function ChatPanel() {
|
||||
</div>
|
||||
|
||||
{/* Composer */}
|
||||
<div className="flex-none border-t-[3px] border-line bg-panel px-7 pb-[18px] pt-3.5">
|
||||
<div className="flex-none border-t border-line bg-panel px-7 pb-[18px] pt-3.5">
|
||||
<div className="mx-auto max-w-[680px]">
|
||||
<div className="border-[3px] border-line bg-card p-3 shadow-hard" style={{ borderRadius: 8 }}>
|
||||
<div className="border border-line bg-card p-3 shadow-hard" style={{ borderRadius: 8 }}>
|
||||
<textarea
|
||||
rows={1}
|
||||
value={draft}
|
||||
@@ -158,8 +158,8 @@ export function ChatPanel() {
|
||||
<button
|
||||
onClick={streaming ? stopStreaming : submit}
|
||||
disabled={!streaming && !draft.trim()}
|
||||
className={`flex h-[38px] items-center gap-1.5 border-[3px] border-line px-4 text-[14px] text-white shadow-hard-accent disabled:opacity-40 ${
|
||||
streaming ? "bg-warn" : "bg-card-deep"
|
||||
className={`flex h-[38px] items-center gap-1.5 border border-line px-4 text-[14px] text-white shadow-hard-accent disabled:opacity-40 ${
|
||||
streaming ? "bg-warn" : "bg-accent"
|
||||
}`}
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
@@ -201,7 +201,7 @@ function ModeSelector() {
|
||||
<div className="relative" ref={ref}>
|
||||
<button
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className={`flex h-8 items-center gap-1.5 border-2 border-line px-2.5 text-[13px] ${
|
||||
className={`flex h-8 items-center gap-1.5 border border-line px-2.5 text-[13px] ${
|
||||
mode === "plan"
|
||||
? "bg-info text-white"
|
||||
: mode === "yolo"
|
||||
@@ -215,7 +215,7 @@ function ModeSelector() {
|
||||
<ChevronDown size={11} />
|
||||
</button>
|
||||
{open && (
|
||||
<div className="absolute bottom-10 left-0 z-20 w-64 border-[3px] border-line bg-card p-1 shadow-hard">
|
||||
<div className="absolute bottom-10 left-0 z-20 w-64 border border-line bg-card p-1 shadow-hard">
|
||||
{MODES.map((m) => (
|
||||
<button
|
||||
key={m.id}
|
||||
@@ -257,10 +257,10 @@ function PlanCard({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="mb-[11px] overflow-hidden border-[3px] border-line bg-card shadow-hard-sm"
|
||||
className="mb-[11px] overflow-hidden border border-line bg-card shadow-hard-sm"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
<div className="flex items-center gap-2 border-b-2 border-line-soft px-3 py-2">
|
||||
<div className="flex items-center gap-2 border-b border-line-soft px-3 py-2">
|
||||
<span className="font-pixel text-[9px] text-accent">PLAN</span>
|
||||
<span className="text-[12px] text-muted-2">
|
||||
{doneCount > 0
|
||||
@@ -280,7 +280,7 @@ function PlanCard({
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`flex h-[18px] w-[18px] flex-none items-center justify-center border-2 text-[11px] ${
|
||||
className={`flex h-[18px] w-[18px] flex-none items-center justify-center border text-[11px] ${
|
||||
isDone
|
||||
? "border-ok bg-ok text-white"
|
||||
: isActive
|
||||
@@ -324,7 +324,7 @@ function Bubble({
|
||||
if (msg.role === "user") {
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<div className="font-pixel flex h-[34px] w-[34px] flex-none items-center justify-center border-[3px] border-line bg-card-deep text-[11px] text-white">
|
||||
<div className="font-pixel flex h-[34px] w-[34px] flex-none items-center justify-center border border-line bg-card-deep text-[11px] text-ink">
|
||||
M
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -332,7 +332,7 @@ function Bubble({
|
||||
<span className="text-[14px] text-ink">VOUS</span>
|
||||
<span className="text-[13px] text-muted-3">{time}</span>
|
||||
</div>
|
||||
<div className="border-[3px] border-line bg-card px-[14px] py-3 text-[14px] leading-snug text-ink shadow-hard-sm whitespace-pre-wrap" style={{ borderRadius: 7 }}>
|
||||
<div className="border border-line bg-card px-[14px] py-3 text-[14px] leading-snug text-ink shadow-hard-sm whitespace-pre-wrap" style={{ borderRadius: 7 }}>
|
||||
{msg.content}
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,8 +342,8 @@ function Bubble({
|
||||
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<div className="flex h-[34px] w-[34px] flex-none items-center justify-center border-[3px] border-line bg-accent">
|
||||
<div style={{ width: 10, height: 10, background: "#fff" }} />
|
||||
<div className="grid h-[34px] w-[34px] flex-none place-items-center rounded-[9px] border border-accent bg-accent-ghost">
|
||||
<div className="bg-accent" style={{ width: 10, height: 10, borderRadius: 2 }} />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-2 flex items-baseline gap-2">
|
||||
@@ -364,7 +364,7 @@ function Bubble({
|
||||
<ToolCard key={i} call={t} />
|
||||
))}
|
||||
{notice && (
|
||||
<div className="mb-2 border-[3px] border-line bg-card px-3 py-2 text-[13px] text-warn">
|
||||
<div className="mb-2 border border-line bg-card px-3 py-2 text-[13px] text-warn">
|
||||
{notice}
|
||||
</div>
|
||||
)}
|
||||
@@ -416,7 +416,7 @@ function ReasoningPanel({ text, live }: { text: string; live: boolean }) {
|
||||
if (!text) return null;
|
||||
|
||||
return (
|
||||
<div className="mb-[11px] overflow-hidden border-[3px] border-line bg-card shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<div className="mb-[11px] overflow-hidden border border-line bg-card shadow-hard-sm" style={{ borderRadius: 7 }}>
|
||||
<button
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="flex w-full items-center gap-2 px-3 py-2 text-left"
|
||||
@@ -428,7 +428,7 @@ function ReasoningPanel({ text, live }: { text: string; live: boolean }) {
|
||||
<span className="text-[13px] font-medium text-ink">Raisonnement</span>
|
||||
{live && (
|
||||
<span className="flex items-center gap-1 text-[12px] text-accent">
|
||||
<span className="h-2 w-2 animate-pulse border-2 border-line bg-accent" />
|
||||
<span className="h-2 w-2 animate-pulse border border-line bg-accent" />
|
||||
en cours…
|
||||
</span>
|
||||
)}
|
||||
@@ -439,7 +439,7 @@ function ReasoningPanel({ text, live }: { text: string; live: boolean }) {
|
||||
{open && (
|
||||
<div
|
||||
ref={bodyRef}
|
||||
className="scr max-h-[200px] min-h-[60px] resize-y overflow-auto whitespace-pre-wrap border-t-2 border-line-soft bg-card-deep px-3 py-2 text-[12.5px] leading-relaxed text-on-dark-2"
|
||||
className="scr max-h-[200px] min-h-[60px] resize-y overflow-auto whitespace-pre-wrap border-t border-line-soft bg-card-deep px-3 py-2 text-[12.5px] leading-relaxed text-on-dark-2"
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
@@ -458,26 +458,26 @@ function ShellConfirm({
|
||||
onReject: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="ml-[46px] overflow-hidden border-[3px] border-line bg-card shadow-hard" style={{ borderRadius: 7 }}>
|
||||
<div className="flex items-center gap-2 border-b-2 border-line px-3 py-2.5">
|
||||
<div className="ml-[46px] overflow-hidden border border-line bg-card shadow-hard" style={{ borderRadius: 7 }}>
|
||||
<div className="flex items-center gap-2 border-b border-line px-3 py-2.5">
|
||||
<span className="text-[14px] text-accent">run_shell</span>
|
||||
<span className="text-[13px] text-muted-2">· commande sensible à valider</span>
|
||||
</div>
|
||||
<div className="px-3 py-3">
|
||||
<pre className="m-0 mb-3 overflow-auto whitespace-pre-wrap border-2 border-line bg-card-deep px-3 py-2.5 text-[12.5px] text-on-dark">
|
||||
<pre className="m-0 mb-3 overflow-auto whitespace-pre-wrap border border-line bg-card-deep px-3 py-2.5 text-[12.5px] text-on-dark">
|
||||
$ {command}
|
||||
</pre>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={onApprove}
|
||||
className="flex h-[32px] items-center gap-1.5 border-[3px] border-line bg-card-deep px-3.5 text-[13px] text-white shadow-hard-accent"
|
||||
className="flex h-[32px] items-center gap-1.5 border border-line bg-accent px-3.5 text-[13px] text-white shadow-hard-accent"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
Approuver & exécuter
|
||||
</button>
|
||||
<button
|
||||
onClick={onReject}
|
||||
className="flex h-[32px] items-center border-[3px] border-line bg-card px-3.5 text-[13px] text-ink-2"
|
||||
className="flex h-[32px] items-center border border-line bg-card px-3.5 text-[13px] text-ink-2"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
Refuser
|
||||
@@ -490,7 +490,7 @@ function ShellConfirm({
|
||||
|
||||
function Chip({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex h-7 items-center gap-1.5 border-2 border-line bg-card px-2.5 text-[13px] text-ink-2">
|
||||
<div className="flex h-7 items-center gap-1.5 border border-line bg-card px-2.5 text-[13px] text-ink-2">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ export function LeftPanel() {
|
||||
}, [refreshSessions, refreshFiles]);
|
||||
|
||||
return (
|
||||
<div className="flex w-[280px] flex-none flex-col border-r-[3px] border-line bg-panel">
|
||||
<div className="flex w-[280px] flex-none flex-col border-r border-line bg-panel">
|
||||
{/* Historique */}
|
||||
<div className="flex items-center justify-between px-4 pb-2.5 pt-4">
|
||||
<span className="font-pixel text-[10px] text-label">HISTORIQUE</span>
|
||||
@@ -50,7 +50,7 @@ export function LeftPanel() {
|
||||
<div
|
||||
key={s.id}
|
||||
onClick={() => openSession(s.id)}
|
||||
className={`group cursor-pointer border-[3px] border-line px-3 py-2.5 ${
|
||||
className={`group cursor-pointer border border-line px-3 py-2.5 ${
|
||||
active ? "bg-card-deep shadow-hard-accent" : "bg-card"
|
||||
}`}
|
||||
style={{ borderRadius: 7 }}
|
||||
@@ -58,14 +58,14 @@ export function LeftPanel() {
|
||||
<div className="flex items-center gap-1">
|
||||
<div
|
||||
className={`flex-1 truncate text-[14px] leading-tight ${
|
||||
active ? "text-white" : "text-ink-2"
|
||||
active ? "text-ink" : "text-ink-2"
|
||||
}`}
|
||||
>
|
||||
{s.title}
|
||||
</div>
|
||||
{working && (
|
||||
<span
|
||||
className="flex-none border-2 border-line bg-accent px-1.5 py-0.5 text-[10px] leading-none text-white"
|
||||
className="flex-none border border-line bg-accent px-1.5 py-0.5 text-[10px] leading-none text-white"
|
||||
title="Session en cours de travail"
|
||||
>
|
||||
EN COURS
|
||||
@@ -93,7 +93,7 @@ export function LeftPanel() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mx-0 my-4 h-[3px] bg-line" />
|
||||
<div className="mx-0 my-4 h-px bg-line" />
|
||||
|
||||
{/* Fichiers */}
|
||||
<div className="flex items-center justify-between px-4 pb-2">
|
||||
@@ -136,7 +136,7 @@ function FileTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
|
||||
className={`group mb-[3px] flex items-center gap-2 px-2 py-[5px] ${
|
||||
n.type === "file"
|
||||
? active
|
||||
? "cursor-pointer border-2 border-line bg-card-deep text-white"
|
||||
? "cursor-pointer border border-line bg-card-deep text-ink"
|
||||
: "cursor-pointer text-ink-2 hover:bg-base"
|
||||
: "text-muted"
|
||||
}`}
|
||||
@@ -146,7 +146,7 @@ function FileTree({ nodes, depth }: { nodes: FileNode[]; depth: number }) {
|
||||
<span className="text-muted-2">▾</span>
|
||||
) : (
|
||||
<span
|
||||
className={`h-2 w-2 border-2 ${
|
||||
className={`h-2 w-2 border ${
|
||||
active ? "border-white bg-accent" : "border-line bg-muted-2"
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function PreviewPanel() {
|
||||
// Replié : barre fine, aucun contenu monté (pas d'iframe vivante).
|
||||
if (collapsed) {
|
||||
return (
|
||||
<div className="flex w-9 flex-none flex-col items-center border-l-[3px] border-line bg-panel pt-3">
|
||||
<div className="flex w-9 flex-none flex-col items-center border-l border-line bg-panel pt-3">
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
title="Déplier l'aperçu"
|
||||
@@ -80,7 +80,7 @@ export function PreviewPanel() {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="relative flex flex-none flex-col border-l-[3px] border-line bg-panel"
|
||||
className="relative flex flex-none flex-col border-l border-line bg-panel"
|
||||
style={{ width }}
|
||||
>
|
||||
<div
|
||||
@@ -97,7 +97,7 @@ export function PreviewPanel() {
|
||||
title="Glisser pour redimensionner"
|
||||
/>
|
||||
{/* Onglets */}
|
||||
<div className="flex h-[46px] flex-none items-center gap-1.5 border-b-[3px] border-line bg-base px-3">
|
||||
<div className="flex h-[46px] flex-none items-center gap-1.5 border-b border-line bg-base px-3">
|
||||
<Tab active={tab === "preview"} onClick={() => setTab("preview")}>
|
||||
Aperçu
|
||||
</Tab>
|
||||
@@ -106,7 +106,7 @@ export function PreviewPanel() {
|
||||
</Tab>
|
||||
<Tab active={tab === "logs"} onClick={() => setTab("logs")}>
|
||||
Logs
|
||||
<span className="font-pixel ml-1.5 border-2 border-line bg-accent px-1 py-0.5 text-[8px] text-white">
|
||||
<span className="font-pixel ml-1.5 border border-line bg-accent px-1 py-0.5 text-[8px] text-white">
|
||||
{logs.length}
|
||||
</span>
|
||||
</Tab>
|
||||
@@ -125,7 +125,7 @@ export function PreviewPanel() {
|
||||
|
||||
{/* URL bar */}
|
||||
<div className="flex flex-none items-center gap-2 px-3 py-2.5">
|
||||
<div className="flex h-8 flex-1 items-center gap-2 border-[3px] border-line bg-card px-[11px]">
|
||||
<div className="flex h-8 flex-1 items-center gap-2 border border-line bg-card px-[11px]">
|
||||
<span className="text-[13px]">🔒</span>
|
||||
<span className="truncate text-[13px] text-muted-2">
|
||||
{previewPath ? `workspace/${previewPath}` : "workspace/"}
|
||||
@@ -135,7 +135,7 @@ export function PreviewPanel() {
|
||||
<>
|
||||
<button
|
||||
onClick={() => downloadFile(previewPath, useStore.getState().currentProject())}
|
||||
className="flex h-8 items-center gap-1.5 border-[3px] border-line bg-card px-2.5 text-[12px] text-accent"
|
||||
className="flex h-8 items-center gap-1.5 border border-line bg-card px-2.5 text-[12px] text-accent"
|
||||
title={`Télécharger ${previewPath}`}
|
||||
>
|
||||
<DownloadIcon size={13} />
|
||||
@@ -147,7 +147,7 @@ export function PreviewPanel() {
|
||||
void removeFile(previewPath);
|
||||
}
|
||||
}}
|
||||
className="flex h-8 items-center gap-1.5 border-[3px] border-line bg-card px-2.5 text-[12px] text-warn"
|
||||
className="flex h-8 items-center gap-1.5 border border-line bg-card px-2.5 text-[12px] text-warn"
|
||||
title={`Supprimer ${previewPath}`}
|
||||
>
|
||||
<TrashIcon size={13} />
|
||||
@@ -162,7 +162,7 @@ export function PreviewPanel() {
|
||||
<GitPanel />
|
||||
) : /* Onglet Logs (fond sombre) */
|
||||
tab === "logs" ? (
|
||||
<div className="scr mx-3 mb-3 flex-1 overflow-auto border-[3px] border-line bg-card-deep p-3 text-[12.5px]">
|
||||
<div className="scr mx-3 mb-3 flex-1 overflow-auto border border-line bg-card-deep p-3 text-[12.5px]">
|
||||
{logs.length === 0 ? (
|
||||
<div className="py-10 text-center text-on-dark-3">
|
||||
Aucune activité d'outil pour cette session.
|
||||
@@ -171,7 +171,7 @@ export function PreviewPanel() {
|
||||
logs.map((l, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-2 border-b-2 border-chrome-2 py-[7px] last:border-0"
|
||||
className="flex items-center gap-2 border-b border-chrome-2 py-[7px] last:border-0"
|
||||
>
|
||||
<span
|
||||
className={
|
||||
@@ -204,14 +204,14 @@ export function PreviewPanel() {
|
||||
</div>
|
||||
) : (
|
||||
/* Viewport Aperçu / Code (fond clair) */
|
||||
<div className="scr mx-3 mb-3 flex-1 overflow-auto border-[3px] border-line bg-[#faf6ef] shadow-hard">
|
||||
<div className="scr mx-3 mb-3 flex-1 overflow-auto border border-line bg-panel shadow-hard">
|
||||
{!previewPath ? (
|
||||
<Empty>
|
||||
L'aperçu s'affichera ici dès que l'agent générera un fichier (clique
|
||||
aussi un fichier à gauche).
|
||||
</Empty>
|
||||
) : tab === "code" ? (
|
||||
<pre className="m-0 whitespace-pre-wrap p-4 text-[12px] leading-relaxed text-[#2a2018]">
|
||||
<pre className="m-0 whitespace-pre-wrap p-4 text-[12px] leading-relaxed text-ink">
|
||||
{previewContent}
|
||||
</pre>
|
||||
) : isHtml ? (
|
||||
@@ -225,7 +225,7 @@ export function PreviewPanel() {
|
||||
sandbox="allow-scripts"
|
||||
/>
|
||||
) : (
|
||||
<pre className="m-0 whitespace-pre-wrap p-4 text-[12px] leading-relaxed text-[#2a2018]">
|
||||
<pre className="m-0 whitespace-pre-wrap p-4 text-[12px] leading-relaxed text-ink">
|
||||
{previewContent}
|
||||
</pre>
|
||||
)}
|
||||
@@ -276,14 +276,14 @@ function GitPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="scr mx-3 mb-3 flex flex-1 flex-col overflow-hidden border-[3px] border-line bg-card">
|
||||
<div className="scr mx-3 mb-3 flex flex-1 flex-col overflow-hidden border border-line bg-card">
|
||||
{error && (
|
||||
<div className="border-b-2 border-line bg-warn/10 px-3 py-2 text-[12px] text-warn">
|
||||
<div className="border-b border-line bg-warn/10 px-3 py-2 text-[12px] text-warn">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
{/* Liste des commits */}
|
||||
<div className="scr max-h-[45%] overflow-auto border-b-2 border-line">
|
||||
<div className="scr max-h-[45%] overflow-auto border-b border-line">
|
||||
{commits.length === 0 ? (
|
||||
<div className="p-4 text-center text-[12px] text-muted-2">
|
||||
Aucun commit. L'agent commitera chaque modification de code ici.
|
||||
@@ -310,7 +310,7 @@ function GitPanel() {
|
||||
<button
|
||||
onClick={() => doRevert(c.hash)}
|
||||
disabled={busy === c.hash}
|
||||
className="flex-none border-2 border-line bg-card px-2 py-1 text-[11px] text-warn disabled:opacity-40"
|
||||
className="flex-none border border-line bg-card px-2 py-1 text-[11px] text-warn disabled:opacity-40"
|
||||
title="Annuler ce commit (revert)"
|
||||
>
|
||||
{busy === c.hash ? "…" : "↶ Annuler"}
|
||||
@@ -357,8 +357,8 @@ function DiffView({ diff }: { diff: string }) {
|
||||
function Empty({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-2 p-8 text-center">
|
||||
<div className="font-pixel text-[10px] text-[#a98b63]">AUCUN APERÇU</div>
|
||||
<div className="max-w-[240px] text-[13px] text-[#8a7a66]">{children}</div>
|
||||
<div className="font-pixel text-[10px] text-label">AUCUN APERÇU</div>
|
||||
<div className="max-w-[240px] text-[13px] text-muted-2">{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -375,8 +375,8 @@ function Tab({
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`flex h-[30px] items-center border-[3px] border-line px-[13px] text-[13px] ${
|
||||
active ? "bg-card-deep text-white" : "bg-card text-muted"
|
||||
className={`flex h-[30px] items-center border border-line px-[13px] text-[13px] ${
|
||||
active ? "bg-card-deep text-ink" : "bg-card text-muted"
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -130,13 +130,13 @@ export function SettingsView() {
|
||||
<div className="flex items-center gap-3">
|
||||
<PresetBar model={selectedModel} onApplied={refreshConfig} />
|
||||
<div className="flex items-center gap-1.5 text-[13px] text-ok">
|
||||
<span className="h-2 w-2 border-2 border-line bg-ok" />
|
||||
<span className="h-2 w-2 border border-line bg-ok" />
|
||||
{status?.connected ? "Ollama connecté" : "Ollama déconnecté"}
|
||||
</div>
|
||||
<button
|
||||
onClick={save}
|
||||
disabled={!dirty}
|
||||
className="flex h-[34px] items-center gap-1.5 border-[3px] border-line bg-accent px-4 text-[14px] text-white shadow-accent-soft disabled:opacity-40"
|
||||
className="flex h-[34px] items-center gap-1.5 border border-line bg-accent px-4 text-[14px] text-white shadow-accent-soft disabled:opacity-40"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
{saved ? "✓ ENREGISTRÉ" : dirty ? "ENREGISTRER" : "À JOUR"}
|
||||
@@ -159,7 +159,7 @@ export function SettingsView() {
|
||||
</div>
|
||||
<button
|
||||
onClick={refreshModels}
|
||||
className="flex h-7 items-center gap-1.5 border-2 border-line bg-card-soft px-2.5 text-[13px] text-muted-2"
|
||||
className="flex h-7 items-center gap-1.5 border border-line bg-card-soft px-2.5 text-[13px] text-muted-2"
|
||||
>
|
||||
<RefreshIcon size={12} />
|
||||
Actualiser
|
||||
@@ -168,7 +168,7 @@ export function SettingsView() {
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
{models.length === 0 && (
|
||||
<div className="border-2 border-line bg-base p-4 text-center text-[13px] text-muted-2">
|
||||
<div className="border border-line bg-base p-4 text-center text-[13px] text-muted-2">
|
||||
Aucun modèle installé. Télécharge-en un ci-dessous.
|
||||
</div>
|
||||
)}
|
||||
@@ -179,7 +179,7 @@ export function SettingsView() {
|
||||
return (
|
||||
<div
|
||||
key={m.name}
|
||||
className={`flex items-center gap-3 border-[3px] border-line p-3 text-left ${
|
||||
className={`flex items-center gap-3 border border-line p-3 text-left ${
|
||||
on ? "bg-card-deep" : "bg-card-soft"
|
||||
}`}
|
||||
>
|
||||
@@ -188,7 +188,7 @@ export function SettingsView() {
|
||||
className="flex min-w-0 flex-1 items-center gap-3 text-left"
|
||||
>
|
||||
<span
|
||||
className={`flex h-[18px] w-[18px] flex-none items-center justify-center border-[3px] ${
|
||||
className={`flex h-[18px] w-[18px] flex-none items-center justify-center border ${
|
||||
on ? "border-accent" : "border-muted-3"
|
||||
}`}
|
||||
>
|
||||
@@ -196,11 +196,11 @@ export function SettingsView() {
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={`truncate text-[14px] ${on ? "text-white" : "text-ink"}`}>
|
||||
<span className={`truncate text-[14px] ${on ? "text-ink" : "text-ink"}`}>
|
||||
{m.name}
|
||||
</span>
|
||||
{m.name === status?.default_model && (
|
||||
<span className="font-pixel flex-none border-2 border-white bg-accent px-1.5 py-0.5 text-[7px] text-white">
|
||||
<span className="font-pixel flex-none border border-white bg-accent px-1.5 py-0.5 text-[7px] text-white">
|
||||
DÉFAUT
|
||||
</span>
|
||||
)}
|
||||
@@ -237,7 +237,7 @@ export function SettingsView() {
|
||||
<button
|
||||
onClick={() => doDeleteModel(m.name)}
|
||||
disabled={deletingModel === m.name}
|
||||
className="flex-none border-2 border-line bg-base px-2 py-1 text-[12px] text-warn disabled:opacity-50"
|
||||
className="flex-none border border-line bg-base px-2 py-1 text-[12px] text-warn disabled:opacity-50"
|
||||
title={`Supprimer ${m.name}`}
|
||||
>
|
||||
{deletingModel === m.name ? "…" : "Supprimer"}
|
||||
@@ -246,18 +246,18 @@ export function SettingsView() {
|
||||
);
|
||||
})}
|
||||
{deleteError && (
|
||||
<div className="border-2 border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
<div className="border border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
{deleteError}
|
||||
</div>
|
||||
)}
|
||||
{warmError && (
|
||||
<div className="border-2 border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
<div className="border border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
Préchargement : {warmError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="my-4 h-[3px] bg-line" />
|
||||
<div className="my-4 h-px bg-line" />
|
||||
|
||||
<div className="font-pixel mb-2.5 text-[10px] text-label">
|
||||
TÉLÉCHARGER
|
||||
@@ -267,11 +267,11 @@ export function SettingsView() {
|
||||
value={pullName}
|
||||
onChange={(e) => setPullName(e.target.value)}
|
||||
placeholder="gemma2:9b"
|
||||
className="h-9 flex-1 border-[3px] border-line bg-card-soft px-3 text-[13px] text-ink outline-none placeholder:text-muted-3"
|
||||
className="h-9 flex-1 border border-line bg-card-soft px-3 text-[13px] text-ink outline-none placeholder:text-muted-3"
|
||||
/>
|
||||
<button
|
||||
onClick={doPull}
|
||||
className="flex h-9 items-center gap-1.5 border-[3px] border-line bg-card-deep px-3.5 text-[13px] text-white"
|
||||
className="flex h-9 items-center gap-1.5 border border-line bg-accent px-3.5 text-[13px] text-white"
|
||||
>
|
||||
<DownloadIcon />
|
||||
Récupérer
|
||||
@@ -279,14 +279,14 @@ export function SettingsView() {
|
||||
</div>
|
||||
|
||||
{pullStatus && (
|
||||
<div className="border-[3px] border-line bg-card-soft p-3">
|
||||
<div className="border border-line bg-card-soft p-3">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-[13px] text-ink-2">{pullName}</span>
|
||||
<span className="text-[13px] text-accent">
|
||||
{pullStatus} {pullPct ? `· ${pullPct}%` : ""}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex h-3.5 gap-px border-2 border-line bg-card p-px">
|
||||
<div className="flex h-3.5 gap-px border border-line bg-card p-px">
|
||||
<span
|
||||
className="bg-accent transition-all"
|
||||
style={{ width: `${pullPct}%` }}
|
||||
@@ -374,13 +374,13 @@ export function SettingsView() {
|
||||
onChange={(v) => set("num_batch", Math.round(v))}
|
||||
last
|
||||
/>
|
||||
<div className="mt-3 border-2 border-line bg-card-soft px-3 py-2 text-[12px] leading-relaxed text-muted-2">
|
||||
<div className="mt-3 border border-line bg-card-soft px-3 py-2 text-[12px] leading-relaxed text-muted-2">
|
||||
La précision KV (<code>f16</code>/<code>q8_0</code>) est un
|
||||
réglage global du serveur Ollama et nécessite son redémarrage.
|
||||
</div>
|
||||
</Advanced>
|
||||
|
||||
<div className="mt-4 flex items-center gap-3 border-2 border-line bg-base px-3 py-2.5">
|
||||
<div className="mt-4 flex items-center gap-3 border border-line bg-base px-3 py-2.5">
|
||||
<div className="flex-1">
|
||||
<div className="text-[14px] text-ink">Mode réflexion</div>
|
||||
<div className="text-[12px] text-muted-2">
|
||||
@@ -405,7 +405,7 @@ export function SettingsView() {
|
||||
<div
|
||||
key={name}
|
||||
className={`flex items-center gap-3 py-2 ${
|
||||
i > 0 ? "border-t-2 border-line-soft" : ""
|
||||
i > 0 ? "border-t border-line-soft" : ""
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
@@ -424,7 +424,7 @@ export function SettingsView() {
|
||||
);
|
||||
})}
|
||||
{draft.tools.run_shell && (
|
||||
<div className="mt-2 flex items-center gap-3 border-2 border-line bg-base px-3 py-2.5">
|
||||
<div className="mt-2 flex items-center gap-3 border border-line bg-base px-3 py-2.5">
|
||||
<span className="flex-1 text-[13px] text-ink-2">
|
||||
Demander une validation avant chaque commande shell
|
||||
</span>
|
||||
@@ -473,7 +473,7 @@ export function SettingsView() {
|
||||
onClick={() => set(key, !draft[key])}
|
||||
/>
|
||||
))}
|
||||
<div className="flex items-center gap-3 border-t-2 border-line-soft py-2">
|
||||
<div className="flex items-center gap-3 border-t border-line-soft py-2">
|
||||
<span className="flex-1">
|
||||
<span className="block text-[14px] text-ink">Mémoire</span>
|
||||
<span className="block text-[12px] text-muted-2">
|
||||
@@ -489,7 +489,7 @@ export function SettingsView() {
|
||||
e.target.value as AgentConfig["memory_mode"]
|
||||
)
|
||||
}
|
||||
className="h-8 border-[3px] border-line bg-card px-2 text-[13px] text-ink"
|
||||
className="h-8 border border-line bg-card px-2 text-[13px] text-ink"
|
||||
>
|
||||
<option value="off">Désactivée</option>
|
||||
<option value="ondemand">Sur demande</option>
|
||||
@@ -497,7 +497,7 @@ export function SettingsView() {
|
||||
</select>
|
||||
</div>
|
||||
</Advanced>
|
||||
<div className="mt-2 flex items-center gap-3 border-t-2 border-line-soft pt-3">
|
||||
<div className="mt-2 flex items-center gap-3 border-t border-line-soft pt-3">
|
||||
<span className="flex-1">
|
||||
<span className="block text-[14px] text-ink">
|
||||
Maintien en VRAM (préchargement)
|
||||
@@ -509,7 +509,7 @@ export function SettingsView() {
|
||||
<select
|
||||
value={draft.keep_alive}
|
||||
onChange={(e) => set("keep_alive", e.target.value)}
|
||||
className="h-8 border-[3px] border-line bg-card px-2 text-[13px] text-ink"
|
||||
className="h-8 border border-line bg-card px-2 text-[13px] text-ink"
|
||||
>
|
||||
<option value="0">Décharger aussitôt</option>
|
||||
<option value="5m">5 min</option>
|
||||
@@ -542,7 +542,7 @@ export function SettingsView() {
|
||||
value={draft.system_prompt}
|
||||
onChange={(e) => set("system_prompt", e.target.value)}
|
||||
rows={6}
|
||||
className="scr w-full resize-y border-[3px] border-line bg-card-deep p-4 text-[13px] leading-[1.6] text-on-dark outline-none"
|
||||
className="scr w-full resize-y border border-line bg-card-deep p-4 text-[13px] leading-[1.6] text-on-dark outline-none"
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
@@ -595,10 +595,10 @@ function McpCard() {
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
{servers.map((s) => (
|
||||
<div key={s.id} className="border-[3px] border-line bg-base p-3">
|
||||
<div key={s.id} className="border border-line bg-base p-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`h-[11px] w-[11px] flex-none border-2 border-line ${
|
||||
className={`h-[11px] w-[11px] flex-none border border-line ${
|
||||
s.state === "connected"
|
||||
? "bg-ok"
|
||||
: s.state === "error"
|
||||
@@ -623,7 +623,7 @@ function McpCard() {
|
||||
<button
|
||||
onClick={() => doTest(s)}
|
||||
disabled={testing === s.id}
|
||||
className="border-2 border-line bg-card px-2 py-1 text-[11px] text-ink-2"
|
||||
className="border border-line bg-card px-2 py-1 text-[11px] text-ink-2"
|
||||
>
|
||||
{testing === s.id ? "test…" : "Tester"}
|
||||
</button>
|
||||
@@ -636,7 +636,7 @@ function McpCard() {
|
||||
defaultValue={s.params.command ?? ""}
|
||||
onBlur={(e) => setParam(s, "command", e.target.value)}
|
||||
placeholder="commande stdio (ex. npx -y mon-mcp) ou URL"
|
||||
className="border-2 border-line bg-card px-2 py-1 text-[12px] text-ink"
|
||||
className="border border-line bg-card px-2 py-1 text-[12px] text-ink"
|
||||
/>
|
||||
)}
|
||||
{s.env_params.map((k) => (
|
||||
@@ -645,7 +645,7 @@ function McpCard() {
|
||||
defaultValue={s.params[k] ?? ""}
|
||||
onBlur={(e) => setParam(s, k, e.target.value)}
|
||||
placeholder={k}
|
||||
className="border-2 border-line bg-card px-2 py-1 text-[12px] text-ink"
|
||||
className="border border-line bg-card px-2 py-1 text-[12px] text-ink"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -685,7 +685,7 @@ function HardwareCard() {
|
||||
<div className="font-pixel text-[11px] text-ink">MATÉRIEL</div>
|
||||
<button
|
||||
onClick={refresh}
|
||||
className="flex h-7 items-center gap-1.5 border-2 border-line bg-card-soft px-2.5 text-[13px] text-muted-2"
|
||||
className="flex h-7 items-center gap-1.5 border border-line bg-card-soft px-2.5 text-[13px] text-muted-2"
|
||||
>
|
||||
<RefreshIcon size={12} />
|
||||
Rafraîchir
|
||||
@@ -698,7 +698,7 @@ function HardwareCard() {
|
||||
</div>
|
||||
{hw?.loki_gpus.length ? (
|
||||
hw.loki_gpus.map((g) => (
|
||||
<div key={g.index} className="border-2 border-line bg-base px-3 py-2 text-[13px]">
|
||||
<div key={g.index} className="border border-line bg-base px-3 py-2 text-[13px]">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-ink">{g.name}</span>
|
||||
<span className="text-muted-2">
|
||||
@@ -709,7 +709,7 @@ function HardwareCard() {
|
||||
</div>
|
||||
))
|
||||
) : hw?.gpu_override ? (
|
||||
<div className="border-2 border-line bg-base px-3 py-2 text-[13px] text-ink">
|
||||
<div className="border border-line bg-base px-3 py-2 text-[13px] text-ink">
|
||||
{hw.gpu_override.name} ·{" "}
|
||||
{(hw.gpu_override.vram_total_mb / 1024).toFixed(0)} Go{" "}
|
||||
<span className="text-muted-2">(valeur déclarée)</span>
|
||||
@@ -719,7 +719,7 @@ function HardwareCard() {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border-2 border-line bg-base px-3 py-2 text-[12px] text-muted-2">
|
||||
<div className="border border-line bg-base px-3 py-2 text-[12px] text-muted-2">
|
||||
Aucun GPU visible depuis le conteneur — normal si Ollama tourne sur une
|
||||
autre machine. Déclare <code>GPU_VRAM_MB</code> pour l'auto-réglage.
|
||||
</div>
|
||||
@@ -729,10 +729,10 @@ function HardwareCard() {
|
||||
<div className="mb-1 mt-3 text-[12px] font-bold uppercase tracking-wide text-label">
|
||||
Ollama {hw?.ollama_is_local ? "(local)" : "(distant)"}
|
||||
</div>
|
||||
<div className="border-2 border-line bg-base px-3 py-2 text-[13px]">
|
||||
<div className="border border-line bg-base px-3 py-2 text-[13px]">
|
||||
<div className="mb-1 flex items-center gap-2">
|
||||
<span
|
||||
className={`h-2 w-2 border-2 border-line ${
|
||||
className={`h-2 w-2 border border-line ${
|
||||
hw?.ollama.connected ? "bg-ok" : "bg-warn"
|
||||
}`}
|
||||
/>
|
||||
@@ -820,7 +820,7 @@ function BenchCard() {
|
||||
<button
|
||||
onClick={launch}
|
||||
disabled={running || !selectedModel}
|
||||
className="flex h-8 items-center gap-1.5 border-[3px] border-line bg-accent px-3 text-[13px] text-white shadow-accent-soft disabled:opacity-40"
|
||||
className="flex h-8 items-center gap-1.5 border border-line bg-accent px-3 text-[13px] text-white shadow-accent-soft disabled:opacity-40"
|
||||
style={{ borderRadius: 7 }}
|
||||
>
|
||||
{running ? "ÉVALUATION…" : "TESTER CE MODÈLE"}
|
||||
@@ -833,7 +833,7 @@ function BenchCard() {
|
||||
</div>
|
||||
|
||||
{running && (
|
||||
<div className="border-2 border-line bg-base px-3 py-2">
|
||||
<div className="border border-line bg-base px-3 py-2">
|
||||
{progress.map((p) => (
|
||||
<div key={p.task} className="flex items-center gap-2 py-[2px] text-[13px]">
|
||||
<span className="flex-1 text-ink-2">{p.task}</span>
|
||||
@@ -850,13 +850,13 @@ function BenchCard() {
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<div className="border-2 border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
<div className="border border-warn bg-base px-3 py-2 text-[12px] text-warn">
|
||||
Échec du test : {error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!running && current && (
|
||||
<div className="border-2 border-line bg-base px-3 py-2">
|
||||
<div className="border border-line bg-base px-3 py-2">
|
||||
<div className="mb-1 flex items-center justify-between">
|
||||
<span className="text-[13px] text-ink">Score</span>
|
||||
<span
|
||||
@@ -898,7 +898,7 @@ function Card({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`border-[3px] border-line bg-card p-4 shadow-hard-lg ${className}`}
|
||||
className={`border border-line bg-card p-4 shadow-hard-lg ${className}`}
|
||||
style={{ borderRadius: 9 }}
|
||||
>
|
||||
{children}
|
||||
@@ -911,7 +911,7 @@ function Toggle({ on, onClick }: { on: boolean; onClick: () => void }) {
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`relative h-5 w-[38px] flex-none ${
|
||||
on ? "bg-card-deep" : "border-2 border-line bg-base"
|
||||
on ? "bg-card-deep" : "border border-line bg-base"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
@@ -968,7 +968,7 @@ function PresetBar({
|
||||
value=""
|
||||
disabled={busy || presets.length === 0}
|
||||
onChange={(e) => apply(e.target.value)}
|
||||
className="h-[34px] border-[3px] border-line bg-card px-2 text-[13px] text-ink disabled:opacity-40"
|
||||
className="h-[34px] border border-line bg-card px-2 text-[13px] text-ink disabled:opacity-40"
|
||||
title="Appliquer un preset"
|
||||
>
|
||||
<option value="">
|
||||
@@ -983,7 +983,7 @@ function PresetBar({
|
||||
<button
|
||||
onClick={create}
|
||||
disabled={busy}
|
||||
className="h-[34px] border-[3px] border-line bg-card px-2.5 text-[13px] text-muted-2 disabled:opacity-40"
|
||||
className="h-[34px] border border-line bg-card px-2.5 text-[13px] text-muted-2 disabled:opacity-40"
|
||||
title="Enregistrer les réglages actuels comme preset"
|
||||
>
|
||||
+ Preset
|
||||
@@ -1005,7 +1005,7 @@ function Advanced({
|
||||
label?: string;
|
||||
}) {
|
||||
return (
|
||||
<details className="group mt-3 border-t-2 border-line-soft pt-3">
|
||||
<details className="group mt-3 border-t border-line-soft pt-3">
|
||||
<summary className="flex cursor-pointer select-none items-center gap-1.5 text-[13px] text-muted-2 marker:content-['']">
|
||||
<span className="transition-transform group-open:rotate-90">▸</span>
|
||||
{label}
|
||||
@@ -1032,7 +1032,7 @@ function ToggleRow({
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center gap-3 py-2 ${
|
||||
first ? "" : "border-t-2 border-line-soft"
|
||||
first ? "" : "border-t border-line-soft"
|
||||
}`}
|
||||
>
|
||||
<span className="flex-1">
|
||||
@@ -1070,13 +1070,13 @@ function Slider({
|
||||
<span className="text-[13px] text-ink-2">{label}</span>
|
||||
<span className="text-[13px] text-accent">{fmt(value)}</span>
|
||||
</div>
|
||||
<div className="relative h-[14px] border-2 border-line bg-card">
|
||||
<div className="relative h-[14px] border border-line bg-card">
|
||||
<div
|
||||
className="absolute left-0 top-0 bottom-0 bg-line"
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-1/2 h-5 w-4 -translate-x-1/2 -translate-y-1/2 border-[3px] border-line bg-accent"
|
||||
className="absolute top-1/2 h-5 w-4 -translate-x-1/2 -translate-y-1/2 border border-line bg-accent"
|
||||
style={{ left: `${pct}%` }}
|
||||
/>
|
||||
<input
|
||||
|
||||
+63
-47
@@ -1,9 +1,17 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
/**
|
||||
* Système de design « Loki » — thème néo-brutaliste clair.
|
||||
* Bordures noires épaisses, ombres dures décalées, accent orange,
|
||||
* police pixel (Press Start 2P) pour les libellés, DM Mono pour le corps.
|
||||
* Système de design « Loki » — thème Nocturne clair (Claude Design).
|
||||
*
|
||||
* Reprend les jetons de `Loki App.dc.html` : fond gris froid, surfaces
|
||||
* blanches, filets 1 px, accent bleu ardoise et ombres douces diffuses.
|
||||
* Remplace le thème néo-brutaliste (bordures noires 3 px, ombres dures
|
||||
* décalées, accent orange, police pixel).
|
||||
*
|
||||
* Les NOMS de jetons sont inchangés : seule leur valeur bascule, ce qui fait
|
||||
* suivre toute l'interface sans réécrire les composants. Les jetons
|
||||
* historiquement « sombres » (card-deep, on-dark…) deviennent la surface
|
||||
* sélectionnée claire et son encre, conformément à la maquette.
|
||||
*/
|
||||
export default {
|
||||
content: ["./index.html", "./src/**/*.{ts,tsx}"],
|
||||
@@ -11,60 +19,68 @@ export default {
|
||||
extend: {
|
||||
colors: {
|
||||
// Fonds
|
||||
base: "#ececef", // contenu principal
|
||||
bar: "#18181b", // chrome sombre (barres)
|
||||
panel: "#fafafa", // panneaux latéraux
|
||||
sunken: "#18181b", // encarts sombres (code, invite système)
|
||||
base: "#f7f8fa", // --color-bg
|
||||
bar: "#ffffff", // barres : surface + filet (plus de chrome sombre)
|
||||
panel: "#ffffff", // --color-surface
|
||||
sunken: "#f1f3f6", // encarts (code, invite système) : neutral-900
|
||||
// Surfaces / cartes
|
||||
card: "#ffffff",
|
||||
"card-soft": "#fafafa",
|
||||
"card-deep": "#18181b",
|
||||
// Bordures
|
||||
line: "#18181b", // bordure noire signature
|
||||
"line-soft": "#e4e4e7", // séparateur clair
|
||||
"line-strong": "#18181b",
|
||||
// Accent orange
|
||||
accent: "#ff5436",
|
||||
"accent-1": "#ff6b50",
|
||||
"accent-2": "#e23e22",
|
||||
// Statuts
|
||||
ok: "#22c55e",
|
||||
info: "#3b82f6",
|
||||
warn: "#dc2626",
|
||||
// Textes (sombres sur fond clair)
|
||||
ink: "#18181b",
|
||||
"ink-2": "#27272a",
|
||||
"ink-3": "#3f3f46",
|
||||
muted: "#52525b",
|
||||
"muted-2": "#71717a",
|
||||
"muted-3": "#a1a1aa",
|
||||
"muted-4": "#a1a1aa",
|
||||
label: "#52525b",
|
||||
// Teintes claires utiles sur le chrome sombre
|
||||
"chrome-2": "#27272a",
|
||||
"chrome-3": "#3f3f46",
|
||||
"on-dark": "#e4e4e7",
|
||||
"on-dark-2": "#a1a1aa",
|
||||
"on-dark-3": "#71717a",
|
||||
"card-soft": "#f7f8fa",
|
||||
"card-deep": "#eef2f9", // sélection : accent-900
|
||||
// Filets
|
||||
line: "#e3e6eb", // --color-divider
|
||||
"line-soft": "#e6e9ee", // neutral-800
|
||||
"line-strong": "#c3c9d2", // neutral-700
|
||||
// Accent bleu ardoise
|
||||
accent: "#2f4a7a", // --color-accent
|
||||
"accent-1": "#4a6797", // accent-600
|
||||
"accent-2": "#26406b", // accent-300
|
||||
"accent-ghost": "#eef2f9", // accent-900 (fonds teintés)
|
||||
"accent-line": "#b9c6dd", // accent-700 (bordure de sélection)
|
||||
// Statuts, accordés à la palette
|
||||
ok: "#2f7a5a",
|
||||
info: "#2f4a7a",
|
||||
warn: "#b3403a",
|
||||
// Encres
|
||||
ink: "#1c2536", // --color-text / neutral-100
|
||||
"ink-2": "#29334a", // neutral-200
|
||||
"ink-3": "#3d4a5e", // neutral-300
|
||||
muted: "#55627a", // neutral-400
|
||||
"muted-2": "#6b7686", // neutral-500
|
||||
"muted-3": "#8b95a4", // neutral-600
|
||||
"muted-4": "#8b95a4",
|
||||
label: "#8b95a4", // couleur des « kickers »
|
||||
// Anciennes teintes « sur fond sombre » -> encres sur surface claire
|
||||
"chrome-2": "#e3e6eb",
|
||||
"chrome-3": "#c3c9d2",
|
||||
"on-dark": "#1c2536",
|
||||
"on-dark-2": "#3d4a5e",
|
||||
"on-dark-3": "#6b7686",
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['"DM Mono"', "ui-monospace", "monospace"],
|
||||
mono: ['"DM Mono"', "ui-monospace", "monospace"],
|
||||
pixel: ['"Press Start 2P"', "ui-monospace", "monospace"],
|
||||
sans: ['"Inter"', "ui-sans-serif", "system-ui", "sans-serif"],
|
||||
mono: ["ui-monospace", '"SF Mono"', "Menlo", "monospace"],
|
||||
// Ex-police pixel : devient le « kicker » de la maquette — petites
|
||||
// capitales très espacées. Les libellés existants suivent sans edit.
|
||||
pixel: [
|
||||
'"Inter"',
|
||||
{ letterSpacing: "0.14em", fontWeight: "500" },
|
||||
],
|
||||
},
|
||||
borderRadius: {
|
||||
card: "9px",
|
||||
card: "10px", // --radius-md
|
||||
},
|
||||
boxShadow: {
|
||||
"hard-sm": "3px 3px 0 #18181b",
|
||||
hard: "4px 4px 0 #18181b",
|
||||
"hard-lg": "7px 7px 0 #18181b",
|
||||
"hard-accent": "3px 3px 0 #ff5436",
|
||||
"accent-soft": "3px 3px 0 rgba(255,84,54,.4)",
|
||||
frame: "4px 4px 0 #18181b",
|
||||
// Ombres douces diffuses (--shadow-sm/md/lg) au lieu des ombres dures.
|
||||
"hard-sm": "0 1px 2px rgba(28, 37, 54, 0.06)",
|
||||
hard: "0 2px 8px rgba(28, 37, 54, 0.08)",
|
||||
"hard-lg": "0 8px 28px rgba(28, 37, 54, 0.10)",
|
||||
"hard-accent": "0 2px 8px rgba(47, 74, 122, 0.18)",
|
||||
"accent-soft": "0 1px 2px rgba(47, 74, 122, 0.14)",
|
||||
frame: "0 2px 8px rgba(28, 37, 54, 0.08)",
|
||||
},
|
||||
backgroundImage: {
|
||||
"accent-grad": "linear-gradient(140deg, #ff6b50, #e23e22)",
|
||||
"accent-grad": "linear-gradient(140deg, #4a6797, #2f4a7a)",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user