From 1c514270340f52ba4812f85f17836e920ed4b345 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 30 Jan 2026 18:21:37 -0800 Subject: [PATCH] code --- CLAUDE.md | 1 + .../code/20260130-000000/src/server/index.tsx | 117 +++++++++++++++--- 2 files changed, 99 insertions(+), 19 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ea2aeb5..88e252a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,6 +75,7 @@ Add `toes.tool` to your app's `package.json`: - Receive `?app=` query parameter for the currently selected app - Iframes are cached per tool+app combination (never recreated once loaded) - Tool state persists across tab switches +- **App paths**: When accessing app files, tools must use `APPS_DIR//current` (not just `APPS_DIR/`) to resolve through the version symlink ### CLI Flags diff --git a/apps/code/20260130-000000/src/server/index.tsx b/apps/code/20260130-000000/src/server/index.tsx index 79bb312..74936bb 100644 --- a/apps/code/20260130-000000/src/server/index.tsx +++ b/apps/code/20260130-000000/src/server/index.tsx @@ -1,5 +1,5 @@ import { Hype } from '@because/hype' -import { define, stylesToCSS } from '@because/forge' +import { createThemes, define, stylesToCSS } from '@because/forge' import { readdir, stat } from 'fs/promises' import { readFileSync } from 'fs' import { join, extname, basename } from 'path' @@ -8,18 +8,53 @@ const APPS_DIR = process.env.APPS_DIR! const app = new Hype({ prettyHTML: false }) +// Theme +const theme = createThemes({ + light: { + bg: '#ffffff', + text: '#1a1a1a', + textMuted: '#666666', + border: '#dddddd', + borderSubtle: '#eeeeee', + borderStrong: '#333333', + hover: '#f5f5f5', + surface: '#f5f5f5', + link: '#0066cc', + icon: '#666666', + codeBg: '#fafafa', + error: '#d32f2f', + errorBg: '#ffebee', + }, + dark: { + bg: '#1a1a1a', + text: '#e5e5e5', + textMuted: '#999999', + border: '#404040', + borderSubtle: '#333333', + borderStrong: '#555555', + hover: '#2a2a2a', + surface: '#252525', + link: '#5c9eff', + icon: '#888888', + codeBg: '#1e1e1e', + error: '#ff6b6b', + errorBg: '#3d1f1f', + }, +}) + // Styles const Container = define('CodeBrowserContainer', { fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', padding: '20px', maxWidth: '1200px', margin: '0 auto', + color: theme('text'), }) const Header = define('Header', { marginBottom: '20px', paddingBottom: '10px', - borderBottom: '2px solid #333', + borderBottom: `2px solid ${theme('borderStrong')}`, }) const Title = define('Title', { @@ -29,7 +64,7 @@ const Title = define('Title', { }) const AppName = define('AppName', { - color: '#666', + color: theme('textMuted'), fontSize: '18px', marginTop: '5px', }) @@ -38,20 +73,20 @@ const FileList = define('FileList', { listStyle: 'none', padding: 0, margin: '20px 0', - border: '1px solid #ddd', + border: `1px solid ${theme('border')}`, borderRadius: '4px', overflow: 'hidden', }) const FileItem = define('FileItem', { padding: '10px 15px', - borderBottom: '1px solid #eee', + borderBottom: `1px solid ${theme('borderSubtle')}`, states: { ':last-child': { borderBottom: 'none', }, ':hover': { - backgroundColor: '#f5f5f5', + backgroundColor: theme('hover'), }, } }) @@ -59,7 +94,7 @@ const FileItem = define('FileItem', { const FileLink = define('FileLink', { base: 'a', textDecoration: 'none', - color: '#0066cc', + color: theme('link'), display: 'flex', alignItems: 'center', gap: '8px', @@ -70,9 +105,17 @@ const FileLink = define('FileLink', { } }) +const FileIcon = define('FileIcon', { + base: 'svg', + width: '18px', + height: '18px', + flexShrink: 0, + fill: theme('icon'), +}) + const CodeBlock = define('CodeBlock', { margin: '20px 0', - border: '1px solid #ddd', + border: `1px solid ${theme('border')}`, borderRadius: '4px', overflow: 'auto', selectors: { @@ -81,6 +124,7 @@ const CodeBlock = define('CodeBlock', { padding: '15px', overflow: 'auto', whiteSpace: 'pre', + backgroundColor: theme('codeBg'), }, '& pre code': { whiteSpace: 'pre', @@ -91,16 +135,16 @@ const CodeBlock = define('CodeBlock', { const CodeHeader = define('CodeHeader', { padding: '10px 15px', - backgroundColor: '#f5f5f5', - borderBottom: '1px solid #ddd', + backgroundColor: theme('surface'), + borderBottom: `1px solid ${theme('border')}`, fontWeight: 'bold', fontSize: '14px', }) const Error = define('Error', { - color: '#d32f2f', + color: theme('error'), padding: '20px', - backgroundColor: '#ffebee', + backgroundColor: theme('errorBg'), borderRadius: '4px', margin: '20px 0', }) @@ -108,7 +152,7 @@ const Error = define('Error', { const BackLink = define('BackLink', { base: 'a', textDecoration: 'none', - color: '#0066cc', + color: theme('link'), display: 'inline-flex', alignItems: 'center', gap: '5px', @@ -120,7 +164,36 @@ const BackLink = define('BackLink', { }, }) -app.get('/styles.css', c => c.text(stylesToCSS(), 200, { +const FolderIcon = () => ( + + + +) + +const FileIconSvg = () => ( + + + +) + +const themeScript = ` + (function() { + var theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + document.body.setAttribute('data-theme', theme); + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { + document.body.setAttribute('data-theme', e.matches ? 'dark' : 'light'); + }); + })(); +` + +const baseStyles = ` +body { + background: ${theme('bg')}; + margin: 0; +} +` + +app.get('/styles.css', c => c.text(baseStyles + stylesToCSS(), 200, { 'Content-Type': 'text/css; charset=utf-8', })) @@ -189,6 +262,7 @@ app.get('/', async c => { + +