main f6d8405

minimizable.ts RawHistoryFolder

1import type { Action } from 'svelte/action';
2
3interface MinimizableParameters {
4	enabled?: boolean;
5	onMinimize?: (isMinimized: boolean) => void;
6}
7
8export interface MinimizableNode extends HTMLDivElement {
9	minimize: () => void;
10	restore: () => void;
11	toggleMinimize: () => void;
12	isMinimized: () => boolean;
13}
14
15interface MinimizableAttributes {
16	onminimize?: (e: CustomEvent<{ isMinimized: boolean }>) => void;
17}
18
19interface StoredState {
20	width: number;
21	height: number;
22	x: number;
23	y: number;
24}
25
26export const minimizable: Action<HTMLDivElement, MinimizableParameters, MinimizableAttributes> = (
27	node,
28	params = {}
29) => {
30	const { enabled = true, onMinimize } = params;
31
32	let isMinimized = false;
33	let storedState: StoredState | null = null;
34
35	function calculateMinimizedWidth() {
36		const titleBar = node.querySelector('.title-bar-text');
37		if (!titleBar) return 200;
38
39		// Create a temporary element to measure text width
40		const temp = document.createElement('div');
41		temp.style.position = 'absolute';
42		temp.style.visibility = 'hidden';
43		temp.style.whiteSpace = 'nowrap';
44		temp.style.font = window.getComputedStyle(titleBar).font;
45		temp.textContent = titleBar.textContent;
46		document.body.appendChild(temp);
47
48		// Get the width and add some padding
49		const width = temp.offsetWidth + 100; // Add 100px for buttons and padding
50		document.body.removeChild(temp);
51
52		return Math.max(200, Math.min(400, width)); // Keep width between 200 and 400px
53	}
54
55	function minimize() {
56		if (!enabled || isMinimized) return;
57
58		// Store current state
59		storedState = {
60			width: node.offsetWidth,
61			height: node.offsetHeight,
62			x: parseInt(node.style.left) || 0,
63			y: parseInt(node.style.top) || 0
64		};
65
66		// Check if we're in mobile mode
67		const isMobile = window.innerWidth <= 768;
68
69		if (!isMobile) {
70			// Desktop minimization
71			const minimizedWidth = calculateMinimizedWidth();
72			const minimizedHeight = 32; // Height of title bar
73			const minimizedX = 10;
74			const minimizedY = window.innerHeight - minimizedHeight - 10;
75
76			node.style.width = `${minimizedWidth}px`;
77			node.style.height = `${minimizedHeight}px`;
78			node.style.left = `${minimizedX}px`;
79			node.style.top = `${minimizedY}px`;
80		} else {
81			// Mobile minimization - position at bottom of screen
82			node.style.width = '100%';
83			node.style.height = '100%';
84			node.style.left = '0';
85			node.style.top = `${window.innerHeight - 32}px`; // 32px is title bar height
86		}
87
88		isMinimized = true;
89		node.classList.add('minimized');
90
91		// Hide resize handle
92		const resizeHandle = node.querySelector('.resize-handle');
93		if (resizeHandle) {
94			(resizeHandle as HTMLElement).style.display = 'none';
95		}
96
97		onMinimize?.(true);
98		node.dispatchEvent(
99			new CustomEvent('minimize', {
100				detail: { isMinimized: true }
101			})
102		);
103	}
104
105	function restore() {
106		if (!enabled || !isMinimized || !storedState) return;
107
108		const isMobile = window.innerWidth <= 768;
109
110		if (!isMobile) {
111			// Desktop restoration
112			node.style.width = `${storedState.width}px`;
113			node.style.height = `${storedState.height}px`;
114			node.style.left = `${storedState.x}px`;
115			node.style.top = `${storedState.y}px`;
116		} else {
117			// Mobile restoration
118			node.style.width = '100%';
119			node.style.height = '100%';
120			node.style.left = '0';
121			node.style.top = '0';
122		}
123
124		isMinimized = false;
125		node.classList.remove('minimized');
126
127		// Show resize handle if not in mobile mode
128		const resizeHandle = node.querySelector('.resize-handle');
129		if (resizeHandle) {
130			(resizeHandle as HTMLElement).style.display = !isMobile ? 'block' : 'none';
131		}
132
133		// Ensure window body is visible
134		const windowBody = node.querySelector('.window-body');
135		if (windowBody) {
136			(windowBody as HTMLElement).style.display = 'flex';
137		}
138
139		onMinimize?.(false);
140		node.dispatchEvent(
141			new CustomEvent('minimize', {
142				detail: { isMinimized: false }
143			})
144		);
145	}
146
147	function toggle() {
148		if (isMinimized) {
149			restore();
150		} else {
151			minimize();
152		}
153	}
154
155	// Handle double click on title bar
156	const titleBar = node.querySelector('.title-bar');
157	if (titleBar) {
158		titleBar.addEventListener('dblclick', toggle);
159	}
160
161	// Expose methods to the node
162	Object.assign(node, {
163		minimize,
164		restore,
165		toggleMinimize: toggle,
166		isMinimized: () => isMinimized
167	});
168
169	return {
170		update(newParams: MinimizableParameters) {
171			Object.assign(params, newParams);
172		},
173		destroy() {
174			if (titleBar) {
175				titleBar.removeEventListener('dblclick', toggle);
176			}
177		}
178	};
179};

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close