Просмотр исходного кода

style(studio): typography and visibility overhaul — everything readable

Global:
- Body font-size 15px, line-height 1.6
- Light borders strengthened (oklch 0.88→0.82)
- Dark borders brightened (oklch 0.28→0.33)
- Muted text contrast increased in both themes

Sidebar: 220→230px, text-xs→text-sm for labels, text-sm for items,
  icons text-sm, borders from /20→border-border, + button 6×6

ChatBar: text-[13px]→text-sm throughout, border visible (border-border),
  clear button text-xs, send button w-7→w-7

Dashboard: metadata text-xs→text-sm, buttons text-xs→text-sm with
  larger padding, progress panel text-[13px]→text-sm

LogViewer: text-[12px]→text-sm, timestamp/tag visible
DaemonControl: status text-xs→text-sm, event log text-[13px]→text-sm
TruthFiles: filename text-xs→text-sm, size text-[11px]→text-xs
LanguageSelector: cards w-64→w-72, p-8→p-10, borders visible
Ma 7 месяцев назад
Родитель
Сommit
5f1aa4308c

+ 3 - 3
packages/studio/src/App.tsx

@@ -75,15 +75,15 @@ export function App() {
 
       <div className="flex-1 flex flex-col min-w-0">
         {/* Thin utility strip — pushed inward */}
-        <div className="h-12 shrink-0 border-b border-border/20 flex items-center justify-end px-8 gap-4">
+        <div className="h-12 shrink-0 border-b border-border flex items-center justify-end px-8 gap-4">
           <button
             onClick={() => setTheme(isDark ? "light" : "dark")}
-            className="w-7 h-7 flex items-center justify-center rounded-full bg-secondary/40 text-muted-foreground/60 hover:text-foreground hover:bg-secondary transition-all text-xs"
+            className="w-8 h-8 flex items-center justify-center rounded-full bg-secondary text-muted-foreground hover:text-foreground hover:bg-secondary/80 transition-all text-sm"
           >
             {isDark ? "☀" : "☽"}
           </button>
           {sse.connected && (
-            <span className="text-[10px] text-muted-foreground/30 uppercase tracking-[0.15em]">
+            <span className="text-xs text-muted-foreground uppercase tracking-wide">
               {t("nav.connected")}
             </span>
           )}

+ 6 - 6
packages/studio/src/components/ChatBar.tsx

@@ -118,7 +118,7 @@ export function ChatBar({ t, sse }: {
     : "Tell InkOS what to do...";
 
   return (
-    <div className="border-t border-border/20 bg-card/60 backdrop-blur-md mx-4 mb-3 rounded-lg">
+    <div className="border border-border bg-card/60 backdrop-blur-md mx-4 mb-3 rounded-lg">
       {/* Expanded message area */}
       {expanded && messages.length > 0 && (
         <div className="max-w-3xl mx-auto">
@@ -129,7 +129,7 @@ export function ChatBar({ t, sse }: {
             {messages.map((msg, i) => (
               <div
                 key={i}
-                className={`text-[13px] leading-relaxed ${
+                className={`text-sm leading-relaxed ${
                   msg.role === "user"
                     ? "text-foreground"
                     : msg.content.startsWith("✗")
@@ -139,12 +139,12 @@ export function ChatBar({ t, sse }: {
                         : "text-primary"
                 }`}
               >
-                {msg.role === "user" && <span className="text-muted-foreground/40 mr-2">›</span>}
+                {msg.role === "user" && <span className="text-muted-foreground mr-2">›</span>}
                 {msg.content}
               </div>
             ))}
             {loading && !messages.some((m) => m.content.startsWith("⋯")) && (
-              <div className="text-[13px] text-muted-foreground animate-pulse">⋯</div>
+              <div className="text-sm text-muted-foreground animate-pulse">⋯</div>
             )}
           </div>
         </div>
@@ -162,14 +162,14 @@ export function ChatBar({ t, sse }: {
             onKeyDown={(e) => e.key === "Enter" && handleSubmit()}
             placeholder={chatPlaceholder}
             disabled={loading}
-            className="w-full bg-transparent text-[13px] text-foreground placeholder:text-muted-foreground/25 focus:outline-none disabled:opacity-50"
+            className="w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:opacity-50"
           />
         </div>
         <div className="flex items-center gap-3 shrink-0">
           {expanded && messages.length > 0 && (
             <button
               onClick={() => { setExpanded(false); setMessages([]); }}
-              className="text-[10px] text-muted-foreground/30 hover:text-muted-foreground transition-colors"
+              className="text-xs text-muted-foreground hover:text-foreground transition-colors"
             >
               Clear
             </button>

+ 6 - 6
packages/studio/src/components/Sidebar.tsx

@@ -27,7 +27,7 @@ export function Sidebar({ nav, activePage, t }: {
   const { data: daemon } = useApi<{ running: boolean }>("/daemon");
 
   return (
-    <aside className="w-[220px] shrink-0 border-r border-border/30 bg-card/30 flex flex-col h-full overflow-y-auto">
+    <aside className="w-[230px] shrink-0 border-r border-border bg-card/40 flex flex-col h-full overflow-y-auto">
       {/* Logo — generous vertical breathing room */}
       <div className="px-6 pt-7 pb-6">
         <button onClick={nav.toDashboard} className="flex items-baseline gap-0.5 hover:opacity-70 transition-opacity">
@@ -39,10 +39,10 @@ export function Sidebar({ nav, activePage, t }: {
       {/* Books section */}
       <div className="flex-1 px-4">
         <div className="px-2 mb-3 flex items-center justify-between">
-          <span className="text-[11px] uppercase tracking-[0.12em] text-muted-foreground/60 font-medium">{t("nav.books")}</span>
+          <span className="text-xs uppercase tracking-[0.12em] text-muted-foreground font-medium">{t("nav.books")}</span>
           <button
             onClick={nav.toBookCreate}
-            className="w-5 h-5 flex items-center justify-center rounded text-[11px] text-muted-foreground/40 hover:text-primary hover:bg-primary/10 transition-all"
+            className="w-6 h-6 flex items-center justify-center rounded text-sm text-muted-foreground hover:text-primary hover:bg-primary/10 transition-all"
           >
             +
           </button>
@@ -72,7 +72,7 @@ export function Sidebar({ nav, activePage, t }: {
       </div>
 
       {/* System nav — generous spacing from book list */}
-      <div className="border-t border-border/20 mt-4 pt-4 pb-5 px-4 space-y-1">
+      <div className="border-t border-border mt-4 pt-4 pb-5 px-4 space-y-1">
         <SidebarItem
           label={t("nav.config")}
           icon="⚙"
@@ -115,9 +115,9 @@ function SidebarItem({ label, icon, active, onClick, badge, badgeColor }: {
           : "text-foreground/50 hover:text-foreground hover:bg-muted/40"
       }`}
     >
-      <span className="text-xs w-4 text-center opacity-50">{icon}</span>
+      <span className="text-sm w-4 text-center opacity-60">{icon}</span>
       <span className="flex-1">{label}</span>
-      {badge && <span className={`text-[9px] ${badgeColor ?? ""}`}>{badge}</span>}
+      {badge && <span className={`text-xs ${badgeColor ?? ""}`}>{badge}</span>}
     </button>
   );
 }

+ 9 - 9
packages/studio/src/hooks/use-colors.ts

@@ -2,23 +2,23 @@ import type { Theme } from "./use-theme";
 
 export function useColors(_theme: Theme) {
   return {
-    card: "border-border/60 hover:border-primary/30 transition-all duration-200",
-    cardStatic: "border-border/60",
+    card: "border-border hover:border-primary/40 transition-all duration-200",
+    cardStatic: "border-border",
     surface: "bg-card",
     muted: "text-muted-foreground",
     subtle: "text-muted-foreground",
     link: "hover:text-primary transition-colors cursor-pointer",
-    input: "bg-input/30 border border-border text-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 transition-all duration-200",
+    input: "bg-input/40 border border-border text-foreground focus:border-primary focus:ring-2 focus:ring-primary/20 transition-all duration-200",
     btnPrimary: "bg-primary text-primary-foreground hover:opacity-90 active:opacity-80 transition-opacity",
     btnSecondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 transition-colors",
     btnSuccess: "bg-emerald-600 text-white hover:bg-emerald-700 dark:bg-emerald-700 dark:hover:bg-emerald-600 transition-colors",
     btnDanger: "bg-destructive text-destructive-foreground hover:opacity-90 transition-opacity",
-    tableHeader: "bg-muted/50 text-muted-foreground text-xs uppercase tracking-wider",
-    tableDivide: "divide-border/60",
-    tableHover: "hover:bg-muted/30 transition-colors",
-    error: "border-destructive/30 bg-destructive/5 text-destructive",
-    info: "border-primary/20 bg-primary/5 text-primary",
-    code: "bg-muted text-muted-foreground font-mono text-sm",
+    tableHeader: "bg-muted/60 text-muted-foreground text-xs uppercase tracking-wider font-medium",
+    tableDivide: "divide-border",
+    tableHover: "hover:bg-muted/40 transition-colors",
+    error: "border-destructive/50 bg-destructive/10 text-destructive",
+    info: "border-primary/30 bg-primary/8 text-primary",
+    code: "bg-muted text-foreground/80 font-mono",
     active: "text-emerald-500",
     paused: "text-amber-500",
     mono: "font-mono text-sm",

+ 8 - 6
packages/studio/src/index.css

@@ -48,13 +48,13 @@
   --secondary: oklch(0.93 0.01 80);
   --secondary-foreground: oklch(0.25 0.02 55);
   --muted: oklch(0.93 0.008 80);
-  --muted-foreground: oklch(0.50 0.02 55);
+  --muted-foreground: oklch(0.45 0.025 55);
   --accent: oklch(0.93 0.012 55);
   --accent-foreground: oklch(0.25 0.02 55);
   --destructive: oklch(0.55 0.2 25);
   --destructive-foreground: oklch(0.98 0 0);
-  --border: oklch(0.88 0.012 75);
-  --input: oklch(0.88 0.012 75);
+  --border: oklch(0.82 0.015 75);
+  --input: oklch(0.82 0.015 75);
   --ring: oklch(0.62 0.14 55);
   --amber: oklch(0.75 0.15 70);
   --amber-foreground: oklch(0.25 0.05 55);
@@ -73,13 +73,13 @@
   --secondary: oklch(0.22 0.018 55);
   --secondary-foreground: oklch(0.82 0.025 75);
   --muted: oklch(0.22 0.015 55);
-  --muted-foreground: oklch(0.55 0.02 70);
+  --muted-foreground: oklch(0.62 0.025 70);
   --accent: oklch(0.25 0.02 55);
   --accent-foreground: oklch(0.85 0.025 75);
   --destructive: oklch(0.60 0.2 25);
   --destructive-foreground: oklch(0.98 0 0);
-  --border: oklch(0.28 0.02 55);
-  --input: oklch(0.25 0.018 55);
+  --border: oklch(0.33 0.025 55);
+  --input: oklch(0.30 0.02 55);
   --ring: oklch(0.72 0.15 65);
   --amber: oklch(0.72 0.15 65);
   --amber-foreground: oklch(0.13 0.012 55);
@@ -87,6 +87,8 @@
 
 body {
   font-family: var(--font-sans);
+  font-size: 15px;
+  line-height: 1.6;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   background-color: var(--background);

+ 5 - 5
packages/studio/src/pages/DaemonControl.tsx

@@ -55,7 +55,7 @@ export function DaemonControl({ nav, theme, t, sse }: { nav: Nav; theme: Theme;
       <div className="flex items-baseline justify-between">
         <h1 className="font-serif text-3xl">Daemon</h1>
         <div className="flex items-center gap-3">
-          <span className={`text-xs uppercase tracking-widest ${isRunning ? "text-emerald-500" : "text-muted-foreground/50"}`}>
+          <span className={`text-sm uppercase tracking-wide font-medium ${isRunning ? "text-emerald-500" : "text-muted-foreground"}`}>
             {isRunning ? "Running" : "Stopped"}
           </span>
           {isRunning ? (
@@ -80,12 +80,12 @@ export function DaemonControl({ nav, theme, t, sse }: { nav: Nav; theme: Theme;
 
       {/* Daemon event log */}
       <div className={`border ${c.cardStatic} rounded-lg`}>
-        <div className="px-4 py-3 border-b border-border/40">
-          <span className="text-xs uppercase tracking-widest text-muted-foreground">Event Log</span>
+        <div className="px-5 py-3.5 border-b border-border">
+          <span className="text-sm uppercase tracking-wide text-muted-foreground font-medium">Event Log</span>
         </div>
         <div className="p-4 max-h-[500px] overflow-y-auto">
           {daemonEvents.length > 0 ? (
-            <div className="space-y-1 font-mono text-[13px]">
+            <div className="space-y-1.5 font-mono text-sm">
               {daemonEvents.map((msg, i) => {
                 const d = msg.data as Record<string, unknown>;
                 return (
@@ -98,7 +98,7 @@ export function DaemonControl({ nav, theme, t, sse }: { nav: Nav; theme: Theme;
               })}
             </div>
           ) : (
-            <div className="text-muted-foreground/40 text-sm italic py-8 text-center">
+            <div className="text-muted-foreground text-sm italic py-8 text-center">
               {isRunning ? "Waiting for events..." : "Start the daemon to see events"}
             </div>
           )}

+ 6 - 6
packages/studio/src/pages/Dashboard.tsx

@@ -93,7 +93,7 @@ export function Dashboard({ nav, sse, theme, t }: { nav: Nav; sse: { messages: R
                   >
                     {book.title}
                   </button>
-                  <div className="flex items-center gap-3 mt-1.5 text-xs text-muted-foreground">
+                  <div className="flex items-center gap-3 mt-2 text-sm text-muted-foreground">
                     <span className="uppercase tracking-wider">{book.genre}</span>
                     <span className="text-border">|</span>
                     <span>{book.chaptersWritten} {t("dash.chapters")}</span>
@@ -114,7 +114,7 @@ export function Dashboard({ nav, sse, theme, t }: { nav: Nav; sse: { messages: R
                   <button
                     onClick={() => postApi(`/books/${book.id}/write-next`)}
                     disabled={isWriting}
-                    className={`px-3 py-2 text-xs rounded-md transition-all ${
+                    className={`px-4 py-2.5 text-sm rounded-md transition-all ${
                       isWriting
                         ? "bg-primary/20 text-primary cursor-wait"
                         : "bg-secondary text-secondary-foreground hover:bg-secondary/80"
@@ -124,7 +124,7 @@ export function Dashboard({ nav, sse, theme, t }: { nav: Nav; sse: { messages: R
                   </button>
                   <button
                     onClick={() => nav.toAnalytics(book.id)}
-                    className="px-3 py-2 text-xs rounded-md bg-secondary text-secondary-foreground hover:bg-secondary/80 transition-colors"
+                    className="px-4 py-2.5 text-sm rounded-md bg-secondary text-secondary-foreground hover:bg-secondary/80 transition-colors"
                   >
                     {t("dash.stats")}
                   </button>
@@ -142,9 +142,9 @@ export function Dashboard({ nav, sse, theme, t }: { nav: Nav; sse: { messages: R
 
       {/* Live writing progress panel */}
       {writingBooks.size > 0 && logEvents.length > 0 && (
-        <div className="border border-primary/20 bg-primary/5 rounded-lg p-5">
-          <h3 className="text-xs uppercase tracking-widest text-primary/70 mb-3">{t("dash.writingProgress")}</h3>
-          <div className="space-y-1 text-[13px] font-mono text-muted-foreground">
+        <div className="border border-primary/30 bg-primary/5 rounded-lg p-6">
+          <h3 className="text-sm uppercase tracking-wide text-primary font-medium mb-4">{t("dash.writingProgress")}</h3>
+          <div className="space-y-1.5 text-sm font-mono text-muted-foreground">
             {logEvents.map((msg, i) => {
               const d = msg.data as { tag?: string; message?: string };
               return (

+ 2 - 2
packages/studio/src/pages/LanguageSelector.tsx

@@ -15,7 +15,7 @@ export function LanguageSelector({ onSelect }: { onSelect: (lang: "zh" | "en") =
         <div className="flex gap-6">
           <button
             onClick={() => onSelect("zh")}
-            className="group w-64 border border-border/60 rounded-lg p-8 hover:border-primary/40 transition-all duration-300 text-left"
+            className="group w-72 border border-border rounded-lg p-10 hover:border-primary/50 transition-all duration-300 text-left"
           >
             <div className="font-serif text-2xl mb-2">中文创作</div>
             <div className="text-sm text-muted-foreground leading-relaxed">
@@ -28,7 +28,7 @@ export function LanguageSelector({ onSelect }: { onSelect: (lang: "zh" | "en") =
 
           <button
             onClick={() => onSelect("en")}
-            className="group w-64 border border-border/60 rounded-lg p-8 hover:border-primary/40 transition-all duration-300 text-left"
+            className="group w-72 border border-border rounded-lg p-10 hover:border-primary/50 transition-all duration-300 text-left"
           >
             <div className="font-serif text-2xl mb-2 italic">English Writing</div>
             <div className="text-sm text-muted-foreground leading-relaxed">

+ 6 - 6
packages/studio/src/pages/LogViewer.tsx

@@ -37,7 +37,7 @@ export function LogViewer({ nav, theme, t }: { nav: Nav; theme: Theme; t: TFunct
         <h1 className="font-serif text-3xl">Logs</h1>
         <button
           onClick={() => refetch()}
-          className={`px-3 py-2 text-xs rounded-md ${c.btnSecondary}`}
+          className={`px-4 py-2.5 text-sm rounded-md ${c.btnSecondary}`}
         >
           Refresh
         </button>
@@ -46,11 +46,11 @@ export function LogViewer({ nav, theme, t }: { nav: Nav; theme: Theme; t: TFunct
       <div className={`border ${c.cardStatic} rounded-lg overflow-hidden`}>
         <div className="p-4 max-h-[600px] overflow-y-auto">
           {data?.entries && data.entries.length > 0 ? (
-            <div className="space-y-0.5 font-mono text-[12px] leading-relaxed">
+            <div className="space-y-1 font-mono text-sm leading-relaxed">
               {data.entries.map((entry, i) => (
                 <div key={i} className="flex gap-2">
                   {entry.timestamp && (
-                    <span className="text-muted-foreground/40 shrink-0 w-20 tabular-nums">
+                    <span className="text-muted-foreground shrink-0 w-20 tabular-nums">
                       {new Date(entry.timestamp).toLocaleTimeString()}
                     </span>
                   )}
@@ -60,21 +60,21 @@ export function LogViewer({ nav, theme, t }: { nav: Nav; theme: Theme; t: TFunct
                     </span>
                   )}
                   {entry.tag && (
-                    <span className="text-primary/40 shrink-0">[{entry.tag}]</span>
+                    <span className="text-primary/70 shrink-0">[{entry.tag}]</span>
                   )}
                   <span className="text-foreground/80">{entry.message}</span>
                 </div>
               ))}
             </div>
           ) : (
-            <div className="text-muted-foreground/40 text-sm italic py-12 text-center">
+            <div className="text-muted-foreground text-sm italic py-12 text-center">
               No log entries. Logs appear after running write/draft/daemon operations.
             </div>
           )}
         </div>
       </div>
 
-      <p className="text-xs text-muted-foreground/50">
+      <p className="text-sm text-muted-foreground">
         Showing last 100 entries from <code className="font-mono bg-muted/50 px-1 rounded">inkos.log</code>
       </p>
     </div>

+ 2 - 2
packages/studio/src/pages/TruthFiles.tsx

@@ -48,8 +48,8 @@ export function TruthFiles({ bookId, nav, theme, t }: { bookId: string; nav: Nav
                   : "hover:bg-muted/30 text-muted-foreground"
               }`}
             >
-              <div className="font-mono text-xs truncate">{f.name}</div>
-              <div className="text-[11px] text-muted-foreground/60 mt-0.5">{f.size.toLocaleString()} chars</div>
+              <div className="font-mono text-sm truncate">{f.name}</div>
+              <div className="text-xs text-muted-foreground mt-0.5">{f.size.toLocaleString()} chars</div>
             </button>
           ))}
           {(!data?.files || data.files.length === 0) && (