Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(playground): add leaf connection icons inside playground VSCODE-650 #885

Merged
merged 18 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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
Binary file added fonts/mongodb-icons.woff
Copy link
Contributor Author

@gagik gagik Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can either have this font generated in CI or just do it ourselves at changes? I feel like this is a small enough file and one we will likely not touch often (and would also require package.json updates) so perhaps it's fine just having this as part of repo files?

Binary file not shown.
754 changes: 754 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"release-draft": "node ./scripts/release-draft.js",
"reformat": "prettier --write .",
"snyk-test": "node scripts/snyk-test.js",
"generate-icon-font": "ts-node ./scripts/generate-icon-font.ts",
"generate-vulnerability-report": "mongodb-sbom-tools generate-vulnerability-report --snyk-reports=.sbom/snyk-test-result.json --dependencies=.sbom/dependencies.json --fail-on=high",
"create-vulnerability-tickets": "mongodb-sbom-tools generate-vulnerability-report --snyk-reports=.sbom/snyk-test-result.json --dependencies=.sbom/dependencies.json --create-jira-issues",
"prepare": "husky",
Expand Down Expand Up @@ -1154,6 +1155,22 @@
"description": "Specify a shell command that is run to start the browser for authenticating with the OIDC identity provider for the server connection. Leave this empty for default browser."
}
}
},
"icons": {
"mdb-connection-active": {
"description": "MongoDB Icon",
"default": {
"fontPath": "./fonts/mongodb-icons.woff",
"fontCharacter": "\\ea01"
}
},
"mdb-connection-inactive": {
"description": "MongoDB Icon",
"default": {
"fontPath": "./fonts/mongodb-icons.woff",
"fontCharacter": "\\ea02"
}
}
}
},
"dependencies": {
Expand Down Expand Up @@ -1198,7 +1215,6 @@
"@mongodb-js/oidc-mock-provider": "^0.10.1",
"@mongodb-js/oidc-plugin": "^0.4.0",
"@mongodb-js/prettier-config-devtools": "^1.0.1",
"mongodb-rag-core": "^0.4.1",
"@mongodb-js/sbom-tools": "^0.7.2",
"@mongodb-js/signing-utils": "^0.3.6",
"@mongosh/service-provider-core": "^2.3.3",
Expand Down Expand Up @@ -1241,6 +1257,7 @@
"mocha-junit-reporter": "^2.2.1",
"mocha-multi": "^1.1.7",
"mongodb-client-encryption": "^6.1.0",
"mongodb-rag-core": "^0.4.1",
"mongodb-runner": "^5.7.0",
"node-fetch": "^2.7.0",
"node-loader": "^0.6.0",
Expand All @@ -1260,6 +1277,7 @@
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"typescript": "^4.9.5",
"webfont": "^11.2.26",
"webpack": "^5.95.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^5.1.4",
Expand Down
63 changes: 63 additions & 0 deletions scripts/generate-icon-font.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import webfont from 'webfont';
import fs from 'fs/promises';
import { GlyphData } from 'webfont/dist/src/types';

/** Icons to include in the generated icon font */
const INCLUDED_ICONS = ['connection-active', 'connection-inactive'];

/**
* Generates an icon font from the included icons and outputs package.json
* configuration field for those icons as specified in
* https://code.visualstudio.com/api/references/icons-in-labels
*/
async function main(): Promise<void> {
const font = await webfont({
Copy link
Contributor Author

@gagik gagik Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's worth it to have a external dev dependency / script for this. We can potentially utilize Vscode's support for icons fonts for i.e. easily having different color versions of the same icons and, as in this case, easily including icons in different places. Open to discussing this if there are other ideas though; at least for codelenses the need for an icon font is unavoidable so we could either ask design to make it or generate it from this script as done here.
The link from the comment:
https://code.visualstudio.com/api/references/icons-in-labels

files: INCLUDED_ICONS.map((icon) => `./images/light/${icon}.svg`),
fontName: 'MongoDB Icons',
formats: ['woff'],
normalize: true,
centerHorizontally: true,
});

if (!font.woff) {
throw new Error('Error occurred generating template');
}

await fs.writeFile('./fonts/mongodb-icons.woff', font.woff);

const iconsConfig = {};
font.glyphsData?.forEach((glyph) => {
if (!glyph.metadata?.name) {
throw new Error('There is a glyph without a name');
}
iconsConfig[`mdb-${glyph.metadata.name}`] = {
description: 'MongoDB Icon',
default: {
fontPath: './fonts/mongodb-icons.woff',
fontCharacter: getUnicodeHex(glyph),
},
};
});

// Prints out the VSCode configuration package.json
console.info(
JSON.stringify(
{
icons: iconsConfig,
},
undefined,
2
)
);
}

function getUnicodeHex(glyph: GlyphData): string {
if (glyph.metadata?.unicode == undefined) {
throw new Error('No unicode defined');
}
const hex = glyph.metadata?.unicode[0].codePointAt(0)!.toString(16);

return `\\${hex}`;
}

main();
2 changes: 2 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ enum EXTENSION_COMMANDS {

// Chat participant.
OPEN_PARTICIPANT_CODE_IN_PLAYGROUND = 'mdb.openParticipantCodeInPlayground',
SEND_MESSAGE_TO_PARTICIPANT = 'mdb.sendMessageToParticipant',
SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT = 'mdb.sendMessageToParticipantFromInput',
RUN_PARTICIPANT_CODE = 'mdb.runParticipantCode',
CONNECT_WITH_PARTICIPANT = 'mdb.connectWithParticipant',
SELECT_DATABASE_WITH_PARTICIPANT = 'mdb.selectDatabaseWithParticipant',
Expand Down
2 changes: 1 addition & 1 deletion src/connectionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export default class ConnectionController {
ignoreFocusOut: true,
placeHolder:
'e.g. mongodb+srv://username:[email protected]/admin',
prompt: 'Enter your connection string (SRV or standard)',
prompt: 'Enter your SRV or standard connection string',
validateInput: (uri: string) => {
if (
!uri.startsWith('mongodb://') &&
Expand Down
1 change: 1 addition & 0 deletions src/documentSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum DocumentSource {
DOCUMENT_SOURCE_TREEVIEW = 'treeview',
DOCUMENT_SOURCE_PLAYGROUND = 'playground',
DOCUMENT_SOURCE_COLLECTIONVIEW = 'collectionview',
DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS = 'query with copilot codelens',
}
10 changes: 5 additions & 5 deletions src/editors/activeConnectionCodeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default class ActiveConnectionCodeLensProvider
this._onDidChangeCodeLenses.fire();
});

this._activeConnectionChangedHandler = () => {
this._activeConnectionChangedHandler = (): void => {
this._onDidChangeCodeLenses.fire();
};
this._connectionController.addEventListener(
Expand All @@ -50,10 +50,10 @@ export default class ActiveConnectionCodeLensProvider
? getDBFromConnectionString(connectionString)
: null;
message = defaultDB
? `Currently connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}. Click here to change connection.`
: `Currently connected to ${this._connectionController.getActiveConnectionName()}. Click here to change connection.`;
? `$(mdb-connection-active)Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}`
: `$(mdb-connection-active)Connected to ${this._connectionController.getActiveConnectionName()}`;
} else {
message = 'Disconnected. Click here to connect.';
message = '$(mdb-connection-inactive)Connect';
}

codeLens.command = {
Expand All @@ -65,7 +65,7 @@ export default class ActiveConnectionCodeLensProvider
return [codeLens];
}

deactivate() {
deactivate(): void {
this._connectionController.removeEventListener(
DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED,
this._activeConnectionChangedHandler
Expand Down
9 changes: 9 additions & 0 deletions src/editors/editorsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type PlaygroundResultProvider from './playgroundResultProvider';
import { PLAYGROUND_RESULT_SCHEME } from './playgroundResultProvider';
import { StatusView } from '../views';
import type TelemetryService from '../telemetry/telemetryService';
import type { QueryWithCopilotCodeLensProvider } from './queryWithCopilotCodeLensProvider';

const log = createLogger('editors controller');

Expand Down Expand Up @@ -102,6 +103,7 @@ export default class EditorsController {
_exportToLanguageCodeLensProvider: ExportToLanguageCodeLensProvider;
_editDocumentCodeLensProvider: EditDocumentCodeLensProvider;
_collectionDocumentsCodeLensProvider: CollectionDocumentsCodeLensProvider;
_queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider;

constructor({
context,
Expand All @@ -115,6 +117,7 @@ export default class EditorsController {
playgroundSelectionCodeActionProvider,
playgroundDiagnosticsCodeActionProvider,
editDocumentCodeLensProvider,
queryWithCopilotCodeLensProvider,
}: {
context: vscode.ExtensionContext;
connectionController: ConnectionController;
Expand All @@ -127,6 +130,7 @@ export default class EditorsController {
playgroundSelectionCodeActionProvider: PlaygroundSelectionCodeActionProvider;
playgroundDiagnosticsCodeActionProvider: PlaygroundDiagnosticsCodeActionProvider;
editDocumentCodeLensProvider: EditDocumentCodeLensProvider;
queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider;
}) {
this._connectionController = connectionController;
this._playgroundController = playgroundController;
Expand Down Expand Up @@ -160,6 +164,7 @@ export default class EditorsController {
playgroundSelectionCodeActionProvider;
this._playgroundDiagnosticsCodeActionProvider =
playgroundDiagnosticsCodeActionProvider;
this._queryWithCopilotCodeLensProvider = queryWithCopilotCodeLensProvider;

vscode.workspace.onDidCloseTextDocument((e) => {
const uriParams = new URLSearchParams(e.uri.query);
Expand Down Expand Up @@ -410,6 +415,10 @@ export default class EditorsController {
)
);
this._context.subscriptions.push(
vscode.languages.registerCodeLensProvider(
{ language: 'javascript' },
this._queryWithCopilotCodeLensProvider
),
vscode.languages.registerCodeLensProvider(
{ language: 'javascript' },
this._activeConnectionCodeLensProvider
Expand Down
47 changes: 47 additions & 0 deletions src/editors/queryWithCopilotCodeLensProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as vscode from 'vscode';
import EXTENSION_COMMANDS from '../commands';
import type { SendMessageToParticipantFromInputOptions } from '../participant/participantTypes';
import { isPlayground } from '../utils/playground';
import { COPILOT_EXTENSION_ID } from '../participant/constants';
import { DocumentSource } from '../documentSource';

export class QueryWithCopilotCodeLensProvider
implements vscode.CodeLensProvider
{
constructor() {}

readonly onDidChangeCodeLenses: vscode.Event<void> =
vscode.extensions.onDidChange;

provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] {
if (!isPlayground(document.uri)) {
return [];
}

// We can only detect whether a user has the Copilot extension active
// but not whether it has an active subscription.
const hasCopilotChatActive =
vscode.extensions.getExtension(COPILOT_EXTENSION_ID)?.isActive === true;

if (!hasCopilotChatActive) {
return [];
}

const options: SendMessageToParticipantFromInputOptions = {
prompt: 'Describe the query you would like to generate',
placeHolder:
'e.g. Find the document in sample_mflix.users with the name of Kayden Washington',
messagePrefix: '/query',
isNewChat: true,
source: DocumentSource.DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS,
};

return [
new vscode.CodeLens(new vscode.Range(0, 0, 0, 0), {
title: '✨ Generate query with MongoDB Copilot',
command: EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT,
arguments: [options],
}),
];
}
}
34 changes: 28 additions & 6 deletions src/mdbExtensionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ import type {
} from './participant/participant';
import ParticipantController from './participant/participant';
import type { OpenSchemaCommandArgs } from './participant/prompts/schema';
import { QueryWithCopilotCodeLensProvider } from './editors/queryWithCopilotCodeLensProvider';
import type {
SendMessageToParticipantOptions,
SendMessageToParticipantFromInputOptions,
} from './participant/participantTypes';

// This class is the top-level controller for our extension.
// Commands which the extensions handles are defined in the function `activate`.
Expand All @@ -67,6 +72,7 @@ export default class MDBExtensionController implements vscode.Disposable {
_telemetryService: TelemetryService;
_languageServerController: LanguageServerController;
_webviewController: WebviewController;
_queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider;
_playgroundResultProvider: PlaygroundResultProvider;
_activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider;
_editDocumentCodeLensProvider: EditDocumentCodeLensProvider;
Expand Down Expand Up @@ -107,6 +113,8 @@ export default class MDBExtensionController implements vscode.Disposable {
this._connectionController,
this._editDocumentCodeLensProvider
);
this._queryWithCopilotCodeLensProvider =
new QueryWithCopilotCodeLensProvider();
this._activeConnectionCodeLensProvider =
new ActiveConnectionCodeLensProvider(this._connectionController);
this._exportToLanguageCodeLensProvider =
Expand Down Expand Up @@ -145,6 +153,7 @@ export default class MDBExtensionController implements vscode.Disposable {
playgroundDiagnosticsCodeActionProvider:
this._playgroundDiagnosticsCodeActionProvider,
editDocumentCodeLensProvider: this._editDocumentCodeLensProvider,
queryWithCopilotCodeLensProvider: this._queryWithCopilotCodeLensProvider,
});
this._webviewController = new WebviewController({
connectionController: this._connectionController,
Expand Down Expand Up @@ -306,6 +315,22 @@ export default class MDBExtensionController implements vscode.Disposable {
});
}
);
this.registerParticipantCommand(
EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT,
async (options: SendMessageToParticipantOptions) => {
await this._participantController.sendMessageToParticipant(options);
return true;
}
);
this.registerParticipantCommand(
EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT,
async (options: SendMessageToParticipantFromInputOptions) => {
await this._participantController.sendMessageToParticipantFromInput(
options
);
return true;
}
);
this.registerParticipantCommand(
EXTENSION_COMMANDS.RUN_PARTICIPANT_CODE,
({ runnableContent }: RunParticipantCodeCommandArgs) => {
Expand Down Expand Up @@ -950,12 +975,9 @@ export default class MDBExtensionController implements vscode.Disposable {

const copilot = vscode.extensions.getExtension('github.copilot-chat');
if (result?.title === action) {
await vscode.commands.executeCommand('workbench.action.chat.newChat');
await vscode.commands.executeCommand(
'workbench.action.chat.clearHistory'
);
await vscode.commands.executeCommand('workbench.action.chat.open', {
query: '@MongoDB ',
await this._participantController.sendMessageToParticipant({
message: '',
isNewChat: true,
isPartialQuery: true,
});
this._telemetryService.trackCopilotIntroductionClicked({
Expand Down
1 change: 1 addition & 0 deletions src/participant/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChatMetadataStore } from './chatMetadata';

export const CHAT_PARTICIPANT_ID = 'mongodb.participant';
export const CHAT_PARTICIPANT_MODEL = 'gpt-4o';
export const COPILOT_EXTENSION_ID = 'GitHub.copilot';

export type ParticipantResponseType =
| 'query'
Expand Down
Loading
Loading