Skip to content

Commit 30c6334

Browse files
committed
small readme update + add new setting to choose to open docs in webview or browser
1 parent 116c131 commit 30c6334

8 files changed

Lines changed: 192 additions & 145 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,5 @@ The short answer is no, there isn't an intentional Remote Code Execution vulnera
105105

106106
If you have an idea or want to contribute a feature, please first [create an issue](https://github.com/VSC-NeuroPilot/neuropilot/issues) or send a message to `@Pasu4` in the project's [post on the Neuro Discord](https://discord.com/channels/574720535888396288/1350968830230396938).
107107
If you make a pull request that contributes code, please run `npm run lint src` and resolve any errors that did not get auto-fixed, preferrably before each commit.
108+
109+
Please also refer to our [contributor docs](https://vsc-neuropilot.github.io/neuropilot/contributors).

package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,26 @@
560560
"type": "boolean",
561561
"default": false,
562562
"markdownDescription": "Make the real cursor follow Neuro's cursor."
563+
},
564+
"neuropilot.defaultOpenDocsWindow": {
565+
"type": "string",
566+
"default": "ask",
567+
"enum": [
568+
"ask",
569+
"alwaysBrowser",
570+
"alwaysWebView"
571+
],
572+
"enumItemLabels": [
573+
"Ask where to open",
574+
"Always open in browser",
575+
"Always open in a WebView"
576+
],
577+
"enumDescriptions": [
578+
"Always ask where to open the docs",
579+
"Always open docs in your browser",
580+
"Always open docs in a new WebView window"
581+
],
582+
"markdownDescription": "Choose where to open docs links.\n\nThis applies to both docs commands."
563583
}
564584
}
565585
}

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class Config {
9494
get cursorFollowsNeuro(): boolean { return get('cursorFollowsNeuro')!; }
9595
get currentlyAsNeuroAPI(): string { return get('currentlyAsNeuroAPI')!; }
9696
get docsURL(): string { return get('docsURL')!; }
97+
get defaultOpenDocsWindow(): string { return get('defaultOpenDocsWindow')!; }
9798

9899
get terminals(): { name: string; path: string; args?: string[]; }[] { return get('terminals')!; }
99100
}

src/desktop/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import {
1111
setupClientConnectedHandlers,
1212
createStatusBarItem,
1313
deactivate as commonDeactivate,
14-
registerDocsLink,
1514
getDecorationRenderOptions,
1615
obtainExtensionState,
1716
reloadPermissions,
1817
} from '../shared/extension';
18+
import { registerDocsLink } from '../shared/docs';
1919
import { registerChatParticipant } from '../chat';
2020
import { registerUnsupervisedActions, registerUnsupervisedHandlers } from './unsupervised';
2121

src/shared/docs.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import * as vscode from 'vscode';
2+
import { logOutput } from '../utils';
3+
import { CONFIG } from '../config';
4+
5+
// Shared docs management
6+
export const docsOptions: Record<string, string> = {
7+
'NeuroPilot': CONFIG.docsURL,
8+
'NeuroPilot (Local Dev Default)': 'http://127.0.0.1:4321/neuropilot',
9+
};
10+
export let docsItems: string[] = Object.keys(docsOptions);
11+
12+
export function registerDocsLink(name: string, link: string) {
13+
docsOptions[name] = link;
14+
docsItems = Object.keys(docsOptions);
15+
}
16+
17+
export function openDocsPage(docsSite: string, subpage = '/'): string {
18+
let constructedDocsPage: string = docsSite;
19+
if (subpage.startsWith('/')) {
20+
constructedDocsPage += subpage;
21+
} else {
22+
constructedDocsPage += '/' + subpage;
23+
}
24+
25+
logOutput('DEBUG', `Constructed docs page is ${constructedDocsPage}`);
26+
27+
const docsOrigin = new URL(docsSite).origin;
28+
29+
const htmlpage =
30+
'<!DOCTYPE html>' +
31+
'<html lang="en">' +
32+
'<head>' +
33+
' <meta charset="UTF-8">' +
34+
` <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src ${docsOrigin}; script-src 'none'; style-src 'unsafe-inline';">` +
35+
' <meta name="viewport" content="width=device-width, initial-scale=1.0">' +
36+
' <title>NeuroPilot Docs WebView</title>' +
37+
' <style>' +
38+
' html, body, iframe {' +
39+
' width: 100%;' +
40+
' height: 100%;' +
41+
' margin: 0;' +
42+
' padding: 0;' +
43+
' overflow: hidden;' +
44+
' }' +
45+
' iframe {' +
46+
' display: block;' +
47+
' }' +
48+
' </style>' +
49+
'</head>' +
50+
'<body>' +
51+
` <iframe src="${constructedDocsPage}" frameborder="0"></iframe>` +
52+
'</body>' +
53+
'</html>';
54+
55+
return htmlpage;
56+
}
57+
58+
export function openDocsPanel(selectedOption: string, baseUrl: string, subpage?: string): void {
59+
const panel = vscode.window.createWebviewPanel(
60+
'docsWebView',
61+
`${selectedOption} Docs (WebView)`,
62+
vscode.ViewColumn.Active,
63+
{ enableScripts: true },
64+
);
65+
panel.webview.html = openDocsPage(baseUrl, subpage);
66+
}
67+
68+
export async function openDocsOnTarget(selectedOption: string, baseUrl: string, subpage?: string) {
69+
if (CONFIG.defaultOpenDocsWindow === 'ask') {
70+
const windowPanel = vscode.window.createQuickPick();
71+
windowPanel.items = [
72+
{
73+
label: 'Default web browser',
74+
},
75+
{
76+
label: 'In-editor WebView',
77+
},
78+
];
79+
windowPanel.placeholder = 'Where do you want to open this?';
80+
81+
windowPanel.onDidAccept(async () => {
82+
const selectedTarget = windowPanel.selectedItems[0].label;
83+
if (selectedTarget === 'Default web browser') {
84+
vscode.env.openExternal(await vscode.env.asExternalUri(vscode.Uri.parse(baseUrl, true)));
85+
} else if (selectedTarget === 'In-editor WebView') {
86+
openDocsPanel(selectedOption, baseUrl, subpage);
87+
} else {
88+
logOutput('ERROR', 'Unknown target option supplied for opening docs.');
89+
return;
90+
};
91+
windowPanel.hide();
92+
});
93+
windowPanel.show();
94+
}
95+
else if (CONFIG.defaultOpenDocsWindow === 'alwaysBrowser') vscode.env.openExternal(await vscode.env.asExternalUri(vscode.Uri.parse(baseUrl, true)));
96+
else if (CONFIG.defaultOpenDocsWindow === 'alwaysWebView') openDocsPanel(selectedOption, baseUrl, subpage);
97+
}
98+
99+
export function registerDocsCommands() {
100+
const showDocsCommand = vscode.commands.registerCommand('neuropilot.showDocsHomepage', async () => {
101+
const quickPick = vscode.window.createQuickPick();
102+
quickPick.items = docsItems.map(key => ({ label: key }));
103+
quickPick.placeholder = 'Select a documentation base URL';
104+
105+
quickPick.onDidAccept(async () => {
106+
const selectedOption = quickPick.selectedItems[0]?.label;
107+
if (!selectedOption) {
108+
vscode.window.showErrorMessage('No documentation option selected.');
109+
quickPick.hide();
110+
return;
111+
}
112+
113+
const baseUrl = docsOptions[selectedOption];
114+
logOutput('DEBUG', `Opening ${selectedOption}'s docs at URL ${baseUrl}`);
115+
116+
await openDocsOnTarget(selectedOption, baseUrl);
117+
118+
quickPick.hide();
119+
});
120+
121+
quickPick.show();
122+
});
123+
124+
const openSpecificDocsCommand = vscode.commands.registerCommand('neuropilot.openSpecificDocsPage', async (args?: { subpage?: string }) => {
125+
const quickPick = vscode.window.createQuickPick();
126+
quickPick.items = docsItems.map(key => ({ label: key }));
127+
quickPick.placeholder = 'Select a documentation base URL';
128+
129+
quickPick.onDidAccept(async () => {
130+
const selectedOption = quickPick.selectedItems[0].label;
131+
if (!selectedOption) {
132+
vscode.window.showErrorMessage('No documentation option selected.');
133+
quickPick.hide();
134+
return;
135+
}
136+
137+
const baseUrl = docsOptions[selectedOption];
138+
139+
let subpage: string | undefined;
140+
if (args && typeof args.subpage === 'string') {
141+
subpage = args.subpage;
142+
} else {
143+
subpage = await vscode.window.showInputBox({
144+
prompt: 'Enter the docs subpath (e.g., /guide, /api, etc.)',
145+
placeHolder: '/',
146+
});
147+
}
148+
149+
if (!subpage) {
150+
vscode.window.showErrorMessage('No subpage specified.');
151+
quickPick.hide();
152+
return;
153+
}
154+
155+
logOutput('DEBUG', `Opening ${selectedOption}'s docs at URL ${baseUrl}${subpage}`);
156+
157+
await openDocsOnTarget(selectedOption, baseUrl, subpage);
158+
159+
quickPick.hide();
160+
});
161+
162+
quickPick.show();
163+
});
164+
165+
return [showDocsCommand, openSpecificDocsCommand];
166+
}

src/shared/extension.ts

Lines changed: 1 addition & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -10,104 +10,9 @@ import { editorChangeHandler, fileSaveListener, moveNeuroCursorHere, toggleSaveA
1010
import { emergencyDenyRequests, acceptRceRequest, denyRceRequest, revealRceNotification } from '../rce';
1111
import type { GitExtension } from '../types/git';
1212
import { getGitExtension } from '../git';
13-
14-
// Shared docs management
15-
let docsOptions: Record<string, string>;
16-
let docsItems: string[];
17-
18-
export function registerDocsLink(name: string, link: string) {
19-
docsOptions[name] = link;
20-
docsItems = Object.keys(docsOptions);
21-
}
22-
23-
function initializeDocsOptions() {
24-
docsOptions = {
25-
'NeuroPilot': CONFIG.docsURL,
26-
'NeuroPilot (Local Dev Default)': 'http://127.0.0.1:4321/neuropilot',
27-
};
28-
docsItems = Object.keys(docsOptions);
29-
}
13+
import { registerDocsCommands, registerDocsLink } from './docs';
3014

3115
// Shared commands
32-
export function registerDocsCommands() {
33-
const showDocsCommand = vscode.commands.registerCommand('neuropilot.showDocsHomepage', async () => {
34-
const quickPick = vscode.window.createQuickPick();
35-
quickPick.items = docsItems.map(key => ({ label: key }));
36-
quickPick.placeholder = 'Select a documentation base URL';
37-
38-
quickPick.onDidAccept(async () => {
39-
const selectedOption = quickPick.selectedItems[0]?.label;
40-
if (!selectedOption) {
41-
vscode.window.showErrorMessage('No documentation option selected.');
42-
quickPick.hide();
43-
return;
44-
}
45-
46-
const baseUrl = docsOptions[selectedOption];
47-
logOutput('DEBUG', `Opening ${selectedOption}'s docs at URL ${baseUrl}`);
48-
49-
const panel = vscode.window.createWebviewPanel(
50-
'docsWebView',
51-
`${selectedOption} Docs (WebView)`,
52-
vscode.ViewColumn.Active,
53-
{ enableScripts: true },
54-
);
55-
panel.webview.html = openDocsPage(baseUrl);
56-
quickPick.hide();
57-
});
58-
59-
quickPick.show();
60-
});
61-
62-
const openSpecificDocsCommand = vscode.commands.registerCommand('neuropilot.openSpecificDocsPage', async (args?: { subpage?: string }) => {
63-
const quickPick = vscode.window.createQuickPick();
64-
quickPick.items = docsItems.map(key => ({ label: key }));
65-
quickPick.placeholder = 'Select a documentation base URL';
66-
67-
quickPick.onDidAccept(async () => {
68-
const selectedOption = quickPick.selectedItems[0]?.label;
69-
if (!selectedOption) {
70-
vscode.window.showErrorMessage('No documentation option selected.');
71-
quickPick.hide();
72-
return;
73-
}
74-
75-
const baseUrl = docsOptions[selectedOption];
76-
77-
let subpage: string | undefined;
78-
if (args && typeof args.subpage === 'string') {
79-
subpage = args.subpage;
80-
} else {
81-
subpage = await vscode.window.showInputBox({
82-
prompt: 'Enter the docs subpath (e.g., /guide, /api, etc.)',
83-
placeHolder: '/',
84-
});
85-
}
86-
87-
if (!subpage) {
88-
vscode.window.showErrorMessage('No subpage specified.');
89-
quickPick.hide();
90-
return;
91-
}
92-
93-
logOutput('DEBUG', `Opening ${selectedOption}'s docs at URL ${baseUrl}${subpage}`);
94-
95-
const panel = vscode.window.createWebviewPanel(
96-
'docsWebView',
97-
`${selectedOption} Docs (WebView)`,
98-
vscode.ViewColumn.Active,
99-
{ enableScripts: true },
100-
);
101-
panel.webview.html = openDocsPage(baseUrl, subpage);
102-
quickPick.hide();
103-
});
104-
105-
quickPick.show();
106-
});
107-
108-
return [showDocsCommand, openSpecificDocsCommand];
109-
}
110-
11116
export function registerCommonCommands() {
11217
return [
11318
vscode.commands.registerCommand('neuropilot.reconnect', reconnect),
@@ -172,8 +77,6 @@ export function setupCommonEventHandlers() {
17277
}
17378

17479
export function initializeCommonState(context: vscode.ExtensionContext) {
175-
initializeDocsOptions();
176-
17780
NEURO.context = context;
17881
NEURO.url = CONFIG.websocketUrl;
17982
NEURO.gameName = CONFIG.gameName;
@@ -307,47 +210,6 @@ function switchCurrentNeuroAPIUser() {
307210
quickPick.show();
308211
}
309212

310-
function openDocsPage(docsSite: string, subpage = '/'): string {
311-
let constructedDocsPage: string = docsSite;
312-
if (subpage.startsWith('/')) {
313-
constructedDocsPage += subpage;
314-
} else {
315-
constructedDocsPage += '/' + subpage;
316-
}
317-
318-
logOutput('DEBUG', `Constructed docs page is ${constructedDocsPage}`);
319-
320-
const docsOrigin = new URL(docsSite).origin;
321-
322-
const htmlpage =
323-
'<!DOCTYPE html>' +
324-
'<html lang="en">' +
325-
'<head>' +
326-
' <meta charset="UTF-8">' +
327-
` <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src ${docsOrigin}; script-src 'none'; style-src 'unsafe-inline';">` +
328-
' <meta name="viewport" content="width=device-width, initial-scale=1.0">' +
329-
' <title>NeuroPilot Docs WebView</title>' +
330-
' <style>' +
331-
' html, body, iframe {' +
332-
' width: 100%;' +
333-
' height: 100%;' +
334-
' margin: 0;' +
335-
' padding: 0;' +
336-
' overflow: hidden;' +
337-
' }' +
338-
' iframe {' +
339-
' display: block;' +
340-
' }' +
341-
' </style>' +
342-
'</head>' +
343-
'<body>' +
344-
` <iframe src="${constructedDocsPage}" frameborder="0"></iframe>` +
345-
'</body>' +
346-
'</html>';
347-
348-
return htmlpage;
349-
}
350-
351213
export function obtainExtensionState(): void {
352214
const copilotChat = vscode.extensions.getExtension('github.copilot-chat')?.isActive;
353215
EXTENSIONS.copilotChat = copilotChat === true;

src/web/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import {
99
setupClientConnectedHandlers,
1010
createStatusBarItem,
1111
deactivate as commonDeactivate,
12-
registerDocsLink,
1312
obtainExtensionState,
1413
getDecorationRenderOptions,
1514
reloadPermissions,
1615
} from '../shared/extension';
16+
import { registerDocsLink } from '../shared/docs';
1717
import { registerUnsupervisedActions, registerUnsupervisedHandlers } from './unsupervised';
1818

1919
export { registerDocsLink };

0 commit comments

Comments
 (0)