diff --git a/apps/web/public/index.html b/apps/web/public/index.html index 72e95f4..93e7d20 100644 --- a/apps/web/public/index.html +++ b/apps/web/public/index.html @@ -7,9 +7,9 @@ - Rico Thrift Converter + Rico
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 26e31c1..63e8c6a 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -4,6 +4,8 @@ import { ParserService } from './lib/parser/service'; import { initRicoWasm } from './lib/rico'; import { EXAMPLE_THRIFT, EXAMPLE_JSON } from './lib/examples'; import { Header } from './components/layout/Header'; +import { Footer } from './components/layout/Footer'; +import { ArrowRight } from './components/icons/ArrowRight'; import { CONVERSION_MODES, type ConversionMode, @@ -159,8 +161,8 @@ function App() { : 'thrift'; return ( -
-
+
+
-
+
+
+
+ +
+
+ +
); diff --git a/apps/web/src/assets/icons/crates.svg b/apps/web/src/assets/icons/crates.svg new file mode 100644 index 0000000..ba4701a --- /dev/null +++ b/apps/web/src/assets/icons/crates.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/apps/web/src/assets/icons/github.svg b/apps/web/src/assets/icons/github.svg new file mode 100644 index 0000000..aeecc2a --- /dev/null +++ b/apps/web/src/assets/icons/github.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/apps/web/src/assets/icons/npm.svg b/apps/web/src/assets/icons/npm.svg new file mode 100644 index 0000000..849f43f --- /dev/null +++ b/apps/web/src/assets/icons/npm.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/apps/web/src/assets/icons/vercel.svg b/apps/web/src/assets/icons/vercel.svg new file mode 100644 index 0000000..2a93c42 --- /dev/null +++ b/apps/web/src/assets/icons/vercel.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/web/src/components/converter/ConversionToggle.tsx b/apps/web/src/components/converter/ConversionToggle.tsx index 41cdf8f..c8dc01a 100644 --- a/apps/web/src/components/converter/ConversionToggle.tsx +++ b/apps/web/src/components/converter/ConversionToggle.tsx @@ -1,11 +1,9 @@ -import { Button } from '../ui/button'; -import { ArrowLeftRight } from 'lucide-react'; -import { cn } from '../../lib/utils'; +import { RefreshCw } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; interface ConversionToggleProps { isThriftToJson: boolean; - isDisabled?: boolean; + isDisabled: boolean; onToggle: () => void; } @@ -15,91 +13,56 @@ export function ConversionToggle({ onToggle }: ConversionToggleProps) { return ( - + ); } diff --git a/apps/web/src/components/editor/ThriftEditor.tsx b/apps/web/src/components/editor/ThriftEditor.tsx index f492b3d..cc57836 100644 --- a/apps/web/src/components/editor/ThriftEditor.tsx +++ b/apps/web/src/components/editor/ThriftEditor.tsx @@ -55,10 +55,10 @@ export function ThriftEditor({
-
+
@@ -95,7 +95,7 @@ export function ThriftEditor({ readOnly={!editable} extensions={[json()]} className={cn( - 'h-full overflow-auto [&_.cm-gutters]:border-none [&_.cm-gutters]:bg-white [&_.cm-focused]:outline-none dark:[&_.cm-gutters]:bg-gray-800', + 'h-full overflow-auto [&_.cm-gutters]:border-none [&_.cm-gutters]:bg-white [&_.cm-focused]:outline-none dark:[&_.cm-gutters]:bg-[#0d1117]', !editable && 'cursor-text select-text' )} basicSetup={{ diff --git a/apps/web/src/components/icons/ArrowRight.tsx b/apps/web/src/components/icons/ArrowRight.tsx new file mode 100644 index 0000000..122bc8b --- /dev/null +++ b/apps/web/src/components/icons/ArrowRight.tsx @@ -0,0 +1,21 @@ +import { cn } from '../../lib/utils'; + +interface ArrowRightProps { + className?: string; +} + +export function ArrowRight({ className }: ArrowRightProps) { + return ( + + + + ); +} diff --git a/apps/web/src/components/layout/Footer.tsx b/apps/web/src/components/layout/Footer.tsx new file mode 100644 index 0000000..0855b7b --- /dev/null +++ b/apps/web/src/components/layout/Footer.tsx @@ -0,0 +1,56 @@ +import githubIcon from '../../assets/icons/github.svg'; +import npmIcon from '../../assets/icons/npm.svg'; +import cratesIcon from '../../assets/icons/crates.svg'; +import vercelIcon from '../../assets/icons/vercel.svg'; + +const linkClass = + 'group flex items-center gap-1.5 text-gray-600/90 transition-all hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200'; +const dividerClass = 'h-4 w-px bg-gray-200/50 dark:bg-gray-700/30'; +const imageClass = + 'h-4 w-4 transition-colors [filter:invert(0.4)_sepia(0)_saturate(0)] group-hover:[filter:invert(0)_sepia(0)_saturate(1)] dark:[filter:invert(0.7)_sepia(0)_saturate(0)] dark:group-hover:[filter:invert(0.9)_sepia(0)_saturate(0)]'; +const textClass = 'text-xs font-medium tracking-wide'; + +export const Footer = () => { + return ( + + ); +}; diff --git a/apps/web/src/components/layout/Header.tsx b/apps/web/src/components/layout/Header.tsx index b3426e4..3a4de10 100644 --- a/apps/web/src/components/layout/Header.tsx +++ b/apps/web/src/components/layout/Header.tsx @@ -1,6 +1,6 @@ import { RicoLogo } from '../logo/RicoLogo'; import { ConversionToggle } from '../converter/ConversionToggle'; -import { ParserStatus } from '../status/ParserStatus'; +import { StatusDisplay } from '../status/StatusDisplay'; import { ThemeToggle } from '../theme/ThemeToggle'; import type { WasmStatus, @@ -26,19 +26,15 @@ export function Header({ parserMetrics }: HeaderProps) { return ( -
-
+
+
- +
-

+

Rico - - {' '} - Thrift Converter -

-

+

Convert between Thrift IDL and JSON with ease

@@ -52,13 +48,13 @@ export function Header({ />
-
- + -
+
diff --git a/apps/web/src/components/status/ParserStatus.tsx b/apps/web/src/components/status/ParserStatus.tsx deleted file mode 100644 index 0116520..0000000 --- a/apps/web/src/components/status/ParserStatus.tsx +++ /dev/null @@ -1,139 +0,0 @@ -import { cn } from '../../lib/utils'; -import { - Cpu, - Loader2, - ShieldCheck, - AlertOctagon, - Sparkles, - CircuitBoard -} from 'lucide-react'; - -interface ParserStatusProps { - wasmStatus: { - isInitializing: boolean; - error?: string; - }; - parserStatus: { - isLoading: boolean; - error?: string; - }; - metrics?: { - parseTime?: number; - stringifyTime?: number; - }; - className?: string; -} - -export function ParserStatus({ - wasmStatus, - parserStatus, - metrics, - className -}: ParserStatusProps) { - const getWasmStatus = () => { - if (wasmStatus.isInitializing) { - return { - icon: Loader2, - color: 'text-amber-500', - bg: 'bg-amber-50', - label: 'WASM', - animate: 'animate-spin' - }; - } - if (wasmStatus.error) { - return { - icon: AlertOctagon, - color: 'text-red-500', - bg: 'bg-red-50', - label: 'WASM', - animate: false - }; - } - return { - icon: CircuitBoard, - color: 'text-emerald-500', - bg: 'bg-emerald-50', - label: 'WASM', - animate: false - }; - }; - - const getParserStatus = () => { - if (parserStatus.error) { - return { - icon: AlertOctagon, - color: 'text-red-500', - bg: 'bg-red-50', - label: 'Parser', - animate: false - }; - } - if (parserStatus.isLoading) { - return { - icon: Sparkles, - color: 'text-blue-500', - bg: 'bg-blue-50', - label: 'Parser', - animate: 'animate-pulse' - }; - } - return { - icon: ShieldCheck, - color: 'text-emerald-500', - bg: 'bg-emerald-50', - label: 'Parser', - animate: false - }; - }; - - const wasmInfo = getWasmStatus(); - const parserInfo = getParserStatus(); - const WasmIcon = wasmInfo.icon; - const ParserIcon = parserInfo.icon; - - return ( -
- {/* WASM Status */} -
- - - {wasmInfo.label} - -
- - {/* Parser Status */} -
- - - {parserInfo.label} - -
- - {/* Performance Metrics */} - {metrics && metrics.parseTime && metrics.parseTime > 0 && ( -
- - - {Math.round(metrics.parseTime)}ms - -
- )} -
- ); -} diff --git a/apps/web/src/components/status/StatusDisplay.tsx b/apps/web/src/components/status/StatusDisplay.tsx new file mode 100644 index 0000000..8639405 --- /dev/null +++ b/apps/web/src/components/status/StatusDisplay.tsx @@ -0,0 +1,80 @@ +import { Activity, Loader2, Cpu } from 'lucide-react'; +import type { + WasmStatus, + ParserStatus, + ParserMetrics +} from '../../lib/constants'; + +interface StatusDisplayProps { + wasmStatus: WasmStatus; + parserStatus: ParserStatus; + metrics: ParserMetrics; +} + +export function StatusDisplay({ + wasmStatus, + parserStatus, + metrics +}: StatusDisplayProps) { + const isLoading = parserStatus.isLoading; + const hasError = parserStatus.error; + const parseTime = metrics.parseTime ?? 0; + const showParserStatus = isLoading || hasError || parseTime > 0; + + return ( +
+
+
+ + + {wasmStatus.error + ? 'Error' + : wasmStatus.isInitializing + ? 'Loading' + : 'Ready'} + +
+ + {showParserStatus && ( +
+ {isLoading ? ( + + ) : hasError ? ( + + ) : ( + + )} +
+ {hasError ? ( + Error + ) : isLoading ? ( + + Processing + + ) : ( + + {parseTime.toFixed(1)}ms + + )} +
+
+ )} +
+
+ ); +} diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 1e0b645..51c373f 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -37,33 +37,33 @@ } .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; + --background: 220 20% 10%; + --foreground: 220 15% 95%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; + --muted: 220 15% 15%; + --muted-foreground: 220 15% 65%; - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; + --popover: 220 20% 10%; + --popover-foreground: 220 15% 95%; - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; + --card: 220 20% 10%; + --card-foreground: 220 15% 95%; - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; + --border: 220 15% 20%; + --input: 220 15% 20%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; + --primary: 220 15% 95%; + --primary-foreground: 220 20% 10%; - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; + --secondary: 220 15% 15%; + --secondary-foreground: 220 15% 95%; - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; + --accent: 220 15% 15%; + --accent-foreground: 220 15% 95%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 85.7% 97.3%; + --destructive: 0 70% 35%; + --destructive-foreground: 0 85% 95%; - --ring: 217.2 32.6% 17.5%; + --ring: 220 15% 20%; } } diff --git a/apps/web/tailwind.config.ts b/apps/web/tailwind.config.ts index 33d7fc4..528e3df 100644 --- a/apps/web/tailwind.config.ts +++ b/apps/web/tailwind.config.ts @@ -13,8 +13,8 @@ const config: Config = { }, extend: { fontFamily: { - sans: ['Inter', 'system-ui', 'sans-serif'], - display: ['"Jersey 15"', 'serif'] + sans: ['Concert One', 'system-ui', 'sans-serif'], + display: ['Jersey 15', 'serif'] }, backdropBlur: { xl: '20px'