import { Hype } from '@because/hype' import { createThemes, define, stylesToCSS } from '@because/forge' import { readdir, stat } from 'fs/promises' import { readFileSync } from 'fs' import { join, extname, basename } from 'path' import type { Child } from 'hono/jsx' 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('Container', { 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 ${theme('borderStrong')}`, }) const Title = define('Title', { margin: 0, fontSize: '24px', fontWeight: 'bold', }) const Subtitle = define('Subtitle', { color: theme('textMuted'), fontSize: '18px', marginTop: '5px', }) const FileList = define('FileList', { listStyle: 'none', padding: 0, margin: '20px 0', border: `1px solid ${theme('border')}`, borderRadius: '4px', overflow: 'hidden', }) const FileItem = define('FileItem', { padding: '10px 15px', borderBottom: `1px solid ${theme('borderSubtle')}`, states: { ':last-child': { borderBottom: 'none', }, ':hover': { backgroundColor: theme('hover'), }, } }) const FileLink = define('FileLink', { base: 'a', textDecoration: 'none', color: theme('link'), display: 'flex', alignItems: 'center', gap: '8px', states: { ':hover': { textDecoration: 'underline', }, } }) 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 ${theme('border')}`, borderRadius: '4px', overflow: 'auto', selectors: { '& pre': { margin: 0, padding: '15px', overflow: 'auto', whiteSpace: 'pre', backgroundColor: theme('codeBg'), }, '& pre code': { whiteSpace: 'pre', fontFamily: 'monospace', }, }, }) const CodeHeader = define('CodeHeader', { padding: '10px 15px', backgroundColor: theme('surface'), borderBottom: `1px solid ${theme('border')}`, fontWeight: 'bold', fontSize: '14px', }) const ErrorBox = define('ErrorBox', { color: theme('error'), padding: '20px', backgroundColor: theme('errorBg'), borderRadius: '4px', margin: '20px 0', }) const BackLink = define('BackLink', { base: 'a', textDecoration: 'none', color: theme('link'), display: 'inline-flex', alignItems: 'center', gap: '5px', marginBottom: '15px', states: { ':hover': { textDecoration: 'underline', }, }, }) const FolderIcon = () => ( ) const FileIconSvg = () => ( ) const initScript = ` (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'); }); function sendHeight() { window.parent.postMessage({ type: 'resize-iframe', height: document.documentElement.scrollHeight }, '*'); } sendHeight(); new ResizeObserver(sendHeight).observe(document.body); window.addEventListener('load', sendHeight); })(); ` const baseStyles = ` body { background: ${theme('bg')}; margin: 0; } ` interface LayoutProps { title: string subtitle?: string children: Child highlight?: boolean } function Layout({ title, subtitle, children, highlight }: LayoutProps) { return ( {title} {highlight && ( <> )}