diff --git a/packages/app/src/file-pane/find/index.web.tsx b/packages/app/src/file-pane/find/index.web.tsx index 70801aa0ca..6cd27fe9c4 100644 --- a/packages/app/src/file-pane/find/index.web.tsx +++ b/packages/app/src/file-pane/find/index.web.tsx @@ -10,9 +10,11 @@ import { createPortal } from "react-dom"; import { EditorView } from "@codemirror/view"; import { Search } from "lucide-react-native"; import { View } from "react-native"; -import { StyleSheet } from "react-native-unistyles"; +import { StyleSheet, withUnistyles } from "react-native-unistyles"; import { useTranslation } from "react-i18next"; -import { Button } from "@/components/ui/button"; +import { mutedIconColorMapping } from "@/components/ui/icon-button-chrome"; +import { paneContentToolbarIconSize, ToolbarButton } from "@/components/ui/pane-content-toolbar"; +import { useIsCompactFormFactor } from "@/constants/layout"; import { PaneFind, type PaneFindHandle } from "@/pane-find"; import { usePaneFocus } from "@/panels/pane-context"; import { hasActiveWebOverlay } from "@/lib/overlay-root"; @@ -22,6 +24,8 @@ import { FileFindModel } from "./model.web"; export { FileFindModel } from "./model.web"; +const SearchIcon = withUnistyles(Search, mutedIconColorMapping); + export function FileFind({ model, editor, @@ -31,6 +35,7 @@ export function FileFind({ }) { const { t } = useTranslation(); const state = useSyncExternalStore(model.subscribe, model.getSnapshot, model.getSnapshot); + const isCompact = useIsCompactFormFactor(); const widget = useRef(null); const { isInteractive, focusPane } = usePaneFocus(); const active = useRetainedPanelActive(); @@ -75,13 +80,9 @@ export function FileFind({ if (!state.panel) return ( - - - ) : null} + ) : null} + ); }); -const styles = StyleSheet.create((theme) => ({ - widget: { - width: 480, - maxWidth: "100%", - padding: theme.spacing[2], - gap: theme.spacing[1], - backgroundColor: theme.colors.surface1, - borderWidth: theme.borderWidth[1], - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.md, - }, - findRow: { flexDirection: "row", flexWrap: "wrap", alignItems: "center", gap: theme.spacing[1] }, - queryRow: { - flex: 1, - minWidth: 140, - flexDirection: "row", - alignItems: "center", - gap: theme.spacing[1], - }, - row: { flexDirection: "row", alignItems: "center", gap: theme.spacing[1] }, - replacement: { gap: theme.spacing[1] }, - input: { - flex: 1, - minWidth: 0, - minHeight: CONTROL_HEIGHTS.compact, - paddingHorizontal: theme.spacing[2], - paddingVertical: theme.spacing[1], - fontSize: theme.fontSize.base, - color: theme.colors.foreground, - backgroundColor: theme.colors.surface0, - borderWidth: theme.borderWidth[1], - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.sm, - }, - status: { - flex: 1, - color: theme.colors.foregroundMuted, - fontSize: theme.fontSize.sm, - paddingHorizontal: theme.spacing[2], - }, -})); +/** Desktop find rows run one step tighter than a form field — this is pane chrome. */ +const COLLAPSED_FIELD_HEIGHT = 28; +const FIND_WIDGET_WIDTH = 340; + +const styles = StyleSheet.create((theme) => { + const geometry = createControlGeometry(theme); + + return { + widget: { + width: FIND_WIDGET_WIDTH, + maxWidth: "100%", + flexDirection: "row", + padding: theme.spacing[1.5], + gap: theme.spacing[1], + backgroundColor: theme.colors.surface1, + borderWidth: theme.borderWidth[1], + borderColor: theme.colors.border, + borderRadius: theme.borderRadius.lg, + ...theme.shadow.md, + }, + // The disclosure column spans both rows so the two fields share a leading rail. + gutter: { alignItems: "center", justifyContent: "center", flexShrink: 0 }, + rows: { flex: 1, minWidth: 0, gap: theme.spacing[1] }, + row: { flexDirection: "row", alignItems: "center", gap: theme.spacing[1] }, + field: { + flex: 1, + minWidth: 0, + flexDirection: "row", + alignItems: "center", + gap: theme.spacing[2], + paddingHorizontal: theme.spacing[2], + backgroundColor: theme.colors.surface2, + borderRadius: theme.borderRadius.md, + }, + controlRest: { ...geometry.controlRest }, + controlHover: { ...geometry.controlHover }, + controlActive: { ...geometry.controlActive }, + input: { + flex: 1, + minWidth: 0, + paddingHorizontal: 0, + paddingVertical: 0, + color: theme.colors.foreground, + outlineWidth: 0, + outlineColor: "transparent", + ...geometry.fieldTextSm, + }, + status: { + flexShrink: 0, + color: theme.colors.foregroundMuted, + fontSize: theme.fontSize.sm, + }, + // Ghost actions sit on the field's rail, so they carry the field's padding. + replaceAction: { paddingHorizontal: theme.spacing[2] }, + }; +});