Skip to content

Commit bf5b838

Browse files
committed
Adds welcome page webview for first-time installs
(#4769, #4773, PLG-138)
1 parent 211339f commit bf5b838

19 files changed

+1344
-29
lines changed

docs/telemetry-events.md

Lines changed: 40 additions & 27 deletions
Large diffs are not rendered by default.

src/constants.telemetry.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,9 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
392392
/** Sent when the walkthrough is opened */
393393
'walkthrough/action': WalkthroughActionEvent;
394394
'walkthrough/completion': WalkthroughCompletionEvent;
395+
396+
/** Sent when an action is taken in the welcome webview */
397+
'welcome/action': WelcomeActionEvent;
395398
}
396399

397400
type WebviewShowAbortedEvents = {
@@ -1366,6 +1369,10 @@ interface WalkthroughCompletionEvent {
13661369
'context.key': WalkthroughContextKeys;
13671370
}
13681371

1372+
type WelcomeActionNames = 'plus/sign-up';
1373+
1374+
type WelcomeActionEvent = { type: 'command'; name: WelcomeActionNames; command: string; detail?: string };
1375+
13691376
type WebviewContextEventData = {
13701377
'context.webview.id': string;
13711378
'context.webview.type': string;
@@ -1449,6 +1456,7 @@ export type Sources =
14491456
| 'view'
14501457
| 'view:hover'
14511458
| 'walkthrough'
1459+
| 'welcome'
14521460
| 'whatsnew'
14531461
| 'worktrees';
14541462

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { consume } from '@lit/context';
2+
import { css, html, LitElement, nothing } from 'lit';
3+
import { customElement, property, state } from 'lit/decorators.js';
4+
import type { State } from '../../../home/protocol';
5+
import { CollapseSectionCommand } from '../../../home/protocol';
6+
import { ipcContext } from '../../shared/contexts/ipc';
7+
import type { HostIpc } from '../../shared/ipc';
8+
import '../../shared/components/button';
9+
import { stateContext } from '../context';
10+
import './welcome-page';
11+
12+
declare global {
13+
interface HTMLElementTagNameMap {
14+
'gl-welcome-overlay': GlWelcomeOverlay;
15+
}
16+
}
17+
18+
@customElement('gl-welcome-overlay')
19+
export class GlWelcomeOverlay extends LitElement {
20+
static override shadowRootOptions: ShadowRootInit = {
21+
...LitElement.shadowRootOptions,
22+
delegatesFocus: true,
23+
};
24+
25+
static override styles = [
26+
css`
27+
:host {
28+
--background-color: var(--vscode-sideBar-background);
29+
--shadow-color: var(--vscode-sideBar-foreground);
30+
--dialog-margin: 1rem;
31+
--scrollbar-width: 10px;
32+
}
33+
34+
.overlay {
35+
display: block;
36+
position: fixed;
37+
inset: 0;
38+
overflow: auto;
39+
background-color: var(--background-color);
40+
}
41+
42+
.close-button {
43+
position: fixed;
44+
top: 12px;
45+
right: 12px;
46+
z-index: 2;
47+
}
48+
49+
gl-welcome-page {
50+
--page-background-color: var(--background-color);
51+
--page-margin-left: var(--dialog-margin);
52+
--page-margin-right: var(--dialog-margin);
53+
}
54+
55+
gl-welcome-page::part(page) {
56+
padding: var(--dialog-margin);
57+
box-sizing: border-box;
58+
}
59+
`,
60+
];
61+
62+
@property({ type: String })
63+
webroot?: string;
64+
65+
@property({ type: Boolean })
66+
private isLightTheme = false;
67+
68+
@consume<State>({ context: stateContext, subscribe: true })
69+
@state()
70+
private _state!: State;
71+
72+
@consume<HostIpc>({ context: ipcContext, subscribe: true })
73+
@state()
74+
private _ipc!: HostIpc;
75+
76+
@state()
77+
private closed = false;
78+
79+
override render(): unknown {
80+
const { welcomeOverlayCollapsed, walkthroughSupported, newInstall } = this._state;
81+
if (this.closed || welcomeOverlayCollapsed || walkthroughSupported || !newInstall) {
82+
return nothing;
83+
}
84+
85+
return html`
86+
<div class="overlay">
87+
<div class="close-button">
88+
<gl-button appearance="toolbar" tooltip="Dismiss Welcome Overlay" @click=${() => this.onClose()}
89+
><code-icon icon="close"></code-icon
90+
></gl-button>
91+
</div>
92+
<gl-welcome-page
93+
.webroot=${this.webroot}
94+
.isLightTheme=${this.isLightTheme}
95+
closeable
96+
@close=${() => this.onClose()}
97+
></gl-welcome-page>
98+
</div>
99+
`;
100+
}
101+
102+
private onClose() {
103+
this.closed = true;
104+
105+
this._ipc.sendCommand(CollapseSectionCommand, {
106+
section: 'welcomeOverlay',
107+
collapsed: true,
108+
});
109+
}
110+
}
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import { css } from 'lit';
2+
3+
const colorScheme = css`
4+
:host {
5+
--accent-color: #cb64ff;
6+
--text-color: var(--vscode-descriptionForeground);
7+
--em-color: var(--vscode-sideBar-foreground);
8+
--link-color: var(--vscode-textLink-foreground);
9+
--card-background: var(--vscode-textBlockQuote-background);
10+
11+
--hero-gradient: radial-gradient(76.32% 76.32% at 50% 7.24%, #7b00ff 29.72%, rgba(255, 0, 242, 0) 100%);
12+
--trial-button-gradient: linear-gradient(90deg, #7900c9 0%, #196fff 100%);
13+
--trial-button-border: none;
14+
--trial-button-text-color: #fff;
15+
}
16+
17+
:host-context(.vscode-light) {
18+
--hero-gradient: radial-gradient(62.4% 62.4% at 50% 7.24%, #7b00ff 29.72%, rgba(255, 0, 242, 0) 100%);
19+
}
20+
21+
:host-context(.vscode-dark) {
22+
--hero-gradient: radial-gradient(76.32% 76.32% at 50% 7.24%, #7b00ff 29.72%, rgba(255, 0, 242, 0) 100%);
23+
}
24+
25+
:host-context(.vscode-high-contrast) {
26+
--hero-gradient: transparent;
27+
--trial-button-gradient: var(--color-button-background);
28+
--trial-button-border: 1px solid var(--vscode-button-border);
29+
--trial-button-text-color: var(--color-button-foreground);
30+
}
31+
32+
:host-context(.vscode-high-contrast-light) {
33+
--accent-color: #500070;
34+
}
35+
:host-context(.vscode-high-contrast:not(.vscode-high-contrast-light)) {
36+
--accent-color: #ffc0ff;
37+
}
38+
`;
39+
40+
const typography = css`
41+
:host {
42+
font-size: var(--vscode-font-size);
43+
44+
--h1-font-size: 1.7em;
45+
--p-font-size: 1.23em;
46+
--card-font-size: 1em;
47+
}
48+
49+
@media (max-width: 640px) {
50+
:host {
51+
font-size: var(--vscode-editor-font-size);
52+
--h1-font-size: 1em;
53+
--p-font-size: 1em;
54+
--card-font-size: 1em;
55+
}
56+
}
57+
58+
@media (max-width: 300px) {
59+
:host {
60+
font-size: calc(var(--vscode-editor-font-size) * 0.8);
61+
}
62+
}
63+
`;
64+
65+
const main = css`
66+
:host {
67+
--page-margin-left: 0px;
68+
--page-margin-right: 0px;
69+
display: block;
70+
height: 100%;
71+
}
72+
73+
.welcome {
74+
max-height: 100%;
75+
overflow: auto;
76+
position: relative;
77+
}
78+
`;
79+
80+
const heroGradient = css`
81+
.welcome::before {
82+
content: ' ';
83+
position: absolute;
84+
top: 0;
85+
left: 50%;
86+
transform: translateX(-50%) translateY(-40%);
87+
z-index: -1;
88+
89+
background: var(--hero-gradient);
90+
border-radius: 100%;
91+
opacity: 0.25;
92+
filter: blur(53px);
93+
94+
width: 620px;
95+
height: 517px;
96+
max-width: 100%;
97+
}
98+
99+
@media (max-width: 400px) {
100+
.welcome::before {
101+
height: 273px;
102+
}
103+
}
104+
`;
105+
106+
const section = css`
107+
.section {
108+
display: flex;
109+
flex-flow: column;
110+
justify-content: center;
111+
align-items: center;
112+
text-align: center;
113+
}
114+
.section h1 {
115+
color: var(--em-color);
116+
}
117+
.section h2 {
118+
color: var(--em-color);
119+
font-weight: normal;
120+
font-size: var(--p-font-size);
121+
}
122+
.section p {
123+
color: var(--text-color);
124+
}
125+
.section .accent {
126+
color: var(--accent-color);
127+
}
128+
.section a {
129+
color: var(--link-color);
130+
text-decoration: none;
131+
}
132+
133+
.section.plain p {
134+
max-width: 30em;
135+
font-size: var(--p-font-size);
136+
}
137+
138+
.section.start-trial {
139+
display: flex;
140+
gap: 0.5em;
141+
margin: 2em 3.1em 1.5em;
142+
}
143+
.section.start-trial p {
144+
width: 100%;
145+
}
146+
.section.start-trial gl-button.start-trial-button {
147+
background: var(--trial-button-gradient);
148+
border: var(--trial-button-border);
149+
color: var(--trial-button-text-color);
150+
}
151+
.section.start-trial gl-button {
152+
width: 100%;
153+
}
154+
155+
@media (min-width: 640px) {
156+
.section.start-trial gl-button {
157+
width: initial;
158+
}
159+
.section.start-trial gl-button.start-trial-button {
160+
--button-padding: 0.4em 4em;
161+
}
162+
}
163+
164+
@media (max-width: 400px) {
165+
.section.start-trial {
166+
margin: 2em 0 1.5em;
167+
}
168+
}
169+
170+
.section.wide {
171+
margin-left: calc(-1 * var(--page-margin-left));
172+
margin-right: calc(-1 * var(--page-margin-right));
173+
}
174+
`;
175+
176+
const header = css`
177+
.logo {
178+
transform: scale(0.7);
179+
}
180+
181+
.header {
182+
margin-top: 3em;
183+
max-width: 620px;
184+
margin-left: auto;
185+
margin-right: auto;
186+
}
187+
.header gitlens-logo {
188+
transform: translateX(-0.75rem);
189+
}
190+
.header h1 {
191+
margin-bottom: 0;
192+
font-size: var(--h1-font-size);
193+
}
194+
195+
@media (max-width: 640px) {
196+
.logo {
197+
transform: scale(0.5);
198+
}
199+
.header {
200+
margin-top: 1.5em;
201+
}
202+
}
203+
`;
204+
205+
const cards = css`
206+
.card {
207+
border-radius: 0.63em;
208+
background-color: var(--card-background);
209+
padding: 1.8em;
210+
text-align: initial;
211+
}
212+
213+
@media (max-width: 640px) {
214+
.card {
215+
padding: 1em;
216+
}
217+
}
218+
219+
@media (max-width: 300px) {
220+
.card {
221+
padding: 0.5em 0.5em 1em;
222+
}
223+
}
224+
225+
.card h1 {
226+
margin: 0;
227+
font-size: var(--card-font-size);
228+
}
229+
230+
.card p {
231+
margin: 0.5em 0 0;
232+
font-size: var(--card-font-size);
233+
}
234+
235+
.card p:last-child {
236+
margin: 1em 0 0;
237+
}
238+
239+
.card img {
240+
max-width: 100%;
241+
}
242+
`;
243+
244+
const scrollableFeatures = css`
245+
gl-scrollable-features {
246+
--side-shadow-padding: max(var(--page-margin-left), var(--page-margin-right));
247+
--side-shadow-color: var(--page-background-color);
248+
}
249+
`;
250+
251+
export const welcomeStyles = css`
252+
${colorScheme} ${typography} ${main}
253+
${heroGradient} ${section} ${header}
254+
${scrollableFeatures}
255+
${cards}
256+
`;

0 commit comments

Comments
 (0)