diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 000000000..8cf6f4554 --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,7 @@ +# Mintlify Docs Deployment +FROM node:20-alpine +WORKDIR /app +RUN npm install -g mintlify@latest +COPY . . +EXPOSE 3000 +CMD ["sh", "-c", "mintlify dev --port ${PORT:-3000}"] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbcf2975f..fdaba957d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,6 +100,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) diff: specifier: ^8.0.3 version: 8.0.3 @@ -112,6 +115,9 @@ importers: next-auth: specifier: ^4.24.13 version: 4.24.13(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) radix-ui: specifier: ^1.4.3 version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -2806,6 +2812,12 @@ packages: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} + cmdk@1.1.1: + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -3981,6 +3993,12 @@ packages: nodemailer: optional: true + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next@16.1.6: resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==} engines: {node: '>=20.9.0'} @@ -7809,6 +7827,18 @@ snapshots: cluster-key-slot@1.1.2: {} + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.4) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -9025,6 +9055,11 @@ snapshots: react-dom: 19.2.4(react@19.2.4) uuid: 8.3.2 + next-themes@0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.1.6 diff --git a/railway.json b/railway.json new file mode 100644 index 000000000..4c4efc029 --- /dev/null +++ b/railway.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://railway.app/railway.schema.json", + "build": { + "builder": "DOCKERFILE", + "dockerfilePath": "docs/Dockerfile" + }, + "deploy": { + "restartPolicyType": "ON_FAILURE", + "restartPolicyMaxRetries": 3 + } +} diff --git a/web/package.json b/web/package.json index 03d349dac..de463cb4f 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,7 @@ "@radix-ui/react-slot": "^1.2.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "diff": "^8.0.3", "lucide-react": "^0.525.0", "next": "^16.1.6", diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx index 349e48ef1..389047e00 100644 --- a/web/src/app/layout.tsx +++ b/web/src/app/layout.tsx @@ -13,7 +13,7 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + {children} diff --git a/web/src/components/dashboard/config-sections/AiSection.tsx b/web/src/components/dashboard/config-sections/AiSection.tsx index e8fce6b68..c0b271572 100644 --- a/web/src/components/dashboard/config-sections/AiSection.tsx +++ b/web/src/components/dashboard/config-sections/AiSection.tsx @@ -1,8 +1,9 @@ 'use client'; import { SystemPromptEditor } from '@/components/dashboard/system-prompt-editor'; -import { ToggleSwitch } from '@/components/dashboard/toggle-switch'; import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; import type { GuildConfig } from '@/lib/config-utils'; import { SYSTEM_PROMPT_MAX_LENGTH } from '@/types/config'; @@ -28,12 +29,18 @@ export function AiSection({ AI Chat Configure the AI assistant behavior. - +
+ + +
diff --git a/web/src/components/dashboard/config-sections/ModerationSection.tsx b/web/src/components/dashboard/config-sections/ModerationSection.tsx index 3f4d804b8..58abc3482 100644 --- a/web/src/components/dashboard/config-sections/ModerationSection.tsx +++ b/web/src/components/dashboard/config-sections/ModerationSection.tsx @@ -1,12 +1,11 @@ 'use client'; -import { ToggleSwitch } from '@/components/dashboard/toggle-switch'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; import type { GuildConfig } from '@/lib/config-utils'; -const inputClasses = - 'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; - interface ModerationSectionProps { draftConfig: GuildConfig; saving: boolean; @@ -36,56 +35,65 @@ export function ModerationSection({ Configure moderation, escalation, and logging settings. - - +
- Auto-delete flagged messages - + Auto-delete flagged messages + + onFieldChange('autoDelete', v)} + onCheckedChange={(v) => onFieldChange('autoDelete', v)} disabled={saving} - label="Auto Delete" + aria-label="Toggle auto-delete" />
DM Notifications {(['warn', 'timeout', 'kick', 'ban'] as const).map((action) => (
- {action} - + {action} + + onDmNotificationChange(action, v)} + onCheckedChange={(v) => onDmNotificationChange(action, v)} disabled={saving} - label={`DM on ${action}`} + aria-label={`DM on ${action}`} />
))}
- Escalation Enabled - + Escalation Enabled + + onEscalationChange(v)} + onCheckedChange={(v) => onEscalationChange(v)} disabled={saving} - label="Escalation" + aria-label="Toggle escalation" />
diff --git a/web/src/components/dashboard/config-sections/NumberField.tsx b/web/src/components/dashboard/config-sections/NumberField.tsx index a013320b4..fd174c6c5 100644 --- a/web/src/components/dashboard/config-sections/NumberField.tsx +++ b/web/src/components/dashboard/config-sections/NumberField.tsx @@ -1,5 +1,9 @@ -const inputClasses = - 'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; +'use client'; + +import { useId } from 'react'; + +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; interface NumberFieldProps { label: string; @@ -11,10 +15,12 @@ interface NumberFieldProps { } export function NumberField({ label, value, onChange, disabled, min, step }: NumberFieldProps) { + const id = useId(); return ( - + ); } diff --git a/web/src/components/dashboard/config-sections/TriageSection.tsx b/web/src/components/dashboard/config-sections/TriageSection.tsx index 393510c43..bc91eebd0 100644 --- a/web/src/components/dashboard/config-sections/TriageSection.tsx +++ b/web/src/components/dashboard/config-sections/TriageSection.tsx @@ -1,13 +1,12 @@ 'use client'; -import { ToggleSwitch } from '@/components/dashboard/toggle-switch'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; import type { GuildConfig } from '@/lib/config-utils'; import { NumberField } from './NumberField'; -const inputClasses = - 'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; - interface TriageSectionProps { draftConfig: GuildConfig; saving: boolean; @@ -33,37 +32,37 @@ export function TriageSection({ Configure message triage classifier, responder models, and channels. - - - ); diff --git a/web/src/components/dashboard/config-sections/WelcomeSection.tsx b/web/src/components/dashboard/config-sections/WelcomeSection.tsx index a908b58f4..349ae39a8 100644 --- a/web/src/components/dashboard/config-sections/WelcomeSection.tsx +++ b/web/src/components/dashboard/config-sections/WelcomeSection.tsx @@ -1,12 +1,11 @@ 'use client'; -import { ToggleSwitch } from '@/components/dashboard/toggle-switch'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; +import { Textarea } from '@/components/ui/textarea'; import type { GuildConfig } from '@/lib/config-utils'; -const inputClasses = - 'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; - interface WelcomeSectionProps { draftConfig: GuildConfig; saving: boolean; @@ -28,27 +27,27 @@ export function WelcomeSection({ Welcome Messages Greet new members when they join the server. - -