mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-12 08:27:27 +02:00
feat: add working theme selector and improve settings UI
Added fully functional theme selector and improved the settings UI with better components and cleaner copy. Changes: - Created Switch component (shadcn/radix-ui) for boolean settings - Added working theme selector that integrates with existing theme system - Uses useTheme hook to display available themes (Dark, Light, Plan9) - Reordered sections: Appearance first, Post second - Reordered settings: Theme first in Appearance section - Replaced Checkbox with Switch for better UX on boolean toggles - Simplified copy: "Add Grimoire tag to published events" instead of listing kinds - Simplified copy: "Display client identifiers in events" instead of "via Grimoire" mention - Better layout: Label/description on left, Switch on right Settings now use proper form components: - Switch for boolean toggles (include client tag, show client tags) - Button group for theme selection - Clean justify-between layout for settings rows The theme selector works immediately - clicking Dark/Light/Plan9 applies the theme instantly via the existing ThemeProvider context.
This commit is contained in:
71
package-lock.json
generated
71
package-lock.json
generated
@@ -22,6 +22,7 @@
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
@@ -3371,6 +3372,76 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
|
||||
"integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"@radix-ui/react-scroll-area": "^1.2.10",
|
||||
"@radix-ui/react-slider": "^1.3.6",
|
||||
"@radix-ui/react-slot": "^1.2.4",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@tiptap/core": "^3.15.3",
|
||||
|
||||
@@ -1,21 +1,73 @@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
|
||||
import { Checkbox } from "./ui/checkbox";
|
||||
import { Switch } from "./ui/switch";
|
||||
import { Button } from "./ui/button";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
import { useTheme } from "@/lib/themes";
|
||||
|
||||
export function SettingsViewer() {
|
||||
const { settings, updateSetting } = useSettings();
|
||||
const { themeId, setTheme, availableThemes } = useTheme();
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<Tabs defaultValue="post" className="flex-1 flex flex-col">
|
||||
<Tabs defaultValue="appearance" className="flex-1 flex flex-col">
|
||||
<div className="border-b px-6 py-3">
|
||||
<TabsList>
|
||||
<TabsTrigger value="post">Post</TabsTrigger>
|
||||
<TabsTrigger value="appearance">Appearance</TabsTrigger>
|
||||
<TabsTrigger value="post">Post</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<TabsContent value="appearance" className="m-0 p-6 space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-1">Appearance</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Customize display preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm">Theme</label>
|
||||
<div className="flex gap-2">
|
||||
{availableThemes.map((theme) => (
|
||||
<Button
|
||||
key={theme.id}
|
||||
variant={themeId === theme.id ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => setTheme(theme.id)}
|
||||
className="capitalize"
|
||||
>
|
||||
{theme.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<label
|
||||
htmlFor="show-client-tags"
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
Show client tags
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Display client identifiers in events
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="show-client-tags"
|
||||
checked={settings?.appearance?.showClientTags ?? true}
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
updateSetting("appearance", "showClientTags", checked)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="post" className="m-0 p-6 space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-1">Post Settings</h3>
|
||||
@@ -25,57 +77,25 @@ export function SettingsViewer() {
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Checkbox
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-0.5">
|
||||
<label
|
||||
htmlFor="include-client-tag"
|
||||
className="text-sm font-medium cursor-pointer"
|
||||
>
|
||||
Include client tag
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Add Grimoire tag to published events
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="include-client-tag"
|
||||
checked={settings?.post?.includeClientTag ?? true}
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
updateSetting("post", "includeClientTag", checked)
|
||||
}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="include-client-tag"
|
||||
className="text-sm cursor-pointer"
|
||||
>
|
||||
Include client tag
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Add Grimoire tag to posts, spells, and deletions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="appearance" className="m-0 p-6 space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-1">Appearance</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Customize display preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start space-x-3">
|
||||
<Checkbox
|
||||
id="show-client-tags"
|
||||
checked={settings?.appearance?.showClientTags ?? true}
|
||||
onCheckedChange={(checked: boolean) =>
|
||||
updateSetting("appearance", "showClientTags", checked)
|
||||
}
|
||||
/>
|
||||
<div className="space-y-1">
|
||||
<label
|
||||
htmlFor="show-client-tags"
|
||||
className="text-sm cursor-pointer"
|
||||
>
|
||||
Show client tags
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Display "via Grimoire" and other client identifiers
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
27
src/components/ui/switch.tsx
Normal file
27
src/components/ui/switch.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as React from "react";
|
||||
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
));
|
||||
Switch.displayName = SwitchPrimitives.Root.displayName;
|
||||
|
||||
export { Switch };
|
||||
Reference in New Issue
Block a user