mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
feat: migrate to Tailwind CSS v4
Replace @nuxtjs/tailwindcss with @tailwindcss/vite, move theme tokens into @theme inline in main.css, drop tailwind.config.ts.
This commit is contained in:
parent
32a8c2ce4e
commit
c75567cf15
8 changed files with 264 additions and 530 deletions
|
|
@ -9,8 +9,8 @@ Core pipeline: GitHub Webhook → Worker (verify + filter + format) → Discord
|
|||
## Key Decisions
|
||||
|
||||
- Runtime: Cloudflare Workers via the Nitro `cloudflare_module` preset (`_worker.js`), H3 event handlers in `server/routes/`
|
||||
- UI: Vue 3 + Tailwind CSS v3 (`@nuxtjs/tailwindcss`); admin console is a client-side SPA (`routeRules: "/admin/**": { ssr: false }`), home/legal pages render server-side
|
||||
- Styling: all theme colors are RGB-triplet CSS variables in `app/assets/css/main.css` mapped into `tailwind.config.ts` (so `bg-accent/10` opacity modifiers work); the design tokens switch with `prefers-color-scheme` (unless `<html data-theme="light">`); repeated control patterns are `@apply` component classes in the CSS `@layer components`
|
||||
- UI: Vue 3 + Tailwind CSS v4 (`@tailwindcss/vite`); admin console is a client-side SPA (`routeRules: "/admin/**": { ssr: false }`), home/legal pages render server-side
|
||||
- Styling: all theme colors are RGB-triplet CSS variables in `app/assets/css/main.css` declared as `@theme inline` tokens (so `bg-accent/10` opacity modifiers work); the design tokens switch with `prefers-color-scheme` (unless `<html data-theme="light">`); repeated control patterns are `@apply` component classes in the CSS `@layer components`
|
||||
- Discord interactions: HTTPS Interactions Endpoint (`POST /discord/interactions`, Ed25519-signed) — no Discord Gateway / Durable Object; bot stays offline, messages always sent via REST
|
||||
- Storage: D1 is the source of truth for config (routes/groups via `d1_routes`/`d1_groups`, see `server/lib/storage/config-store.ts`), send/audit logs, dedup (`dedup_keys`), delivery state (`delivery_state`) and message tracking (`message_tracking` via `server/lib/storage/d1.ts` `canUseD1` gate). KV keeps only cache + short-lived/ephemeral state (tokens, OAuth state, admin sessions, `msg:*`-adjacent locks, per-group secrets `tenant:*`, invites, i18n overrides) with explicit TTLs. R2 parks oversized queue payloads (`webhooks/YYYY/MM/DD/*.json`, `server/lib/storage/payload.ts`) instead of KV. A `storage-prune` scheduled task cleans up expired dedup/delivery/message-tracking rows
|
||||
- Signature verification: Web Crypto API (HMAC-SHA256 for GitHub/Gitea, Ed25519 for Discord, timing-safe secret-token compare for Telegram)
|
||||
|
|
@ -34,7 +34,7 @@ Core pipeline: GitHub Webhook → Worker (verify + filter + format) → Discord
|
|||
```text
|
||||
app/ # Vue 3 UI (Nuxt app dir)
|
||||
├── app.vue # root component (NuxtPage)
|
||||
├── assets/css/main.css # Tailwind entry: theme tokens (RGB-triplet vars) + @layer components (@apply) + Vue transition glue
|
||||
├── assets/css/main.css # Tailwind v4 entry: @import "tailwindcss" + @theme inline tokens (RGB-triplet vars) + @layer components (@apply) + Vue transition glue
|
||||
├── pages/ # index (landing), terms, privacy, admin/[...slug] (console SPA)
|
||||
├── components/ # ConsolePage (sidebar shell + topbar), AdminHome (overview dashboard),
|
||||
│ # RouteCard/Editor (RouteEditor has FilterNodeEditor AST builder + TagInput chips),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,61 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Theme tokens — RGB triplets so Tailwind opacity modifiers (bg-accent/10)
|
||||
* work against them; values switch automatically with prefers-color-scheme
|
||||
* (unless an explicit data-theme="light" is set on <html>).
|
||||
* ------------------------------------------------------------------------- */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
@theme inline {
|
||||
--font-ui: "Plus Jakarta Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
--radius: 12px;
|
||||
--radius-sm: 8px;
|
||||
--radius: 12px;
|
||||
--shadow-card: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
--shadow-card-hover: 0 8px 24px -12px rgba(15, 23, 42, 0.25);
|
||||
--shadow-accent-lg: 0 12px 28px -10px rgb(var(--accent));
|
||||
--shadow-drawer: -16px 0 48px rgba(15, 23, 42, 0.12);
|
||||
--shadow-modal: 0 12px 40px rgba(15, 23, 42, 0.25);
|
||||
--animate-rise: rise 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) both;
|
||||
|
||||
--color-bg: rgb(var(--bg));
|
||||
--color-surface: rgb(var(--surface));
|
||||
--color-surface-2: rgb(var(--surface-2));
|
||||
--color-surface-3: rgb(var(--surface-3));
|
||||
--color-border: rgb(var(--border));
|
||||
--color-border-strong: rgb(var(--border-strong));
|
||||
--color-text: rgb(var(--text));
|
||||
--color-muted: rgb(var(--muted));
|
||||
--color-faint: rgb(var(--faint));
|
||||
--color-accent: rgb(var(--accent));
|
||||
--color-accent-strong: rgb(var(--accent-strong));
|
||||
--color-accent-dim: rgb(var(--accent-dim));
|
||||
--color-accent-text: rgb(var(--accent-text));
|
||||
--color-accent-border: rgb(var(--accent-border));
|
||||
--color-ok: rgb(var(--ok));
|
||||
--color-ok-dim: rgb(var(--ok-dim));
|
||||
--color-warn: rgb(var(--warn));
|
||||
--color-warn-dim: rgb(var(--warn-dim));
|
||||
--color-bad: rgb(var(--bad));
|
||||
--color-bad-dim: rgb(var(--bad-dim));
|
||||
--color-info: rgb(var(--info));
|
||||
--color-info-dim: rgb(var(--info-dim));
|
||||
--color-body-text: rgb(var(--body-text));
|
||||
--color-code-bg: rgb(var(--code-bg));
|
||||
}
|
||||
|
||||
@keyframes rise {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(8px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--header-bg: rgba(255, 255, 255, 0.82);
|
||||
--scrim: rgba(15, 23, 42, 0.4);
|
||||
--knob: #ffffff;
|
||||
|
|
@ -77,10 +124,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html,
|
||||
body {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ bun run dev # Start local dev server
|
|||
```text
|
||||
app/ # Vue 3 UI (Nuxt app dir)
|
||||
├── app.vue # Root component (NuxtPage)
|
||||
├── assets/css/main.css # Tailwind CSS entry: theme tokens (RGB-triplet CSS variables) + @layer components (@apply)
|
||||
├── assets/css/main.css # Tailwind CSS v4 entry: @import "tailwindcss" + @theme inline tokens (RGB-triplet CSS variables) + @layer components (@apply)
|
||||
├── pages/ # index (landing), terms, privacy, admin/[...slug] (console SPA)
|
||||
├── components/ # ConsolePage, RouteCard/Editor, GroupEditor, MembersPanel, WebhookPanel,
|
||||
│ # SendLogs, AuditLog, AppToasts, LegalLayout
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ bun run dev # 启动本地开发服务器
|
|||
```text
|
||||
app/ # Vue 3 UI(Nuxt app 目录)
|
||||
├── app.vue # 根组件 (NuxtPage)
|
||||
├── assets/css/main.css # Tailwind CSS 入口:主题令牌(RGB 三元组 CSS 变量)+ @layer components (@apply)
|
||||
├── assets/css/main.css # Tailwind CSS v4 入口:@import "tailwindcss" + @theme inline 令牌(RGB 三元组 CSS 变量)+ @layer components (@apply)
|
||||
├── pages/ # index(落地页)、terms、privacy、admin/[...slug](控制台 SPA)
|
||||
├── components/ # ConsolePage、RouteCard/Editor、GroupEditor、MembersPanel、WebhookPanel、
|
||||
│ # SendLogs、AuditLog、AppToasts、LegalLayout
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
export default defineNuxtConfig({
|
||||
compatibilityDate: "2025-07-15",
|
||||
modules: ["@nuxtjs/tailwindcss"],
|
||||
css: ["~/assets/css/main.css"],
|
||||
// Target: Cloudflare Workers (single _worker.js via the cloudflare_module preset).
|
||||
nitro: {
|
||||
preset: "cloudflare_module",
|
||||
|
|
@ -23,9 +25,8 @@ export default defineNuxtConfig({
|
|||
],
|
||||
},
|
||||
},
|
||||
tailwindcss: {
|
||||
cssPath: "~/assets/css/main.css",
|
||||
configPath: "tailwind.config",
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
devtools: { enabled: false },
|
||||
runtimeConfig: {
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^5.20260801.1",
|
||||
"@nuxtjs/tailwindcss": "^6.14.0",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.21.0",
|
||||
"@typescript-eslint/parser": "^8.21.0",
|
||||
"eslint": "^10.9.0",
|
||||
"markdownlint-cli": "^0.49.1",
|
||||
"prettier": "^3.9.6",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^5.7.0",
|
||||
"vitepress": "^1.6.4",
|
||||
"vue-router": "^5.2.0",
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
import type { Config } from "tailwindcss";
|
||||
|
||||
/**
|
||||
* Theme colors are RGB-triplet CSS variables (see app/assets/css/main.css),
|
||||
* so every token supports Tailwind opacity modifiers (bg-accent/10, ...)
|
||||
* and switches automatically with prefers-color-scheme.
|
||||
*/
|
||||
const themeColors = {
|
||||
bg: "rgb(var(--bg) / <alpha-value>)",
|
||||
surface: "rgb(var(--surface) / <alpha-value>)",
|
||||
"surface-2": "rgb(var(--surface-2) / <alpha-value>)",
|
||||
"surface-3": "rgb(var(--surface-3) / <alpha-value>)",
|
||||
border: "rgb(var(--border) / <alpha-value>)",
|
||||
"border-strong": "rgb(var(--border-strong) / <alpha-value>)",
|
||||
text: "rgb(var(--text) / <alpha-value>)",
|
||||
muted: "rgb(var(--muted) / <alpha-value>)",
|
||||
faint: "rgb(var(--faint) / <alpha-value>)",
|
||||
accent: "rgb(var(--accent) / <alpha-value>)",
|
||||
"accent-strong": "rgb(var(--accent-strong) / <alpha-value>)",
|
||||
"accent-dim": "rgb(var(--accent-dim) / <alpha-value>)",
|
||||
"accent-text": "rgb(var(--accent-text) / <alpha-value>)",
|
||||
"accent-border": "rgb(var(--accent-border) / <alpha-value>)",
|
||||
ok: "rgb(var(--ok) / <alpha-value>)",
|
||||
"ok-dim": "rgb(var(--ok-dim) / <alpha-value>)",
|
||||
warn: "rgb(var(--warn) / <alpha-value>)",
|
||||
"warn-dim": "rgb(var(--warn-dim) / <alpha-value>)",
|
||||
bad: "rgb(var(--bad) / <alpha-value>)",
|
||||
"bad-dim": "rgb(var(--bad-dim) / <alpha-value>)",
|
||||
info: "rgb(var(--info) / <alpha-value>)",
|
||||
"info-dim": "rgb(var(--info-dim) / <alpha-value>)",
|
||||
"body-text": "rgb(var(--body-text) / <alpha-value>)",
|
||||
"code-bg": "rgb(var(--code-bg) / <alpha-value>)",
|
||||
};
|
||||
|
||||
export default <Partial<Config>>{
|
||||
content: [],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: themeColors,
|
||||
fontFamily: {
|
||||
ui: "var(--font-ui)",
|
||||
mono: "var(--font-mono)",
|
||||
},
|
||||
borderRadius: {
|
||||
sm: "var(--radius-sm)",
|
||||
DEFAULT: "var(--radius)",
|
||||
},
|
||||
boxShadow: {
|
||||
card: "var(--shadow)",
|
||||
"card-hover": "0 8px 24px -12px rgba(15, 23, 42, 0.25)",
|
||||
"accent-lg": "0 12px 28px -10px var(--accent)",
|
||||
drawer: "-16px 0 48px rgba(15, 23, 42, 0.12)",
|
||||
modal: "0 12px 40px rgba(15, 23, 42, 0.25)",
|
||||
},
|
||||
keyframes: {
|
||||
rise: {
|
||||
from: { opacity: "0", transform: "translateY(8px)" },
|
||||
to: { opacity: "1", transform: "none" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
rise: "rise 0.35s cubic-bezier(0.2, 0.7, 0.2, 1) both",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue