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.
- 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 +134 -68
- 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/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
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(chmod:*)",
|
|
5
|
-
"Bash(npm install)",
|
|
6
|
-
"Bash(npm start)",
|
|
7
|
-
"Bash(pkill:*)",
|
|
8
|
-
"Bash(node:*)",
|
|
9
|
-
"Bash(curl:*)",
|
|
10
|
-
"Bash(npm install:*)",
|
|
11
|
-
"Bash(git init:*)",
|
|
12
|
-
"Bash(git add:*)",
|
|
13
|
-
"Bash(git commit:*)",
|
|
14
|
-
"Bash(git rm:*)",
|
|
15
|
-
"Bash(ssh:*)",
|
|
16
|
-
"Bash(git restore:*)",
|
|
17
|
-
"Bash(npx gh-here:*)",
|
|
18
|
-
"Bash(git checkout:*)",
|
|
19
|
-
"Bash(git push:*)",
|
|
20
|
-
"Bash(git pull:*)",
|
|
21
|
-
"Bash(npm publish:*)",
|
|
22
|
-
"Bash(timeout:*)",
|
|
23
|
-
"Bash(npm version:*)",
|
|
24
|
-
"Bash(gh release create:*)",
|
|
25
|
-
"Bash(tree:*)"
|
|
26
|
-
],
|
|
27
|
-
"deny": [],
|
|
28
|
-
"ask": []
|
|
29
|
-
}
|
|
30
|
-
}
|
package/SAMPLE.md
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
# Syntax Highlighting Test
|
|
2
|
-
|
|
3
|
-
This file contains code samples in various languages to test syntax highlighting in markdown rendering.
|
|
4
|
-
|
|
5
|
-
## JavaScript
|
|
6
|
-
|
|
7
|
-
```javascript
|
|
8
|
-
function greet(name) {
|
|
9
|
-
const message = `Hello, ${name}!`;
|
|
10
|
-
console.log(message);
|
|
11
|
-
return message;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class Person {
|
|
15
|
-
constructor(name, age) {
|
|
16
|
-
this.name = name;
|
|
17
|
-
this.age = age;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
introduce() {
|
|
21
|
-
return `My name is ${this.name} and I'm ${this.age} years old.`;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Python
|
|
27
|
-
|
|
28
|
-
```python
|
|
29
|
-
def calculate_fibonacci(n):
|
|
30
|
-
"""Calculate the nth Fibonacci number."""
|
|
31
|
-
if n <= 1:
|
|
32
|
-
return n
|
|
33
|
-
return calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2)
|
|
34
|
-
|
|
35
|
-
class Animal:
|
|
36
|
-
def __init__(self, name, species):
|
|
37
|
-
self.name = name
|
|
38
|
-
self.species = species
|
|
39
|
-
|
|
40
|
-
def make_sound(self):
|
|
41
|
-
return f"{self.name} the {self.species} makes a sound"
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Go
|
|
45
|
-
|
|
46
|
-
```go
|
|
47
|
-
package main
|
|
48
|
-
|
|
49
|
-
import (
|
|
50
|
-
"fmt"
|
|
51
|
-
"time"
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
type User struct {
|
|
55
|
-
Name string
|
|
56
|
-
Email string
|
|
57
|
-
CreatedAt time.Time
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
func (u *User) SendEmail(message string) error {
|
|
61
|
-
fmt.Printf("Sending email to %s: %s\n", u.Email, message)
|
|
62
|
-
return nil
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
func main() {
|
|
66
|
-
user := &User{
|
|
67
|
-
Name: "John Doe",
|
|
68
|
-
Email: "john@example.com",
|
|
69
|
-
CreatedAt: time.Now(),
|
|
70
|
-
}
|
|
71
|
-
user.SendEmail("Welcome!")
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Rust
|
|
76
|
-
|
|
77
|
-
```rust
|
|
78
|
-
use std::collections::HashMap;
|
|
79
|
-
|
|
80
|
-
fn main() {
|
|
81
|
-
let mut scores = HashMap::new();
|
|
82
|
-
scores.insert(String::from("Blue"), 10);
|
|
83
|
-
scores.insert(String::from("Red"), 50);
|
|
84
|
-
|
|
85
|
-
for (key, value) in &scores {
|
|
86
|
-
println!("{}: {}", key, value);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
struct Rectangle {
|
|
91
|
-
width: u32,
|
|
92
|
-
height: u32,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
impl Rectangle {
|
|
96
|
-
fn area(&self) -> u32 {
|
|
97
|
-
self.width * self.height
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## TypeScript
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
interface User {
|
|
106
|
-
id: number;
|
|
107
|
-
name: string;
|
|
108
|
-
email: string;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function fetchUser(id: number): Promise<User> {
|
|
112
|
-
const response = await fetch(`/api/users/${id}`);
|
|
113
|
-
const data: User = await response.json();
|
|
114
|
-
return data;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
class UserService {
|
|
118
|
-
private users: Map<number, User> = new Map();
|
|
119
|
-
|
|
120
|
-
addUser(user: User): void {
|
|
121
|
-
this.users.set(user.id, user);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
getUser(id: number): User | undefined {
|
|
125
|
-
return this.users.get(id);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## Bash
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
#!/bin/bash
|
|
134
|
-
|
|
135
|
-
# Function to backup files
|
|
136
|
-
backup_files() {
|
|
137
|
-
local source_dir=$1
|
|
138
|
-
local backup_dir=$2
|
|
139
|
-
|
|
140
|
-
if [ ! -d "$backup_dir" ]; then
|
|
141
|
-
mkdir -p "$backup_dir"
|
|
142
|
-
fi
|
|
143
|
-
|
|
144
|
-
tar -czf "$backup_dir/backup-$(date +%Y%m%d).tar.gz" "$source_dir"
|
|
145
|
-
echo "Backup completed successfully!"
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
backup_files "/home/user/documents" "/home/user/backups"
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## SQL
|
|
152
|
-
|
|
153
|
-
```sql
|
|
154
|
-
-- Create users table
|
|
155
|
-
CREATE TABLE users (
|
|
156
|
-
id SERIAL PRIMARY KEY,
|
|
157
|
-
username VARCHAR(50) UNIQUE NOT NULL,
|
|
158
|
-
email VARCHAR(100) UNIQUE NOT NULL,
|
|
159
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
-- Insert some data
|
|
163
|
-
INSERT INTO users (username, email) VALUES
|
|
164
|
-
('john_doe', 'john@example.com'),
|
|
165
|
-
('jane_smith', 'jane@example.com');
|
|
166
|
-
|
|
167
|
-
-- Query with JOIN
|
|
168
|
-
SELECT u.username, o.order_date, o.total
|
|
169
|
-
FROM users u
|
|
170
|
-
INNER JOIN orders o ON u.id = o.user_id
|
|
171
|
-
WHERE o.total > 100
|
|
172
|
-
ORDER BY o.order_date DESC;
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## JSON
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"name": "gh-here",
|
|
180
|
-
"version": "2.0.0",
|
|
181
|
-
"description": "A local GitHub-like file browser",
|
|
182
|
-
"dependencies": {
|
|
183
|
-
"express": "^4.18.2",
|
|
184
|
-
"highlight.js": "^11.9.0",
|
|
185
|
-
"marked": "^12.0.0"
|
|
186
|
-
},
|
|
187
|
-
"scripts": {
|
|
188
|
-
"start": "node bin/gh-here.js",
|
|
189
|
-
"test": "jest"
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
## CSS
|
|
195
|
-
|
|
196
|
-
```css
|
|
197
|
-
:root {
|
|
198
|
-
--primary-color: #0366d6;
|
|
199
|
-
--background-color: #ffffff;
|
|
200
|
-
--text-color: #24292e;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.container {
|
|
204
|
-
max-width: 1200px;
|
|
205
|
-
margin: 0 auto;
|
|
206
|
-
padding: 20px;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
.button {
|
|
210
|
-
display: inline-block;
|
|
211
|
-
padding: 10px 20px;
|
|
212
|
-
background-color: var(--primary-color);
|
|
213
|
-
color: white;
|
|
214
|
-
border-radius: 5px;
|
|
215
|
-
transition: background-color 0.3s ease;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
.button:hover {
|
|
219
|
-
background-color: #0256c7;
|
|
220
|
-
cursor: pointer;
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
## HTML
|
|
225
|
-
|
|
226
|
-
```html
|
|
227
|
-
<!DOCTYPE html>
|
|
228
|
-
<html lang="en">
|
|
229
|
-
<head>
|
|
230
|
-
<meta charset="UTF-8">
|
|
231
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
232
|
-
<title>Sample Page</title>
|
|
233
|
-
<link rel="stylesheet" href="styles.css">
|
|
234
|
-
</head>
|
|
235
|
-
<body>
|
|
236
|
-
<header>
|
|
237
|
-
<h1>Welcome to My Site</h1>
|
|
238
|
-
<nav>
|
|
239
|
-
<ul>
|
|
240
|
-
<li><a href="#home">Home</a></li>
|
|
241
|
-
<li><a href="#about">About</a></li>
|
|
242
|
-
<li><a href="#contact">Contact</a></li>
|
|
243
|
-
</ul>
|
|
244
|
-
</nav>
|
|
245
|
-
</header>
|
|
246
|
-
<main>
|
|
247
|
-
<p>This is the main content area.</p>
|
|
248
|
-
</main>
|
|
249
|
-
<script src="script.js"></script>
|
|
250
|
-
</body>
|
|
251
|
-
</html>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## Java
|
|
255
|
-
|
|
256
|
-
```java
|
|
257
|
-
import java.util.ArrayList;
|
|
258
|
-
import java.util.List;
|
|
259
|
-
|
|
260
|
-
public class BankAccount {
|
|
261
|
-
private String accountNumber;
|
|
262
|
-
private double balance;
|
|
263
|
-
private List<Transaction> transactions;
|
|
264
|
-
|
|
265
|
-
public BankAccount(String accountNumber, double initialBalance) {
|
|
266
|
-
this.accountNumber = accountNumber;
|
|
267
|
-
this.balance = initialBalance;
|
|
268
|
-
this.transactions = new ArrayList<>();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
public void deposit(double amount) {
|
|
272
|
-
if (amount > 0) {
|
|
273
|
-
balance += amount;
|
|
274
|
-
transactions.add(new Transaction("DEPOSIT", amount));
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
public boolean withdraw(double amount) {
|
|
279
|
-
if (amount > 0 && balance >= amount) {
|
|
280
|
-
balance -= amount;
|
|
281
|
-
transactions.add(new Transaction("WITHDRAWAL", amount));
|
|
282
|
-
return true;
|
|
283
|
-
}
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
```
|
package/lib/validation.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Input validation and security utilities
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Validates that a path is within the allowed working directory
|
|
9
|
-
* Prevents path traversal attacks
|
|
10
|
-
*/
|
|
11
|
-
function validatePath(requestPath, workingDir) {
|
|
12
|
-
const fullPath = path.resolve(path.join(workingDir, requestPath || ''));
|
|
13
|
-
return fullPath.startsWith(workingDir);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Sanitizes file paths to prevent injection
|
|
18
|
-
*/
|
|
19
|
-
function sanitizePath(filePath) {
|
|
20
|
-
if (!filePath) {
|
|
21
|
-
return '';
|
|
22
|
-
}
|
|
23
|
-
return filePath.replace(/\.\./g, '').replace(/[<>:"|?*]/g, '');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Validates commit message
|
|
28
|
-
*/
|
|
29
|
-
function validateCommitMessage(message) {
|
|
30
|
-
if (!message || typeof message !== 'string') {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
return message.trim().length > 0 && message.trim().length <= 5000;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validates filename
|
|
38
|
-
*/
|
|
39
|
-
function validateFilename(filename) {
|
|
40
|
-
if (!filename || typeof filename !== 'string') {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const sanitized = filename.trim();
|
|
45
|
-
|
|
46
|
-
if (sanitized.length === 0 || sanitized.length > 255) {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const invalidChars = /[<>:"|?*\x00-\x1F]/;
|
|
51
|
-
if (invalidChars.test(sanitized)) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Validates an array of file paths
|
|
60
|
-
*/
|
|
61
|
-
function validateFilePaths(files) {
|
|
62
|
-
if (!Array.isArray(files) || files.length === 0) {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return files.every(file => {
|
|
67
|
-
return typeof file === 'string' && file.trim().length > 0;
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
module.exports = {
|
|
72
|
-
validatePath,
|
|
73
|
-
sanitizePath,
|
|
74
|
-
validateCommitMessage,
|
|
75
|
-
validateFilename,
|
|
76
|
-
validateFilePaths
|
|
77
|
-
};
|