diff --git a/apps/web/src/components/RightPanelTabs.tsx b/apps/web/src/components/RightPanelTabs.tsx index 83524009c19..fe652b6fde7 100644 --- a/apps/web/src/components/RightPanelTabs.tsx +++ b/apps/web/src/components/RightPanelTabs.tsx @@ -358,6 +358,7 @@ export function RightPanelTabs(props: RightPanelTabsProps) {
void }) { + return ( + + + } + > + + + {props.isPending ? "Refreshing…" : "Refresh files"} + + ); +} + +function FileSearchField(props: { + ariaLabel: string; + name: string; + onClose: () => void; + onValueChange: (value: string) => void; + value: string; +}) { + return ( + + props.onValueChange(event.target.value)} + onKeyDown={(event) => { + if (event.key !== "Escape") return; + props.onClose(); + event.currentTarget.blur(); + }} + /> + + ); +} + export default function FileBrowserPanel({ environmentId, cwd, @@ -176,9 +228,17 @@ export default function FileBrowserPanel({ } }, paths: [], - search: true, + search: false, unsafeCSS: TREE_UNSAFE_CSS, }); + const search = useFileTreeSearch(model); + const handleSearchValueChange = (value: string) => { + if (value.trim().length === 0) { + search.close(); + return; + } + search.setValue(value); + }; useEffect(() => { if (previousTreePathsRef.current === treePaths) return; @@ -187,11 +247,6 @@ export default function FileBrowserPanel({ model.resetPaths(treePaths); }, [entryKinds, model, treePaths]); - const fileCount = useMemo( - () => entries.reduce((count, entry) => count + (entry.kind === "file" ? 1 : 0), 0), - [entries], - ); - // Tag tree drags with the composer mention payload. The row is read from // the composed event path (the tree's shadow root is open), so this does // not depend on running after the tree's own dragstart handler; the drag @@ -223,32 +278,15 @@ export default function FileBrowserPanel({ className="flex min-h-0 flex-1 flex-col bg-background" data-file-browser-panel={`${environmentId}:${cwd}`} > -
-
-
{projectName}
-
- {entriesQuery.isPending && entriesQuery.data === null - ? "Indexing…" - : `${fileCount.toLocaleString()} files`} - {entriesQuery.data?.truncated ? " · partial" : ""} -
-
- - +
+ +
{entriesQuery.error && entriesQuery.data === null ? (
{entriesQuery.error}
diff --git a/apps/web/src/components/preview/PreviewChromeRow.tsx b/apps/web/src/components/preview/PreviewChromeRow.tsx index 8044b472264..1f4eb95d62b 100644 --- a/apps/web/src/components/preview/PreviewChromeRow.tsx +++ b/apps/web/src/components/preview/PreviewChromeRow.tsx @@ -166,7 +166,7 @@ export function PreviewChromeRow({
- + ) { +const inputGroupVariants = cva( + "relative inline-flex w-full min-w-0 items-center rounded-lg border text-base text-foreground ring-ring/24 transition-shadow has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/64 has-[input:focus-visible,textarea:focus-visible]:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/16 has-[textarea]:h-auto has-data-[align=block-end]:h-auto has-data-[align=block-start]:h-auto has-data-[align=block-end]:flex-col has-data-[align=block-start]:flex-col has-[input:focus-visible,textarea:focus-visible]:border-ring has-[input[aria-invalid],textarea[aria-invalid]]:border-destructive/36 has-autofill:bg-foreground/4 has-[input:disabled,textarea:disabled]:opacity-64 has-[input:disabled,textarea:disabled,input:focus-visible,textarea:focus-visible,input[aria-invalid],textarea[aria-invalid]]:shadow-none has-[input:focus-visible,textarea:focus-visible]:ring-[3px] sm:text-sm dark:has-autofill:bg-foreground/8 dark:has-[input[aria-invalid],textarea[aria-invalid]]:ring-destructive/24 has-data-[align=inline-start]:**:[[data-size=sm]_input]:ps-1.5 has-data-[align=inline-end]:**:[[data-size=sm]_input]:pe-1.5 *:[[data-slot=input-control],[data-slot=textarea-control]]:contents *:[[data-slot=input-control],[data-slot=textarea-control]]:before:hidden has-[[data-align=block-start],[data-align=block-end]]:**:[input]:h-auto has-data-[align=inline-start]:**:[input]:ps-2 has-data-[align=inline-end]:**:[input]:pe-2 has-data-[align=block-end]:**:[input]:pt-1.5 has-data-[align=block-start]:**:[input]:pb-1.5 **:[textarea]:min-h-20.5 **:[textarea]:resize-none **:[textarea]:py-[calc(--spacing(3)-1px)] **:[textarea]:max-sm:min-h-23.5 **:[textarea_button]:rounded-[calc(var(--radius-md)-1px)]", + { + defaultVariants: { + variant: "default", + }, + variants: { + variant: { + default: + "border-input bg-background not-dark:bg-clip-padding shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-has-[input:disabled,textarea:disabled]:not-has-[input:focus-visible,textarea:focus-visible]:not-has-[input[aria-invalid],textarea[aria-invalid]]:before:shadow-[0_1px_--theme(--color-black/4%)] dark:bg-input/32 dark:not-has-[input:disabled,textarea:disabled]:not-has-[input:focus-visible,textarea:focus-visible]:not-has-[input[aria-invalid],textarea[aria-invalid]]:before:shadow-[0_-1px_--theme(--color-white/6%)]", + ghost: + "border-transparent bg-transparent shadow-none hover:bg-muted/40 has-[input:focus-visible,textarea:focus-visible]:bg-background", + }, + }, + }, +); + +function InputGroup({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { return (