v0.1.5 0b43029

minimizable.ts RawHistoryFolder

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

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