Issue reporter wizard#317381
Conversation
Squashed from agents/issue-reporting-flow-fixes-8ab13430. Original branch preserved at agents/issue-reporting-flow-fixes-8ab13430.backup. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a new “issue reporter wizard” flow that can run inside an editor tab (instead of the classic auxiliary-window reporter), adding native screenshot capture, screenshot annotation, screen recording, and (desktop) GitHub attachment upload support. It also adjusts performance-info collection to optionally bypass caching and remove the default workspace file-walk cap on user-initiated refreshes.
Changes:
- Add editor-tab-based issue reporter wizard UI, including keybindings and a dedicated editor input/pane.
- Add screenshot/recording services (browser fallbacks + native Electron implementations) and GitHub “mobile upload API” attachment uploads via the main process.
- Enhance performance-info collection with options (
skipCache,unbounded) and improve workspace stats file-type reporting.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/test/electron-browser/workbenchTestServices.ts | Extends test native host stub with new native-host APIs (media access status, GitHub upload). |
| src/vs/workbench/contrib/issue/electron-browser/nativeScreenshotService.ts | New Electron implementation of IScreenshotService using INativeHostService.getScreenshot. |
| src/vs/workbench/contrib/issue/electron-browser/nativeRecordingService.ts | New Electron implementation of IRecordingService using getDisplayMedia + MediaRecorder. |
| src/vs/workbench/contrib/issue/electron-browser/nativeIssueFormService.ts | Routes issue reporter opening between legacy window and wizard, collecting native OS properties first. |
| src/vs/workbench/contrib/issue/electron-browser/nativeGitHubUploadService.ts | New desktop GitHub upload service delegating upload to the main process to bypass CORS. |
| src/vs/workbench/contrib/issue/electron-browser/issueService.ts | Adds config-gated wizard flow and background async data population for the reporter. |
| src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts | Registers wizard setting, editor pane/input, and native implementations of new services. |
| src/vs/workbench/contrib/issue/common/issue.ts | Extends IssueSource enum with Unknown. |
| src/vs/workbench/contrib/issue/browser/screenshotService.ts | Introduces IScreenshotService + browser fallback implementation. |
| src/vs/workbench/contrib/issue/browser/screenshotAnnotation.ts | Adds in-wizard screenshot annotation editor (canvas-based). |
| src/vs/workbench/contrib/issue/browser/recordingService.ts | Introduces IRecordingService + browser fallback implementation. |
| src/vs/workbench/contrib/issue/browser/media/issueReporterOverlay.css | Adds wizard/editor-tab UI styling (including floating capture bar and annotation UI). |
| src/vs/workbench/contrib/issue/browser/issueReporterModel.ts | Adjusts serialization (system info, extensions markdown) and adds issueSource support. |
| src/vs/workbench/contrib/issue/browser/issueReporterKeybindings.ts | Adds wizard-scoped keybindings (capture screenshot / toggle recording). |
| src/vs/workbench/contrib/issue/browser/issueReporterEditorPane.ts | New editor pane hosting the wizard overlay, wiring screenshot/recording/upload flows. |
| src/vs/workbench/contrib/issue/browser/issueReporterEditorInput.ts | New singleton editor input with close-confirmation for unsaved wizard changes. |
| src/vs/workbench/contrib/issue/browser/issueFormService.ts | Adds config-gated routing to wizard, GitHub attachment upload, and URL-length mitigation. |
| src/vs/workbench/contrib/issue/browser/issue.contribution.ts | Registers browser fallbacks for new services and loads wizard keybindings in web. |
| src/vs/workbench/contrib/issue/browser/githubUploadService.ts | Introduces IGitHubUploadService + browser fallback implementation. |
| src/vs/workbench/browser/layout.ts | Minor formatting-only change. |
| src/vs/platform/process/electron-main/processMainService.ts | Extends getPerformanceInfo to accept options and forwards to diagnostics. |
| src/vs/platform/process/common/process.ts | Updates IProcessService.getPerformanceInfo signature and documents options. |
| src/vs/platform/native/electron-main/nativeHostMainService.ts | Adds getMediaAccessStatus and main-process GitHub mobile upload API implementation. |
| src/vs/platform/native/common/native.ts | Extends ICommonNativeHostService with media access status + mobile upload API. |
| src/vs/platform/diagnostics/node/diagnosticsService.ts | Adds workspace-stats cache bypass/unbounded walk options and improves file-type accounting. |
| src/vs/platform/diagnostics/common/diagnostics.ts | Extends diagnostics performance-info API to accept options. |
| src/vs/code/electron-main/app.ts | Adds setDisplayMediaRequestHandler to auto-select the screen containing the requesting window. |
Copilot's findings
Comments suppressed due to low confidence (2)
src/vs/workbench/test/electron-browser/workbenchTestServices.ts:191
TestNativeHostService.uploadFileViaMobileApiis currently declared with no parameters, butINativeHostService.uploadFileViaMobileApirequires(token, repoId, fileName, fileBytes, contentType)and returns the upload result. This will fail type-checking and can break tests that rely on the stub. Align the stub signature with the interface (it can keep returning an empty result).
async profileRenderer(): Promise<any> { throw new Error(); }
async startTracing(): Promise<void> { throw new Error(); }
async getScreenshot(rect?: IRectangle): Promise<VSBuffer | undefined> { return undefined; }
async uploadFileViaMobileApi(): Promise<{ fileName: string; assetUrl: string; contentType: string }> { return { fileName: '', assetUrl: '', contentType: '' }; }
async showToast(options: IToastOptions): Promise<IToastResult> { return { supported: false, clicked: false }; }
src/vs/workbench/contrib/issue/browser/issueReporterEditorPane.ts:515
saveRecordingAndAddwrites the recording file under.../issue-recordings/but does not ensure that directory exists first.IFileService.writeFiledoes not create parent folders, so saving can fail on a fresh profile. Ensure the folder is created (e.g. viafileService.createFolder) before writing the file.
const extension = data.mimeType.includes('mp4') ? 'mp4' : 'webm';
const fileName = `vscode-recording-${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
const target = URI.joinPath(this.environmentService.userRoamingDataHome, 'issue-recordings', fileName);
const arrayBuffer = await data.blob.arrayBuffer();
await this.fileService.writeFile(target, VSBuffer.wrap(new Uint8Array(arrayBuffer)));
this.logService.info(`[IssueReporterEditorPane] Recording saved to ${target.toString()}`);
- Files reviewed: 27/28 changed files
- Comments generated: 6
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/vs/workbench/contrib/issue/browser/issueFormService.ts:141
filesToProcessalways uploads screenshots asimage/pngwith a.pngfilename, but the native screenshot capture returns adata:image/jpegURL when the user doesn't annotate. That produces a MIME/extension mismatch (JPEG bytes labeled as PNG). Consider parsing the MIME type from the data URL and choosing bothcontentTypeand filename extension accordingly.
for (let i = 0; i < screenshots.length; i++) {
const dataUrl = screenshots[i].annotatedDataUrl ?? screenshots[i].dataUrl;
const bytes = this.dataUrlToBytes(dataUrl);
if (bytes) {
filesToProcess.push({ key: dataUrl, name: `screenshot-${i + 1}.png`, bytes, contentType: 'image/png' });
}
}
- Files reviewed: 27/28 changed files
- Comments generated: 6
There was a problem hiding this comment.
Copilot's findings
Comments suppressed due to low confidence (1)
src/vs/workbench/contrib/issue/browser/issueFormService.ts:147
- Each recording is uploaded with the same name (
recording.${ext}). If the user attaches multiple recordings, the resulting attachments become ambiguous (and some backends may treat duplicate names poorly). Consider generating unique names (e.g.recording-1.mp4, timestamp-based names, etc.) similar to the screenshot naming.
for (const rec of recordings) {
const fileContent = await this.fileService.readFile(URI.file(rec.filePath));
const ext = rec.filePath.endsWith('.mp4') ? 'mp4' : 'webm';
const contentType = ext === 'mp4' ? 'video/mp4' : 'video/webm';
filesToProcess.push({ key: `recording:${rec.filePath}`, name: `recording.${ext}`, bytes: fileContent.value.buffer, contentType });
}
- Files reviewed: 27/28 changed files
- Comments generated: 3
No description provided.