gh-here 3.0.3 → 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.
Files changed (41) hide show
  1. package/.env +0 -0
  2. package/.playwright-mcp/fixed-alignment.png +0 -0
  3. package/.playwright-mcp/fixed-layout.png +0 -0
  4. package/.playwright-mcp/gh-here-home-header-table.png +0 -0
  5. package/.playwright-mcp/gh-here-home.png +0 -0
  6. package/.playwright-mcp/line-selection-multiline.png +0 -0
  7. package/.playwright-mcp/line-selection-test-after.png +0 -0
  8. package/.playwright-mcp/line-selection-test-before.png +0 -0
  9. package/.playwright-mcp/page-2026-01-03T17-58-21-336Z.png +0 -0
  10. package/lib/constants.js +25 -15
  11. package/lib/content-search.js +212 -0
  12. package/lib/error-handler.js +39 -28
  13. package/lib/file-utils.js +438 -287
  14. package/lib/git.js +10 -54
  15. package/lib/gitignore.js +70 -41
  16. package/lib/renderers.js +15 -19
  17. package/lib/server.js +70 -193
  18. package/lib/symbol-parser.js +600 -0
  19. package/package.json +1 -1
  20. package/public/app.js +134 -68
  21. package/public/js/constants.js +50 -34
  22. package/public/js/content-search-handler.js +551 -0
  23. package/public/js/file-viewer.js +437 -0
  24. package/public/js/focus-mode.js +280 -0
  25. package/public/js/inline-search.js +659 -0
  26. package/public/js/modal-manager.js +14 -28
  27. package/public/js/symbol-outline.js +454 -0
  28. package/public/js/utils.js +152 -94
  29. package/public/styles.css +2049 -296
  30. package/.claude/settings.local.json +0 -30
  31. package/SAMPLE.md +0 -287
  32. package/lib/validation.js +0 -77
  33. package/public/app.js.backup +0 -1902
  34. package/public/js/draft-manager.js +0 -36
  35. package/public/js/editor-manager.js +0 -159
  36. package/test.js +0 -138
  37. package/tests/draftManager.test.js +0 -241
  38. package/tests/fileTypeDetection.test.js +0 -111
  39. package/tests/httpService.test.js +0 -268
  40. package/tests/languageDetection.test.js +0 -145
  41. package/tests/pathUtils.test.js +0 -136
@@ -1,102 +1,92 @@
1
1
  /**
2
- * Utility functions for path and URL manipulation
2
+ * Utility functions for path manipulation, language detection, and HTML escaping
3
+ * @module utils
3
4
  */
4
5
 
5
- export const PathUtils = {
6
- getCurrentPath() {
7
- const currentUrl = new URL(window.location.href);
8
- return currentUrl.searchParams.get('path') || '';
9
- },
10
-
11
- getParentPath(currentPath) {
12
- if (!currentPath || currentPath === '') {
13
- return null;
14
- }
6
+ // ============================================================================
7
+ // Language Map (alpha-sorted by key)
8
+ // ============================================================================
15
9
 
16
- const pathParts = currentPath.split('/').filter(p => p);
17
- if (pathParts.length === 0) {
18
- return null;
19
- }
20
-
21
- pathParts.pop();
22
- return pathParts.join('/');
23
- },
10
+ const LANGUAGE_MAP = {
11
+ bash: 'shell',
12
+ c: 'c',
13
+ cc: 'cpp',
14
+ clj: 'clojure',
15
+ cpp: 'cpp',
16
+ css: 'css',
17
+ cxx: 'cpp',
18
+ dart: 'dart',
19
+ fish: 'shell',
20
+ go: 'go',
21
+ groovy: 'groovy',
22
+ h: 'c',
23
+ hpp: 'cpp',
24
+ htm: 'html',
25
+ html: 'html',
26
+ java: 'java',
27
+ js: 'javascript',
28
+ json: 'json',
29
+ jsx: 'javascript',
30
+ kt: 'kotlin',
31
+ less: 'less',
32
+ log: 'plaintext',
33
+ lua: 'lua',
34
+ md: 'markdown',
35
+ mjs: 'javascript',
36
+ php: 'php',
37
+ pl: 'perl',
38
+ ps1: 'powershell',
39
+ py: 'python',
40
+ r: 'r',
41
+ rb: 'ruby',
42
+ rs: 'rust',
43
+ sass: 'sass',
44
+ scala: 'scala',
45
+ scss: 'scss',
46
+ sh: 'shell',
47
+ sql: 'sql',
48
+ swift: 'swift',
49
+ ts: 'typescript',
50
+ tsx: 'typescript',
51
+ txt: 'plaintext',
52
+ xml: 'xml',
53
+ yaml: 'yaml',
54
+ yml: 'yaml',
55
+ zsh: 'shell'
56
+ };
24
57
 
25
- buildFilePath(currentPath, filename) {
26
- return currentPath ? `${currentPath}/${filename}` : filename;
27
- },
58
+ // ============================================================================
59
+ // HTML Utilities
60
+ // ============================================================================
28
61
 
29
- getFileName(filePath) {
30
- return filePath.split('/').pop() || 'file.txt';
31
- },
32
-
33
- buildPathUrl(basePath, targetPath) {
34
- return targetPath ? `${basePath}?path=${encodeURIComponent(targetPath)}` : basePath;
35
- },
62
+ /**
63
+ * Escapes HTML special characters to prevent XSS attacks
64
+ * @param {string} text - Text to escape
65
+ * @returns {string} Escaped HTML string
66
+ */
67
+ export function escapeHtml(text) {
68
+ if (typeof text !== 'string') return '';
69
+ const div = document.createElement('div');
70
+ div.textContent = text;
71
+ return div.innerHTML;
72
+ }
36
73
 
37
- getDirectoryPath(filePath) {
38
- const parts = filePath.split('/').filter(p => p);
39
- if (parts.length <= 1) {
40
- return '';
41
- }
42
- return parts.slice(0, -1).join('/');
43
- }
44
- };
74
+ // ============================================================================
75
+ // Language Detection
76
+ // ============================================================================
45
77
 
46
78
  /**
47
- * Language detection utility
79
+ * Detects programming language from filename extension
80
+ * @param {string} filename - Filename to analyze
81
+ * @returns {string} Language identifier for syntax highlighting
48
82
  */
49
83
  export function getLanguageFromExtension(filename) {
50
- const ext = filename.split('.').pop().toLowerCase();
51
- const languageMap = {
52
- js: 'javascript',
53
- mjs: 'javascript',
54
- jsx: 'javascript',
55
- ts: 'typescript',
56
- tsx: 'typescript',
57
- html: 'html',
58
- htm: 'html',
59
- css: 'css',
60
- scss: 'scss',
61
- sass: 'sass',
62
- less: 'less',
63
- json: 'json',
64
- xml: 'xml',
65
- yaml: 'yaml',
66
- yml: 'yaml',
67
- py: 'python',
68
- java: 'java',
69
- go: 'go',
70
- rs: 'rust',
71
- php: 'php',
72
- rb: 'ruby',
73
- swift: 'swift',
74
- kt: 'kotlin',
75
- dart: 'dart',
76
- c: 'c',
77
- cpp: 'cpp',
78
- cc: 'cpp',
79
- cxx: 'cpp',
80
- h: 'c',
81
- hpp: 'cpp',
82
- sh: 'shell',
83
- bash: 'shell',
84
- zsh: 'shell',
85
- fish: 'shell',
86
- ps1: 'powershell',
87
- sql: 'sql',
88
- r: 'r',
89
- scala: 'scala',
90
- clj: 'clojure',
91
- lua: 'lua',
92
- pl: 'perl',
93
- groovy: 'groovy',
94
- md: 'markdown',
95
- txt: 'plaintext',
96
- log: 'plaintext'
97
- };
98
-
84
+ if (!filename) return 'plaintext';
85
+
99
86
  const basename = filename.toLowerCase();
87
+ const ext = filename.split('.').pop()?.toLowerCase();
88
+
89
+ // Special filenames
100
90
  if (basename === 'dockerfile' || basename.startsWith('dockerfile.')) {
101
91
  return 'dockerfile';
102
92
  }
@@ -110,14 +100,82 @@ export function getLanguageFromExtension(filename) {
110
100
  return 'json';
111
101
  }
112
102
 
113
- return languageMap[ext] || 'plaintext';
103
+ return LANGUAGE_MAP[ext] || 'plaintext';
114
104
  }
115
105
 
106
+ // ============================================================================
107
+ // Path Utilities
108
+ // ============================================================================
109
+
116
110
  /**
117
- * HTML escaping utility
111
+ * Path manipulation utilities
118
112
  */
119
- export function escapeHtml(text) {
120
- const div = document.createElement('div');
121
- div.textContent = text;
122
- return div.innerHTML;
123
- }
113
+ export const PathUtils = {
114
+ /**
115
+ * Builds a file path from directory and filename
116
+ * @param {string} currentPath - Current directory path
117
+ * @param {string} filename - Filename to append
118
+ * @returns {string} Combined file path
119
+ */
120
+ buildFilePath(currentPath, filename) {
121
+ return currentPath ? `${currentPath}/${filename}` : filename;
122
+ },
123
+
124
+ /**
125
+ * Builds a URL with path query parameter
126
+ * @param {string} basePath - Base URL path
127
+ * @param {string} targetPath - Target file/directory path
128
+ * @returns {string} URL with encoded path parameter
129
+ */
130
+ buildPathUrl(basePath, targetPath) {
131
+ return targetPath ? `${basePath}?path=${encodeURIComponent(targetPath)}` : basePath;
132
+ },
133
+
134
+ /**
135
+ * Gets the current path from URL query parameters
136
+ * @returns {string} Current path or empty string
137
+ */
138
+ getCurrentPath() {
139
+ const currentUrl = new URL(window.location.href);
140
+ return currentUrl.searchParams.get('path') || '';
141
+ },
142
+
143
+ /**
144
+ * Gets the directory portion of a file path
145
+ * @param {string} filePath - Full file path
146
+ * @returns {string} Directory path
147
+ */
148
+ getDirectoryPath(filePath) {
149
+ const parts = filePath.split('/').filter(p => p);
150
+ if (parts.length <= 1) {
151
+ return '';
152
+ }
153
+ return parts.slice(0, -1).join('/');
154
+ },
155
+
156
+ /**
157
+ * Gets the filename from a full path
158
+ * @param {string} filePath - Full file path
159
+ * @returns {string} Filename
160
+ */
161
+ getFileName(filePath) {
162
+ return filePath.split('/').pop() || 'file.txt';
163
+ },
164
+
165
+ /**
166
+ * Gets the parent directory path
167
+ * @param {string} currentPath - Current path
168
+ * @returns {string|null} Parent path or null if at root
169
+ */
170
+ getParentPath(currentPath) {
171
+ if (!currentPath || currentPath === '') {
172
+ return null;
173
+ }
174
+ const pathParts = currentPath.split('/').filter(p => p);
175
+ if (pathParts.length === 0) {
176
+ return null;
177
+ }
178
+ pathParts.pop();
179
+ return pathParts.join('/');
180
+ }
181
+ };