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
21 changes: 16 additions & 5 deletions Frontend/src/scripts/features/repo/diffView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { qsa, escapeHtml } from '../../lib/dom';
import { buildCtxMenu, CtxItem } from '../../lib/menu';
import { TAURI } from '../../lib/tauri';
import { notify } from '../../lib/notify';
import { state, prefs } from '../../state/state';
import { state, prefs, disableDefaultSelectAll } from '../../state/state';
import type { FileStatus, ConflictDetails } from '../../types';
import { buildPatchForSelectedHunks } from '../diff';
import { diffEl, diffHeadPath, listEl } from './context';
Expand Down Expand Up @@ -292,9 +292,19 @@ function bindConflictActions(root: HTMLElement, file: FileStatus, details: Confl
}
}

function clearAllFileSelections() {
if (!listEl) return;
const rows = listEl.querySelectorAll<HTMLElement>('li.row');
rows.forEach((row) => {
row.classList.remove('picked');
const cb = row.querySelector<HTMLInputElement>('input.pick');
if (cb) { cb.checked = false; (cb as any).indeterminate = false; }
});
}

export function toggleFilePick(path: string, on: boolean) {
if (!path) return;
state.defaultSelectAll = false;
disableDefaultSelectAll();
if (on) state.selectedFiles.add(path);
else state.selectedFiles.delete(path);
if (state.currentFile && state.currentFile === path) {
Expand Down Expand Up @@ -355,7 +365,8 @@ function bindHunkToggles(root: HTMLElement) {
const boxes = root.querySelectorAll<HTMLInputElement>('input.pick-hunk');
boxes.forEach((b) => {
b.addEventListener('change', () => {
state.defaultSelectAll = false;
const clearedImplicit = disableDefaultSelectAll(true);
if (clearedImplicit) clearAllFileSelections();
const idx = Number(b.dataset.hunk || -1);
if (b.checked) {
if (!state.selectedHunks.includes(idx)) state.selectedHunks.push(idx);
Expand Down Expand Up @@ -387,7 +398,8 @@ function bindHunkToggles(root: HTMLElement) {
const lineBoxes = root.querySelectorAll<HTMLInputElement>('input.pick-line');
lineBoxes.forEach((b) => {
b.addEventListener('change', () => {
state.defaultSelectAll = false;
const clearedImplicit = disableDefaultSelectAll(true);
if (clearedImplicit) clearAllFileSelections();
const hunk = Number(b.dataset.hunk || -1);
const line = Number(b.dataset.line || -1);
if (!state.currentFile || hunk < 0 || line < 0) return;
Expand Down Expand Up @@ -541,4 +553,3 @@ export function updateListCheckboxForPath(path: string, checked: boolean, indete
(cb as any).indeterminate = indeterminate;
}
}

4 changes: 2 additions & 2 deletions Frontend/src/scripts/features/repo/filter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { state, prefs } from '../../state/state';
import { state, prefs, disableDefaultSelectAll } from '../../state/state';
import { filterInput, selectAllBox } from './context';
import { renderList } from './list';
import { getVisibleFiles } from './selectionState';
Expand All @@ -8,7 +8,7 @@ export function bindFilter() {
filterInput?.addEventListener('input', () => renderList());
selectAllBox?.addEventListener('change', () => {
if (prefs.tab !== 'changes') return;
state.defaultSelectAll = false;
disableDefaultSelectAll();
const files = getVisibleFiles();
toggleSelectAll(Boolean(selectAllBox?.checked), files);
renderList();
Expand Down
3 changes: 3 additions & 0 deletions Frontend/src/scripts/features/repo/hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export async function hydrateStatus() {
state.files = Array.isArray(result?.files) ? (result.files as any) : [];
const currentPaths = new Set((state.files || []).map((f) => f.path));
if (state.defaultSelectAll) {
state.selectionImplicitAll = true;
state.selectedFiles = new Set(Array.from(currentPaths));
} else {
state.selectionImplicitAll = false;
state.selectedFiles.forEach((p) => { if (!currentPaths.has(p)) state.selectedFiles.delete(p); });
}
(state as any).ahead = Number((result as any)?.ahead || 0);
Expand All @@ -39,6 +41,7 @@ export async function hydrateStatus() {
console.warn('hydrateStatus failed', e);
state.files = [];
state.selectedFiles.clear();
state.selectionImplicitAll = false;
renderList();
window.dispatchEvent(new CustomEvent('app:status-updated'));
}
Expand Down
6 changes: 3 additions & 3 deletions Frontend/src/scripts/features/repo/interactions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { buildCtxMenu, CtxItem } from '../../lib/menu';
import { notify } from '../../lib/notify';
import { TAURI } from '../../lib/tauri';
import { state } from '../../state/state';
import { state, disableDefaultSelectAll } from '../../state/state';
import type { FileStatus } from '../../types';
import { openStashConfirm } from '../stashConfirm';
import { dragState, listEl } from './context';
Expand Down Expand Up @@ -33,7 +33,7 @@ export function onFileClick(e: MouseEvent, file: FileStatus, index: number, visi
if (state.selectedFiles.has(p)) state.selectedFiles.delete(p);
else state.selectedFiles.add(p);
}
state.defaultSelectAll = false;
disableDefaultSelectAll();
updateSelectAllState(visible);
renderListAfterRangeSelect(file);
} else if (isToggle) {
Expand Down Expand Up @@ -124,7 +124,7 @@ export function onFileMouseDown(e: MouseEvent, file: FileStatus, index: number,
}

export function applySelect(path: string, on: boolean, rowEl: HTMLElement | null, visible: FileStatus[], mode: 'diff' | 'commit') {
state.defaultSelectAll = false;
disableDefaultSelectAll();
if (mode === 'commit') {
if (on) state.selectedFiles.add(path); else state.selectedFiles.delete(path);
if (rowEl) rowEl.classList.toggle('picked', on);
Expand Down
13 changes: 13 additions & 0 deletions Frontend/src/scripts/state/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const state = {
behind: 0 as number, // commits behind upstream
aheadIds: new Set<string>() as Set<string>, // IDs of commits ahead of upstream
defaultSelectAll: true as boolean, // by default select all files/hunks until user toggles
selectionImplicitAll: true as boolean, // true when select-all was auto-applied (no manual picks yet)
// Selection state
selectedFiles: new Set<string>(),
currentFile: '' as string,
Expand Down Expand Up @@ -53,3 +54,15 @@ export const statusClass = (s: string) =>
s === 'A' ? 'add' :
s === 'M' ? 'mod' :
s === 'D' ? 'del' : 'mod';

// Disable the implicit "select all" mode. When clearImplicit is true, drop the
// auto-filled selection set so later logic only sees explicit user picks.
export function disableDefaultSelectAll(clearImplicit = false): boolean {
const hadImplicit = state.defaultSelectAll && state.selectionImplicitAll;
if (clearImplicit && hadImplicit) {
state.selectedFiles.clear();
}
state.defaultSelectAll = false;
state.selectionImplicitAll = false;
return Boolean(clearImplicit && hadImplicit);
}
Loading