From 8cf2f8ca294e61bdb3ae20a7d3888f2836edc249 Mon Sep 17 00:00:00 2001 From: "Dennis (via Claude+Gemma)" Date: Sat, 23 May 2026 05:34:31 +0200 Subject: [PATCH] =?UTF-8?q?feat(keyboard-shortcuts):=20Cmd/Ctrl-K=20Comman?= =?UTF-8?q?d-Palette=20f=C3=BCr=20Navigation=20[tsc:fail]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .phase6-state.json | 5 +- GENERATION_LOG.md | 14 +++ apps/web/src/components/CommandPalette.tsx | 139 +++++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/components/CommandPalette.tsx diff --git a/.phase6-state.json b/.phase6-state.json index c8d5d67..9c91d7a 100644 --- a/.phase6-state.json +++ b/.phase6-state.json @@ -2,9 +2,10 @@ "completed_features": [ "password-change" ], - "current_feature": "calendar-week-view", + "current_feature": "keyboard-shortcuts", "started_at": "2026-05-23T05:30:16.203066", "attempted_features": [ - "audit-log" + "audit-log", + "calendar-week-view" ] } \ No newline at end of file diff --git a/GENERATION_LOG.md b/GENERATION_LOG.md index d9f088b..ebc9d06 100644 --- a/GENERATION_LOG.md +++ b/GENERATION_LOG.md @@ -692,3 +692,17 @@ src/routes/audit-log.ts(3,10): error TS2724: '"../db/schema"' has no exported me undefined /home/dark/Developer/EmberClone/apps/api:  ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command failed with exit code 2: tsc --noEmit -p tsconfig.json +- `05:33:40` **INFO** Committed feature calendar-week-view +- `05:33:40` **INFO** Pushed: rc=0 + +## Phase-3 Feature: keyboard-shortcuts (2026-05-23 05:33:40) + +- `05:33:40` **INFO** Description: Cmd/Ctrl-K Command-Palette für Navigation +- `05:33:40` **INFO** Generating apps/web/src/components/CommandPalette.tsx (Command-Palette Modal. Trigger: Cmd/Ctrl+K via window-keydown. Zeigt L…) +- `05:34:30` **INFO** wrote 5704 chars in 49.9s (attempt 1) +- `05:34:30` **INFO** Running tsc --noEmit on api… +- `05:34:31` **WARN** tsc errors: +src/routes/audit-log.ts(3,10): error TS2724: '"../db/schema"' has no exported member named 'auditLogs'. Did you mean 'auditLog'? +undefined +/home/dark/Developer/EmberClone/apps/api: + ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL  Command failed with exit code 2: tsc --noEmit -p tsconfig.json diff --git a/apps/web/src/components/CommandPalette.tsx b/apps/web/src/components/CommandPalette.tsx new file mode 100644 index 0000000..3467cc3 --- /dev/null +++ b/apps/web/src/components/CommandPalette.tsx @@ -0,0 +1,139 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { useNavigate } from '@tanstack/react-router'; +import { Search, Command, X } from 'lucide-react'; + +interface CommandItem { + id: string; + label: string; + path: string; + icon: React.ReactNode; + category: string; +} + +const COMMAND_ITEMS: CommandItem[] = [ + { id: 'dash', label: 'Dashboard', path: '/', icon: 📊, category: 'General' }, + { id: 'time', label: 'Time Entries', path: '/time-entries', icon: ⏱️, category: 'Tracking' }, + { id: 'cust', label: 'Customers', path: '/customers', icon: 👥, category: 'CRM' }, + { id: 'proj', label: 'Projects', path: '/projects', icon: 📁, category: 'CRM' }, + { id: 'cal', label: 'Calendar', path: '/calendar', icon: 📅, category: 'General' }, + { id: 'sett', label: 'Settings', path: '/settings', icon: ⚙️, category: 'System' }, + { id: 'prof', label: 'Profile', path: '/profile', icon: 👤, category: 'System' }, +]; + +export default function CommandPalette() { + const [isOpen, setIsOpen] = useState(false); + const [query, setQuery] = useState(''); + const [selectedIndex, setSelectedIndex] = useState(0); + const navigate = useNavigate(); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + setIsOpen((prev) => !prev); + } + if (e.key === 'Escape' && isOpen) { + setIsOpen(false); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [isOpen]); + + const filteredItems = useMemo(() => { + return COMMAND_ITEMS.filter((item) => + item.label.toLowerCase().includes(query.toLowerCase()) + ); + }, [query]); + + useEffect(() => { + setSelectedIndex(0); + }, [query]); + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'ArrowDown') { + e.preventDefault(); + setSelectedIndex((prev) => (prev + 1) % filteredItems.length); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + setSelectedIndex((prev) => (prev - 1 + filteredItems.length) % filteredItems.length); + } else if (e.key === 'Enter' && filteredItems[selectedIndex]) { + const item = filteredItems[selectedIndex]; + navigate({ to: item.path }); + setIsOpen(false); + setQuery(''); + } + }; + + if (!isOpen) return null; + + return ( +
setIsOpen(false)} + > +
e.stopPropagation()} + onKeyDown={handleKeyDown} + > +
+ + setQuery(e.target.value)} + /> +
+ K +
+
+ +
+ {filteredItems.length > 0 ? ( +
+ {filteredItems.map((item, index) => ( +
{ + navigate({ to: item.path }); + setIsOpen(false); + setQuery(''); + }} + > +
+ {item.icon} + {item.label} +
+ {item.category} +
+ ))} +
+ ) : ( +
+ No results found for "{query}" +
+ )} +
+ +
+ Navigation + +
+
+
+ ); +} \ No newline at end of file