diff --git a/apps/app/.env.example b/apps/app/.env.example
new file mode 100644
index 0000000..f8d6638
--- /dev/null
+++ b/apps/app/.env.example
@@ -0,0 +1 @@
+VITE_CLERK_PUBLISHABLE_KEY=""
\ No newline at end of file
diff --git a/apps/app/README.md b/apps/app/README.md
new file mode 100644
index 0000000..c961b95
--- /dev/null
+++ b/apps/app/README.md
@@ -0,0 +1 @@
+# boring.tools App
diff --git a/apps/app/index.html b/apps/app/index.html
new file mode 100644
index 0000000..6653e26
--- /dev/null
+++ b/apps/app/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ boring.tools
+
+
+
+
+
+
diff --git a/apps/app/package.json b/apps/app/package.json
new file mode 100644
index 0000000..0404338
--- /dev/null
+++ b/apps/app/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@boring.tools/app",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@boring.tools/ui": "workspace:*",
+ "@clerk/clerk-react": "^5.9.4",
+ "@tanstack/react-router": "^1.58.15",
+ "lucide-react": "^0.446.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.9.0",
+ "@tanstack/router-devtools": "^1.58.15",
+ "@tanstack/router-plugin": "^1.58.12",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@vitejs/plugin-react": "^4.3.1",
+ "autoprefixer": "^10.4.20",
+ "eslint": "^9.9.0",
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
+ "eslint-plugin-react-refresh": "^0.4.9",
+ "globals": "^15.9.0",
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.13",
+ "typescript": "^5.5.3",
+ "typescript-eslint": "^8.0.1",
+ "vite": "^5.4.1"
+ }
+}
diff --git a/apps/app/postcss.config.js b/apps/app/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/apps/app/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/apps/app/public/vite.svg b/apps/app/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/apps/app/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/app/src/assets/react.svg b/apps/app/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/apps/app/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/app/src/base.css b/apps/app/src/base.css
new file mode 100644
index 0000000..95d8cab
--- /dev/null
+++ b/apps/app/src/base.css
@@ -0,0 +1,81 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 47.4% 11.2%;
+
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 47.4% 11.2%;
+
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 47.4% 11.2%;
+
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+
+ --destructive: 0 100% 50%;
+ --destructive-foreground: 210 40% 98%;
+
+ --ring: 215 20.2% 65.1%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 224 71% 4%;
+ --foreground: 213 31% 91%;
+
+ --muted: 223 47% 11%;
+ --muted-foreground: 215.4 16.3% 56.9%;
+
+ --accent: 216 34% 17%;
+ --accent-foreground: 210 40% 98%;
+
+ --popover: 224 71% 4%;
+ --popover-foreground: 215 20.2% 65.1%;
+
+ --border: 216 34% 17%;
+ --input: 216 34% 17%;
+
+ --card: 224 71% 4%;
+ --card-foreground: 213 31% 91%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 1.2%;
+
+ --secondary: 222.2 47.4% 11.2%;
+ --secondary-foreground: 210 40% 98%;
+
+ --destructive: 0 63% 31%;
+ --destructive-foreground: 210 40% 98%;
+
+ --ring: 216 34% 17%;
+
+ --radius: 0.5rem;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ font-feature-settings: "rlig" 1, "calt" 1;
+ }
+}
diff --git a/apps/app/src/main.tsx b/apps/app/src/main.tsx
new file mode 100644
index 0000000..57e58b5
--- /dev/null
+++ b/apps/app/src/main.tsx
@@ -0,0 +1,41 @@
+import { ThemeProvider } from '@boring.tools/ui'
+import { ClerkProvider } from '@clerk/clerk-react'
+import { RouterProvider, createRouter } from '@tanstack/react-router'
+import { StrictMode } from 'react'
+import ReactDOM from 'react-dom/client'
+import './base.css'
+
+// Import the generated route tree
+import { routeTree } from './routeTree.gen'
+
+// Create a new router instance
+const router = createRouter({ routeTree })
+
+// Register the router instance for type safety
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
+
+const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
+
+if (!PUBLISHABLE_KEY) {
+ throw new Error('Missing Publishable Key')
+}
+
+// Render the app
+// biome-ignore lint/style/noNonNullAssertion:
+const rootElement = document.getElementById('root')!
+if (!rootElement.innerHTML) {
+ const root = ReactDOM.createRoot(rootElement)
+ root.render(
+
+
+
+
+
+
+ ,
+ )
+}
diff --git a/apps/app/src/routeTree.gen.ts b/apps/app/src/routeTree.gen.ts
new file mode 100644
index 0000000..62e0204
--- /dev/null
+++ b/apps/app/src/routeTree.gen.ts
@@ -0,0 +1,116 @@
+/* prettier-ignore-start */
+
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file is auto-generated by TanStack Router
+
+import { createFileRoute } from '@tanstack/react-router'
+
+// Import Routes
+
+import { Route as rootRoute } from './routes/__root'
+
+// Create Virtual Routes
+
+const AboutLazyImport = createFileRoute('/about')()
+const IndexLazyImport = createFileRoute('/')()
+
+// Create/Update Routes
+
+const AboutLazyRoute = AboutLazyImport.update({
+ path: '/about',
+ getParentRoute: () => rootRoute,
+} as any).lazy(() => import('./routes/about.lazy').then((d) => d.Route))
+
+const IndexLazyRoute = IndexLazyImport.update({
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any).lazy(() => import('./routes/index.lazy').then((d) => d.Route))
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexLazyImport
+ parentRoute: typeof rootRoute
+ }
+ '/about': {
+ id: '/about'
+ path: '/about'
+ fullPath: '/about'
+ preLoaderRoute: typeof AboutLazyImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// Create and export the route tree
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexLazyRoute
+ '/about': typeof AboutLazyRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexLazyRoute
+ '/about': typeof AboutLazyRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexLazyRoute
+ '/about': typeof AboutLazyRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/about'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/about'
+ id: '__root__' | '/' | '/about'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexLazyRoute: typeof IndexLazyRoute
+ AboutLazyRoute: typeof AboutLazyRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexLazyRoute: IndexLazyRoute,
+ AboutLazyRoute: AboutLazyRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes()
+
+/* prettier-ignore-end */
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/about"
+ ]
+ },
+ "/": {
+ "filePath": "index.lazy.tsx"
+ },
+ "/about": {
+ "filePath": "about.lazy.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/app/src/routes/__root.tsx b/apps/app/src/routes/__root.tsx
new file mode 100644
index 0000000..145a7b6
--- /dev/null
+++ b/apps/app/src/routes/__root.tsx
@@ -0,0 +1,33 @@
+import { ThemeToggle } from '@boring.tools/ui'
+import { SignIn, SignedIn, SignedOut, UserButton } from '@clerk/clerk-react'
+import { Link, Outlet, createRootRoute } from '@tanstack/react-router'
+import { TanStackRouterDevtools } from '@tanstack/router-devtools'
+
+export const Route = createRootRoute({
+ component: () => (
+ <>
+
+
+
+
+
+
+ <>
+
+
+ Home
+ {' '}
+
+ About
+
+
+
+
+
+
+
+ >
+
+ >
+ ),
+})
diff --git a/apps/app/src/routes/about.lazy.tsx b/apps/app/src/routes/about.lazy.tsx
new file mode 100644
index 0000000..71eaf17
--- /dev/null
+++ b/apps/app/src/routes/about.lazy.tsx
@@ -0,0 +1,9 @@
+import { createLazyFileRoute } from '@tanstack/react-router'
+
+export const Route = createLazyFileRoute('/about')({
+ component: About,
+})
+
+function About() {
+ return Hello from About!
+}
diff --git a/apps/app/src/routes/index.lazy.tsx b/apps/app/src/routes/index.lazy.tsx
new file mode 100644
index 0000000..f8f08fe
--- /dev/null
+++ b/apps/app/src/routes/index.lazy.tsx
@@ -0,0 +1,13 @@
+import { createLazyFileRoute } from '@tanstack/react-router'
+
+export const Route = createLazyFileRoute('/')({
+ component: Index,
+})
+
+function Index() {
+ return (
+
+
Welcome Home!
+
+ )
+}
diff --git a/apps/app/src/vite-env.d.ts b/apps/app/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/apps/app/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/app/tailwind.config.js b/apps/app/tailwind.config.js
new file mode 100644
index 0000000..ebc989b
--- /dev/null
+++ b/apps/app/tailwind.config.js
@@ -0,0 +1,80 @@
+const { fontFamily } = require('tailwindcss/defaultTheme')
+
+/** @type {import('tailwindcss').Config} */
+export default {
+ darkMode: ['class'],
+ content: [
+ './index.html',
+ './src/**/*.{js,ts,jsx,tsx}',
+ '../../packages/ui/**/*.{js,ts,jsx,tsx}',
+ ],
+ theme: {
+ container: {
+ center: true,
+ padding: '2rem',
+ screens: {
+ '2xl': '1400px',
+ },
+ },
+ extend: {
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))',
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))',
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))',
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))',
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))',
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))',
+ },
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)',
+ },
+ fontFamily: {
+ sans: ['var(--font-sans)', ...fontFamily.sans],
+ },
+ keyframes: {
+ 'accordion-down': {
+ from: { height: '0' },
+ to: { height: 'var(--radix-accordion-content-height)' },
+ },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: '0' },
+ },
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ },
+ },
+ },
+ plugins: [require('tailwindcss-animate')],
+}
diff --git a/apps/app/tsconfig.json b/apps/app/tsconfig.json
new file mode 100644
index 0000000..f0a2350
--- /dev/null
+++ b/apps/app/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"]
+}
diff --git a/apps/app/vite.config.ts b/apps/app/vite.config.ts
new file mode 100644
index 0000000..0e26ecd
--- /dev/null
+++ b/apps/app/vite.config.ts
@@ -0,0 +1,8 @@
+import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
+import react from '@vitejs/plugin-react'
+import { defineConfig } from 'vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [TanStackRouterVite(), react()],
+})
diff --git a/bun.lockb b/bun.lockb
index 3a8715c..5084c70 100755
Binary files a/bun.lockb and b/bun.lockb differ