@@ -2,13 +2,14 @@ import * as select from 'select-dom';
2
2
import * as ghInjection from 'github-injection' ;
3
3
import { ConfigProvider } from '../config' ;
4
4
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" ;
6
7
7
8
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" ;
10
11
export const NAV_BTN_CLASS_SELECTOR = "." + NAV_BTN_CLASS ;
11
-
12
+
12
13
export const CSS_REF_BTN_CONTAINER = "gitpod-btn-container" ;
13
14
export const CSS_REF_NO_CONTAINER = "no-container" ;
14
15
}
@@ -42,19 +43,19 @@ export class GitHubInjector extends InjectorBase {
42
43
43
44
checkIsInjected ( ) : boolean {
44
45
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 ) ;
47
48
}
48
49
49
50
async inject ( ) : Promise < void > {
50
51
// ghInjection triggers an event whenever only parts of the GitHub page have been reloaded
51
- ghInjection ( ( ) => {
52
+ ghInjection ( ( ) => {
52
53
if ( ! this . checkIsInjected ( ) ) {
53
54
this . injectButtons ( ) ;
54
55
}
55
-
56
+
56
57
( async ( ) => {
57
- await rewritePeriodKeybindGitHub ( ) ;
58
+ await rewritePeriodKeybindGitHub ( ) ;
58
59
} ) ( ) ;
59
60
} ) ;
60
61
}
@@ -71,27 +72,31 @@ abstract class ButtonInjectorBase implements ButtonInjector {
71
72
protected readonly btnClasses : string ,
72
73
protected readonly float : boolean = true ,
73
74
protected readonly asFirstChild : boolean = false
74
- ) { }
75
+ ) { }
75
76
76
77
abstract isApplicableToCurrentPage ( ) : boolean ;
77
78
78
- inject ( currentUrl : string , openAsPopup : boolean ) {
79
+ protected adjustButton ( a : HTMLAnchorElement ) {
80
+ // do nothing
81
+ }
82
+
83
+ inject ( urlInfo : UrlInfo , openAsPopup : boolean ) {
79
84
const actionbar = select ( this . parentSelector ) ;
80
85
if ( ! actionbar ) {
81
86
return ;
82
87
}
83
88
84
89
const oldBtn = document . getElementById ( Gitpodify . NAV_BTN_ID ) ;
85
90
if ( oldBtn ) {
86
- if ( ! checkIsBtnUpToDate ( oldBtn , currentUrl ) ) {
91
+ if ( ! checkIsBtnUpToDate ( oldBtn , urlInfo . gitpodUrl ) ) {
87
92
// update button
88
- ( oldBtn as HTMLAnchorElement ) . href = currentUrl ;
93
+ ( oldBtn as HTMLAnchorElement ) . href = urlInfo . gitpodUrl ;
89
94
}
90
95
// button is there and up-to-date
91
96
return ;
92
97
}
93
98
94
- const btn = this . renderButton ( currentUrl , openAsPopup ) ;
99
+ const btn = this . genButton ( urlInfo . host , urlInfo . originUrl ) ;
95
100
96
101
const btnGroup = actionbar . getElementsByClassName ( "BtnGroup" ) ;
97
102
const detailsBtn = Array . from ( actionbar . children )
@@ -111,41 +116,64 @@ abstract class ButtonInjectorBase implements ButtonInjector {
111
116
}
112
117
113
118
const primaryButtons = actionbar . getElementsByClassName ( "btn-primary" ) ;
114
- if ( primaryButtons && primaryButtons . length > 1 ) {
119
+ if ( primaryButtons && primaryButtons . length > 2 ) {
115
120
Array . from ( primaryButtons )
116
- . slice ( 0 , primaryButtons . length - 1 )
121
+ . slice ( 0 , primaryButtons . length - 2 )
117
122
. forEach ( primaryButton => primaryButton . classList . replace ( "btn-primary" , "btn-secondary" ) ) ;
118
123
}
119
124
}
120
125
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 ) {
122
157
let classes = this . btnClasses + ` ${ Gitpodify . NAV_BTN_CLASS } ` ;
123
158
if ( this . float ) {
124
159
classes = classes + ` float-right` ;
125
160
}
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>` )
149
177
}
150
178
}
151
179
@@ -155,7 +183,7 @@ class PullInjector extends ButtonInjectorBase {
155
183
}
156
184
157
185
isApplicableToCurrentPage ( ) : boolean {
158
- return window . location . pathname . includes ( "/pull/" ) ;
186
+ return window . location . pathname . includes ( "/pull/" ) ;
159
187
}
160
188
}
161
189
@@ -165,7 +193,7 @@ class IssueInjector extends ButtonInjectorBase {
165
193
}
166
194
167
195
isApplicableToCurrentPage ( ) : boolean {
168
- return window . location . pathname . includes ( "/issues/" ) ;
196
+ return window . location . pathname . includes ( "/issues/" ) ;
169
197
}
170
198
}
171
199
0 commit comments