diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..909e028d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,49 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - main
+
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+ steps:
+ - id: checkout
+ name: Checkout
+ uses: actions/checkout@v3
+ - id: setup-bun
+ name: Setup Bun
+ uses: oven-sh/setup-bun@v1
+ with:
+ bun-version: latest
+ - id: install-deps
+ name: Install dependencies
+ run: |
+ bun install
+
+ - name: Lint
+ run: bun lint
+
+ typecheck:
+ runs-on: ubuntu-latest
+ steps:
+ - id: checkout
+ name: Checkout
+ uses: actions/checkout@v3
+ - id: setup-bun
+ name: Setup Bun
+ uses: oven-sh/setup-bun@v1
+ with:
+ bun-version: latest
+ - id: install-deps
+ name: Install dependencies
+ run: |
+ bun install
+
+ - name: Typecheck
+ run: bun nuxi typecheck
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..431a96be
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,26 @@
+name: Release
+
+permissions:
+ contents: write
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - id: setup-bun
+ name: Setup Bun
+ uses: oven-sh/setup-bun@v1
+ with:
+ bun-version: latest
+
+ - run: bunx changelogithub
+ env:
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..4a7f73a2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Nuxt dev/build outputs
+.output
+.data
+.nuxt
+.nitro
+.cache
+dist
+
+# Node dependencies
+node_modules
+
+# Logs
+logs
+*.log
+
+# Misc
+.DS_Store
+.fleet
+.idea
+
+# Local env files
+.env
+.env.*
+!.env.example
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..725f5f38
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,48 @@
+{
+ // Enable the ESlint flat config support
+ // (remove this if your ESLint extension above v3.0.5)
+ "eslint.experimental.useFlatConfig": true,
+
+ // Disable the default formatter, use eslint instead
+ "prettier.enable": false,
+ "editor.formatOnSave": true,
+
+ // Auto fix
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.organizeImports": "never"
+ },
+
+ // Silent the stylistic rules in you IDE, but still auto fix them
+ "eslint.rules.customizations": [
+ { "rule": "style/*", "severity": "off" },
+ { "rule": "format/*", "severity": "off" },
+ { "rule": "*-indent", "severity": "off" },
+ { "rule": "*-spacing", "severity": "off" },
+ { "rule": "*-spaces", "severity": "off" },
+ { "rule": "*-order", "severity": "off" },
+ { "rule": "*-dangle", "severity": "off" },
+ { "rule": "*-newline", "severity": "off" },
+ { "rule": "*quotes", "severity": "off" },
+ { "rule": "*semi", "severity": "off" }
+ ],
+
+ // Enable eslint for all supported languages
+ "eslint.validate": [
+ "javascript",
+ "javascriptreact",
+ "typescript",
+ "typescriptreact",
+ "vue",
+ "html",
+ "markdown",
+ "json",
+ "jsonc",
+ "yaml",
+ "toml",
+ "xml",
+ "gql",
+ "graphql",
+ "astro"
+ ]
+}
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..f5db2a2d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,75 @@
+# Nuxt 3 Minimal Starter
+
+Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
+
+## Setup
+
+Make sure to install the dependencies:
+
+```bash
+# npm
+npm install
+
+# pnpm
+pnpm install
+
+# yarn
+yarn install
+
+# bun
+bun install
+```
+
+## Development Server
+
+Start the development server on `http://localhost:3000`:
+
+```bash
+# npm
+npm run dev
+
+# pnpm
+pnpm run dev
+
+# yarn
+yarn dev
+
+# bun
+bun run dev
+```
+
+## Production
+
+Build the application for production:
+
+```bash
+# npm
+npm run build
+
+# pnpm
+pnpm run build
+
+# yarn
+yarn build
+
+# bun
+bun run build
+```
+
+Locally preview production build:
+
+```bash
+# npm
+npm run preview
+
+# pnpm
+pnpm run preview
+
+# yarn
+yarn preview
+
+# bun
+bun run preview
+```
+
+Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.
diff --git a/app.vue b/app.vue
new file mode 100644
index 00000000..a495b757
--- /dev/null
+++ b/app.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/assets/css/tailwind.css b/assets/css/tailwind.css
new file mode 100644
index 00000000..8a04b2ba
--- /dev/null
+++ b/assets/css/tailwind.css
@@ -0,0 +1,78 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 5.9% 10%;
+
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: 240 5.9% 10%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+
+ --ring: 240 10% 3.9%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
+
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+
+ --border: 240 3.7% 15.9%;
+ --input: 240 3.7% 15.9%;
+
+ --primary: 0 0% 98%;
+ --primary-foreground: 240 5.9% 10%;
+
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: 0 0% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+
+ --ring: 240 4.9% 83.9%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
\ No newline at end of file
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 00000000..f354b85d
Binary files /dev/null and b/bun.lockb differ
diff --git a/components.json b/components.json
new file mode 100644
index 00000000..91c859b6
--- /dev/null
+++ b/components.json
@@ -0,0 +1,16 @@
+{
+ "$schema": "https://shadcn-vue.com/schema.json",
+ "style": "default",
+ "typescript": true,
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "assets/css/tailwind.css",
+ "baseColor": "zinc",
+ "cssVariables": true
+ },
+ "framework": "nuxt",
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils"
+ }
+}
diff --git a/components/ui/button/Button.vue b/components/ui/button/Button.vue
new file mode 100644
index 00000000..5cfd668a
--- /dev/null
+++ b/components/ui/button/Button.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/components/ui/button/index.ts b/components/ui/button/index.ts
new file mode 100644
index 00000000..1b00c326
--- /dev/null
+++ b/components/ui/button/index.ts
@@ -0,0 +1,35 @@
+import { type VariantProps, cva } from 'class-variance-authority'
+
+export { default as Button } from './Button.vue'
+
+export const buttonVariants = cva(
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+ outline:
+ 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
+ secondary:
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
+ link: 'text-primary underline-offset-4 hover:underline',
+ },
+ size: {
+ default: 'h-10 px-4 py-2',
+ xs: 'h-7 rounded px-2',
+ sm: 'h-9 rounded-md px-3',
+ lg: 'h-11 rounded-md px-8',
+ icon: 'h-10 w-10',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+)
+
+export type ButtonVariants = VariantProps
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..32ff0b90
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,18 @@
+import antfu from '@antfu/eslint-config';
+
+export default antfu({
+ stylistic: {
+ indent: 2,
+ quotes: 'single',
+ semi: true,
+ },
+ typescript: true,
+ vue: true,
+}, {
+ rules: {
+ 'style/brace-style': ['warn', '1tbs', { allowSingleLine: true }],
+ 'vue/block-order': ['error', {
+ order: ['template', 'script', 'style'],
+ }],
+ },
+});
diff --git a/lib/utils.ts b/lib/utils.ts
new file mode 100644
index 00000000..9ad0df42
--- /dev/null
+++ b/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/nuxt.config.ts b/nuxt.config.ts
new file mode 100644
index 00000000..860d6611
--- /dev/null
+++ b/nuxt.config.ts
@@ -0,0 +1,18 @@
+// https://nuxt.com/docs/api/configuration/nuxt-config
+export default defineNuxtConfig({
+ modules: [
+ '@nuxtjs/tailwindcss',
+ 'shadcn-nuxt',
+ ],
+ shadcn: {
+ /**
+ * Prefix for all the imported component
+ */
+ prefix: '',
+ /**
+ * Directory that the component lives in.
+ * @default "./components/ui"
+ */
+ componentDir: './components/ui',
+ },
+});
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..bcb50bd7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "nuxt-app",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "build": "nuxt build",
+ "dev": "nuxt dev",
+ "generate": "nuxt generate",
+ "preview": "nuxt preview",
+ "postinstall": "nuxt prepare",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix"
+ },
+ "dependencies": {
+ "@nuxtjs/tailwindcss": "^6.12.0",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.1",
+ "lucide-vue-next": "^0.378.0",
+ "nuxt": "^3.11.2",
+ "radix-vue": "^1.8.0",
+ "shadcn-nuxt": "^0.10.4",
+ "tailwind-merge": "^2.3.0",
+ "tailwindcss-animate": "^1.0.7",
+ "vue": "^3.4.27",
+ "vue-router": "^4.3.2"
+ },
+ "devDependencies": {
+ "@antfu/eslint-config": "^2.18.1",
+ "eslint": "9.2.0",
+ "vue-tsc": "^2.0.19"
+ }
+}
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 00000000..18993ad9
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 00000000..b9ed69c1
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "../.nuxt/tsconfig.server.json"
+}
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 00000000..6bb5adc1
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,86 @@
+const animate = require('tailwindcss-animate');
+
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ darkMode: ['class'],
+ safelist: ['dark'],
+ prefix: '',
+
+ 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: {
+ xl: 'calc(var(--radius) + 4px)',
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)',
+ },
+ 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 },
+ },
+ 'collapsible-down': {
+ from: { height: 0 },
+ to: { height: 'var(--radix-collapsible-content-height)' },
+ },
+ 'collapsible-up': {
+ from: { height: 'var(--radix-collapsible-content-height)' },
+ to: { height: 0 },
+ },
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ 'collapsible-down': 'collapsible-down 0.2s ease-in-out',
+ 'collapsible-up': 'collapsible-up 0.2s ease-in-out',
+ },
+ },
+ },
+ plugins: [animate],
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..a746f2a7
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ // https://nuxt.com/docs/guide/concepts/typescript
+ "extends": "./.nuxt/tsconfig.json"
+}