Browse Source

feat(canvas-tui): Polish UI - softer colors, better footer format

- Add ✿ CANVAS flower icon to header
- Use dimColor for softer contrast on borders and text
- Right-align dropdown with 1-space padding to match header
- Footer shows date + time, not just time
- Controls in square brackets: [Tab] Files [e] Edit [m] on [q] Quit
- Add dense-text.md test file for dropdown overlay testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
0xDarkMatter 3 months ago
parent
commit
daa6b4720d

+ 9 - 10
canvas-tui/src/app.tsx

@@ -213,13 +213,11 @@ export const App: React.FC<AppProps> = ({ watchPath, watchDir, enableMouse = tru
     }
   });
 
-  // Status message - truncate path to just filename
+  // Status info
   const currentFileName = currentFilePath.split(/[/\\]/).pop() || 'file';
-  const statusMessage = error
-    ? `Error: ${error}`
-    : syncStatus === 'waiting'
-    ? `Waiting: ${currentFileName}`
-    : `Synced: ${currentFileName}`;
+  const timestampStr = lastUpdate
+    ? `${lastUpdate.toLocaleDateString()} ${lastUpdate.toLocaleTimeString()}`
+    : null;
 
   const scrollHint = totalLines > contentHeight
     ? ` ${scrollOffset + 1}-${Math.min(scrollOffset + contentHeight, totalLines)}/${totalLines}`
@@ -229,13 +227,13 @@ export const App: React.FC<AppProps> = ({ watchPath, watchDir, enableMouse = tru
   const hints = isEditing
     ? 'Editing...'
     : isDropdownOpen
-    ? 'Tab:close ↑↓:nav Enter:open'
-    : `Tab:files e:edit m:${mouseHint} q:quit${scrollHint}`;
+    ? '[Tab] Close [↑↓] Nav [Enter] Open'
+    : `[Tab] Files [e] Edit [m] ${mouseHint} [q] Quit${scrollHint}`;
 
   return (
     <Box flexDirection="column" height={rows}>
       <Header
-        title="Canvas (beta)"
+        title="✿ CANVAS"
         width={cols}
         files={files}
         currentFile={currentFilePath}
@@ -267,7 +265,8 @@ export const App: React.FC<AppProps> = ({ watchPath, watchDir, enableMouse = tru
 
       <StatusBar
         status={syncStatus}
-        message={statusMessage}
+        filename={currentFileName}
+        timestamp={timestampStr}
         hints={hints}
         width={cols}
       />

+ 9 - 8
canvas-tui/src/components/Header.tsx

@@ -54,12 +54,13 @@ export const Header: React.FC<HeaderProps> = ({
           // Reversed video for selected item
           lines.push(`\x1B[7m ${name} \x1B[0m`);
         } else {
-          lines.push(` ${name} `);
+          // Dim text for unselected
+          lines.push(`\x1B[2m ${name} \x1B[0m`);
         }
       });
 
       if (otherFiles.length > 6) {
-        lines.push(` +${otherFiles.length - 6} more `);
+        lines.push(`\x1B[2m +${otherFiles.length - 6} more \x1B[0m`);
       }
     }
 
@@ -73,7 +74,7 @@ export const Header: React.FC<HeaderProps> = ({
       const row = startRow + idx;
       const plainLen = line.replace(/\x1B\[[0-9;]*m/g, '').length;
       const padding = maxLen - plainLen;
-      const startCol = cols - maxLen - 4; // Right margin of 4
+      const startCol = cols - maxLen - 1; // Right margin of 1 (matches header padding)
       output += `\x1B[${row};${startCol}H${' '.repeat(padding)}${line}`;
     });
 
@@ -86,8 +87,8 @@ export const Header: React.FC<HeaderProps> = ({
       let clear = '\x1B[s';
       lines.forEach((_, idx) => {
         const row = startRow + idx;
-        const startCol = cols - maxLen - 4;
-        clear += `\x1B[${row};${startCol}H${' '.repeat(maxLen + 4)}`;
+        const startCol = cols - maxLen - 1;
+        clear += `\x1B[${row};${startCol}H${' '.repeat(maxLen + 1)}`;
       });
       clear += '\x1B[u';
       process.stdout.write(clear);
@@ -97,12 +98,12 @@ export const Header: React.FC<HeaderProps> = ({
   return (
     <Box flexDirection="column">
       {/* Header bar - always single line with border */}
-      <Box borderStyle="single" borderBottom={true} borderTop={false} borderLeft={false} borderRight={false}>
+      <Box borderStyle="single" borderBottom={true} borderTop={false} borderLeft={false} borderRight={false} borderDimColor>
         <Box width={width}>
           <Text bold color="blue">{leftContent}</Text>
           <Box flexGrow={1} />
-          <Text color="gray">{displayName} {isDropdownOpen ? '▲' : '▼'}</Text>
-          <Text>    </Text>
+          <Text dimColor>{displayName} {isDropdownOpen ? '▲' : '▼'}</Text>
+          <Text> </Text>
         </Box>
       </Box>
     </Box>

+ 16 - 6
canvas-tui/src/components/StatusBar.tsx

@@ -3,12 +3,13 @@ import { Box, Text } from 'ink';
 
 interface StatusBarProps {
   status: 'waiting' | 'synced' | 'watching';
-  message: string;
+  filename: string;
+  timestamp: string | null;
   hints: string;
   width: number;
 }
 
-export const StatusBar: React.FC<StatusBarProps> = ({ status, message, hints, width }) => {
+export const StatusBar: React.FC<StatusBarProps> = ({ status, filename, timestamp, hints, width }) => {
   const statusColors: Record<string, string> = {
     waiting: 'yellow',
     synced: 'green',
@@ -18,11 +19,19 @@ export const StatusBar: React.FC<StatusBarProps> = ({ status, message, hints, wi
   const statusIcons: Record<string, string> = {
     waiting: '...',
     synced: '***',
-    watching: '>>>'
+    watching: '...'
+  };
+
+  const statusLabels: Record<string, string> = {
+    waiting: 'Waiting',
+    synced: 'Synced',
+    watching: 'Watching'
   };
 
   const statusColor = statusColors[status] || 'white';
-  const statusIcon = statusIcons[status] || '?';
+  const statusIcon = statusIcons[status] || ' ';
+  const statusLabel = statusLabels[status] || '';
+  const timeStr = timestamp ? ` ${timestamp}` : '';
 
   return (
     <Box
@@ -31,14 +40,15 @@ export const StatusBar: React.FC<StatusBarProps> = ({ status, message, hints, wi
       borderBottom={false}
       borderLeft={false}
       borderRight={false}
+      borderDimColor
       flexDirection="column"
     >
       <Box width={width} justifyContent="space-between">
         <Box>
           <Text color={statusColor}>[{statusIcon}]</Text>
-          <Text> {message}</Text>
+          <Text dimColor> {statusLabel}: {filename}{timeStr}</Text>
         </Box>
-        <Text color="gray">{hints}</Text>
+        <Text dimColor>{hints}</Text>
       </Box>
     </Box>
   );