@@ -167,6 +167,7 @@ import { CODE_BLOCK_TREE_SITTER_PARSERS } from "./codeBlockParsers";
167167import { resolveTuiPaths } from "./config";
168168import { resolveComposerPrimaryAction } from "./composerAction";
169169import { parseStandaloneComposerModeCommand } from "./composerCommands";
170+ import { isCommandPaletteProjectPathQuery } from "./commandPaletteProjects";
170171import { clampSlashCommandMenuIndex, resolveTuiSlashCommandMenu } from "./composerSlashMenu";
171172import { formatReasoningEffortLabel, truncateToolbarLabel } from "./composerControlLabels";
172173import {
@@ -6059,13 +6060,34 @@ export function App({
60596060 ],
60606061 [commandPaletteProjectItems, commandPaletteStaticItems, commandPaletteThreadItems],
60616062 );
6062- const visibleCommandPaletteItems = useMemo(
6063- () =>
6064- commandPaletteItems
6065- .filter((item) => commandPaletteTextMatches(item, commandPaletteQuery))
6066- .slice(0, COMMAND_PALETTE_MAX_ITEMS),
6067- [commandPaletteItems, commandPaletteQuery],
6068- );
6063+ const commandPaletteProjectPathItem = useMemo<CommandPaletteItem | null>(() => {
6064+ const rawQuery = commandPaletteQuery.trim();
6065+ if (!isCommandPaletteProjectPathQuery(rawQuery)) {
6066+ return null;
6067+ }
6068+
6069+ const workspaceRoot = normalizeWorkspaceRoot(rawQuery, paths.homeDir);
6070+ const existingProject = projects.find((project) => project.workspaceRoot === workspaceRoot);
6071+ return {
6072+ id: "project:path",
6073+ section: "Projects",
6074+ icon: "",
6075+ label: existingProject ? `Open ${existingProject.title}` : "Add project from path",
6076+ description: workspaceRoot,
6077+ trailingLabel: "Enter",
6078+ keywords: ["add project", "workspace", "folder", "path", rawQuery, workspaceRoot],
6079+ disabled: projectPathBusy,
6080+ };
6081+ }, [commandPaletteQuery, paths.homeDir, projectPathBusy, projects]);
6082+ const visibleCommandPaletteItems = useMemo(() => {
6083+ const filteredItems = commandPaletteItems.filter((item) =>
6084+ commandPaletteTextMatches(item, commandPaletteQuery),
6085+ );
6086+ const items = commandPaletteProjectPathItem
6087+ ? [commandPaletteProjectPathItem, ...filteredItems]
6088+ : filteredItems;
6089+ return items.slice(0, COMMAND_PALETTE_MAX_ITEMS);
6090+ }, [commandPaletteItems, commandPaletteProjectPathItem, commandPaletteQuery]);
60696091 const changedSettingLabels = [
60706092 ...(appSettings.theme !== DEFAULT_APP_THEME ? ["Theme"] : []),
60716093 ...(tuiThemeId !== DEFAULT_TUI_THEME_ID ? ["Theme preset"] : []),
@@ -8101,7 +8123,7 @@ export function App({
81018123 key.name === "linefeed"
81028124 ) {
81038125 key.preventDefault();
8104- runCommandPaletteItem(visibleCommandPaletteItems[commandPaletteIndex]);
8126+ void runCommandPaletteItem(visibleCommandPaletteItems[commandPaletteIndex]);
81058127 return;
81068128 }
81078129 }
@@ -9499,13 +9521,47 @@ export function App({
94999521 return projectId;
95009522 }
95019523
9524+ function openProjectChat(projectId: string) {
9525+ const latestThread = threadsByProject.get(projectId)?.[0];
9526+ if (latestThread) {
9527+ clearSelection();
9528+ setSelectionAnchorThreadId(latestThread.id);
9529+ selectThread(projectId, latestThread.id);
9530+ setStatus("Ready");
9531+ return;
9532+ }
9533+
9534+ openDraftThread(projectId);
9535+ }
9536+
9537+ async function submitCommandPaletteProjectPath(rawWorkspaceRoot: string): Promise<void> {
9538+ if (projectPathBusy) return;
9539+ setProjectPathBusy(true);
9540+ setProjectPathError(null);
9541+
9542+ try {
9543+ const projectId = await createProject(rawWorkspaceRoot);
9544+ openProjectChat(projectId);
9545+ setOverlayMenu(null);
9546+ setCommandPaletteQuery("");
9547+ } catch (error) {
9548+ const message =
9549+ error instanceof Error ? error.message : "Failed to add project from that path.";
9550+ setProjectPathError(message);
9551+ setStatus(message);
9552+ } finally {
9553+ setProjectPathBusy(false);
9554+ }
9555+ }
9556+
95029557 async function submitProjectPath(rawWorkspaceRoot: string): Promise<void> {
95039558 if (projectPathBusy) return;
95049559 setProjectPathBusy(true);
95059560 setProjectPathError(null);
95069561
95079562 try {
9508- await createProject(rawWorkspaceRoot);
9563+ const projectId = await createProject(rawWorkspaceRoot);
9564+ openProjectChat(projectId);
95099565 closeProjectPathPrompt();
95109566 } catch (error) {
95119567 const message =
@@ -10433,18 +10489,24 @@ export function App({
1043310489 setOverlayAnchor(null);
1043410490 setCommandPaletteQuery("");
1043510491 setCommandPaletteIndex(0);
10492+ setProjectPathError(null);
1043610493 setFocusArea("settings");
1043710494 } else {
1043810495 setCommandPaletteQuery("");
10496+ setProjectPathError(null);
1043910497 setFocusArea("composer");
1044010498 }
1044110499 logger.log(next ? "overlay.open" : "overlay.close", { menu: "command-palette" });
1044210500 return next;
1044310501 });
1044410502 }
1044510503
10446- function runCommandPaletteItem(item: CommandPaletteItem | undefined) {
10504+ async function runCommandPaletteItem(item: CommandPaletteItem | undefined): Promise<void> {
1044710505 if (!item || item.disabled) return;
10506+ if (item.id === "project:path") {
10507+ await submitCommandPaletteProjectPath(commandPaletteQuery);
10508+ return;
10509+ }
1044810510 setOverlayMenu(null);
1044910511 setCommandPaletteQuery("");
1045010512 if (item.id === "thread:new") {
@@ -16378,6 +16440,7 @@ export function App({
1637816440 onMouseDown={() => {
1637916441 setOverlayMenu(null);
1638016442 setCommandPaletteQuery("");
16443+ setProjectPathError(null);
1638116444 }}
1638216445 >
1638316446 <box
@@ -16418,12 +16481,14 @@ export function App({
1641816481 onInput={(value) => {
1641916482 setCommandPaletteQuery(value);
1642016483 setCommandPaletteIndex(0);
16484+ setProjectPathError(null);
1642116485 }}
1642216486 onKeyDown={(key) => {
1642316487 if (key.name === "escape") {
1642416488 key.preventDefault();
1642516489 setOverlayMenu(null);
1642616490 setCommandPaletteQuery("");
16491+ setProjectPathError(null);
1642716492 }
1642816493 }}
1642916494 style={{
@@ -16463,7 +16528,9 @@ export function App({
1646316528 {...(item.disabled !== undefined ? { disabled: item.disabled } : {})}
1646416529 {...(item.trailingLabel ? { trailingLabel: item.trailingLabel } : {})}
1646516530 onHover={() => setCommandPaletteIndex(index)}
16466- onPress={() => runCommandPaletteItem(item)}
16531+ onPress={() => {
16532+ void runCommandPaletteItem(item);
16533+ }}
1646716534 />
1646816535 {item.description ? (
1646916536 <text
@@ -16478,6 +16545,12 @@ export function App({
1647816545 );
1647916546 })
1648016547 )}
16548+ {projectPathError && commandPaletteProjectPathItem ? (
16549+ <text
16550+ content={truncateTitleForDisplay(projectPathError, commandPaletteWidth - 4)}
16551+ style={{ fg: PALETTE.warning, marginTop: 1, marginLeft: 1 }}
16552+ />
16553+ ) : null}
1648116554 <text
1648216555 content="↑↓ navigate · Enter run · Esc close"
1648316556 style={{ fg: PALETTE.subtle, marginTop: 1, marginLeft: 1 }}
0 commit comments