| 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,6 +150,7 @@ export function buildProjectActionItems(input: { |
| 150 | icon: (project: Project) => ReactNode; | 150 | icon: (project: Project) => ReactNode; |
| 151 | runProject: (project: Project) => Promise<void>; | 151 | runProject: (project: Project) => Promise<void>; |
| 152 | searchTerms?: (project: Project) => ReadonlyArray<string>; | 152 | searchTerms?: (project: Project) => ReadonlyArray<string>; |
| 153 | renderDescription?: (project: Project) => ReactNode; | ||
| 153 | shortcutCommand?: KeybindingCommand; | 154 | shortcutCommand?: KeybindingCommand; |
| 154 | }): CommandPaletteActionItem[] { | 155 | }): CommandPaletteActionItem[] { |
| 155 | return input.projects.map((project) => ({ | 156 | return input.projects.map((project) => ({ |
| ... | @@ -157,7 +158,7 @@ export function buildProjectActionItems(input: { | ... | @@ -157,7 +158,7 @@ export function buildProjectActionItems(input: { |
| 157 | value: `${input.valuePrefix}:${project.environmentId}:${project.id}`, | 158 | value: `${input.valuePrefix}:${project.environmentId}:${project.id}`, |
| 158 | searchTerms: [project.title, project.workspaceRoot, ...(input.searchTerms?.(project) ?? [])], | 159 | searchTerms: [project.title, project.workspaceRoot, ...(input.searchTerms?.(project) ?? [])], |
| 159 | title: project.title, | 160 | title: project.title, |
| 160 | description: project.workspaceRoot, | 161 | description: input.renderDescription?.(project) ?? project.workspaceRoot, |
| 161 | icon: input.icon(project), | 162 | icon: input.icon(project), |
| 162 | ...(input.shortcutCommand !== undefined ? { shortcutCommand: input.shortcutCommand } : {}), | 163 | ...(input.shortcutCommand !== undefined ? { shortcutCommand: input.shortcutCommand } : {}), |
| 163 | run: async () => { | 164 | run: async () => { |
apps/web/src/components/CommandPalette.tsx+56-3| ... | @@ -41,6 +41,7 @@ import { | ... | @@ -41,6 +41,7 @@ import { |
| 41 | LinkIcon, | 41 | LinkIcon, |
| 42 | MessageSquareIcon, | 42 | MessageSquareIcon, |
| 43 | PaletteIcon, | 43 | PaletteIcon, |
| 44 | ServerIcon, | ||
| 44 | SettingsIcon, | 45 | SettingsIcon, |
| 45 | SquarePenIcon, | 46 | SquarePenIcon, |
| 46 | TextSearchIcon, | 47 | TextSearchIcon, |
| ... | @@ -131,7 +132,11 @@ import { ProjectFavicon } from "./ProjectFavicon"; | ... | @@ -131,7 +132,11 @@ import { ProjectFavicon } from "./ProjectFavicon"; |
| 131 | import { ProjectFilePicker } from "./files/ProjectFilePicker"; | 132 | import { ProjectFilePicker } from "./files/ProjectFilePicker"; |
| 132 | import { ProjectContentSearchDialog } from "./search/ProjectContentSearchDialog"; | 133 | import { ProjectContentSearchDialog } from "./search/ProjectContentSearchDialog"; |
| 133 | import { toggleThemeEditorForTheme } from "./settings/themeEditorStore"; | 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 | import { ThreadRowLeadingStatus, ThreadRowTrailingStatus } from "./ThreadStatusIndicators"; | 140 | import { ThreadRowLeadingStatus, ThreadRowTrailingStatus } from "./ThreadStatusIndicators"; |
| 136 | import { primaryServerKeybindingsAtom, primaryServerProvidersAtom } from "../state/server"; | 141 | import { primaryServerKeybindingsAtom, primaryServerProvidersAtom } from "../state/server"; |
| 137 | import { | 142 | import { |
| ... | @@ -657,6 +662,27 @@ function OpenCommandPaletteDialog(props: { | ... | @@ -657,6 +662,27 @@ function OpenCommandPaletteDialog(props: { |
| 657 | ), | 662 | ), |
| 658 | [environments], | 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 | const orderedProjects = useMemo( | 686 | const orderedProjects = useMemo( |
| 661 | () => | 687 | () => |
| 662 | orderItemsByPreferredIds({ | 688 | orderItemsByPreferredIds({ |
| ... | @@ -1011,8 +1037,29 @@ function OpenCommandPaletteDialog(props: { | ... | @@ -1011,8 +1037,29 @@ function OpenCommandPaletteDialog(props: { |
| 1011 | valuePrefix: "new-thread-in", | 1037 | valuePrefix: "new-thread-in", |
| 1012 | searchTerms: (project) => { | 1038 | searchTerms: (project) => { |
| 1013 | const group = projectGroupByTargetKey.get(`${project.environmentId}:${project.id}`); | 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 | return ( | 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 | icon: projectFavicon, | 1065 | icon: projectFavicon, |
| ... | @@ -1033,7 +1080,13 @@ function OpenCommandPaletteDialog(props: { | ... | @@ -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 | const allThreadItems = useMemo( | 1092 | const allThreadItems = useMemo( |
apps/web/src/components/ThreadCommandSubtitle.tsx+8-8| ... | @@ -18,20 +18,20 @@ export type ThreadCommandSubtitleVariant = | ... | @@ -18,20 +18,20 @@ export type ThreadCommandSubtitleVariant = |
| 18 | export const THREAD_COMMAND_SUBTITLE_VARIANT: ThreadCommandSubtitleVariant = | 18 | export const THREAD_COMMAND_SUBTITLE_VARIANT: ThreadCommandSubtitleVariant = |
| 19 | "favicon-workspace-harness"; | 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 | return <span className="shrink-0 text-muted-foreground/50">·</span>; | 24 | return <span className="shrink-0 text-muted-foreground/50">·</span>; |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | function WorkspaceIcon(props: { variant: ThreadCommandSubtitleVariant; isWorktree: boolean }) { | 27 | function WorkspaceIcon(props: { variant: ThreadCommandSubtitleVariant; isWorktree: boolean }) { |
| 28 | if (props.isWorktree) { | 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 | if (props.variant === "favicon-branch-harness") { | 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 | export function ThreadCommandSubtitle(props: { | 37 | export function ThreadCommandSubtitle(props: { |
| ... | @@ -82,7 +82,7 @@ export function ThreadCommandSubtitle(props: { | ... | @@ -82,7 +82,7 @@ export function ThreadCommandSubtitle(props: { |
| 82 | 82 | ||
| 83 | {branchLabel ? ( | 83 | {branchLabel ? ( |
| 84 | <> | 84 | <> |
| 85 | {projectLabel ? <Dot /> : null} | 85 | {projectLabel ? <CommandPaletteMetaDot /> : null} |
| 86 | <span className="inline-flex min-w-0 items-center gap-1"> | 86 | <span className="inline-flex min-w-0 items-center gap-1"> |
| 87 | <WorkspaceIcon variant={variant} isWorktree={isWorktree} /> | 87 | <WorkspaceIcon variant={variant} isWorktree={isWorktree} /> |
| 88 | <span className="min-w-0 truncate">{branchLabel}</span> | 88 | <span className="min-w-0 truncate">{branchLabel}</span> |
| ... | @@ -92,7 +92,7 @@ export function ThreadCommandSubtitle(props: { | ... | @@ -92,7 +92,7 @@ export function ThreadCommandSubtitle(props: { |
| 92 | 92 | ||
| 93 | {showHarness && props.driverKind ? ( | 93 | {showHarness && props.driverKind ? ( |
| 94 | <> | 94 | <> |
| 95 | {projectLabel || branchLabel ? <Dot /> : null} | 95 | {projectLabel || branchLabel ? <CommandPaletteMetaDot /> : null} |
| 96 | <ProviderInstanceIcon | 96 | <ProviderInstanceIcon |
| 97 | driverKind={props.driverKind} | 97 | driverKind={props.driverKind} |
| 98 | displayName={props.providerDisplayName ?? props.driverKind} | 98 | displayName={props.providerDisplayName ?? props.driverKind} |
| ... | @@ -103,7 +103,7 @@ export function ThreadCommandSubtitle(props: { | ... | @@ -103,7 +103,7 @@ export function ThreadCommandSubtitle(props: { |
| 103 | 103 | ||
| 104 | {props.isCurrent ? ( | 104 | {props.isCurrent ? ( |
| 105 | <> | 105 | <> |
| 106 | {projectLabel || branchLabel || showHarness ? <Dot /> : null} | 106 | {projectLabel || branchLabel || showHarness ? <CommandPaletteMetaDot /> : null} |
| 107 | <span className="shrink-0">Current thread</span> | 107 | <span className="shrink-0">Current thread</span> |
| 108 | </> | 108 | </> |
| 109 | ) : null} | 109 | ) : null} |