Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions src/components/mobile/MobileFilterBar.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,53 @@
import type { ReactNode } from "react";
import { Icon } from "@iconify/react";
import { useTranslation } from "react-i18next";

/**
* Search / filter field for mobile lists. `pill` is the M3 search bar (48 dp, fully
* rounded, tonal fill) used under a large top app bar; `attr` names the data hooks
* (`data-<attr>` on the input, `data-<attr>-clear` on the clear button).
* The one search / filter field for mobile: the M3 search bar (48 dp, fully rounded,
* tonal fill, no outline). Every mobile search uses it — top-level lists, sheets and
* plugin screens — so search always reads as the same control, distinct from the 8 px
* outlined form fields. `inset` is the padding around the bar (sheets bring their own
* gutter); `attr` names the data hooks (`data-<attr>` on the input, `data-<attr>-clear`
* on the clear button); `trailing` sits before the clear button (e.g. a result count).
*/
export default function MobileFilterBar({
value, onChange, placeholder, pill = false, attr = "mobile-filter", clearLabel, className = "",
value, onChange, placeholder, attr = "mobile-filter", clearLabel, autoFocus, trailing,
inset = "px-4 py-2", className = "",
}: {
value: string;
onChange: (v: string) => void;
placeholder?: string;
pill?: boolean;
attr?: string;
clearLabel?: string;
autoFocus?: boolean;
trailing?: ReactNode;
inset?: string;
className?: string;
}) {
const { t } = useTranslation();
const effectivePlaceholder = placeholder ?? t("mobile.filterBar.placeholder");
return (
<div className={`${pill ? "px-4 py-2" : "px-3 py-2"} shrink-0 ${className}`}>
<div
className={pill ? "flex items-center gap-3 px-4 h-12 rounded-full" : "flex items-center gap-2 px-3 h-12 rounded-xl"}
style={pill ? { background: "var(--t-bg-card)" } : { background: "var(--t-bg-card)", border: "1px solid var(--t-border)" }}
>
<Icon icon="lucide:search" width={pill ? 20 : 16} className="text-(--t-text-dim) shrink-0" />
<div className={`${inset} shrink-0 ${className}`}>
<div className="flex items-center gap-3 px-4 h-12 rounded-full" style={{ background: "var(--t-bg-card)" }}>
<Icon icon="lucide:search" width={20} className="text-(--t-text-dim) shrink-0" />
<input
{...{ [`data-${attr}`]: "" }}
autoFocus={autoFocus}
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={effectivePlaceholder}
aria-label={effectivePlaceholder}
className={`flex-1 min-w-0 self-stretch bg-transparent outline-none ${pill ? "text-base" : "text-sm"} text-(--t-text-primary) placeholder:text-(--t-text-dim)`}
className="flex-1 min-w-0 self-stretch bg-transparent outline-none text-base text-(--t-text-primary) placeholder:text-(--t-text-dim)"
/>
{trailing}
{value && (
<button
{...{ [`data-${attr}-clear`]: "" }}
onClick={() => onChange("")}
aria-label={clearLabel}
aria-label={clearLabel ?? t("mobile.hostsScreen.clearSearchAriaLabel")}
className="vt-icon-btn -mr-3 text-(--t-text-dim) active:text-(--t-text-primary)"
>
<Icon icon="lucide:x" width={pill ? 20 : 16} />
<Icon icon="lucide:x" width={20} />
</button>
)}
</div>
Expand Down
1 change: 0 additions & 1 deletion src/components/mobile/screens/MobileHostsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ export default function MobileHostsScreen() {
<div data-mobile-hosts-scroll className="flex-1 overflow-y-auto overflow-x-hidden scrollbar-none" style={{ overscrollBehavior: "contain" }} onScroll={onScroll}>
<MobileLargeTitle ref={titleRef} />
<MobileFilterBar
pill
attr="mobile-host-search"
value={search}
onChange={setSearch}
Expand Down
6 changes: 3 additions & 3 deletions src/components/mobile/sheets/HostPickerSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useHostPingStore } from "@/stores/hostPingStore";
import { useToggle } from "@/stores/toggleSettingsStore";
import type { Connection } from "@/types";
import BottomSheet from "./BottomSheet";
import MobileFilterBar from "../MobileFilterBar";

function PickRow({ c, attr, pingEnabled, onPick }: { c: Connection; attr: string; pingEnabled: boolean; onPick: (id: string) => void }) {
const pingStatus = useHostPingStore((s) => s.statuses[c.id]);
Expand Down Expand Up @@ -62,9 +63,8 @@ export default function HostPickerSheet({

return (
<BottomSheet title={title} onClose={onClose}>
<input {...{ [`data-${attr}-search`]: "" }} autoFocus={autoFocusSearch} value={q} onChange={(e) => setQ(e.target.value)} placeholder={t("mobile.hostsScreen.searchPlaceholder")}
className="w-full rounded-xl px-3 h-12 text-sm outline-none text-(--t-text-primary) mb-2"
style={{ background: "var(--t-bg-card)", border: "1px solid var(--t-border)" }} />
<MobileFilterBar attr={`${attr}-search`} autoFocus={autoFocusSearch} value={q} onChange={setQ} inset="px-1 pb-2"
placeholder={t("mobile.hostsScreen.searchPlaceholder")} />
{vaults.length > 1 && (
<div className="flex gap-1.5 overflow-x-auto -mt-2 py-2 -mx-1 px-1">
{[{ id: null, name: t("shared.hostPicker.allVaults") }, ...vaults].map((v) => (
Expand Down
9 changes: 2 additions & 7 deletions src/components/mobile/sheets/MobileSnippetTargetSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useEffect } from "react";
import { Icon } from "@iconify/react";
import { useTranslation } from "react-i18next";
import BottomSheet from "./BottomSheet";
import MobileFilterBar from "../MobileFilterBar";
import { useMobileNavStore } from "@/stores/mobileNavStore";
import { useSnippetStore } from "@/stores/snippetStore";
import { useAllSnippets } from "@/hooks/useAllSnippets";
Expand Down Expand Up @@ -81,13 +82,7 @@ export default function MobileSnippetTargetSheet(

return (
<BottomSheet title={t(execute ? "mobile.sheets.snippetTarget.executeTitle" : "mobile.sheets.snippetTarget.insertTitle", { name: snippet.name })} onClose={closeSheet}>
<div className="px-3 pb-2">
<div className="flex items-center gap-2 rounded-xl px-3 h-10" style={{ background: "var(--t-bg-card)", border: "1px solid var(--t-border)" }}>
<Icon icon="lucide:search" width={16} className="text-(--t-text-dim)" />
<input data-snippet-target-search value={p.search} onChange={(e) => p.setSearch(e.target.value)} placeholder={t("mobile.filterBar.placeholder")}
className="flex-1 bg-transparent text-sm outline-none text-(--t-text-primary)" />
</div>
</div>
<MobileFilterBar attr="snippet-target-search" value={p.search} onChange={p.setSearch} inset="px-1 pb-2" />

{p.filteredSessions.length > 0 && (
<>
Expand Down
30 changes: 17 additions & 13 deletions src/plugins/process-manager/components/MobileProcessesScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,23 @@ export function createMobileProcessesScreen(api: PluginAPI): FC<MobileScreenProp
</div>
) : (
<>
<div className="flex items-center gap-2 px-3 py-2 border-b border-(--t-border) shrink-0">
<Icon icon="lucide:search" width={16} className="text-(--t-text-muted) shrink-0" />
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder={t("filterPlaceholder")}
className="flex-1 bg-transparent text-sm text-(--t-text-primary) placeholder:text-(--t-text-dim) outline-hidden"
/>
{snapshot && (
<span className="text-[11px] text-(--t-text-dim) shrink-0">
{entries.length}/{snapshot.entries.length}
</span>
)}
{/* Same M3 search bar as the app's MobileFilterBar (48 dp pill, tonal fill). */}
<div className="px-4 py-2 shrink-0">
<div className="flex items-center gap-3 px-4 h-12 rounded-full" style={{ background: "var(--t-bg-card)" }}>
<Icon icon="lucide:search" width={20} className="text-(--t-text-dim) shrink-0" />
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder={t("filterPlaceholder")}
aria-label={t("filterPlaceholder")}
className="flex-1 min-w-0 self-stretch bg-transparent text-base text-(--t-text-primary) placeholder:text-(--t-text-dim) outline-hidden"
/>
{snapshot && (
<span className="text-xs tabular-nums text-(--t-text-dim) shrink-0">
{entries.length}/{snapshot.entries.length}
</span>
)}
</div>
</div>

<div className="flex items-center gap-2 px-3 py-2 border-b border-(--t-border) shrink-0 overflow-x-auto">
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/process-manager/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "plugin-process-manager",
"name": "Process Manager",
"version": "1.3.5",
"version": "1.3.6",
"minAppVersion": "0.20.0",
"description": "Monitor and manage running processes for local and SSH sessions.",
"icon": "lucide:cpu",
Expand Down
Loading