diff --git a/package-lock.json b/package-lock.json index 2cb85d9..6876ad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "filesmith", - "version": "0.5.2", + "version": "0.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "filesmith", - "version": "0.5.2", + "version": "0.5.3", "hasInstallScript": true, "license": "MIT", "devDependencies": { diff --git a/package.json b/package.json index b4b25f5..2717027 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "filesmith", - "version": "0.5.2", + "version": "0.5.3", "description": "A desktop file toolkit: convert, compress, resize, upscale, remove backgrounds, and PDF tools.", "author": "Max", "license": "MIT", diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index bc36cff..75dec1c 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -46,19 +46,12 @@ import { import { engineFor, tabAccepts, tabById, toolCardById, type TabId } from '@shared/tabs' import { TitleBar } from './components/shell/TitleBar' import { Sidebar } from './components/shell/Sidebar' -import { StatusBar } from './components/shell/StatusBar' import { crumbsFor } from './components/shell/crumbs' import { useSidebar } from './components/shell/useSidebar' import { useRailPrefs } from './components/shell/useRailPrefs' import { sidebarVerbs } from './components/shell/railPrefs' -import { statusSummary } from './components/shell/statusModel' import { shortcutFor } from './components/shell/shortcuts' -import { - activeGroupFor, - headerCheck, - oneGroupIds, - toggleAllIds -} from './components/queue/selectAll' +import { activeGroupFor, headerCheck, toggleAllIds } from './components/queue/selectAll' import { QueueTable } from './components/queue/QueueTable' import { QueueToolbar } from './components/queue/QueueToolbar' import { doneSamples, queueTotals, type RowActionKind } from './components/queue/rowModel' @@ -152,7 +145,6 @@ export default function App(): JSX.Element { const sidebar = useSidebar() const rail = useRailPrefs() // Ids of the last run per workspace, for "Converting 3 of 6" (spec 6.4). - const [batches, setBatches] = useState>({}) // Per-workspace column sort; null is insertion order (spec 4.2). const [sorts, setSorts] = useState>({}) // Inspector tab: per-session view state, not persisted (spec 3.4). @@ -473,7 +465,6 @@ export default function App(): JSX.Element { * with (spec 4.3). A merge row retries with the same input list, because * run() stores `mergeInputs` in its `runOptions`. */ function retry(ids: string[]): void { - const started: string[] = [] for (const id of ids) { const it = cur.items.find((i) => i.id === id) if (!it || (it.status !== 'failed' && it.status !== 'canceled')) continue @@ -485,9 +476,7 @@ export default function App(): JSX.Element { input: it.file.path, options: opts }) - started.push(id) } - if (started.length) setBatches((b) => ({ ...b, [qKey]: started })) } function onRowAction(id: string, kind: RowActionKind): void { @@ -819,7 +808,6 @@ export default function App(): JSX.Element { anchorId = src.id } dispatch({ type: 'markQueued', ids: [anchorId], options: { ...opts, mergeInputs: paths } }) - setBatches((b) => ({ ...b, [qKey]: [anchorId] })) void window.filesmith.runJob({ id: anchorId, tool: 'pdf', @@ -849,7 +837,6 @@ export default function App(): JSX.Element { if (newSources.length) dispatch({ type: 'addSources', items: newSources, key: qKey }) if (!targets.length) return dispatch({ type: 'markQueued', ids: targets.map((t) => t.id), options: opts }) - setBatches((b) => ({ ...b, [qKey]: targets.map((t) => t.id) })) for (const t of targets) { void window.filesmith.runJob({ id: t.id, tool, input: t.path, options: opts }) } @@ -1065,14 +1052,6 @@ export default function App(): JSX.Element { const crumbs = crumbsFor(state.tab, card ?? null, activeGroup) const verbLabel = card ? card.label : tab.label - const summary = statusSummary( - onToolsGrid || onCompleted || state.tab === 'settings' ? [] : cur.items, - batches[qKey] ?? null, - verbLabel, - genRun.running && genRun.message && !genRun.message.startsWith('Generating') - ? genRun.message - : null - ) const showInspector = !onToolsGrid && !onCompleted && state.tab !== 'settings' // Inspector head and Run label (spec 3.4, 4.1). @@ -1328,18 +1307,6 @@ export default function App(): JSX.Element { )} - - dispatch({ - type: 'selectIds', - ids: oneGroupIds( - cur.items, - cur.items.filter((i) => i.status === 'failed').map((i) => i.id) - ) - }) - } - /> diff --git a/src/renderer/src/components/shell/StatusBar.tsx b/src/renderer/src/components/shell/StatusBar.tsx deleted file mode 100644 index 0a0d554..0000000 --- a/src/renderer/src/components/shell/StatusBar.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { JSX } from 'react' -import { Icon } from '../icons/Icon' -import { ProgressBar } from '../ui/ProgressBar' -import type { StatusSummary } from './statusModel' - -export function StatusBar({ - summary, - onFailedClick -}: { - summary: StatusSummary - onFailedClick: () => void -}): JSX.Element { - return ( -
- {summary.running && ( -
- - {summary.running.label} - -
- )} - {summary.message && ( -
- - {summary.message} - -
- )} - {summary.done &&
{summary.done}
} -
- {summary.failed > 0 && ( - - )} -
-
- ) -} diff --git a/src/renderer/src/components/shell/statusModel.ts b/src/renderer/src/components/shell/statusModel.ts deleted file mode 100644 index 948121d..0000000 --- a/src/renderer/src/components/shell/statusModel.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { inInput, type QueueItem } from '../../state' - -export interface StatusSummary { - running: { label: string; pct: number } | null - /** Generate's startup line ("Starting ComfyUI"), shown with an indeterminate bar. */ - message: string | null - done: string | null - failed: number -} - -const GERUND: Record = { - Convert: 'Converting', - Compress: 'Compressing', - Resize: 'Resizing', - Upscale: 'Upscaling', - 'Remove BG': 'Removing backgrounds', - Generate: 'Generating' -} - -export function verbGerund(label: string): string { - return GERUND[label] ?? 'Processing' -} - -/** Status bar content (spec 3.5 and 6.4). `batch` is the ids of the last run in - * this workspace; it is renderer-only and not persisted. */ -export function statusSummary( - items: QueueItem[], - batch: string[] | null, - verb: string, - message?: string | null -): StatusSummary { - const inputs = items.filter(inInput) - let running: StatusSummary['running'] = null - if (batch?.length) { - const ids = new Set(batch) - const rows = inputs.filter((i) => ids.has(i.id)) - const live = rows.filter((i) => i.status === 'queued' || i.status === 'running') - if (live.length) { - const done = rows.filter((i) => i.status === 'done').length - const run = rows.filter((i) => i.status === 'running') - const settled = rows.filter((i) => ['done', 'failed', 'canceled'].includes(i.status)).length - const partial = run.reduce((s, i) => s + (i.hasProgress ? i.percent / 100 : 0), 0) - running = { - label: `${verbGerund(verb)} ${done + run.length} of ${rows.length}`, - pct: Math.round(((settled + partial) / rows.length) * 100) - } - } - } - const doneN = inputs.filter((i) => i.status === 'done').length - return { - running, - message: message || null, - done: doneN ? `${doneN} of ${inputs.length} done` : null, - failed: inputs.filter((i) => i.status === 'failed').length - } -} diff --git a/src/renderer/src/theme/base.css b/src/renderer/src/theme/base.css index 617ea78..36169f7 100644 --- a/src/renderer/src/theme/base.css +++ b/src/renderer/src/theme/base.css @@ -97,10 +97,10 @@ svg.i10 { white-space: nowrap; } -/* App frame (spec 3): title bar, workbench, status bar. */ +/* App frame (spec 3): title bar and workbench. */ .app { display: grid; - grid-template-rows: 32px 1fr 24px; + grid-template-rows: 32px 1fr; width: 100vw; height: 100vh; min-width: 0; diff --git a/src/renderer/src/theme/workbench.css b/src/renderer/src/theme/workbench.css index b489955..3897b2c 100644 --- a/src/renderer/src/theme/workbench.css +++ b/src/renderer/src/theme/workbench.css @@ -742,54 +742,6 @@ box-shadow: inset 0 0 0 3px var(--inv-bg); } -/* status bar */ -.statusbar { - border-top: 1px solid var(--line); - display: flex; - align-items: stretch; - font: 12px var(--mono); - color: var(--fg2); -} -.sitem { - display: flex; - align-items: center; - gap: 8px; - padding: 0 12px; - line-height: 1; - border-right: 1px solid var(--line); -} -.sitem:hover { - background: var(--hover); - color: var(--fg1); -} -.mini { - width: 96px; - height: 2px; - background: var(--track); - position: relative; - overflow: hidden; -} -.mini i { - position: absolute; - left: 0; - top: 0; - bottom: 0; - background: var(--fg1); -} -.sright { - margin-left: auto; - display: flex; -} -.sright .sitem { - border-right: 0; - border-left: 1px solid var(--line); -} -.sitem.warn { - font-weight: 600; - color: var(--fg1); -} - -/* preview */ .wipe { margin: 16px; height: 208px; diff --git a/test/shell-model.test.ts b/test/shell-model.test.ts index 8644b91..0c57cc9 100644 --- a/test/shell-model.test.ts +++ b/test/shell-model.test.ts @@ -6,11 +6,9 @@ import { normalizeOrder, sidebarVerbs } from '../src/renderer/src/components/shell/railPrefs' -import { statusSummary, verbGerund } from '../src/renderer/src/components/shell/statusModel' import { shortcutFor } from '../src/renderer/src/components/shell/shortcuts' import type { KeyLike } from '../src/renderer/src/components/queue/tableKeys' import { TABS, toolCardById, type TabId } from '@shared/tabs' -import type { QueueItem } from '../src/renderer/src/state' describe('breadcrumb', () => { it('shows the verb, then the active group as the current segment', () => { @@ -72,51 +70,6 @@ describe('rail preferences', () => { }) }) -const row = (id: string, status: QueueItem['status'], percent = 0): QueueItem => ({ - id, - file: { path: `C:/${id}.png`, name: `${id}.png`, ext: '.png', kind: 'image', size: 1 }, - thumb: null, - status, - percent, - hasProgress: percent > 0 -}) - -describe('status bar', () => { - const items = [ - row('a', 'done'), - row('b', 'done'), - row('c', 'running', 62), - row('d', 'queued'), - row('e', 'queued'), - row('f', 'failed') - ] - it('reports the batch in flight, done count and failures', () => { - const s = statusSummary(items, ['a', 'b', 'c', 'd', 'e', 'f'], 'Convert') - expect(s.running).toEqual({ label: 'Converting 3 of 6', pct: 60 }) - expect(s.done).toBe('2 of 6 done') - expect(s.failed).toBe(1) - }) - it('drops the running item once the batch settles or without a batch', () => { - const settled = items.map((i) => - i.status === 'queued' || i.status === 'running' ? { ...i, status: 'done' as const } : i - ) - expect(statusSummary(settled, ['a', 'b', 'c', 'd', 'e', 'f'], 'Convert').running).toBeNull() - expect(statusSummary(items, null, 'Convert').running).toBeNull() - }) - it('renders nothing for an empty queue', () => { - expect(statusSummary([], null, 'Convert')).toEqual({ - running: null, - message: null, - done: null, - failed: 0 - }) - }) - it('names each verb', () => { - expect(verbGerund('Remove BG')).toBe('Removing backgrounds') - expect(verbGerund('Merge')).toBe('Processing') - }) -}) - describe('global shortcuts', () => { const k = (key: string, o: Partial> = {}): KeyLike => ({ key,