diff --git a/app/favicon.ico b/app/favicon.ico index 718d6fe..b4d882d 100644 Binary files a/app/favicon.ico and b/app/favicon.ico differ diff --git a/app/layout.tsx b/app/layout.tsx index 04c01f0..c817cd3 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -16,8 +16,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "alixz.ovh", + description: "My editing and programming portfolio.", }; export default function RootLayout({ diff --git a/app/page.tsx b/app/page.tsx index a9bbaf4..c46f5b5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,39 +1,53 @@ -import { H1, H2, H3, P } from "@/components/typography"; -import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { H1, H2, P } from "@/components/typography"; +import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; import Image from "next/image"; import Link from "next/link"; -import { Link2 } from "lucide-react"; +import { ExternalLink } from "lucide-react"; +import { Suspense } from "react"; +import { Wakatime } from "@/components/wakatime"; +import { MyBarChartSkeleton } from "@/components/bar-chart"; +import { WakatimeHint } from "@/components/wakatime-hint"; export default function Page() { - return <> -

alixz.ovh

-

Editing and programming are what makes my kokoro go dokidoki :-)

-

Projects

- - uwu2x cover - - uwu2x - - An After Effects extension that improves the quality and the smoothness of clips, similar to what Topaz and Flowframes do, but in one click ! -

- Used by 7000 people every month, and seen +300,000 times on Youtube. -
-
- - - - - - - - -
- ; + return <> +

alixz.ovh

+

Editing and programming are what makes my kokoro go dokidoki :-)

+ +

+ Monthly coding stats{" "} + + + +

+ }> + + + +

Projects

+ + uwu2x cover + + uwu2x + + An After Effects extension that improves the quality and the smoothness of clips, similar to what Topaz and Flowframes do, but in one click ! +
+ Used by 7000 people every month, and seen +300,000 times on Youtube. +
+
+ + + + + + + + +
+ ; } \ No newline at end of file diff --git a/components/bar-chart.tsx b/components/bar-chart.tsx new file mode 100644 index 0000000..c4d1070 --- /dev/null +++ b/components/bar-chart.tsx @@ -0,0 +1,165 @@ +"use client" + +import { Bar, BarChart, LabelList, XAxis, YAxis, Cell } from "recharts" +import { cn } from "@/lib/utils" + +import { + ChartContainer, + type ChartConfig, +} from "@/components/ui/chart" + +const COLORS = [ + "#4ade80", + "#60a5fa", + "#f87171", + "#fb923c", + "#c084fc", +] + +const chartConfig = { + total_seconds: { + label: "Seconds", + }, +} satisfies ChartConfig + +type ChartData = { + language: string + total_seconds: number + label: string +} + +interface MyBarChartProps { + data: ChartData[] + className?: string +} + +interface MyBarChartSkeletonProps { + className?: string +} + +export function MyBarChart({ data, className }: MyBarChartProps) { + return ( + + + + + + {data.map((_, index) => ( + + ))} + ( + + {String(value)} + + )} + /> + + + + ) +} + +export function MyBarChartSkeleton({ className }: MyBarChartSkeletonProps) { + const fakeData = [ + { language: "a", total_seconds: 102000 }, + { language: "b", total_seconds: 59000 }, + { language: "c", total_seconds: 41000 }, + { language: "d", total_seconds: 28000 }, + { language: "e", total_seconds: 17000 }, + ] + + return ( + + + + + + + + ) +} + +export function MyBarChartError({ className }: MyBarChartSkeletonProps) { + const fakeData = [ + { language: "a", total_seconds: 102000 }, + { language: "b", total_seconds: 59000 }, + { language: "c", total_seconds: 41000 }, + { language: "d", total_seconds: 28000 }, + { language: "e", total_seconds: 17000 }, + ] + + return ( +
+ + + + + + + +
+ + Failed to fetch Wakatime stats :/ + +
+
+ ) +} \ No newline at end of file diff --git a/components/code-block.tsx b/components/code-block.tsx new file mode 100644 index 0000000..780b338 --- /dev/null +++ b/components/code-block.tsx @@ -0,0 +1,36 @@ +import * as React from 'react' +import CodeEditorPrimitive from '@uiw/react-textarea-code-editor' + +import { cn } from '@/lib/utils' + +function CodeEditor({ + className, + ...props +}: React.ComponentProps) { + return ( + .w-tc-editor-preview]:px-3! [&>.w-tc-editor-preview]:py-2! [&>.w-tc-editor-preview]:m-0! [&>.w-tc-editor-preview]:min-h-16!', + '[&>.w-tc-editor-preview]:rounded-md! [&>.w-tc-editor-preview]:bg-transparent! [&>.w-tc-editor-preview]:dark:bg-input/30! [&>.w-tc-editor-preview]:shadow-xs! [&>.w-tc-editor-preview]:transition-[color,box-shadow]!', + '[&>.w-tc-editor-preview]:text-base! [&>.w-tc-editor-preview]:md:text-sm! [&>.w-tc-editor-preview]:field-sizing-content!', + '[&>.w-tc-editor-preview]:text-foreground! [&>.w-tc-editor-preview]:placeholder:text-muted-foreground!', + '[&>.w-tc-editor-preview]:outline-none!', + '[&>.w-tc-editor-preview]:border! [&>.w-tc-editor-preview]:border-input!', + '[&>.w-tc-editor-text]:px-3! [&>.w-tc-editor-text]:py-2! [&>.w-tc-editor-text]:m-0!', + '[&>.w-tc-editor-text]:text-base! [&>.w-tc-editor-text]:md:text-sm! [&>.w-tc-editor-text]:field-sizing-content!', + '[&>.w-tc-editor-text]:text-foreground! [&>.w-tc-editor-text]:placeholder:text-muted-foreground! [&>.w-tc-editor-text]:opacity-100!', + '[&>.w-tc-editor-text]:subpixel-antialiased!', + '[&>.w-tc-editor-text]:disabled:cursor-not-allowed! [&:has(>.w-tc-editor-text:disabled)]:opacity-50!', + '[&:has(>.w-tc-editor-text[aria-invalid="true"])]:[&>.w-tc-editor-preview]:ring-destructive/20! dark:[&:has(>.w-tc-editor-text[aria-invalid="true"])]:[&>.w-tc-editor-preview]:ring-destructive/40! [&:has(>.w-tc-editor-text[aria-invalid="true"])]:[&>.w-tc-editor-preview]:border-destructive!', + className, + )} + {...props} + /> + ) +} + +export { CodeEditor } \ No newline at end of file diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx index e018a73..13d013b 100644 --- a/components/theme-provider.tsx +++ b/components/theme-provider.tsx @@ -4,8 +4,8 @@ import * as React from "react" import { ThemeProvider as NextThemesProvider } from "next-themes" export function ThemeProvider({ - children, - ...props + children, + ...props }: React.ComponentProps) { - return {children} + return {children} } \ No newline at end of file diff --git a/components/ui/chart.tsx b/components/ui/chart.tsx new file mode 100644 index 0000000..5f31f72 --- /dev/null +++ b/components/ui/chart.tsx @@ -0,0 +1,353 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { cn } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +function ChartContainer({ + id, + className, + children, + config, + ...props +}: React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] +}) { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +} + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +