| author | |
| committer | |
| log | fda740ad7b28334a6c579d801db22ad8f5914b5f |
| tree | 3ac73673805d905428e5c15da15e819d8127293d |
| parent | 3b5d476eb6af9d14efe037e9d163848d6fd048d7 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>3 files changed, 66 insertions(+), 12 deletions(-)
apps/web/src/components/CommandPalette.logic.ts+2-1| ... | ... | @@ -150,6 +150,7 @@ export function buildProjectActionItems(input: { |
| 150 | 150 | icon: (project: Project) => ReactNode; |
| 151 | 151 | runProject: (project: Project) => Promise<void>; |
| 152 | 152 | searchTerms?: (project: Project) => ReadonlyArray<string>; |
| 153 | renderDescription?: (project: Project) => ReactNode; | |
| 153 | 154 | shortcutCommand?: KeybindingCommand; |
| 154 | 155 | }): CommandPaletteActionItem[] { |
| 155 | 156 | return input.projects.map((project) => ({ |
| ... | ... | @@ -157,7 +158,7 @@ export function buildProjectActionItems(input: { |
| 157 | 158 | value: `${input.valuePrefix}:${project.environmentId}:${project.id}`, |
| 158 | 159 | searchTerms: [project.title, project.workspaceRoot, ...(input.searchTerms?.(project) ?? [])], |
| 159 | 160 | title: project.title, |
| 160 | description: project.workspaceRoot, | |
| 161 | description: input.renderDescription?.(project) ?? project.workspaceRoot, | |
| 161 | 162 | icon: input.icon(project), |
| 162 | 163 | ...(input.shortcutCommand !== undefined ? { shortcutCommand: input.shortcutCommand } : {}), |
| 163 | 164 | run: async () => { |
apps/web/src/components/CommandPalette.tsx+56-3| ... | ... | @@ -41,6 +41,7 @@ import { |
| 41 | 41 | LinkIcon, |
| 42 | 42 | MessageSquareIcon, |
| 43 | 43 | PaletteIcon, |
| 44 | ServerIcon, | |
| 44 | 45 | SettingsIcon, |
| 45 | 46 | SquarePenIcon, |
| 46 | 47 | TextSearchIcon, |
| ... | ... | @@ -131,7 +132,11 @@ import { ProjectFavicon } from "./ProjectFavicon"; |
| 131 | 132 | import { ProjectFilePicker } from "./files/ProjectFilePicker"; |
| 132 | 133 | import { ProjectContentSearchDialog } from "./search/ProjectContentSearchDialog"; |
| 133 | 134 | import { toggleThemeEditorForTheme } from "./settings/themeEditorStore"; |
| 134 | import { ThreadCommandSubtitle } from "./ThreadCommandSubtitle"; | |
| 135 | import { | |
| 136 | COMMAND_PALETTE_META_ICON_CLASS, | |
| 137 | CommandPaletteMetaDot, | |
| 138 | ThreadCommandSubtitle, | |
| 139 | } from "./ThreadCommandSubtitle"; | |
| 135 | 140 | import { ThreadRowLeadingStatus, ThreadRowTrailingStatus } from "./ThreadStatusIndicators"; |
| 136 | 141 | import { primaryServerKeybindingsAtom, primaryServerProvidersAtom } from "../state/server"; |
| 137 | 142 | import { |
| ... | ... | @@ -657,6 +662,27 @@ function OpenCommandPaletteDialog(props: { |
| 657 | 662 | ), |
| 658 | 663 | [environments], |
| 659 | 664 | ); |
| 665 | const projectEnvironmentLocationById = useMemo( | |
| 666 | () => | |
| 667 | new Map( | |
| 668 | environments.map((environment) => { | |
| 669 | const isPrimary = environment.entry.target._tag === "PrimaryConnectionTarget"; | |
| 670 | const isLocal = isPrimary || isDesktopLocalConnectionTarget(environment.entry.target); | |
| 671 | return [ | |
| 672 | environment.environmentId, | |
| 673 | { | |
| 674 | kind: isLocal ? "local" : "remote", | |
| 675 | label: isPrimary | |
| 676 | ? "Local" | |
| 677 | : isLocal | |
| 678 | ? `${environment.label} (Local)` | |
| 679 | : environment.label, | |
| 680 | }, | |
| 681 | ] as const; | |
| 682 | }), | |
| 683 | ), | |
| 684 | [environments], | |
| 685 | ); | |
| 660 | 686 | const orderedProjects = useMemo( |
| 661 | 687 | () => |
| 662 | 688 | orderItemsByPreferredIds({ |
| ... | ... | @@ -1011,8 +1037,29 @@ function OpenCommandPaletteDialog(props: { |
| 1011 | 1037 | valuePrefix: "new-thread-in", |
| 1012 | 1038 | searchTerms: (project) => { |
| 1013 | 1039 | const group = projectGroupByTargetKey.get(`${project.environmentId}:${project.id}`); |
| 1040 | const location = projectEnvironmentLocationById.get(project.environmentId); | |
| 1041 | return [ | |
| 1042 | ...(group?.memberProjects.flatMap((member) => [member.title, member.workspaceRoot]) ?? | |
| 1043 | []), | |
| 1044 | ...(location ? [location.label] : []), | |
| 1045 | ]; | |
| 1046 | }, | |
| 1047 | renderDescription: (project) => { | |
| 1048 | const location = projectEnvironmentLocationById.get(project.environmentId) ?? { | |
| 1049 | kind: "remote", | |
| 1050 | label: "Remote", | |
| 1051 | }; | |
| 1014 | 1052 | return ( |
| 1015 | group?.memberProjects.flatMap((member) => [member.title, member.workspaceRoot]) ?? [] | |
| 1053 | <span className="flex min-w-0 items-center gap-1"> | |
| 1054 | <span className="inline-flex min-w-0 items-center gap-1"> | |
| 1055 | {location.kind === "remote" ? ( | |
| 1056 | <ServerIcon aria-hidden className={COMMAND_PALETTE_META_ICON_CLASS} /> | |
| 1057 | ) : null} | |
| 1058 | <span className="truncate">{location.label}</span> | |
| 1059 | </span> | |
| 1060 | <CommandPaletteMetaDot /> | |
| 1061 | <span className="truncate">{project.workspaceRoot}</span> | |
| 1062 | </span> | |
| 1016 | 1063 | ); |
| 1017 | 1064 | }, |
| 1018 | 1065 | icon: projectFavicon, |
| ... | ... | @@ -1033,7 +1080,13 @@ function OpenCommandPaletteDialog(props: { |
| 1033 | 1080 | }, |
| 1034 | 1081 | }), |
| 1035 | 1082 | ), |
| 1036 | [contextualProjectRef, handleNewThread, pickerProjects, projectGroupByTargetKey], | |
| 1083 | [ | |
| 1084 | contextualProjectRef, | |
| 1085 | handleNewThread, | |
| 1086 | pickerProjects, | |
| 1087 | projectEnvironmentLocationById, | |
| 1088 | projectGroupByTargetKey, | |
| 1089 | ], | |
| 1037 | 1090 | ); |
| 1038 | 1091 | |
| 1039 | 1092 | const allThreadItems = useMemo( |
apps/web/src/components/ThreadCommandSubtitle.tsx+8-8| ... | ... | @@ -18,20 +18,20 @@ export type ThreadCommandSubtitleVariant = |
| 18 | 18 | export const THREAD_COMMAND_SUBTITLE_VARIANT: ThreadCommandSubtitleVariant = |
| 19 | 19 | "favicon-workspace-harness"; |
| 20 | 20 | |
| 21 | const META_ICON_CLASS = "size-3 shrink-0 text-muted-foreground/70"; | |
| 21 | export const COMMAND_PALETTE_META_ICON_CLASS = "size-3 shrink-0 text-muted-foreground/70"; | |
| 22 | 22 | |
| 23 | function Dot() { | |
| 23 | export function CommandPaletteMetaDot() { | |
| 24 | 24 | return <span className="shrink-0 text-muted-foreground/50">·</span>; |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | 27 | function WorkspaceIcon(props: { variant: ThreadCommandSubtitleVariant; isWorktree: boolean }) { |
| 28 | 28 | if (props.isWorktree) { |
| 29 | return <FolderGit2Icon className={META_ICON_CLASS} aria-hidden />; | |
| 29 | return <FolderGit2Icon className={COMMAND_PALETTE_META_ICON_CLASS} aria-hidden />; | |
| 30 | 30 | } |
| 31 | 31 | if (props.variant === "favicon-branch-harness") { |
| 32 | return <GitBranchIcon className={META_ICON_CLASS} aria-hidden />; | |
| 32 | return <GitBranchIcon className={COMMAND_PALETTE_META_ICON_CLASS} aria-hidden />; | |
| 33 | 33 | } |
| 34 | return <FolderIcon className={META_ICON_CLASS} aria-hidden />; | |
| 34 | return <FolderIcon className={COMMAND_PALETTE_META_ICON_CLASS} aria-hidden />; | |
| 35 | 35 | } |
| 36 | 36 | |
| 37 | 37 | export function ThreadCommandSubtitle(props: { |
| ... | ... | @@ -82,7 +82,7 @@ export function ThreadCommandSubtitle(props: { |
| 82 | 82 | |
| 83 | 83 | {branchLabel ? ( |
| 84 | 84 | <> |
| 85 | {projectLabel ? <Dot /> : null} | |
| 85 | {projectLabel ? <CommandPaletteMetaDot /> : null} | |
| 86 | 86 | <span className="inline-flex min-w-0 items-center gap-1"> |
| 87 | 87 | <WorkspaceIcon variant={variant} isWorktree={isWorktree} /> |
| 88 | 88 | <span className="min-w-0 truncate">{branchLabel}</span> |
| ... | ... | @@ -92,7 +92,7 @@ export function ThreadCommandSubtitle(props: { |
| 92 | 92 | |
| 93 | 93 | {showHarness && props.driverKind ? ( |
| 94 | 94 | <> |
| 95 | {projectLabel || branchLabel ? <Dot /> : null} | |
| 95 | {projectLabel || branchLabel ? <CommandPaletteMetaDot /> : null} | |
| 96 | 96 | <ProviderInstanceIcon |
| 97 | 97 | driverKind={props.driverKind} |
| 98 | 98 | displayName={props.providerDisplayName ?? props.driverKind} |
| ... | ... | @@ -103,7 +103,7 @@ export function ThreadCommandSubtitle(props: { |
| 103 | 103 | |
| 104 | 104 | {props.isCurrent ? ( |
| 105 | 105 | <> |
| 106 | {projectLabel || branchLabel || showHarness ? <Dot /> : null} | |
| 106 | {projectLabel || branchLabel || showHarness ? <CommandPaletteMetaDot /> : null} | |
| 107 | 107 | <span className="shrink-0">Current thread</span> |
| 108 | 108 | </> |
| 109 | 109 | ) : null} |