Add template

This commit is contained in:
Alois 2026-07-29 15:28:20 +02:00
commit 5800a5ebbf
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
27 changed files with 7866 additions and 1 deletions

38
apps/web/index.html Normal file
View file

@ -0,0 +1,38 @@
<!doctype html>
<html lang="en" class="dark" data-theme="methanium">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#171e26" />
<title>Methanium Vite Template</title>
<style>
html.dark,
html.dark body {
background-color: #171e26;
color: #d2e0f0;
color-scheme: dark;
}
html.light,
html.light body {
background-color: #edf1f4;
color: #10161d;
color-scheme: light;
}
</style>
<script>
const savedTheme = localStorage.getItem("theme");
const theme =
savedTheme === "light" ||
(savedTheme === "system" &&
matchMedia("(prefers-color-scheme: light)").matches)
? "light"
: "dark";
document.documentElement.classList.replace("dark", theme);
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

28
apps/web/package.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "@methanium/template-web",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@methanium/template-mtp": "workspace:*",
"@methanium/ui": "*",
"@tanstack/react-router": "^1.131.35",
"lucide-react": "^1.21.0",
"mtp": "*",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@tailwindcss/vite": "^4.3.1",
"@tanstack/router-plugin": "^1.131.35",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^5.0.4",
"tailwindcss": "^4.3.1",
"vite": "^7.1.12"
}
}

View file

@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" width="4441.4" height="6468.8" shape-rendering="geometricPrecision" viewBox="0 0 4441 6469">
<path fill="#331c4e" d="m1714 6033-33-248-5-676 69-1110-247-2069-388-302 196 3614Z"/>
<path fill="#6a438a" d="m1864 2038-214 3093 31 640 820-1486 125-2262Z"/>
<path fill="#291641" d="m3130 1586-504 437-125 2262 305 315Z"/>
<path fill="#0e0619" d="m2501 4285 305 315-120 1018-384 830h-56Z"/>
<path fill="#211136" d="m1762 6196 247 254 237-2 255-2163-822 1476 17 221Z"/>
<path fill="#3f245b" d="m740 4907 255-267 719 1393 48 163-340-112Z"/>
<path fill="#05020d" d="m1407 6080-147 198 414 172h335l-247-254Z"/>
<path fill="#030106" d="m1273 6283-85 173 486-6Z"/>
<path fill="#0a0413" d="m10 5229 297-41 966 1095-85 173-100 3Z"/>
<path fill="#5b3878" d="m740 4907 682 1177-149 199-966-1095Z"/>
<path fill="#794f9a" d="M159 4369 10 5229l297-41Z"/>
<path fill="#b984c5" d="m307 5188 433-281-581-538Z"/>
<path fill="#d4abda" d="m159 4369 581 538 255-267Z"/>
<path d="m2686 5604 312 564-179 263-517 17Z"/>
<path fill="#130922" d="m2686 5604 294-421 588-268 48 443-618 810Z"/>
<path fill="#975dac" d="m2686 5604 1115-1590-379-274-666 1279Z"/>
<path fill="#6a438a" d="m2980 5183 588-268 511-814-429-152Z"/>
<path fill="#190c2b" d="m4079 4101 352 111-815 1146-48-443Z"/>
<path fill="#f0d7f2" d="m2045 11-547 1919 366 108 762-15 70-59Z"/>
<path fill="#a558aa" d="m2045 11 651 1953 434-378Z"/>
<path fill="#b06ab6" d="m2045 11-547 1919-388-302Z"/>
<path fill="#885ca8" d="m1498 1930 238 1992 128-1884Z"/>
<path fill="#c698d0" d="m3650 3949 429 152 273-790Z"/>
<path fill="#4c2d68" d="m4079 4101 352 111-79-901Z"/>
<path fill="#e2c1e6" d="m3422 3740 228 209 702-638Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,57 @@
import { Moon, Sun } from "lucide-react";
import { Button, useTheme } from "@methanium/ui";
const homepageUrl = "https://methanium.net";
export function Header() {
const { resolvedPolarity, setTheme } = useTheme();
const isDark = resolvedPolarity === "dark";
return (
<header className="sticky top-0 z-40 h-12 border-b border-sidebar-border bg-sidebar text-sidebar-foreground shadow-sm">
<div className="flex h-full items-center justify-between overflow-x-auto px-[10px]">
<Button
nativeButton={false}
variant="ghost"
size="sm"
className="mr-1 shrink-0 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
style={{ width: "46.59px", height: "36px", padding: "3px 13px" }}
render={<a href="/" aria-label="Methanium home" />}
>
<img src="/methanium.svg" alt="" className="h-8 w-6 object-contain" />
</Button>
<nav
className="mr-auto flex h-full shrink-0 items-center"
aria-label="Primary"
>
<Button
nativeButton={false}
variant="ghost"
size="sm"
className="h-9 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
render={<a href={homepageUrl} />}
>
Homepage
</Button>
</nav>
<div className="flex h-full items-center gap-1">
<Button
type="button"
variant="ghost"
size="icon"
className="size-8 text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
onClick={() => setTheme(isDark ? "light" : "dark")}
>
{isDark ? (
<Sun className="size-3.5" />
) : (
<Moon className="size-3.5" />
)}
</Button>
</div>
</div>
</header>
);
}

32
apps/web/src/main.tsx Normal file
View file

@ -0,0 +1,32 @@
import "./styles.css";
import { BUILT_IN_THEMES, ThemeProvider } from "@methanium/ui";
import { Provider as MTPProvider } from "@methanium/template-mtp";
import { createRouter, RouterProvider } from "@tanstack/react-router";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { routeTree } from "./routeTree.gen";
import { mtpOptions } from "./mtp-options.ts";
const router = createRouter({ routeTree });
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ThemeProvider
defaultTheme="dark"
themes={BUILT_IN_THEMES}
defaultParentThemeId="methanium"
>
<MTPProvider options={mtpOptions}>
<RouterProvider router={router} />
</MTPProvider>
</ThemeProvider>
</StrictMode>,
);

View file

@ -0,0 +1,13 @@
import type { MTPClientOptions } from "mtp";
// Replace null with your application's stable MTP connection configuration.
export const mtpOptions: MTPClientOptions | null = null;
// Example:
// export const mtpOptions: MTPClientOptions = {
// url: "https://mtp.example.com:4433",
// credentials: { clientId: 1, keyring: new Uint8Array([...]) },
// hostPublicKey: "...",
// descriptor: "web",
// pings: true,
// };

View file

@ -0,0 +1,59 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/'
fileRoutesByTo: FileRoutesByTo
to: '/'
id: '__root__' | '/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
}
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()

View file

@ -0,0 +1,17 @@
import { createRootRoute, Outlet } from "@tanstack/react-router";
import { Header } from "../components/layout/Header";
const EmptyPage = () => (
<main className="min-h-[calc(100vh-3rem)] bg-background" />
);
export const Route = createRootRoute({
component: () => (
<div className="min-h-screen bg-background text-foreground">
<Header />
<Outlet />
</div>
),
notFoundComponent: EmptyPage,
});

View file

@ -0,0 +1,7 @@
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({ component: Home });
function Home() {
return <main className="min-h-[calc(100vh-3rem)] bg-background" />;
}

2
apps/web/src/styles.css Normal file
View file

@ -0,0 +1,2 @@
@import "tailwindcss";
@import "@methanium/ui/index.css";

10
apps/web/type-maps.yaml Normal file
View file

@ -0,0 +1,10 @@
protocol_version: "1.0"
type_maps:
"1.0":
CommunicationTypes:
ExampleRequest: 32
ExampleResponse: 33
DataTypes:
Message: 32
ReceivedAt: 33

28
apps/web/vite.config.ts Normal file
View file

@ -0,0 +1,28 @@
import { methaniumUi } from "@methanium/ui/vite";
import { tanstackRouter } from "@tanstack/router-plugin/vite";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { mtp } from "mtp/vite";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, type Plugin } from "vite";
const root = path.dirname(fileURLToPath(import.meta.url));
export default defineConfig({
plugins: [
tanstackRouter({ target: "react", autoCodeSplitting: true }),
tailwindcss(),
react(),
methaniumUi({ defaultThemeId: "methanium" }),
mtp({ typeMaps: "./type-maps.yaml" }) as Plugin,
],
resolve: {
alias: {
"@methanium/template-mtp": path.resolve(
root,
"../../packages/mtp/index.ts",
),
},
},
});