Skip to content
Draft
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
5 changes: 5 additions & 0 deletions contributions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5691,6 +5691,11 @@
"label": "Show Visual File History View",
"commandPalette": "gitlens:enabled"
},
"gitlens.showWelcomePage": {
"label": "Show Welcome",
"icon": "$(heart)",
"commandPalette": "gitlens:enabled"
},
"gitlens.showWorkspacesView": {
"label": "Show Cloud Workspaces View",
"commandPalette": "gitlens:enabled && !gitlens:hasVirtualFolders"
Expand Down
111 changes: 87 additions & 24 deletions docs/telemetry-events.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8364,6 +8364,12 @@
"title": "Show Visual File History View",
"category": "GitLens"
},
{
"command": "gitlens.showWelcomePage",
"title": "Show Welcome",
"category": "GitLens",
"icon": "$(heart)"
},
{
"command": "gitlens.showWorkspacesView",
"title": "Show Cloud Workspaces View",
Expand Down Expand Up @@ -13216,6 +13222,10 @@
"command": "gitlens.showTimelineView",
"when": "gitlens:enabled"
},
{
"command": "gitlens.showWelcomePage",
"when": "gitlens:enabled"
},
{
"command": "gitlens.showWorkspacesView",
"when": "gitlens:enabled && !gitlens:hasVirtualFolders"
Expand Down
1 change: 1 addition & 0 deletions src/constants.commands.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ export type ContributedPaletteCommands =
| 'gitlens.showTagsView'
| 'gitlens.showTimelinePage'
| 'gitlens.showTimelineView'
| 'gitlens.showWelcomePage'
| 'gitlens.showWorkspacesView'
| 'gitlens.showWorktreesView'
| 'gitlens.startWork'
Expand Down
10 changes: 10 additions & 0 deletions src/constants.telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,9 @@ export interface TelemetryEvents extends WebviewShowAbortedEvents, WebviewShownE
/** Sent when the walkthrough is opened */
'walkthrough/action': WalkthroughActionEvent;
'walkthrough/completion': WalkthroughCompletionEvent;

/** Sent when an action is taken in the welcome webview */
'welcome/action': WelcomeActionEvent;
}

type WebviewShowAbortedEvents = {
Expand Down Expand Up @@ -1202,6 +1205,12 @@ interface WalkthroughCompletionEvent {
'context.key': WalkthroughContextKeys;
}

type WelcomeActionNames = 'plus/sign-up';

type WelcomeActionEvent =
| { type: 'command'; name: WelcomeActionNames; command: string; detail?: string }
| { type: 'url'; name: WelcomeActionNames; url: string; detail?: string };

type WebviewContextEventData = {
'context.webview.id': string;
'context.webview.type': string;
Expand Down Expand Up @@ -1282,6 +1291,7 @@ export type Sources =
| 'view'
| 'view:hover'
| 'walkthrough'
| 'welcome'
| 'whatsnew'
| 'worktrees';

Expand Down
2 changes: 1 addition & 1 deletion src/constants.views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export type GroupableTreeViewTypes = Extract<
>;
export type GroupableTreeViewIds<T extends GroupableTreeViewTypes = GroupableTreeViewTypes> = TreeViewIds<T>;

export type WebviewTypes = 'composer' | 'graph' | 'patchDetails' | 'settings' | 'timeline';
export type WebviewTypes = 'composer' | 'graph' | 'patchDetails' | 'settings' | 'timeline' | 'welcome';
export type WebviewIds = `gitlens.${WebviewTypes}`;

export type WebviewViewTypes = 'commitDetails' | 'graph' | 'graphDetails' | 'home' | 'patchDetails' | 'timeline';
Expand Down
4 changes: 4 additions & 0 deletions src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { registerTimelineWebviewCommands, registerTimelineWebviewPanel } from '.
import { RebaseEditorProvider } from './webviews/rebase/rebaseEditor';
import { registerSettingsWebviewCommands, registerSettingsWebviewPanel } from './webviews/settings/registration';
import { WebviewsController } from './webviews/webviewsController';
import { registerWelcomeWebviewPanel } from './webviews/welcome/registration';

export type Environment = 'dev' | 'staging' | 'production';

Expand Down Expand Up @@ -257,6 +258,9 @@ export class Container {
this._disposables.push(settingsPanels);
this._disposables.push(registerSettingsWebviewCommands(settingsPanels));

const welcomePanels = registerWelcomeWebviewPanel(webviews);
this._disposables.push(welcomePanels);

this._disposables.push(new ViewFileDecorationProvider());

const patchDetailsPanels = registerPatchDetailsWebviewPanel(webviews);
Expand Down
6 changes: 6 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,12 @@ async function showWhatsNew(
if (previousVersion == null) {
Logger.log(`GitLens first-time install; window.focused=${window.state.focused}`);

// Show welcome webview on first install for IDEs that don't support walkthroughs (e.g., Cursor)
// For IDEs that support walkthroughs, the walkthrough will be shown instead
if (window.state.focused && !container.walkthrough.isWalkthroughSupported) {
void executeCommand('gitlens.showWelcomePage');
}

return;
}

Expand Down
11 changes: 11 additions & 0 deletions src/webviews/apps/media/feature-graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions src/webviews/apps/media/feature-timeline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions src/webviews/apps/welcome/components/feature-card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import '../../shared/components/button';
import '../../shared/components/code-icon';

declare global {
interface HTMLElementTagNameMap {
'gl-feature-card': GlFeatureCard;
}
}

@customElement('gl-feature-card')
export class GlFeatureCard extends LitElement {
static override styles = [
css`
:host {
display: flex;
gap: 1em;
}

.image {
width: 50%;
}

.content {
margin-top: 0.5em;
display: block;
}

@media (max-width: 400px) {
:host {
flex-direction: column;
}

.image {
width: 100%;
}

.content {
margin-top: 0;
margin-left: 0.3em;
margin-right: 0.3em;
}

::slotted(*) {
width: 100%;
}
}
`,
];

override render(): unknown {
return html`
<div class="image">
<slot name="image"></slot>
</div>
<div class="content">
<slot></slot>
</div>
`;
}
}
110 changes: 110 additions & 0 deletions src/webviews/apps/welcome/components/feature-carousel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { css, html, LitElement } from 'lit';
import { customElement, queryAssignedElements, state } from 'lit/decorators.js';
import '../../shared/components/button';
import '../../shared/components/code-icon';

declare global {
interface HTMLElementTagNameMap {
'gl-feature-carousel': GlFeatureCarousel;
}
}

@customElement('gl-feature-carousel')
export class GlFeatureCarousel extends LitElement {
static override styles = [
css`
:host {
display: block;
width: 100%;
}

.carousel {
display: flex;
gap: 1em;
justify-content: center;
}

.button {
display: flex;
align-items: center;
}

.content {
flex: 1;
max-width: 520px;
display: flex;
align-items: center;
justify-content: center;
}

::slotted(*) {
display: none;
}

::slotted([data-active]) {
display: flex;
width: 100%;
}
`,
];

@queryAssignedElements({ flatten: true })
private cards!: HTMLElement[];

@state()
private currentIndex = 0;

override firstUpdated(): void {
this.updateActiveCard();
}

private updateActiveCard(): void {
this.cards.forEach((card, index) => {
if (index === this.currentIndex) {
card.setAttribute('data-active', '');
} else {
card.removeAttribute('data-active');
}
});
}

private handlePrevious(): void {
if (this.cards.length === 0) return;
this.currentIndex = (this.currentIndex - 1 + this.cards.length) % this.cards.length;
this.updateActiveCard();
}

private handleNext(): void {
if (this.cards.length === 0) return;
this.currentIndex = (this.currentIndex + 1) % this.cards.length;
this.updateActiveCard();
}

private handleSlotChange(): void {
this.currentIndex = 0;
this.updateActiveCard();
}

override render(): unknown {
return html`
<div class="carousel">
<gl-button
class="button"
appearance="input"
@click=${this.handlePrevious}
aria-label="Previous feature"
>
<code-icon icon="chevron-left" size="20"></code-icon>
</gl-button>

<div class="content">
<slot @slotchange=${this.handleSlotChange}></slot>
</div>

<gl-button class="button" appearance="input" @click=${this.handleNext} aria-label="Next feature">
<code-icon icon="chevron-right" size="20"></code-icon>
</gl-button>
</div>
`;
}
}
Loading
Loading