Theme and default functionality

This commit is contained in:
2026-02-27 21:49:14 +01:00
parent 3c17c73390
commit 3e61019b7f
5 changed files with 121 additions and 71 deletions
+89 -62
View File
@@ -46,72 +46,99 @@
} }
:root { :root {
--radius: 0.625rem; --radius: 0.75rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0); --background: oklch(0.97 0.01 240);
--card: oklch(1 0 0); --foreground: oklch(0.22 0.02 240);
--card-foreground: oklch(0.145 0 0);
--card: oklch(0.99 0.005 240);
--card-foreground: oklch(0.22 0.02 240);
--popover: oklch(1 0 0); --popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0); --popover-foreground: oklch(0.22 0.02 240);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0); /* Primary = architectural blue */
--secondary: oklch(0.97 0 0); --primary: oklch(0.55 0.16 250);
--secondary-foreground: oklch(0.205 0 0); --primary-foreground: oklch(0.98 0.01 250);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0); --secondary: oklch(0.93 0.02 240);
--accent: oklch(0.97 0 0); --secondary-foreground: oklch(0.28 0.02 240);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325); --muted: oklch(0.94 0.015 240);
--border: oklch(0.922 0 0); --muted-foreground: oklch(0.55 0.02 240);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0); /* Accent = subtle violet for graph highlights */
--chart-1: oklch(0.646 0.222 41.116); --accent: oklch(0.92 0.03 270);
--chart-2: oklch(0.6 0.118 184.704); --accent-foreground: oklch(0.30 0.04 270);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429); --destructive: oklch(0.62 0.22 25);
--chart-5: oklch(0.769 0.188 70.08);
--sidebar: oklch(0.985 0 0); --border: oklch(0.90 0.015 240);
--sidebar-foreground: oklch(0.145 0 0); --input: oklch(0.90 0.015 240);
--sidebar-primary: oklch(0.205 0 0); --ring: oklch(0.55 0.16 250);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0); /* Charts (clean + modern) */
--sidebar-accent-foreground: oklch(0.205 0 0); --chart-1: oklch(0.55 0.16 250);
--sidebar-border: oklch(0.922 0 0); --chart-2: oklch(0.60 0.14 200);
--sidebar-ring: oklch(0.708 0 0); --chart-3: oklch(0.62 0.18 280);
--chart-4: oklch(0.70 0.15 140);
--chart-5: oklch(0.65 0.20 30);
--sidebar: oklch(0.95 0.01 240);
--sidebar-foreground: oklch(0.25 0.02 240);
--sidebar-primary: oklch(0.55 0.16 250);
--sidebar-primary-foreground: oklch(0.98 0.01 250);
--sidebar-accent: oklch(0.92 0.02 250);
--sidebar-accent-foreground: oklch(0.30 0.02 250);
--sidebar-border: oklch(0.88 0.015 240);
--sidebar-ring: oklch(0.55 0.16 250);
} }
.dark { .dark {
--background: oklch(0.145 0 0); --background: oklch(0.17 0.02 250);
--foreground: oklch(0.985 0 0); --foreground: oklch(0.94 0.01 240);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0); --card: oklch(0.21 0.02 250);
--popover: oklch(0.205 0 0); --card-foreground: oklch(0.94 0.01 240);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0); --popover: oklch(0.23 0.02 250);
--primary-foreground: oklch(0.205 0 0); --popover-foreground: oklch(0.94 0.01 240);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0); /* Strong but not neon */
--muted: oklch(0.269 0 0); --primary: oklch(0.65 0.17 250);
--muted-foreground: oklch(0.708 0 0); --primary-foreground: oklch(0.12 0.02 250);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0); --secondary: oklch(0.27 0.02 250);
--destructive: oklch(0.704 0.191 22.216); --secondary-foreground: oklch(0.94 0.01 240);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%); --muted: oklch(0.25 0.02 250);
--ring: oklch(0.556 0 0); --muted-foreground: oklch(0.70 0.02 240);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48); /* Accent for graph edges / highlights */
--chart-3: oklch(0.769 0.188 70.08); --accent: oklch(0.60 0.18 280);
--chart-4: oklch(0.627 0.265 303.9); --accent-foreground: oklch(0.96 0.01 240);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0); --destructive: oklch(0.68 0.20 25);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376); --border: oklch(0.30 0.02 250);
--sidebar-primary-foreground: oklch(0.985 0 0); --input: oklch(0.30 0.02 250);
--sidebar-accent: oklch(0.269 0 0); --ring: oklch(0.65 0.17 250);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%); /* Chart colors (balanced + readable on dark canvas) */
--sidebar-ring: oklch(0.556 0 0); --chart-1: oklch(0.65 0.17 250);
--chart-2: oklch(0.70 0.14 180);
--chart-3: oklch(0.72 0.18 300);
--chart-4: oklch(0.75 0.15 140);
--chart-5: oklch(0.72 0.20 40);
--sidebar: oklch(0.19 0.02 250);
--sidebar-foreground: oklch(0.92 0.01 240);
--sidebar-primary: oklch(0.65 0.17 250);
--sidebar-primary-foreground: oklch(0.12 0.02 250);
--sidebar-accent: oklch(0.27 0.02 250);
--sidebar-accent-foreground: oklch(0.92 0.01 240);
--sidebar-border: oklch(0.30 0.02 250);
--sidebar-ring: oklch(0.65 0.17 250);
} }
@layer base { @layer base {
+22 -4
View File
@@ -6,14 +6,19 @@ import { useWorkspace } from "@/hooks/workspace";
import { ChevronRight } from "lucide-react"; import { ChevronRight } from "lucide-react";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { EntityId } from "@/lib/types"; import { Route } from "@/lib/types";
import { Button } from "@/components/ui/button";
import {
createDefaultWorkspace,
saveWorkspaceToLocalStorage,
} from "@/lib/workspace";
export default function Workspace() { export default function Workspace() {
const [currentWorkspace, setCurrentWorkspace] = useState<string>(); const [currentWorkspace, setCurrentWorkspace] = useState<string>();
const { workspace, loadWorkspace } = useWorkspace(); const { workspace, loadWorkspace } = useWorkspace();
const [route, setRoute] = useState<EntityId>(); const [route, setRoute] = useState<Route>({});
useEffect(() => { useEffect(() => {
setCurrentWorkspace(Cookies.get("workspace")); setCurrentWorkspace(Cookies.get("workspace"));
@@ -21,6 +26,8 @@ export default function Workspace() {
useEffect(() => { useEffect(() => {
loadWorkspace(currentWorkspace); loadWorkspace(currentWorkspace);
if (currentWorkspace) Cookies.set("workspace", currentWorkspace);
else Cookies.remove("workspace");
}, [currentWorkspace]); }, [currentWorkspace]);
return workspace ? ( return workspace ? (
@@ -30,12 +37,23 @@ export default function Workspace() {
<header className="w-full border-b-2 pb-3 pt-2 hover:bg-accent/20 flex items-center text-foreground/90"> <header className="w-full border-b-2 pb-3 pt-2 hover:bg-accent/20 flex items-center text-foreground/90">
{workspace.name} {workspace.name}
<ChevronRight className="h-5" /> <ChevronRight className="h-5" />
{workspace.entities[route || ""]?.name} {workspace.entities[route.entity || ""]?.name}
</header> </header>
</div> </div>
</SidebarTrigger> </SidebarTrigger>
<Router workspace={workspace} /> <Router workspace={workspace} />
</AppSidebar> </AppSidebar>
) : null; ) : (
<main
className="flex h-svh items-center justify-center"
onClick={() => {
const w = createDefaultWorkspace();
saveWorkspaceToLocalStorage(w);
setCurrentWorkspace(w.id);
}}
>
<Button>Create a new workspace</Button>
</main>
);
} }
+2 -2
View File
@@ -16,7 +16,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from "../ui/dropdown-menu"; } from "../ui/dropdown-menu";
import { ChevronDown } from "lucide-react"; import { ChevronDown } from "lucide-react";
import { EntityId, SetState, Workspace } from "@/lib/types"; import { EntityId, Route, SetState, Workspace } from "@/lib/types";
export default function AppSidebar({ export default function AppSidebar({
children, children,
@@ -25,7 +25,7 @@ export default function AppSidebar({
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
workspace: Workspace; workspace: Workspace;
setRoute: SetState<EntityId | undefined>; setRoute: SetState<Route>;
}>) { }>) {
return ( return (
<SidebarProvider> <SidebarProvider>
+3 -3
View File
@@ -9,7 +9,7 @@ import {
} from "../ui/sidebar"; } from "../ui/sidebar";
import { Collapsible, CollapsibleContent } from "../ui/collapsible"; import { Collapsible, CollapsibleContent } from "../ui/collapsible";
import { ChevronDown } from "lucide-react"; import { ChevronDown } from "lucide-react";
import { EntityId, SetState, Workspace } from "@/lib/types"; import { EntityId, Route, SetState, Workspace } from "@/lib/types";
export type SidebarEntity = export type SidebarEntity =
| { | {
@@ -29,7 +29,7 @@ export default function EntitySwitch({
}: { }: {
entity: SidebarEntity; entity: SidebarEntity;
workspace: Workspace; workspace: Workspace;
setRoute: SetState<EntityId | undefined>; setRoute: SetState<Route>;
}) { }) {
if (entity.kind === "group") { if (entity.kind === "group") {
return ( return (
@@ -65,7 +65,7 @@ export default function EntitySwitch({
<SidebarMenuItem> <SidebarMenuItem>
<SidebarMenuButton <SidebarMenuButton
onClick={() => { onClick={() => {
setRoute(entity.id); setRoute({ workspace: workspace.id, entity: entity.id });
}} }}
> >
{workspace.entities[entity.id].name} {workspace.entities[entity.id].name}
+5
View File
@@ -6,6 +6,11 @@ export type EntityId = string;
export type TagId = string; export type TagId = string;
export type TaskId = string; export type TaskId = string;
export interface Route {
workspace?: string;
entity?: EntityId;
}
export interface Workspace { export interface Workspace {
id: string; id: string;
name: string; name: string;