Skip to content

Commit 2422cab

Browse files
committed
Support start with other IDEs
1 parent bcf7e96 commit 2422cab

10 files changed

+657
-83
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"dom-loaded": "^2.0.0",
2020
"github-injection": "^1.0.1",
2121
"select-dom": "^5.1.0",
22-
"webextension-polyfill": "^0.7.0"
22+
"webextension-polyfill": "^0.7.0",
23+
"@gitpod/gitpod-protocol": "main"
2324
},
2425
"devDependencies": {
2526
"copy-webpack-plugin": "^7.0.0",

src/gitpodify.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const init = async (injectedByUserClick: boolean = false) => {
4242

4343
if (injectedByUserClick) {
4444
// User clicked on the Gitpod extension icon. We open the Gitpod with this page as context.
45-
window.open(renderGitpodUrl(config.gitpodURL));
45+
window.open(renderGitpodUrl(config.gitpodURL).gitpodUrl);
4646
}
4747

4848
// Perform the actual, initial injection

src/ide-options.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"clients": {
3+
"jetbrains-gateway": {
4+
"defaultDesktopIDE": "intellij",
5+
"desktopIDEs": [
6+
"intellij",
7+
"goland",
8+
"pycharm",
9+
"phpstorm"
10+
],
11+
"installationSteps": [
12+
"If you don't see an open dialog in your browser, make sure you have the <a target='_blank' class='gp-link' href='https://www.gitpod.io/docs/ides-and-editors/jetbrains-gateway#getting-started-jetbrains-gateway'>JetBrains Gateway with Gitpod Plugin</a> installed on your machine, and then click <b>${OPEN_LINK_LABEL}</b> below."
13+
]
14+
},
15+
"vscode": {
16+
"defaultDesktopIDE": "code-desktop",
17+
"desktopIDEs": [
18+
"code-desktop"
19+
],
20+
"installationSteps": [
21+
"If you don't see an open dialog in your browser, make sure you have <a target='_blank' class='gp-link' href='https://code.visualstudio.com/download'>VS Code</a> installed on your machine, and then click <b>${OPEN_LINK_LABEL}</b> below."
22+
]
23+
},
24+
"vscode-insiders": {
25+
"defaultDesktopIDE": "code-desktop",
26+
"desktopIDEs": [
27+
"code-desktop"
28+
],
29+
"installationSteps": [
30+
"If you don't see an open dialog in your browser, make sure you have <a target='_blank' class='gp-link' href='https://code.visualstudio.com/insiders'>VS Code Insiders</a> installed on your machine, and then click <b>${OPEN_LINK_LABEL}</b> below."
31+
]
32+
}
33+
},
34+
"defaultDesktopIde": "code-desktop",
35+
"defaultIde": "code",
36+
"options": {
37+
"code": {
38+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/code:commit-54b4098031a1a3a600c1fd73d070c4aab27e1cd8",
39+
"label": "Browser",
40+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/code:nightly@sha256:b62ea2f415d46505c1ffa75f171dc00a0b099105e99ef070ee65ec05084fba61",
41+
"logo": "https://ide.gitpod.io/image/ide-logo/vscode.svg",
42+
"orderKey": "00",
43+
"title": "VS Code",
44+
"type": "browser"
45+
},
46+
"code-desktop": {
47+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/code-desktop:commit-b121fec8e215abd2d0328ef28b8808a59852cb4c",
48+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/code-desktop-insiders:commit-b121fec8e215abd2d0328ef28b8808a59852cb4c@sha256:1b65f8959b1eac330e736c18d1824f34c2ad21df81438ba8c3351e04d5f539ad",
49+
"logo": "https://ide.gitpod.io/image/ide-logo/vscode.svg",
50+
"orderKey": "02",
51+
"title": "VS Code",
52+
"type": "desktop"
53+
},
54+
"goland": {
55+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/goland:commit-a4234aa724a4658207fdf6a64e5c07f204da86d1",
56+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/goland:latest@sha256:2c23f10430fb3fe62d19fe39f40b4dea4869eab31ae72e5f7bddeb6679a4dcd7",
57+
"logo": "https://ide.gitpod.io/image/ide-logo/golandLogo.svg",
58+
"orderKey": "05",
59+
"title": "GoLand",
60+
"type": "desktop"
61+
},
62+
"intellij": {
63+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/intellij:commit-bbdbb44b0a85a2c36e6854d818fe920d0ee5369c",
64+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/intellij:latest@sha256:6109564a542707a8962054d864ee0679388b77bcb66c8fd987924a831cbaf486",
65+
"logo": "https://ide.gitpod.io/image/ide-logo/intellijIdeaLogo.svg",
66+
"orderKey": "04",
67+
"title": "IntelliJ IDEA",
68+
"type": "desktop"
69+
},
70+
"phpstorm": {
71+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/phpstorm:commit-a4234aa724a4658207fdf6a64e5c07f204da86d1",
72+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/phpstorm:latest@sha256:81085f80e4227b9e034aa37adb2256c279d2cca4cd3b4101feb3abd8f53e90d2",
73+
"logo": "https://ide.gitpod.io/image/ide-logo/phpstormLogo.svg",
74+
"orderKey": "07",
75+
"title": "PhpStorm",
76+
"type": "desktop"
77+
},
78+
"pycharm": {
79+
"image": "eu.gcr.io/gitpod-core-dev/build/ide/pycharm:commit-a4234aa724a4658207fdf6a64e5c07f204da86d1",
80+
"latestImage": "eu.gcr.io/gitpod-core-dev/build/ide/pycharm:latest@sha256:4698b906f4852d44b48ef3df858fcef73da69628a2da3188cd6e70116d84865e",
81+
"logo": "https://ide.gitpod.io/image/ide-logo/pycharmLogo.svg",
82+
"orderKey": "06",
83+
"title": "PyCharm",
84+
"type": "desktop"
85+
}
86+
}
87+
}

src/injectors/bitbucket-injector.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { InjectorBase, ButtonInjector, checkIsBtnUpToDate } from "./injector";
22
import { ConfigProvider } from "../config";
3-
import { renderGitpodUrl, makeOpenInPopup } from "../utils";
3+
import { renderGitpodUrl, makeOpenInPopup, UrlInfo } from "../utils";
44
import select = require("select-dom");
55

66
namespace Gitpodify {
7-
export const NAV_BTN_ID = "gitpod-btn-nav";
8-
export const NAV_BTN_CLASS = "gitpod-nav-btn";
7+
export const NAV_BTN_ID = "gitpod-btn-nav";
8+
export const NAV_BTN_CLASS = "gitpod-nav-btn";
99
export const NAV_BTN_CLASS_SELECTOR = "." + NAV_BTN_CLASS;
10-
10+
1111
export const CSS_REF_BTN_CONTAINER = "gitpod-btn-container";
1212
export const CSS_REF_NO_CONTAINER = "no-container";
1313
}
@@ -38,8 +38,8 @@ export class BitbucketInjector extends InjectorBase {
3838

3939
checkIsInjected(): boolean {
4040
const button = document.getElementById(`${Gitpodify.NAV_BTN_ID}`);
41-
const currentUrl = renderGitpodUrl(this.config.gitpodURL);
42-
return checkIsBtnUpToDate(button, currentUrl);
41+
const urlInfo = renderGitpodUrl(this.config.gitpodURL);
42+
return checkIsBtnUpToDate(button, urlInfo.gitpodUrl);
4343
}
4444

4545
async inject(): Promise<void> {
@@ -54,17 +54,17 @@ export class BitbucketInjector extends InjectorBase {
5454

5555
abstract class ButtonInjectorBase implements ButtonInjector {
5656

57-
constructor(protected readonly parent: string, protected btnClasses: string, protected readonly up?: number) {
57+
constructor(protected readonly parent: string, protected btnClasses: string, protected readonly up?: number, protected readonly test?: boolean) {
5858

5959
}
6060

6161
abstract isApplicableToCurrentPage(): boolean;
6262

63-
inject(currentUrl: string, openAsPopup: boolean) {
63+
inject(urlInfo: UrlInfo, openAsPopup: boolean) {
6464
let actionbar = select(this.parent);
65-
if(actionbar && this.up) {
66-
for(let i = 0; i < this.up; i++) {
67-
if(actionbar.parentElement) {
65+
if (actionbar && this.up) {
66+
for (let i = 0; i < this.up; i++) {
67+
if (actionbar.parentElement) {
6868
actionbar = actionbar.parentElement;
6969
} else {
7070
return;
@@ -75,6 +75,7 @@ abstract class ButtonInjectorBase implements ButtonInjector {
7575
return;
7676
}
7777

78+
const currentUrl = urlInfo.gitpodUrl;
7879
const oldBtn = document.getElementById(Gitpodify.NAV_BTN_ID);
7980
if (oldBtn) {
8081
if (!checkIsBtnUpToDate(oldBtn, currentUrl)) {
@@ -88,16 +89,16 @@ abstract class ButtonInjectorBase implements ButtonInjector {
8889
const btn = this.renderButton(currentUrl, openAsPopup);
8990

9091
const btnGroup = actionbar.children;
91-
if (btnGroup && btnGroup.length > 0){
92+
if (btnGroup && btnGroup.length > 0) {
9293
actionbar.insertBefore(btn, btnGroup[0]);
93-
}
94+
}
9495
}
9596

9697
protected renderButton(url: string, openAsPopup: boolean, float: boolean = true): HTMLElement {
9798
let classes = Gitpodify.NAV_BTN_CLASS;
9899
if (float) {
99100
classes = `${classes} ${this.btnClasses} aui-button`;
100-
}
101+
}
101102

102103
const container = document.createElement('div');
103104
container.id = Gitpodify.CSS_REF_BTN_CONTAINER;

src/injectors/github-injector.ts

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import * as select from 'select-dom';
22
import * as ghInjection from 'github-injection';
33
import { ConfigProvider } from '../config';
44
import { ButtonInjector, InjectorBase, checkIsBtnUpToDate, rewritePeriodKeybindGitHub } from './injector';
5-
import { renderGitpodUrl, makeOpenInPopup } from '../utils';
5+
import { renderGitpodUrl, ideOptions, createElementFromHTML, UrlInfo } from '../utils';
6+
import { IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol";
67

78
namespace Gitpodify {
8-
export const NAV_BTN_ID = "gitpod-btn-nav";
9-
export const NAV_BTN_CLASS = "gitpod-nav-btn";
9+
export const NAV_BTN_ID = "gitpod-btn-nav";
10+
export const NAV_BTN_CLASS = "gitpod-nav-btn";
1011
export const NAV_BTN_CLASS_SELECTOR = "." + NAV_BTN_CLASS;
11-
12+
1213
export const CSS_REF_BTN_CONTAINER = "gitpod-btn-container";
1314
export const CSS_REF_NO_CONTAINER = "no-container";
1415
}
@@ -42,19 +43,19 @@ export class GitHubInjector extends InjectorBase {
4243

4344
checkIsInjected(): boolean {
4445
const button = document.getElementById(`${Gitpodify.NAV_BTN_ID}`);
45-
const currentUrl = renderGitpodUrl(this.config.gitpodURL);
46-
return checkIsBtnUpToDate(button, currentUrl);
46+
const urlInfo = renderGitpodUrl(this.config.gitpodURL);
47+
return checkIsBtnUpToDate(button, urlInfo.gitpodUrl);
4748
}
4849

4950
async inject(): Promise<void> {
5051
// ghInjection triggers an event whenever only parts of the GitHub page have been reloaded
51-
ghInjection(() => {
52+
ghInjection(() => {
5253
if (!this.checkIsInjected()) {
5354
this.injectButtons();
5455
}
55-
56+
5657
(async () => {
57-
await rewritePeriodKeybindGitHub();
58+
await rewritePeriodKeybindGitHub();
5859
})();
5960
});
6061
}
@@ -71,27 +72,31 @@ abstract class ButtonInjectorBase implements ButtonInjector {
7172
protected readonly btnClasses: string,
7273
protected readonly float: boolean = true,
7374
protected readonly asFirstChild: boolean = false
74-
) {}
75+
) { }
7576

7677
abstract isApplicableToCurrentPage(): boolean;
7778

78-
inject(currentUrl: string, openAsPopup: boolean) {
79+
protected adjustButton(a: HTMLAnchorElement) {
80+
// do nothing
81+
}
82+
83+
inject(urlInfo: UrlInfo, openAsPopup: boolean) {
7984
const actionbar = select(this.parentSelector);
8085
if (!actionbar) {
8186
return;
8287
}
8388

8489
const oldBtn = document.getElementById(Gitpodify.NAV_BTN_ID);
8590
if (oldBtn) {
86-
if (!checkIsBtnUpToDate(oldBtn, currentUrl)) {
91+
if (!checkIsBtnUpToDate(oldBtn, urlInfo.gitpodUrl)) {
8792
// update button
88-
(oldBtn as HTMLAnchorElement).href = currentUrl;
93+
(oldBtn as HTMLAnchorElement).href = urlInfo.gitpodUrl;
8994
}
9095
// button is there and up-to-date
9196
return;
9297
}
9398

94-
const btn = this.renderButton(currentUrl, openAsPopup);
99+
const btn = this.genButton(urlInfo.host, urlInfo.originUrl);
95100

96101
const btnGroup = actionbar.getElementsByClassName("BtnGroup");
97102
const detailsBtn = Array.from(actionbar.children)
@@ -111,41 +116,64 @@ abstract class ButtonInjectorBase implements ButtonInjector {
111116
}
112117

113118
const primaryButtons = actionbar.getElementsByClassName("btn-primary");
114-
if (primaryButtons && primaryButtons.length > 1) {
119+
if (primaryButtons && primaryButtons.length > 2) {
115120
Array.from(primaryButtons)
116-
.slice(0, primaryButtons.length - 1)
121+
.slice(0, primaryButtons.length - 2)
117122
.forEach(primaryButton => primaryButton.classList.replace("btn-primary", "btn-secondary"));
118123
}
119124
}
120125

121-
protected renderButton(url: string, openAsPopup: boolean): HTMLElement {
126+
genOptionsString(ideOptions: IDEOptions, host: string, originUrl: string) {
127+
if (!ideOptions.clients) {
128+
return []
129+
}
130+
return Object.entries(ideOptions.clients ?? {}).reduce((prev, [key, value]) => {
131+
if (key === "vscode-insiders" || !value.desktopIDEs) {
132+
return prev
133+
}
134+
prev.push(...value.desktopIDEs?.map(e => {
135+
const url = `https://${host ?? "gitpod.io"}/#referrer:${key}:${e}/${originUrl}`
136+
const ideOption = ideOptions.options[e]
137+
const title = ideOption.title + (ideOption.type === "desktop" ? "" : ideOption.type.toUpperCase())
138+
return `<a class="select-menu-item" tabindex="0" role="menuitemradio" aria-checked="true" href="${url}" target="_blank">
139+
<img style="margin-top: 3px" aria-hidden="true" height="14" width="14" data-view-component="true" class="octicon octicon-check select-menu-item-icon" src="${ideOption.logo}" alt="logo">
140+
<input type="radio" name="draft" id="draft_off" value="off" >
141+
<div class="select-menu-item-text">
142+
<span class="select-menu-item-heading">${title}</span>
143+
<span class="description text-normal">
144+
Open in ${title}
145+
</span>
146+
<span data-menu-button-text="" hidden="">
147+
${title}
148+
</span>
149+
</div>
150+
</a>`
151+
}))
152+
return prev
153+
}, [] as string[])
154+
}
155+
156+
genButton(host: string, originUrl: string) {
122157
let classes = this.btnClasses + ` ${Gitpodify.NAV_BTN_CLASS}`;
123158
if (this.float) {
124159
classes = classes + ` float-right`;
125160
}
126-
127-
const container = document.createElement('div');
128-
container.id = Gitpodify.CSS_REF_BTN_CONTAINER;
129-
container.className = classes;
130-
131-
const a = document.createElement('a');
132-
a.id = Gitpodify.NAV_BTN_ID;
133-
a.title = "Gitpod";
134-
a.text = "Gitpod"
135-
a.href = url;
136-
a.target = "_blank";
137-
if (openAsPopup) {
138-
makeOpenInPopup(a);
139-
}
140-
a.className = "btn btn-sm btn-primary";
141-
142-
this.adjustButton(a);
143-
144-
container.appendChild(a);
145-
return container;
146-
}
147-
protected adjustButton(a: HTMLAnchorElement) {
148-
// do nothing
161+
return createElementFromHTML(`<div id="${Gitpodify.CSS_REF_BTN_CONTAINER}" class="ml-2 BtnGroup width-full width-md-auto d-flex ${classes}">
162+
<a id="${Gitpodify.NAV_BTN_ID}"
163+
href="${host + "/#" + originUrl}"
164+
class="btn-primary btn btn-primary BtnGroup-item flex-auto"> Gitpod
165+
</a>
166+
<details class="details-reset details-overlay select-menu BtnGroup-parent position-relative">
167+
<summary data-disable-invalid="" data-disable-with="" aria-label="Select editor to open a workspace"
168+
data-view-component="true" class="select-menu-button btn-primary btn BtnGroup-item float-none"
169+
aria-haspopup="menu" role="button">
170+
</summary>
171+
<details-menu class="select-menu-modal position-absolute right-0 js-sync-select-menu-text"
172+
style="z-index: 99" role="menu">
173+
${this.genOptionsString(ideOptions, host, originUrl).join("\n")}
174+
</details-menu>
175+
</details>
176+
</div>`)
149177
}
150178
}
151179

@@ -155,7 +183,7 @@ class PullInjector extends ButtonInjectorBase {
155183
}
156184

157185
isApplicableToCurrentPage(): boolean {
158-
return window.location.pathname.includes("/pull/");
186+
return window.location.pathname.includes("/pull/");
159187
}
160188
}
161189

@@ -165,7 +193,7 @@ class IssueInjector extends ButtonInjectorBase {
165193
}
166194

167195
isApplicableToCurrentPage(): boolean {
168-
return window.location.pathname.includes("/issues/");
196+
return window.location.pathname.includes("/issues/");
169197
}
170198
}
171199

0 commit comments

Comments
 (0)