diff --git a/apps/desktop/src/i18n/en.ts b/apps/desktop/src/i18n/en.ts index 2c96816..805d562 100644 --- a/apps/desktop/src/i18n/en.ts +++ b/apps/desktop/src/i18n/en.ts @@ -72,6 +72,13 @@ export const en = { 'tabs.more': "{count} more tabs", 'tabs.moreShort': "More", 'reviews.keysHint': "Keyboard: J / K next and previous, R reply, C copy, O open in the console", + 'tools.filter': "Find a command", + 'tools.search': "Search {count} commands (name, method, what it does)", + 'tools.searchAria': "Search commands", + 'tools.density': "Layout", + 'tools.list': "List", + 'tools.results': "{count} matching commands", + 'tools.noHits': "No command matches. Try a store word (listing, prices, testflight...).", 'palette.gActions': 'Actions', 'palette.openApp': 'Open', diff --git a/apps/desktop/src/i18n/tr.ts b/apps/desktop/src/i18n/tr.ts index 8159d4a..fc547e5 100644 --- a/apps/desktop/src/i18n/tr.ts +++ b/apps/desktop/src/i18n/tr.ts @@ -74,6 +74,13 @@ export const tr: Record = { 'tabs.more': "{count} sekme daha", 'tabs.moreShort': "Daha fazla", 'reviews.keysHint': "Klavye: J / K sonraki ve önceki, R yanıtla, C kopyala, O konsolda aç", + 'tools.filter': "Komut bul", + 'tools.search': "{count} komutta ara (ad, yöntem, ne yaptığı)", + 'tools.searchAria': "Komut ara", + 'tools.density': "Görünüm", + 'tools.list': "Liste", + 'tools.results': "{count} eşleşen komut", + 'tools.noHits': "Eşleşen komut yok. Bir mağaza kelimesi deneyin (listing, prices, testflight...).", 'palette.gActions': 'İşlemler', 'palette.openApp': 'Aç', diff --git a/apps/desktop/src/screens/tabs/Tools.tsx b/apps/desktop/src/screens/tabs/Tools.tsx index b3714f4..38f1fe2 100644 --- a/apps/desktop/src/screens/tabs/Tools.tsx +++ b/apps/desktop/src/screens/tabs/Tools.tsx @@ -9,8 +9,8 @@ import { api, errorText, type MethodDescriptor, newJob, type Project, type ToolR import { type Key, t } from '../../i18n'; import { useData, useWrite } from '../../store'; import { Icon } from '../../ui/icons'; -import { ErrorNotice, Skeleton } from '../../ui/kit'; -import { Section, StatusPill } from '../../ui/ds'; +import { ErrorNotice, Seg, Skeleton } from '../../ui/kit'; +import { Section, StatusPill, Toolbar } from '../../ui/ds'; import '../../styles/tools.css'; import { WriteButton } from '../../ui/WriteButton'; @@ -111,6 +111,9 @@ export function ToolsTab({ project }: { project: Project }) { const [methods, setMethods] = useState(null); const [err, setErr] = useState(null); const [open, setOpen] = useState(TOOLBOX[0]!.id); + const [q, setQ] = useState(''); + const [dense, setDenseState] = useState(() => { try { return localStorage.getItem('sb.tools.dense') === '1'; } catch { return false; } }); + const setDense = (v: boolean) => { setDenseState(v); try { localStorage.setItem('sb.tools.dense', v ? '1' : '0'); } catch { /* private mode */ } }; useEffect(() => { api.call<{ methods: MethodDescriptor[] }>('system.methods').then(r => setMethods(r.methods), e => setErr(errorText(e))); }, []); @@ -118,12 +121,26 @@ export function ToolsTab({ project }: { project: Project }) { const copy = (c: string) => { navigator.clipboard?.writeText(c).then(() => d.toast({ kind: 'ok', title: t('tools.copied') }), () => {}); }; const groups = TOOLBOX.map(g => ({ g, ms: g.methods.map(x => by.get(x)).filter((x): x is MethodDescriptor => !!x) })); const cur = groups.find(x => x.g.id === open); + const needle = q.trim().toLowerCase(); + const hits = needle ? groups.flatMap(x => x.ms).filter(m => [m.method, m.title, m.description ?? ''].some(x => x.toLowerCase().includes(needle))) : []; return ( -
+

{t('tools.lead')}

+ {methods && ( + +
setQ(e.target.value)} placeholder={t('tools.search', { count: groups.reduce((a, x) => a + x.ms.length, 0) })} aria-label={t('tools.searchAria')} onKeyDown={e => { if (e.key === 'Escape') setQ(''); }} />
+ + setDense(v === 'list')} label={t('tools.density')} options={[['cards', t('pf.cards'), 'grid'], ['list', t('tools.list'), 'layers']]} /> +
+ )} + {needle && ( +
+ {hits.length ?
{hits.map(m => )}
:

{t('tools.noHits')}

} +
+ )} {err && } {!methods && !err && } - {methods && ( + {methods && !needle && (
{groups.map(({ g, ms }) => { const isOpen = open === g.id; @@ -138,7 +155,7 @@ export function ToolsTab({ project }: { project: Project }) { })}
)} - {methods && cur && ( + {methods && cur && !needle && (
{cur.ms.map(m => )}
diff --git a/apps/desktop/src/styles/tools.css b/apps/desktop/src/styles/tools.css index 7d69e07..dbd7c82 100644 --- a/apps/desktop/src/styles/tools.css +++ b/apps/desktop/src/styles/tools.css @@ -46,3 +46,8 @@ .scr-tools .tl-step { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--ds-r); background: var(--ds-inset); border: 1px solid var(--ds-stroke); min-width: 0; } .scr-tools .tl-step-t { display: grid; gap: 4px; flex: 1; min-width: 0; color: var(--ink); font-weight: 600; } .scr-tools .tl-step-t code { font-size: var(--ds-t-micro); color: var(--muted); font-weight: 500; overflow-wrap: anywhere; } +/* list layout: smaller group tiles, one command per row */ +.scr-tools.dense .tl-groups { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; } +.scr-tools.dense .tl-group { padding-top: 8px; padding-bottom: 8px; } +.scr-tools.dense .tl-group-ic { display: none; } +.scr-tools.dense .tl-cmds { grid-template-columns: minmax(0, 1fr); gap: 8px; }