Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions src/vs/base/browser/membrane/membranePortManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

interface IMembraneWindow extends Window {
vscodeToGazePort?: MessagePort;
}

declare const window: IMembraneWindow;

/**
* Manages direct communication from VSCode to Gaze, bypassing the extension.
* Used for low-latency updates like:
* - Editor metrics (scroll, view zones, line positions)
* - Dialogs
* - Notifications
*/
export class GazePortManager {
private static port: MessagePort | null = null;
private static responseHandlers = new Map<string, (response: unknown) => void>();

static setResponseHandler(messageType: string, handler: (response: unknown) => void): void {
GazePortManager.responseHandlers.set(messageType, handler);
}

static ensureInitialized(): void {
if (GazePortManager.port) {
return;
}

GazePortManager.port = window.vscodeToGazePort || null;
if (window.vscodeToGazePort) {
delete window.vscodeToGazePort;
}

if (!GazePortManager.port) {
console.warn('GazePortManager: vscodeToGazePort not available');
return;
}

GazePortManager.port.onmessage = (event) => {
try {
const handler = GazePortManager.responseHandlers.get(event.data.messageType);
handler?.(event.data);
} catch (error) {
console.error(`Error handling ${event.data.messageType}:`, error);
}
};
}

static sendMessage(messageType: string, data: object): void {
try {
GazePortManager.ensureInitialized();
if (!GazePortManager.port) {
return;
}
GazePortManager.port.postMessage({ messageType, ...data });
} catch (error) {
console.error(`Error sending ${messageType}:`, error);
}
}
}
11 changes: 6 additions & 5 deletions src/vs/base/browser/ui/dialog/membraneDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IDialogOptions, IDialogResult } from './dialog.js';
import { Disposable } from '../../../common/lifecycle.js';
import { generateUuid } from '../../../common/uuid.js';
// import { mainWindow } from '../../../browser/window.js';
import { MembranePortManager } from '../../../../base/browser/ui/dialog/membranePortManager.js';
import { GazePortManager } from '../../membrane/membranePortManager.js';

export interface MembraneDialogMessage {
type: 'confirm' | 'prompt' | 'info' | 'warn' | 'error' | 'input';
Expand Down Expand Up @@ -64,7 +64,7 @@ export class MembraneDialog extends Disposable {
this.dialogId = generateUuid();

// Register this instance as the dialog response handler
MembranePortManager.setDialogResponseHandler((response: unknown) => {
GazePortManager.setResponseHandler('membraneDialogResponse', (response: unknown) => {
this.handleDialogResponse(response as MembraneDialogResponse);
});
}
Expand All @@ -89,8 +89,9 @@ export class MembraneDialog extends Disposable {
};


// Send via MessagePort using shared port manager
MembranePortManager.sendMessage('membraneDialog', dialogMessage);
// Send via MessagePort using port manager
GazePortManager.sendMessage('membraneDialog', dialogMessage);


// Set up timeout to prevent hanging dialogs
setTimeout(() => {
Expand Down Expand Up @@ -149,7 +150,7 @@ export class MembraneDialog extends Disposable {

// Send update to Gaze if dialog is currently showing
if (MembraneDialog.pendingResponses.has(this.dialogId)) {
MembranePortManager.sendMessage('membraneDialogUpdate', {
GazePortManager.sendMessage('membraneDialogUpdate', {
id: this.dialogId,
message: message
});
Expand Down
72 changes: 0 additions & 72 deletions src/vs/base/browser/ui/dialog/membranePortManager.ts

This file was deleted.

120 changes: 3 additions & 117 deletions src/vs/code/browser/workbench/workbench.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
Expand All @@ -24,11 +23,8 @@ declare const window: Window & {
SENTRY_CAPTURE_EXCEPTION?: (error: Error) => void;
extensionToGazePort?: MessagePort;
};
import { CommandsRegistry } from '../../../platform/commands/common/commands.js';
import { IEditorService } from '../../../workbench/services/editor/common/editorService.js';
import { isCodeEditor } from '../../../editor/browser/editorBrowser.js';
import type { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
import { mainWindow } from '../../../base/browser/window.js';

type Writeable<T> = { -readonly [P in keyof T]: T[P] };


Expand Down Expand Up @@ -124,6 +120,7 @@ type Writeable<T> = { -readonly [P in keyof T]: T[P] };
{
id: 'membrane.getLaunchParams',
handler: () => {
// eslint-disable-next-line no-restricted-syntax
const meta = mainWindow.document.querySelector(
'meta[name="membrane-launch-params"]',
) as HTMLMetaElement;
Expand Down Expand Up @@ -155,119 +152,8 @@ type Writeable<T> = { -readonly [P in keyof T]: T[P] };
'window.commandCenter': false, // Hide command center
};


// eslint-disable-next-line no-restricted-syntax
const domElement = window.vscodeTargetContainer || document.body;
create(domElement, config);

CommandsRegistry.registerCommand(
'membrane.setViewZones',
async (
accessor: ServicesAccessor,
args: {
uri: string;
zones: Array<{
afterLineNumber: number;
heightInPx: number;
lines: string[];
styled?: boolean;
}>;
},
) => {
const editorService = accessor.get(IEditorService);
const targetUri = URI.parse(args.uri);

const activeControl = editorService.activeTextEditorControl;
if (!activeControl || !isCodeEditor(activeControl)) {
return;
}

const model = activeControl.getModel();
if (!model) {
return;
}

const modelUri = model.uri;
if (
modelUri.scheme !== targetUri.scheme ||
modelUri.path !== targetUri.path
) {
return;
}

// Track zones per-editor (use a WeakMap or store on editor instance)
const existingZoneIds: string[] = (activeControl as any).__membraneViewZones || [];

activeControl.changeViewZones((accessor) => {
// Remove ALL existing zones first
for (const zoneId of existingZoneIds) {
accessor.removeZone(zoneId);
}

// Add all new zones
const newZoneIds: string[] = [];
for (const zone of args.zones) {
const container = document.createElement('div');

if (zone.styled) {
container.style.cssText = `
position: relative;
background: rgba(255, 0, 0, 0.15);
border-left: 1px solid rgba(255, 0, 0, 0.4);
font-family: var(--monaco-monospace-font);
font-size: 12px;
line-height: 18px;
color: rgba(255, 100, 100, 0.9);
`;

zone.lines.forEach((line, idx) => {
const lineDiv = document.createElement('div');
lineDiv.style.cssText = `
position: absolute;
top: ${idx * 18}px;
left: 0;
right: 0;
white-space: pre;
overflow: hidden;
`;
lineDiv.textContent = line;
container.appendChild(lineDiv);
});
}

const zoneId = accessor.addZone({
afterLineNumber: zone.afterLineNumber,
heightInPx: zone.heightInPx,
domNode: container,
suppressMouseDown: false,
});
newZoneIds.push(zoneId);
}

// Store for next call
(activeControl as any).__membraneViewZones = newZoneIds;
});
},
);

CommandsRegistry.registerCommand(
'membrane.getEditorScrollInfo',
(accessor: ServicesAccessor) => {
const editorService = accessor.get(IEditorService);
const activeControl = editorService.activeTextEditorControl;
if (!activeControl || !isCodeEditor(activeControl)) {
return null;
}
const visibleRanges = activeControl.getVisibleRanges();
const firstVisibleLine = visibleRanges[0]?.startLineNumber ?? 1;
return {
scrollTop: activeControl.getScrollTop(),
firstLineTop: activeControl.getTopForLineNumber(firstVisibleLine),
firstVisibleLine: firstVisibleLine,
contentLeft: activeControl.getLayoutInfo().contentLeft,
};
},
);



})();
3 changes: 2 additions & 1 deletion src/vs/editor/common/config/editorOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3406,7 +3406,8 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, IEditorMinima

constructor() {
const defaults: EditorMinimapOptions = {
enabled: true,
// MEMBRANE
enabled: false,
size: 'proportional',
side: 'right',
showSlider: 'mouseover',
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/browser/parts/editor/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
'breadcrumbs.enabled': {
description: localize('enabled', "Enable/disable navigation breadcrumbs."),
type: 'boolean',
default: true
// MEMBRANE
default: false
},
'breadcrumbs.filePath': {
description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."),
Expand Down
Loading
Loading