gh-here 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +0 -0
- package/.playwright-mcp/fixed-alignment.png +0 -0
- package/.playwright-mcp/fixed-layout.png +0 -0
- package/.playwright-mcp/gh-here-home-header-table.png +0 -0
- package/.playwright-mcp/gh-here-home.png +0 -0
- package/.playwright-mcp/line-selection-multiline.png +0 -0
- package/.playwright-mcp/line-selection-test-after.png +0 -0
- package/.playwright-mcp/line-selection-test-before.png +0 -0
- package/.playwright-mcp/page-2026-01-03T17-58-21-336Z.png +0 -0
- package/lib/constants.js +25 -15
- package/lib/content-search.js +212 -0
- package/lib/error-handler.js +39 -28
- package/lib/file-utils.js +438 -287
- package/lib/git.js +10 -54
- package/lib/gitignore.js +70 -41
- package/lib/renderers.js +15 -19
- package/lib/server.js +70 -193
- package/lib/symbol-parser.js +600 -0
- package/package.json +1 -1
- package/public/app.js +207 -73
- package/public/js/constants.js +50 -34
- package/public/js/content-search-handler.js +551 -0
- package/public/js/file-viewer.js +437 -0
- package/public/js/focus-mode.js +280 -0
- package/public/js/inline-search.js +659 -0
- package/public/js/modal-manager.js +14 -28
- package/public/js/navigation.js +5 -0
- package/public/js/symbol-outline.js +454 -0
- package/public/js/utils.js +152 -94
- package/public/styles.css +2049 -296
- package/.claude/settings.local.json +0 -30
- package/SAMPLE.md +0 -287
- package/lib/validation.js +0 -77
- package/public/app.js.backup +0 -1902
- package/public/js/draft-manager.js +0 -36
- package/public/js/editor-manager.js +0 -159
- package/test.js +0 -138
- package/tests/draftManager.test.js +0 -241
- package/tests/fileTypeDetection.test.js +0 -111
- package/tests/httpService.test.js +0 -268
- package/tests/languageDetection.test.js +0 -145
- package/tests/pathUtils.test.js +0 -136
package/public/app.js
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main application entry point
|
|
3
3
|
* Coordinates all modules and initializes the application
|
|
4
|
+
* @module app
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Imports (alpha-sorted)
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
import { ContentSearchHandler } from './js/content-search-handler.js';
|
|
12
|
+
import { copyToClipboard } from './js/clipboard-utils.js';
|
|
9
13
|
import { FileTreeNavigator } from './js/file-tree.js';
|
|
14
|
+
import { FileViewer } from './js/file-viewer.js';
|
|
15
|
+
import { FocusMode } from './js/focus-mode.js';
|
|
16
|
+
import { InlineSearch } from './js/inline-search.js';
|
|
17
|
+
import { KeyboardHandler } from './js/keyboard-handler.js';
|
|
10
18
|
import { NavigationHandler } from './js/navigation.js';
|
|
11
19
|
import { PathUtils } from './js/utils.js';
|
|
20
|
+
import { SearchHandler } from './js/search-handler.js';
|
|
12
21
|
import { showNotification } from './js/notification.js';
|
|
13
|
-
import {
|
|
22
|
+
import { SymbolOutline } from './js/symbol-outline.js';
|
|
23
|
+
import { ThemeManager } from './js/theme-manager.js';
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Application Class
|
|
27
|
+
// ============================================================================
|
|
14
28
|
|
|
15
29
|
class Application {
|
|
16
30
|
constructor() {
|
|
@@ -19,6 +33,12 @@ class Application {
|
|
|
19
33
|
this.keyboardHandler = null;
|
|
20
34
|
this.fileTree = null;
|
|
21
35
|
this.navigationHandler = null;
|
|
36
|
+
this.lastSelectedLine = null;
|
|
37
|
+
this.fileViewer = null;
|
|
38
|
+
this.inlineSearch = null;
|
|
39
|
+
this.focusMode = null;
|
|
40
|
+
this.contentSearch = null;
|
|
41
|
+
this.symbolOutline = null;
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
init() {
|
|
@@ -31,19 +51,22 @@ class Application {
|
|
|
31
51
|
// Re-initialize components after client-side navigation
|
|
32
52
|
document.addEventListener('content-loaded', () => {
|
|
33
53
|
try {
|
|
54
|
+
// Cleanup existing components before re-initializing
|
|
55
|
+
this.cleanupComponents();
|
|
56
|
+
|
|
34
57
|
// Re-initialize theme manager listeners (button might be re-rendered)
|
|
35
58
|
if (this.themeManager) {
|
|
36
59
|
this.themeManager.setupListeners();
|
|
37
60
|
}
|
|
38
|
-
|
|
61
|
+
|
|
39
62
|
// Re-initialize components that need fresh DOM references
|
|
40
63
|
this.searchHandler = new SearchHandler();
|
|
41
64
|
this.keyboardHandler = new KeyboardHandler(this.searchHandler);
|
|
42
|
-
|
|
65
|
+
|
|
43
66
|
// Re-initialize file tree when sidebar becomes visible
|
|
44
67
|
const sidebar = document.querySelector('.file-tree-sidebar');
|
|
45
68
|
const treeContainer = document.getElementById('file-tree');
|
|
46
|
-
|
|
69
|
+
|
|
47
70
|
if (sidebar && treeContainer && !sidebar.classList.contains('hidden')) {
|
|
48
71
|
// Sidebar is visible - initialize or re-initialize file tree
|
|
49
72
|
if (!this.fileTree || !this.fileTree.isInitialized || this.fileTree.treeContainer !== treeContainer) {
|
|
@@ -53,10 +76,14 @@ class Application {
|
|
|
53
76
|
// Sidebar is hidden - don't initialize but keep reference for when it becomes visible
|
|
54
77
|
this.fileTree = null;
|
|
55
78
|
}
|
|
56
|
-
|
|
79
|
+
|
|
57
80
|
this.setupGlobalEventListeners();
|
|
58
81
|
this.setupGitignoreToggle();
|
|
59
|
-
this.
|
|
82
|
+
this.highlightLinesFromHash();
|
|
83
|
+
this.initializeFileViewer();
|
|
84
|
+
this.initializeInlineSearch();
|
|
85
|
+
this.initializeFocusMode();
|
|
86
|
+
this.initializeContentSearch();
|
|
60
87
|
} catch (error) {
|
|
61
88
|
console.error('Error re-initializing components:', error);
|
|
62
89
|
}
|
|
@@ -72,7 +99,7 @@ class Application {
|
|
|
72
99
|
// Initialize components
|
|
73
100
|
this.searchHandler = new SearchHandler();
|
|
74
101
|
this.keyboardHandler = new KeyboardHandler(this.searchHandler);
|
|
75
|
-
|
|
102
|
+
|
|
76
103
|
// Initialize file tree if sidebar is visible (not hidden)
|
|
77
104
|
const sidebar = document.querySelector('.file-tree-sidebar');
|
|
78
105
|
const treeContainer = document.getElementById('file-tree');
|
|
@@ -82,7 +109,84 @@ class Application {
|
|
|
82
109
|
|
|
83
110
|
this.setupGlobalEventListeners();
|
|
84
111
|
this.setupGitignoreToggle();
|
|
85
|
-
this.
|
|
112
|
+
this.highlightLinesFromHash();
|
|
113
|
+
this.initializeFileViewer();
|
|
114
|
+
this.initializeInlineSearch();
|
|
115
|
+
this.initializeFocusMode();
|
|
116
|
+
this.initializeContentSearch();
|
|
117
|
+
this.initializeSymbolOutline();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ========================================================================
|
|
121
|
+
// Component Initialization
|
|
122
|
+
// ========================================================================
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if current page is a file view page
|
|
126
|
+
*/
|
|
127
|
+
isFileViewPage() {
|
|
128
|
+
const fileContent = document.querySelector('.file-content');
|
|
129
|
+
return fileContent?.querySelector('pre code.hljs.with-line-numbers') !== null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Initialize Monaco-based file viewer
|
|
134
|
+
*/
|
|
135
|
+
initializeFileViewer() {
|
|
136
|
+
if (!this.isFileViewPage()) return;
|
|
137
|
+
|
|
138
|
+
// Wait for Monaco to be ready
|
|
139
|
+
if (typeof require === 'undefined' || !window.monacoReady) {
|
|
140
|
+
// Wait a bit and try again
|
|
141
|
+
setTimeout(() => this.initializeFileViewer(), 100);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!this.fileViewer) {
|
|
146
|
+
this.fileViewer = new FileViewer();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Initialize inline search component
|
|
152
|
+
* Note: Skipped when Monaco viewer is active (Monaco has built-in search)
|
|
153
|
+
*/
|
|
154
|
+
initializeInlineSearch() {
|
|
155
|
+
if (!this.isFileViewPage()) return;
|
|
156
|
+
|
|
157
|
+
// Skip if Monaco viewer is active
|
|
158
|
+
if (document.querySelector('.monaco-file-viewer')) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!this.inlineSearch) {
|
|
163
|
+
this.inlineSearch = new InlineSearch();
|
|
164
|
+
}
|
|
165
|
+
this.inlineSearch.init();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
initializeFocusMode() {
|
|
169
|
+
if (!this.focusMode) {
|
|
170
|
+
this.focusMode = new FocusMode();
|
|
171
|
+
}
|
|
172
|
+
this.focusMode.init();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Initialize symbol outline panel
|
|
177
|
+
*/
|
|
178
|
+
initializeSymbolOutline() {
|
|
179
|
+
// Cleanup previous instance
|
|
180
|
+
if (this.symbolOutline) {
|
|
181
|
+
this.symbolOutline.destroy();
|
|
182
|
+
this.symbolOutline = null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Only initialize on file view pages
|
|
186
|
+
if (!this.isFileViewPage()) return;
|
|
187
|
+
|
|
188
|
+
this.symbolOutline = new SymbolOutline();
|
|
189
|
+
this.symbolOutline.init();
|
|
86
190
|
}
|
|
87
191
|
|
|
88
192
|
setupGlobalEventListeners() {
|
|
@@ -104,6 +208,16 @@ class Application {
|
|
|
104
208
|
}
|
|
105
209
|
|
|
106
210
|
handleGlobalClick(e) {
|
|
211
|
+
// Line number selection (like GitHub)
|
|
212
|
+
const lineNumber = e.target.closest('.line-number');
|
|
213
|
+
if (lineNumber) {
|
|
214
|
+
e.preventDefault();
|
|
215
|
+
e.stopPropagation();
|
|
216
|
+
const lineNum = parseInt(lineNumber.textContent.trim(), 10);
|
|
217
|
+
this.handleLineSelection(lineNum, e.shiftKey);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
107
221
|
// Copy path button
|
|
108
222
|
const copyPathBtn = e.target.closest('.copy-path-btn, .file-path-copy-btn');
|
|
109
223
|
if (copyPathBtn) {
|
|
@@ -180,68 +294,6 @@ class Application {
|
|
|
180
294
|
}
|
|
181
295
|
|
|
182
296
|
|
|
183
|
-
setupFileOperations() {
|
|
184
|
-
document.addEventListener('click', async e => {
|
|
185
|
-
if (e.target.closest('.delete-btn')) {
|
|
186
|
-
const btn = e.target.closest('.delete-btn');
|
|
187
|
-
const itemPath = btn.dataset.path;
|
|
188
|
-
const itemName = btn.dataset.name;
|
|
189
|
-
const isDirectory = btn.dataset.isDirectory === 'true';
|
|
190
|
-
|
|
191
|
-
const message = `Are you sure you want to delete ${isDirectory ? 'folder' : 'file'} "${itemName}"?`;
|
|
192
|
-
if (!confirm(message)) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
try {
|
|
197
|
-
const response = await fetch('/api/delete', {
|
|
198
|
-
method: 'POST',
|
|
199
|
-
headers: { 'Content-Type': 'application/json' },
|
|
200
|
-
body: JSON.stringify({ path: itemPath })
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
if (!response.ok) {
|
|
204
|
-
throw new Error('Delete failed');
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
showNotification(`${isDirectory ? 'Folder' : 'File'} deleted successfully`, 'success');
|
|
208
|
-
setTimeout(() => window.location.reload(), 600);
|
|
209
|
-
} catch (error) {
|
|
210
|
-
showNotification('Failed to delete item', 'error');
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (e.target.closest('.rename-btn')) {
|
|
215
|
-
const btn = e.target.closest('.rename-btn');
|
|
216
|
-
const itemPath = btn.dataset.path;
|
|
217
|
-
const currentName = btn.dataset.name;
|
|
218
|
-
const isDirectory = btn.dataset.isDirectory === 'true';
|
|
219
|
-
|
|
220
|
-
const newName = prompt(`Rename ${isDirectory ? 'folder' : 'file'}:`, currentName);
|
|
221
|
-
if (!newName || newName.trim() === currentName) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
const response = await fetch('/api/rename', {
|
|
227
|
-
method: 'POST',
|
|
228
|
-
headers: { 'Content-Type': 'application/json' },
|
|
229
|
-
body: JSON.stringify({ path: itemPath, newName: newName.trim() })
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
if (!response.ok) {
|
|
233
|
-
throw new Error('Rename failed');
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
showNotification(`Renamed to "${newName.trim()}"`, 'success');
|
|
237
|
-
setTimeout(() => window.location.reload(), 600);
|
|
238
|
-
} catch (error) {
|
|
239
|
-
showNotification('Failed to rename item', 'error');
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
297
|
showDiffViewer(filePath) {
|
|
246
298
|
// Simplified - redirect to diff view
|
|
247
299
|
const url = new URL(window.location.href);
|
|
@@ -264,6 +316,88 @@ class Application {
|
|
|
264
316
|
showNotification('Failed to copy raw content', 'error');
|
|
265
317
|
}
|
|
266
318
|
}
|
|
319
|
+
|
|
320
|
+
handleLineSelection(lineNum, shiftKey) {
|
|
321
|
+
// If shift is held and we have a previous selection, select range
|
|
322
|
+
if (shiftKey && this.lastSelectedLine) {
|
|
323
|
+
const start = Math.min(this.lastSelectedLine, lineNum);
|
|
324
|
+
const end = Math.max(this.lastSelectedLine, lineNum);
|
|
325
|
+
this.highlightLines(start, end);
|
|
326
|
+
this.updateUrlHash(start, end);
|
|
327
|
+
} else {
|
|
328
|
+
// Single line selection
|
|
329
|
+
this.highlightLines(lineNum, lineNum);
|
|
330
|
+
this.updateUrlHash(lineNum, lineNum);
|
|
331
|
+
this.lastSelectedLine = lineNum;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
highlightLines(start, end) {
|
|
336
|
+
// Clear all existing selections and highlight new range in one pass
|
|
337
|
+
document.querySelectorAll('.line-container').forEach(el => {
|
|
338
|
+
const lineNum = parseInt(el.dataset.line, 10);
|
|
339
|
+
if (lineNum >= start && lineNum <= end) {
|
|
340
|
+
el.classList.add('selected');
|
|
341
|
+
} else {
|
|
342
|
+
el.classList.remove('selected');
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
updateUrlHash(start, end) {
|
|
348
|
+
const hash = start === end ? `L${start}` : `L${start}-L${end}`;
|
|
349
|
+
// Use history API to update URL without scrolling - preserve path and query params
|
|
350
|
+
const url = new URL(window.location);
|
|
351
|
+
url.hash = hash;
|
|
352
|
+
history.replaceState(null, null, url);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
highlightLinesFromHash() {
|
|
356
|
+
const hash = window.location.hash.slice(1); // Remove #
|
|
357
|
+
if (!hash.startsWith('L')) return;
|
|
358
|
+
|
|
359
|
+
const match = hash.match(/^L(\d+)(?:-L(\d+))?$/);
|
|
360
|
+
if (!match) return;
|
|
361
|
+
|
|
362
|
+
const start = parseInt(match[1], 10);
|
|
363
|
+
const end = match[2] ? parseInt(match[2], 10) : start;
|
|
364
|
+
|
|
365
|
+
this.highlightLines(start, end);
|
|
366
|
+
this.lastSelectedLine = start;
|
|
367
|
+
|
|
368
|
+
// Scroll to the first selected line
|
|
369
|
+
const firstLine = document.querySelector(`.line-container[data-line="${start}"]`);
|
|
370
|
+
if (firstLine) {
|
|
371
|
+
firstLine.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
initializeContentSearch() {
|
|
376
|
+
if (!this.contentSearch) {
|
|
377
|
+
this.contentSearch = new ContentSearchHandler();
|
|
378
|
+
}
|
|
379
|
+
this.contentSearch.init();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Cleanup components before navigation/re-initialization
|
|
384
|
+
*/
|
|
385
|
+
cleanupComponents() {
|
|
386
|
+
// Cleanup file viewer
|
|
387
|
+
if (this.fileViewer && typeof this.fileViewer.destroy === 'function') {
|
|
388
|
+
this.fileViewer.destroy();
|
|
389
|
+
this.fileViewer = null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Cleanup inline search
|
|
393
|
+
if (this.inlineSearch && typeof this.inlineSearch.destroy === 'function') {
|
|
394
|
+
this.inlineSearch.destroy();
|
|
395
|
+
this.inlineSearch = null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Focus mode persists across navigations, no cleanup needed
|
|
399
|
+
// Content search persists across navigations, no cleanup needed
|
|
400
|
+
}
|
|
267
401
|
}
|
|
268
402
|
|
|
269
403
|
const app = new Application();
|
package/public/js/constants.js
CHANGED
|
@@ -1,60 +1,76 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Application constants and configuration
|
|
3
|
+
* @module constants
|
|
3
4
|
*/
|
|
4
5
|
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Configuration
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
5
10
|
export const CONFIG = {
|
|
6
|
-
MONACO_CDN: 'https://unpkg.com/monaco-editor@0.45.0/min/vs',
|
|
7
|
-
MONACO_VERSION: '0.45.0',
|
|
8
|
-
EDITOR_HEIGHT: 600,
|
|
9
11
|
DEFAULT_PORT: 5555,
|
|
12
|
+
MONACO_CDN: 'https://unpkg.com/monaco-editor@0.45.0/min/vs',
|
|
10
13
|
NOTIFICATION_DURATION: 4000
|
|
11
14
|
};
|
|
12
15
|
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Enums
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
13
20
|
export const THEME = {
|
|
14
21
|
DARK: 'dark',
|
|
15
22
|
LIGHT: 'light'
|
|
16
23
|
};
|
|
17
24
|
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Storage Keys
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
18
29
|
export const STORAGE_KEYS = {
|
|
19
|
-
THEME: 'gh-here-theme'
|
|
20
|
-
DRAFT_PREFIX: 'gh-here-draft-'
|
|
30
|
+
THEME: 'gh-here-theme'
|
|
21
31
|
};
|
|
22
32
|
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Keyboard Shortcuts (alpha-sorted)
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
export const KEYBOARD_SHORTCUTS = {
|
|
38
|
+
ESCAPE: 'Escape',
|
|
39
|
+
GO_UP: 'h',
|
|
40
|
+
HELP: '?',
|
|
41
|
+
NAV_DOWN: 'j',
|
|
42
|
+
NAV_UP: 'k',
|
|
43
|
+
OPEN: 'o',
|
|
44
|
+
REFRESH: 'r',
|
|
45
|
+
SEARCH: ['/', 's'],
|
|
46
|
+
SHOW_DIFF: 'd',
|
|
47
|
+
THEME_TOGGLE: 't',
|
|
48
|
+
TOGGLE_GITIGNORE: 'i'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Monaco Editor Options (for FileViewer read-only mode)
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
23
55
|
export const EDITOR_OPTIONS = {
|
|
24
|
-
minimap: { enabled: false },
|
|
25
|
-
lineNumbers: 'on',
|
|
26
|
-
wordWrap: 'off',
|
|
27
|
-
scrollBeyondLastLine: false,
|
|
28
|
-
fontSize: 12,
|
|
29
|
-
lineHeight: 20,
|
|
30
|
-
fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace",
|
|
31
|
-
padding: { top: 16, bottom: 16 },
|
|
32
|
-
renderLineHighlight: 'line',
|
|
33
|
-
selectOnLineNumbers: true,
|
|
34
56
|
automaticLayout: true,
|
|
57
|
+
bracketPairColorization: { enabled: true },
|
|
35
58
|
folding: true,
|
|
36
59
|
foldingHighlight: true,
|
|
37
60
|
foldingStrategy: 'auto',
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
fontFamily: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace",
|
|
62
|
+
fontSize: 12,
|
|
40
63
|
guides: {
|
|
41
64
|
bracketPairs: true,
|
|
42
65
|
indentation: true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
CREATE_FILE: 'c',
|
|
54
|
-
EDIT_FILE: 'e',
|
|
55
|
-
SHOW_DIFF: 'd',
|
|
56
|
-
TOGGLE_GITIGNORE: 'i',
|
|
57
|
-
NAV_DOWN: 'j',
|
|
58
|
-
NAV_UP: 'k',
|
|
59
|
-
OPEN: 'o'
|
|
66
|
+
},
|
|
67
|
+
lineHeight: 20,
|
|
68
|
+
lineNumbers: 'on',
|
|
69
|
+
minimap: { enabled: false },
|
|
70
|
+
padding: { top: 16, bottom: 16 },
|
|
71
|
+
renderLineHighlight: 'line',
|
|
72
|
+
scrollBeyondLastLine: false,
|
|
73
|
+
selectOnLineNumbers: true,
|
|
74
|
+
showFoldingControls: 'mouseover',
|
|
75
|
+
wordWrap: 'off'
|
|
60
76
|
};
|