Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/publish-extension.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ jobs:
VITE_API_URL: ${{secrets.VITE_API_URL}}
VITE_LOGOUT_URL: ${{secrets.VITE_LOGOUT_URL}}
VITE_AUTH_GOOGLE_URL: ${{secrets.VITE_AUTH_GOOGLE_URL}}
VITE_LINKING_GOOGLE_ACCOUNT_URL: ${{secrets.VITE_LINKING_GOOGLE_ACCOUNT_URL}}
2 changes: 1 addition & 1 deletion apps/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { ScrollToTopButton } from "@repo/ui/components/scroll-to-top-button";
import { SidebarProvider } from "@repo/ui/components/ui/sidebar";
import { Toaster } from "@repo/ui/components/ui/sonner";
import { TooltipProvider } from "@repo/ui/components/ui/tooltip";
import { ThemeProvider } from "@repo/ui/providers/theme-provider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { createTRPCClient, httpBatchLink } from "@trpc/client";

import { FallBackRender } from "./components/errors/error-boundary";
import { Wrapper } from "./components/layout/navigation-reset-wrapper";
import { useExtensionWebsocket } from "./hooks/use-extension-websocket";
import { ThemeProvider } from "./providers/theme-provider";
import { TRPCProvider } from "./utils/trpc";

function makeQueryClient() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { useTheme } from "@/providers/theme-provider";
import { ToggleThemeDropDown } from "@repo/ui/components/toggle-theme-dropdown";

import { AuthDropDown } from "./auth-dropdown";

export const NavbarDropDowns = () => {
const { theme: providedTheme, setTheme, resolvedTheme } = useTheme();

return (
<div className="flex gap-4 self-end">
<ToggleThemeDropDown />
<ToggleThemeDropDown
providedTheme={providedTheme}
resolvedTheme={resolvedTheme}
setTheme={setTheme}
/>
<AuthDropDown />
</div>
);
Expand Down
78 changes: 78 additions & 0 deletions apps/dashboard/src/providers/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { createContext, useContext, useEffect, useState } from "react";

import { ResolvedTheme, Theme } from "@repo/ui/types-schemas";

type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};

type ThemeProviderState = {
theme: Theme;
setTheme: (theme: Theme) => void;
resolvedTheme: ResolvedTheme;
};

const initialState: ThemeProviderState = {
theme: "system",
setTheme: () => null,
resolvedTheme: "light",
};

const ThemeProviderContext = createContext<ThemeProviderState>(initialState);

export const ThemeProvider = ({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) => {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
);
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>("light");

useEffect(() => {
const root = window.document.documentElement;

root.classList.remove("light", "dark");

let newTheme: ResolvedTheme;

if (theme === "system") {
newTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
} else {
newTheme = theme;
}

root.classList.add(newTheme);
setResolvedTheme(newTheme);
}, [theme]);

const value = {
theme,
resolvedTheme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};

return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
};

export const useTheme = () => {
const context = useContext(ThemeProviderContext);

if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");

return context;
};
2 changes: 1 addition & 1 deletion apps/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "mooncode",
"displayName": "MoonCode",
"description": "MoonCode is an extension that tracks your coding time (like WakaTime) and gives you a detailed summary about all your coding statistics. With MoonCode, developers get the full history of their coding activity.",
"version": "0.0.52",
"version": "0.0.54",
"icon": "./public/moon.png",
"publisher": "Friedrich482",
"author": {
Expand Down
3 changes: 3 additions & 0 deletions apps/web/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["prettier-plugin-tailwindcss"]
}
11 changes: 11 additions & 0 deletions apps/web/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import type { NextConfig } from "next";

const nextConfig: NextConfig = {
output: "standalone",
turbopack: {
rules: {
"*.svg": {
loaders: ["turbopack-inline-svg-loader"],
condition: {
content: /^[\s\S]{0,4000}$/, // <-- Inline SVGs smaller than ~4Kb
},
as: "*.js",
},
},
},
};

export default nextConfig;
5 changes: 5 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "eslint . --fix"
},
"dependencies": {
"@icons-pack/react-simple-icons": "^13.11.1",
"@repo/common": "*",
"@repo/trpc": "*",
"@repo/ui": "*",
Expand All @@ -18,14 +19,18 @@
"react-dom": "^19.2.0"
},
"devDependencies": {
"@svgr/webpack": "^8.1.0",
"@types/node": "^20.19.32",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
"autoprefixer": "^10.4.24",
"eslint": "^9.39.2",
"eslint-config-next": "^16.0.3",
"postcss": "^8.5.6",
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.6.14",
"tailwindcss": "^4.1.17",
"turbopack-inline-svg-loader": "^1.0.3",
"typescript": "^5.9.3"
},
"overrides": {
Expand Down
17 changes: 14 additions & 3 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { Metadata } from "next";
import { ThemeProvider } from "next-themes";
import { Inter } from "next/font/google";

import { Footer } from "@/components/layout/footer";
import { Header } from "@/components/layout/header/header";
import { ThemeProvider } from "@/providers/theme-provider";

import "@repo/ui/globals.css";

const inter = Inter({ subsets: ["latin"] });
Expand All @@ -18,9 +21,17 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={`${inter.className} antialiased`}>
<ThemeProvider enableSystem attribute="class" defaultTheme="system">
<head />
<body className={`${inter.className} flex flex-col antialiased`}>
<ThemeProvider
enableSystem
attribute="class"
defaultTheme="system"
disableTransitionOnChange
>
<Header />
{children}
<Footer />
</ThemeProvider>
</body>
</html>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const Home = () => {
return <div>MoonCode</div>;
return <main className="flex-1 pt-20"></main>;
};

export default Home;
16 changes: 16 additions & 0 deletions apps/web/src/assets/moon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/src/components/layout/footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const Footer = () => {
return <footer className="mt-auto">footer</footer>;
};
24 changes: 24 additions & 0 deletions apps/web/src/components/layout/header/header-additional-links.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Link from "next/link";

import { HEADER_ADDITIONAL_LINKS } from "@/constants";

import { ThemeToggler } from "./theme-toggler";

export const HeaderAdditionalLinks = () => {
return (
<div className="flex items-center justify-center gap-6 pr-4">
<div className="flex gap-6">
{HEADER_ADDITIONAL_LINKS.map((entry) => (
<Link href={entry.href} key={entry.href}>
<entry.Icon
aria-label={entry.label}
className="hover:text-primary size-5"
/>
</Link>
))}
</div>

<ThemeToggler />
</div>
);
};
13 changes: 13 additions & 0 deletions apps/web/src/components/layout/header/header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { HeaderAdditionalLinks } from "./header-additional-links";
import { Navbar } from "./navbar";
import { Title } from "./title";

export const Header = () => {
return (
<header className="bg-background border-border/50 fixed z-50 flex w-svw justify-between gap-16 border-b p-4">
<Title />
<Navbar />
<HeaderAdditionalLinks />
</header>
);
};
22 changes: 22 additions & 0 deletions apps/web/src/components/layout/header/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Link from "next/link";

import { NAVBAR_LINKS } from "@/constants";

export const Navbar = () => {
return (
<nav className="flex flex-1 max-[49rem]:hidden">
<ul className="flex w-full items-center gap-12">
{NAVBAR_LINKS.map((entry) => (
<li key={entry.text}>
<Link
href={entry.href}
className="text-muted-foreground/60 hover:text-primary"
>
{entry.text}
</Link>
</li>
))}
</ul>
</nav>
);
};
22 changes: 22 additions & 0 deletions apps/web/src/components/layout/header/theme-toggler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";

import { useTheme } from "next-themes";

import { ToggleThemeDropDown } from "@repo/ui/components/toggle-theme-dropdown";
import { ResolvedTheme, Theme } from "@repo/ui/types-schemas";

export const ThemeToggler = () => {
const { theme, resolvedTheme, setTheme } = useTheme() as {
theme: Theme;
resolvedTheme: ResolvedTheme;
setTheme: (theme: Theme) => void;
};

return (
<ToggleThemeDropDown
providedTheme={theme}
resolvedTheme={resolvedTheme}
setTheme={setTheme}
/>
);
};
16 changes: 16 additions & 0 deletions apps/web/src/components/layout/header/title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Image from "next/image";

import moon from "@/assets/moon.svg";

export const Title = () => {
return (
<p className="flex w-fit items-center justify-center gap-3">
<Image
src={moon as unknown as string}
alt="Logo"
className="size-10 shrink-0"
/>
<span className="text-2xl max-[25rem]:hidden">Mooncode</span>
</p>
);
};
30 changes: 30 additions & 0 deletions apps/web/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { type IconType, SiGithub, SiX } from "@icons-pack/react-simple-icons";

export const NAVBAR_LINKS: { href: string; text: string }[] = [
{ href: "architecture", text: "Architecture" },
{
href: "features",
text: "Features",
},
{
href: "installation",
text: "Installation",
},
];

export const HEADER_ADDITIONAL_LINKS: {
href: string;
Icon: IconType;
label: string;
}[] = [
{
href: "https://github.com/Friedrich482/mooncode",
Icon: SiGithub,
label: "GitHub link",
},
{
href: "https://x.com/FriedrichC109",
Icon: SiX,
label: "Twitter Link",
},
];
18 changes: 18 additions & 0 deletions apps/web/src/providers/theme-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import * as React from "react";
import dynamic from "next/dynamic";

const NextThemesProvider = dynamic(
() => import("next-themes").then((e) => e.ThemeProvider),
{
ssr: false,
},
);

export const ThemeProvider = ({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) => {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
};
Loading
Loading