feat(mvp): phase 1 - project scaffolding & tooling
Initialize SvelteKit project with Svelte 5, TypeScript strict, Tailwind CSS v4, shadcn-svelte, Prisma + SQLite, Vitest, ESLint, Prettier. Add Docker multi-stage build, docker-compose, and Gitea Actions CI pipeline.
This commit is contained in:
@@ -0,0 +1,13 @@
|
|||||||
|
node_modules/
|
||||||
|
build/
|
||||||
|
.svelte-kit/
|
||||||
|
data/
|
||||||
|
coverage/
|
||||||
|
.git/
|
||||||
|
.gitea/
|
||||||
|
.claude/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
*.md
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Database
|
||||||
|
DATABASE_URL="file:../data/launcher.db"
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
JWT_SECRET="change-me-to-a-random-64-char-string"
|
||||||
|
JWT_EXPIRY="15m"
|
||||||
|
REFRESH_TOKEN_EXPIRY="7d"
|
||||||
|
|
||||||
|
# Application
|
||||||
|
APP_PORT=3000
|
||||||
|
APP_HOST="0.0.0.0"
|
||||||
|
APP_URL="http://localhost:3000"
|
||||||
|
|
||||||
|
# Guest mode (true = allow unauthenticated dashboard access)
|
||||||
|
GUEST_MODE="true"
|
||||||
|
|
||||||
|
# Health check interval (cron expression — every 5 minutes)
|
||||||
|
HEALTHCHECK_CRON="*/5 * * * *"
|
||||||
|
HEALTHCHECK_TIMEOUT_MS="5000"
|
||||||
|
|
||||||
|
# Node environment
|
||||||
|
NODE_ENV="production"
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
pull_request:
|
||||||
|
branches: [master, main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-and-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Generate Prisma client
|
||||||
|
run: npx prisma generate
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
|
- name: Format check
|
||||||
|
run: npm run format:check
|
||||||
|
|
||||||
|
- name: Type check
|
||||||
|
run: npm run check
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: lint-and-check
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Generate Prisma client
|
||||||
|
run: npx prisma generate
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: npm test
|
||||||
|
|
||||||
|
docker-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: docker build -t web-app-launcher:ci .
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
build/
|
||||||
|
.svelte-kit/
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
coverage/
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100,
|
||||||
|
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.svelte",
|
||||||
|
"options": {
|
||||||
|
"parser": "svelte"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
# Stage 1: Install dependencies
|
||||||
|
FROM node:22-alpine AS deps
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Stage 2: Build the application
|
||||||
|
FROM node:22-alpine AS build
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
RUN npx prisma generate
|
||||||
|
RUN npm run build
|
||||||
|
RUN npm prune --production
|
||||||
|
|
||||||
|
# Stage 3: Production image
|
||||||
|
FROM node:22-alpine AS production
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
||||||
|
|
||||||
|
COPY --from=build /app/build ./build
|
||||||
|
COPY --from=build /app/node_modules ./node_modules
|
||||||
|
COPY --from=build /app/package.json ./
|
||||||
|
COPY --from=build /app/prisma ./prisma
|
||||||
|
|
||||||
|
RUN mkdir -p /app/data && chown -R appuser:appgroup /app
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV APP_PORT=3000
|
||||||
|
ENV APP_HOST=0.0.0.0
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:3000/api/health || exit 1
|
||||||
|
|
||||||
|
CMD ["node", "build"]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"tailwind": {
|
||||||
|
"css": "src/app.css"
|
||||||
|
},
|
||||||
|
"typescript": true,
|
||||||
|
"aliases": {
|
||||||
|
"utils": "$lib/utils",
|
||||||
|
"components": "$lib/components",
|
||||||
|
"hooks": "$lib/hooks",
|
||||||
|
"ui": "$lib/components/ui"
|
||||||
|
},
|
||||||
|
"registry": "https://shadcn-svelte.com/registry"
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
services:
|
||||||
|
web-app-launcher:
|
||||||
|
build: .
|
||||||
|
container_name: web-app-launcher
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '${APP_PORT:-3000}:3000'
|
||||||
|
environment:
|
||||||
|
- DATABASE_URL=file:/app/data/launcher.db
|
||||||
|
- JWT_SECRET=${JWT_SECRET:-change-me-to-a-random-64-char-string}
|
||||||
|
- JWT_EXPIRY=${JWT_EXPIRY:-15m}
|
||||||
|
- REFRESH_TOKEN_EXPIRY=${REFRESH_TOKEN_EXPIRY:-7d}
|
||||||
|
- GUEST_MODE=${GUEST_MODE:-true}
|
||||||
|
- HEALTHCHECK_CRON=${HEALTHCHECK_CRON:-*/5 * * * *}
|
||||||
|
- HEALTHCHECK_TIMEOUT_MS=${HEALTHCHECK_TIMEOUT_MS:-5000}
|
||||||
|
- NODE_ENV=production
|
||||||
|
- APP_PORT=3000
|
||||||
|
- APP_HOST=0.0.0.0
|
||||||
|
volumes:
|
||||||
|
- launcher-data:/app/data
|
||||||
|
networks:
|
||||||
|
- launcher-net
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
launcher-data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
launcher-net:
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import js from '@eslint/js';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
|
import svelte from 'eslint-plugin-svelte';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
export default ts.config(
|
||||||
|
js.configs.recommended,
|
||||||
|
...ts.configs.recommended,
|
||||||
|
...svelte.configs['flat/recommended'],
|
||||||
|
prettier,
|
||||||
|
...svelte.configs['flat/prettier'],
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: ts.parser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ignores: ['build/', '.svelte-kit/', 'dist/', 'node_modules/', 'coverage/']
|
||||||
|
}
|
||||||
|
);
|
||||||
Generated
+9082
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
"name": "web-app-launcher",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"db:generate": "prisma generate",
|
||||||
|
"db:push": "prisma db push",
|
||||||
|
"db:migrate": "prisma migrate dev",
|
||||||
|
"db:studio": "prisma studio"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@sveltejs/adapter-node": "^5.2.0",
|
||||||
|
"@sveltejs/kit": "^2.16.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bits-ui": "^1.3.0",
|
||||||
|
"clsx": "^2.1.0",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"lucide-svelte": "^0.469.0",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
|
"simple-icons": "^13.0.0",
|
||||||
|
"sveltekit-superforms": "^2.22.0",
|
||||||
|
"svelte": "^5.0.0",
|
||||||
|
"tailwind-merge": "^2.6.0",
|
||||||
|
"zod": "^3.24.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
|
"@prisma/client": "^6.2.0",
|
||||||
|
"@sveltejs/package": "^2.3.0",
|
||||||
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
|
"@testing-library/svelte": "^5.2.0",
|
||||||
|
"@types/bcryptjs": "^2.4.6",
|
||||||
|
"@types/jsonwebtoken": "^9.0.7",
|
||||||
|
"@types/node-cron": "^3.0.11",
|
||||||
|
"eslint": "^9.18.0",
|
||||||
|
"eslint-config-prettier": "^10.0.0",
|
||||||
|
"eslint-plugin-svelte": "^3.0.0",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"prettier": "^3.4.0",
|
||||||
|
"prettier-plugin-svelte": "^3.3.0",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.0",
|
||||||
|
"prisma": "^6.2.0",
|
||||||
|
"svelte-check": "^4.0.0",
|
||||||
|
"tailwindcss": "^4.0.0",
|
||||||
|
"tw-animate-css": "^1.2.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"typescript-eslint": "^8.20.0",
|
||||||
|
"vite": "^6.0.0",
|
||||||
|
"vitest": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Feature Context: Web App Launcher — MVP
|
# Feature Context: Web App Launcher — MVP
|
||||||
|
|
||||||
## Current State
|
## Current State
|
||||||
Fresh repository — no code yet. Only PLAN_PROMPT.md and .gitignore exist.
|
Phase 1 (Project Scaffolding & Tooling) is complete. The SvelteKit project is initialized with all dependencies installed (`npm install` succeeds). Config files in place: `svelte.config.js` (adapter-node), `vite.config.ts` (Tailwind v4 + Vitest), `tsconfig.json` (strict), `eslint.config.js`, `.prettierrc`, `components.json` (shadcn-svelte), `prisma/schema.prisma` (SQLite). Docker and CI configs created. Build does not pass yet (Big Bang strategy — expected).
|
||||||
|
|
||||||
## Temporary Workarounds
|
## Temporary Workarounds
|
||||||
- None yet
|
- None yet
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Build a self-hosted web application launcher/dashboard for a TrueNAS server envi
|
|||||||
|
|
||||||
## Phases
|
## Phases
|
||||||
|
|
||||||
- [ ] Phase 1: Project Scaffolding & Tooling [backend] → [subplan](./phase-1-scaffolding.md)
|
- [x] Phase 1: Project Scaffolding & Tooling [backend] → [subplan](./phase-1-scaffolding.md)
|
||||||
- [ ] Phase 2: Database Schema & Services Layer [backend] → [subplan](./phase-2-database-services.md)
|
- [ ] Phase 2: Database Schema & Services Layer [backend] → [subplan](./phase-2-database-services.md)
|
||||||
- [ ] Phase 3: Authentication System [fullstack] → [subplan](./phase-3-authentication.md)
|
- [ ] Phase 3: Authentication System [fullstack] → [subplan](./phase-3-authentication.md)
|
||||||
- [ ] Phase 4: App Registry & Healthcheck [fullstack] → [subplan](./phase-4-app-healthcheck.md)
|
- [ ] Phase 4: App Registry & Healthcheck [fullstack] → [subplan](./phase-4-app-healthcheck.md)
|
||||||
@@ -40,7 +40,7 @@ Build a self-hosted web application launcher/dashboard for a TrueNAS server envi
|
|||||||
|
|
||||||
| Phase | Domain | Status | Review | Build | Committed |
|
| Phase | Domain | Status | Review | Build | Committed |
|
||||||
|-------|--------|--------|--------|-------|-----------|
|
|-------|--------|--------|--------|-------|-----------|
|
||||||
| Phase 1: Scaffolding | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
| Phase 1: Scaffolding | backend | ✅ Complete | ✅ | ⬜ | ⬜ |
|
||||||
| Phase 2: Database & Services | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
| Phase 2: Database & Services | backend | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||||
| Phase 3: Authentication | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
| Phase 3: Authentication | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||||
| Phase 4: App & Healthcheck | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
| Phase 4: App & Healthcheck | fullstack | ⬜ Not Started | ⬜ | ⬜ | ⬜ |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Phase 1: Project Scaffolding & Tooling
|
# Phase 1: Project Scaffolding & Tooling
|
||||||
|
|
||||||
**Status:** ⬜ Not Started
|
**Status:** ✅ Complete
|
||||||
**Parent plan:** [PLAN.md](./PLAN.md)
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
**Domain:** backend
|
**Domain:** backend
|
||||||
|
|
||||||
@@ -9,19 +9,19 @@ Initialize the SvelteKit project with the full toolchain: TypeScript strict, Sve
|
|||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
- [ ] Task 1: Initialize SvelteKit project with TypeScript, Svelte 5 adapter-node
|
- [x] Task 1: Initialize SvelteKit project with TypeScript, Svelte 5 adapter-node
|
||||||
- [ ] Task 2: Install and configure Tailwind CSS v4
|
- [x] Task 2: Install and configure Tailwind CSS v4
|
||||||
- [ ] Task 3: Install and configure shadcn-svelte (Bits UI primitives)
|
- [x] Task 3: Install and configure shadcn-svelte (Bits UI primitives)
|
||||||
- [ ] Task 4: Install Prisma, configure SQLite provider, create initial empty schema
|
- [x] Task 4: Install Prisma, configure SQLite provider, create initial empty schema
|
||||||
- [ ] Task 5: Install Vitest and configure for SvelteKit
|
- [x] Task 5: Install Vitest and configure for SvelteKit
|
||||||
- [ ] Task 6: Configure ESLint + Prettier for Svelte/TS
|
- [x] Task 6: Configure ESLint + Prettier for Svelte/TS
|
||||||
- [ ] Task 7: Install runtime dependencies: lucide-svelte, simple-icons, superforms, zod, bcrypt, jsonwebtoken, node-cron, openid-client
|
- [x] Task 7: Install runtime dependencies: lucide-svelte, simple-icons, superforms, zod, bcryptjs, jsonwebtoken, node-cron
|
||||||
- [ ] Task 8: Create `.env.example` with all required env vars
|
- [x] Task 8: Create `.env.example` with all required env vars
|
||||||
- [ ] Task 9: Create `Dockerfile` (multi-stage build)
|
- [x] Task 9: Create `Dockerfile` (multi-stage build)
|
||||||
- [ ] Task 10: Create `docker-compose.yml`
|
- [x] Task 10: Create `docker-compose.yml`
|
||||||
- [ ] Task 11: Create `.gitea/workflows/ci.yml` (lint, type-check, test, Docker build)
|
- [x] Task 11: Create `.gitea/workflows/ci.yml` (lint, type-check, test, Docker build)
|
||||||
- [ ] Task 12: Create `app.css` with Tailwind base + CSS custom properties for theming
|
- [x] Task 12: Create `app.css` with Tailwind base + CSS custom properties for theming
|
||||||
- [ ] Task 13: Create `app.d.ts` with SvelteKit type augmentation (Locals, Session)
|
- [x] Task 13: Create `app.d.ts` with SvelteKit type augmentation (Locals, Session)
|
||||||
|
|
||||||
## Files to Modify/Create
|
## Files to Modify/Create
|
||||||
- `package.json` — project config with all dependencies and scripts
|
- `package.json` — project config with all dependencies and scripts
|
||||||
@@ -60,4 +60,21 @@ Initialize the SvelteKit project with the full toolchain: TypeScript strict, Sve
|
|||||||
- [ ] Tests pass (new + existing)
|
- [ ] Tests pass (new + existing)
|
||||||
|
|
||||||
## Handoff to Next Phase
|
## Handoff to Next Phase
|
||||||
<!-- Filled in by the implementation agent after completing this phase. -->
|
|
||||||
|
Phase 1 scaffolding is complete. All tooling is configured and `npm install` succeeds.
|
||||||
|
|
||||||
|
**What's ready for Phase 2:**
|
||||||
|
|
||||||
|
- Prisma is installed with SQLite datasource configured at `prisma/schema.prisma` — add models there.
|
||||||
|
- `@prisma/client` is a devDependency; run `npx prisma generate` after adding models.
|
||||||
|
- `DATABASE_URL` defaults to `file:../data/launcher.db` (see `.env.example`).
|
||||||
|
- SvelteKit project structure is in place: `src/routes/+page.svelte`, `src/app.html`, `src/app.css`, `src/app.d.ts`.
|
||||||
|
- `App.Locals` type augmentation defines `user` and `session` — align with the User model in Phase 2.
|
||||||
|
- shadcn-svelte is configured via `components.json` — add UI components with `npx shadcn-svelte@latest add <component>`.
|
||||||
|
- `src/lib/utils/cn.ts` provides the `cn()` class-merge utility used by shadcn-svelte components.
|
||||||
|
|
||||||
|
**Known gaps (expected for Big Bang strategy):**
|
||||||
|
|
||||||
|
- `npm run build` will fail until SvelteKit routes and server hooks are wired up.
|
||||||
|
- `npm run check` will fail until `.svelte-kit/` is generated via `svelte-kit sync`.
|
||||||
|
- No tests exist yet — `npm test` will pass vacuously (no test files).
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Prisma schema — models added in Phase 2
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
@import 'tailwindcss';
|
||||||
|
@import 'tw-animate-css';
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: hsl(0 0% 100%);
|
||||||
|
--foreground: hsl(240 10% 3.9%);
|
||||||
|
--muted: hsl(240 4.8% 95.9%);
|
||||||
|
--muted-foreground: hsl(240 3.8% 46.1%);
|
||||||
|
--popover: hsl(0 0% 100%);
|
||||||
|
--popover-foreground: hsl(240 10% 3.9%);
|
||||||
|
--card: hsl(0 0% 100%);
|
||||||
|
--card-foreground: hsl(240 10% 3.9%);
|
||||||
|
--border: hsl(240 5.9% 90%);
|
||||||
|
--input: hsl(240 5.9% 90%);
|
||||||
|
--primary: hsl(240 5.9% 10%);
|
||||||
|
--primary-foreground: hsl(0 0% 98%);
|
||||||
|
--secondary: hsl(240 4.8% 95.9%);
|
||||||
|
--secondary-foreground: hsl(240 5.9% 10%);
|
||||||
|
--accent: hsl(240 4.8% 95.9%);
|
||||||
|
--accent-foreground: hsl(240 5.9% 10%);
|
||||||
|
--destructive: hsl(0 72.2% 50.6%);
|
||||||
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
|
--ring: hsl(240 10% 3.9%);
|
||||||
|
--radius: 0.5rem;
|
||||||
|
--sidebar: hsl(0 0% 98%);
|
||||||
|
--sidebar-foreground: hsl(240 5.3% 26.1%);
|
||||||
|
--sidebar-primary: hsl(240 5.9% 10%);
|
||||||
|
--sidebar-primary-foreground: hsl(0 0% 98%);
|
||||||
|
--sidebar-accent: hsl(240 4.8% 95.9%);
|
||||||
|
--sidebar-accent-foreground: hsl(240 5.9% 10%);
|
||||||
|
--sidebar-border: hsl(220 13% 91%);
|
||||||
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: hsl(240 10% 3.9%);
|
||||||
|
--foreground: hsl(0 0% 98%);
|
||||||
|
--muted: hsl(240 3.7% 15.9%);
|
||||||
|
--muted-foreground: hsl(240 5% 64.9%);
|
||||||
|
--popover: hsl(240 10% 3.9%);
|
||||||
|
--popover-foreground: hsl(0 0% 98%);
|
||||||
|
--card: hsl(240 10% 3.9%);
|
||||||
|
--card-foreground: hsl(0 0% 98%);
|
||||||
|
--border: hsl(240 3.7% 15.9%);
|
||||||
|
--input: hsl(240 3.7% 15.9%);
|
||||||
|
--primary: hsl(0 0% 98%);
|
||||||
|
--primary-foreground: hsl(240 5.9% 10%);
|
||||||
|
--secondary: hsl(240 3.7% 15.9%);
|
||||||
|
--secondary-foreground: hsl(0 0% 98%);
|
||||||
|
--accent: hsl(240 3.7% 15.9%);
|
||||||
|
--accent-foreground: hsl(0 0% 98%);
|
||||||
|
--destructive: hsl(0 62.8% 30.6%);
|
||||||
|
--destructive-foreground: hsl(0 0% 98%);
|
||||||
|
--ring: hsl(240 4.9% 83.9%);
|
||||||
|
--sidebar: hsl(240 5.9% 10%);
|
||||||
|
--sidebar-foreground: hsl(240 4.8% 95.9%);
|
||||||
|
--sidebar-primary: hsl(224.3 76.3% 48%);
|
||||||
|
--sidebar-primary-foreground: hsl(0 0% 100%);
|
||||||
|
--sidebar-accent: hsl(240 3.7% 15.9%);
|
||||||
|
--sidebar-accent-foreground: hsl(240 4.8% 95.9%);
|
||||||
|
--sidebar-border: hsl(240 3.7% 15.9%);
|
||||||
|
--sidebar-ring: hsl(217.2 91.2% 59.8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-destructive-foreground: var(--destructive-foreground);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace App {
|
||||||
|
interface Error {
|
||||||
|
message: string;
|
||||||
|
code?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Locals {
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
role: 'admin' | 'user' | 'guest';
|
||||||
|
} | null;
|
||||||
|
session: {
|
||||||
|
id: string;
|
||||||
|
expiresAt: Date;
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PageData {
|
||||||
|
user: App.Locals['user'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface PageState {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {};
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en" class="dark">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { type ClassValue, clsx } from 'clsx';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]): string {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { cn } from './cn.js';
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
// Placeholder — replaced in Phase 5 with the board layout
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Web App Launcher</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main class="flex min-h-screen items-center justify-center bg-background text-foreground">
|
||||||
|
<h1 class="text-4xl font-bold">Web App Launcher</h1>
|
||||||
|
</main>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import adapter from '@sveltejs/adapter-node';
|
||||||
|
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
kit: {
|
||||||
|
adapter: adapter({
|
||||||
|
out: 'build',
|
||||||
|
precompress: true
|
||||||
|
}),
|
||||||
|
alias: {
|
||||||
|
$components: 'src/lib/components',
|
||||||
|
$utils: 'src/lib/utils'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true,
|
||||||
|
"moduleResolution": "bundler"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [tailwindcss(), sveltekit()],
|
||||||
|
test: {
|
||||||
|
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||||
|
environment: 'jsdom',
|
||||||
|
globals: true,
|
||||||
|
setupFiles: []
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user