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
-
- 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
+
+ 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 (
+
+