From 1e347887fe7464102c9cb5f12c80f7fa728a7573 Mon Sep 17 00:00:00 2001 From: Adam Leith Date: Fri, 8 May 2026 07:37:33 +0100 Subject: [PATCH] feat(code): project pickers on quill autocomplete Co-Authored-By: Claude Opus 4.7 (1M context) --- .../features/command/components/Command.css | 21 --- .../features/command/components/Command.tsx | 159 ------------------ .../onboarding/components/ProjectSelect.css | 80 --------- .../onboarding/components/ProjectSelect.tsx | 109 +++++++----- .../sidebar/components/ProjectSwitcher.css | 110 ------------ .../sidebar/components/ProjectSwitcher.tsx | 159 ++++++++++++------ 6 files changed, 172 insertions(+), 466 deletions(-) delete mode 100644 apps/code/src/renderer/features/command/components/Command.css delete mode 100644 apps/code/src/renderer/features/command/components/Command.tsx delete mode 100644 apps/code/src/renderer/features/onboarding/components/ProjectSelect.css delete mode 100644 apps/code/src/renderer/features/sidebar/components/ProjectSwitcher.css diff --git a/apps/code/src/renderer/features/command/components/Command.css b/apps/code/src/renderer/features/command/components/Command.css deleted file mode 100644 index 23f8a48f5..000000000 --- a/apps/code/src/renderer/features/command/components/Command.css +++ /dev/null @@ -1,21 +0,0 @@ -.command-dialog-overlay { - position: fixed; - inset: 0; - z-index: 50; - background-color: rgba(0, 0, 0, 0.2); -} - -.command-dialog-content { - position: fixed; - top: var(--space-9); - left: 50%; - z-index: 50; - transform: translateX(-50%); - width: 640px; - max-width: 90vw; - overflow: hidden; - border-radius: var(--radius-2); - border: 1px solid var(--gray-6); - background-color: var(--gray-1); - box-shadow: var(--shadow-6); -} diff --git a/apps/code/src/renderer/features/command/components/Command.tsx b/apps/code/src/renderer/features/command/components/Command.tsx deleted file mode 100644 index 17117169b..000000000 --- a/apps/code/src/renderer/features/command/components/Command.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { - CommandDialog, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - CommandRoot, -} from "cmdk"; -import React from "react"; -import "./Command.css"; - -type CommandRootProps = React.ComponentPropsWithoutRef & { - className?: string; -}; - -const CommandRootWrapper = React.forwardRef< - React.ElementRef, - CommandRootProps ->(({ className, ...props }, ref) => { - return ( - - ); -}); - -CommandRootWrapper.displayName = "CommandRoot"; - -type CommandInputProps = React.ComponentPropsWithoutRef & { - className?: string; -}; - -const CommandInputWrapper = React.forwardRef< - React.ElementRef, - CommandInputProps ->(({ className, ...props }, ref) => { - return ; -}); - -CommandInputWrapper.displayName = "CommandInput"; - -type CommandListProps = React.ComponentPropsWithoutRef & { - className?: string; -}; - -const CommandListWrapper = React.forwardRef< - React.ElementRef, - CommandListProps ->(({ className, ...props }, ref) => { - return ( - - ); -}); - -CommandListWrapper.displayName = "CommandList"; - -type CommandItemProps = React.ComponentPropsWithoutRef & { - className?: string; -}; - -const CommandItemWrapper = React.forwardRef< - React.ElementRef, - CommandItemProps ->(({ className, ...props }, ref) => { - return ( - - ); -}); - -CommandItemWrapper.displayName = "CommandItem"; - -type CommandGroupProps = React.ComponentPropsWithoutRef & { - className?: string; - heading?: string; -}; - -const CommandGroupWrapper = React.forwardRef< - React.ElementRef, - CommandGroupProps ->(({ className, heading, children, ...props }, ref) => { - return ( - - {heading && ( -
{heading}
- )} - {children} -
- ); -}); - -CommandGroupWrapper.displayName = "CommandGroup"; - -type CommandEmptyProps = React.ComponentPropsWithoutRef & { - className?: string; -}; - -const CommandEmptyWrapper = React.forwardRef< - React.ElementRef, - CommandEmptyProps ->(({ className, ...props }, ref) => { - return ( - - ); -}); - -CommandEmptyWrapper.displayName = "CommandEmpty"; - -type CommandDialogProps = React.ComponentPropsWithoutRef< - typeof CommandDialog -> & { - className?: string; - contentClassName?: string; -}; - -const CommandDialogWrapper = ({ - className, - contentClassName, - children, - ...props -}: CommandDialogProps) => { - return ( - - {children} - - ); -}; - -CommandDialogWrapper.displayName = "CommandDialog"; - -export const Command = { - Root: CommandRootWrapper, - Dialog: CommandDialogWrapper, - Input: CommandInputWrapper, - List: CommandListWrapper, - Item: CommandItemWrapper, - Group: CommandGroupWrapper, - Empty: CommandEmptyWrapper, -}; diff --git a/apps/code/src/renderer/features/onboarding/components/ProjectSelect.css b/apps/code/src/renderer/features/onboarding/components/ProjectSelect.css deleted file mode 100644 index 3a6910188..000000000 --- a/apps/code/src/renderer/features/onboarding/components/ProjectSelect.css +++ /dev/null @@ -1,80 +0,0 @@ -.project-select-popover [cmdk-root] { - width: 100%; - background: var(--color-panel-solid); - border-radius: var(--radius-3); - border: 1px solid var(--gray-6); - box-shadow: var(--shadow-6); - overflow: hidden; -} - -.project-select-popover [cmdk-input] { - font-family: var(--default-font-family); - font-size: 13px; - padding: 12px 16px; - width: 100%; - background: transparent; - border: none; - outline: none; - color: var(--gray-12); - caret-color: var(--accent-9); -} - -.project-select-popover [cmdk-input]::placeholder { - color: var(--gray-9); -} - -.project-select-popover [cmdk-list] { - max-height: 240px; - overflow-y: auto; - overflow-x: hidden; - overscroll-behavior: contain; - transition: height 150ms ease; -} - -.project-select-popover [cmdk-list]::-webkit-scrollbar { - width: 8px; -} - -.project-select-popover [cmdk-list]::-webkit-scrollbar-track { - background: transparent; -} - -.project-select-popover [cmdk-list]::-webkit-scrollbar-thumb { - background: var(--gray-6); - border-radius: 4px; -} - -.project-select-popover [cmdk-list]::-webkit-scrollbar-thumb:hover { - background: var(--gray-7); -} - -.project-select-popover [cmdk-item] { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 12px; - border-radius: var(--radius-2); - cursor: pointer; - user-select: none; - transition: background-color 150ms ease; - color: var(--gray-12); - margin: 0 4px; -} - -.project-select-popover [cmdk-item][data-selected="true"] { - background: var(--accent-3); - color: var(--accent-11); -} - -.project-select-popover [cmdk-item]:active { - background: var(--accent-4); -} - -.project-select-popover [cmdk-empty] { - display: flex; - align-items: center; - justify-content: center; - height: 64px; - font-size: 13px; - color: var(--gray-9); -} diff --git a/apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx b/apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx index 2fa0611c2..42ce7f436 100644 --- a/apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx +++ b/apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx @@ -1,8 +1,13 @@ -import { Command } from "@features/command/components/Command"; import { Check } from "@phosphor-icons/react"; -import { Flex, Popover, Text } from "@radix-ui/themes"; +import { + Autocomplete, + AutocompleteInput, + AutocompleteItem, + AutocompleteList, + AutocompleteStatus, +} from "@posthog/quill"; +import { Popover, Text } from "@radix-ui/themes"; import { useState } from "react"; -import "./ProjectSelect.css"; interface ProjectSelectProps { projectId: number; @@ -13,6 +18,8 @@ interface ProjectSelectProps { size?: "1" | "2"; } +type ProjectInfo = { id: number; name: string }; + export function ProjectSelect({ projectId, projectName, @@ -22,13 +29,22 @@ export function ProjectSelect({ size = "2", }: ProjectSelectProps) { const [open, setOpen] = useState(false); - const currentProject = projects.find((p) => p.id === projectId); - const defaultValue = currentProject - ? `${currentProject.name} ${currentProject.id}` - : undefined; - const [highlightedValue, setHighlightedValue] = useState(defaultValue); + const [query, setQuery] = useState(""); const sizeClass = size === "1" ? "text-[13px]" : "text-sm"; + const handleOpenChange = (nextOpen: boolean) => { + setOpen(nextOpen); + if (!nextOpen) setQuery(""); + }; + + const handleSelect = (id: string | null) => { + if (id === null) return; + const next = Number(id); + if (Number.isNaN(next)) return; + onProjectChange(next); + setOpen(false); + }; + if (projects.length <= 1) { return ( @@ -43,15 +59,7 @@ export function ProjectSelect({ {projectName} {" · "} - { - setOpen(nextOpen); - if (nextOpen) { - setHighlightedValue(defaultValue); - } - }} - > +