deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { injectable, inject } from '@theia/core/shared/inversify';
import {
ArrayUtils, CommandRegistry, MenuModelRegistry, nls, PreferenceContribution,
PreferenceDataProperty, PreferenceSchemaService, PreferenceService
} from '@theia/core/lib/common';
import { CommonCommands, CommonMenus, AbstractViewContribution, FrontendApplicationContribution, FrontendApplication } from '@theia/core/lib/browser';
import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
import { GettingStartedWidget } from './getting-started-widget';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { PreviewContribution } from '@theia/preview/lib/browser/preview-contribution';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
/**
* Triggers opening the `GettingStartedWidget`.
*/
export const GettingStartedCommand = {
id: GettingStartedWidget.ID,
label: GettingStartedWidget.LABEL
};
@injectable()
export class GettingStartedContribution extends AbstractViewContribution<GettingStartedWidget> implements FrontendApplicationContribution, PreferenceContribution {
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
@inject(EditorManager)
protected readonly editorManager: EditorManager;
@inject(FileService)
protected readonly fileService: FileService;
@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;
@inject(PreviewContribution)
protected readonly previewContribution: PreviewContribution;
@inject(FrontendApplicationStateService)
protected readonly stateService: FrontendApplicationStateService;
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;
constructor() {
super({
widgetId: GettingStartedWidget.ID,
widgetName: GettingStartedWidget.LABEL,
defaultWidgetOptions: {
area: 'main',
}
});
}
async initSchema(service: PreferenceSchemaService): Promise<void> {
const property: PreferenceDataProperty = {
enumDescriptions: [
nls.localizeByDefault('Start without an editor.'),
nls.localize('theia/getting-started/startup-editor/welcomePage', 'Open the Welcome page, with content to aid in getting started with {0} and extensions.',
FrontendApplicationConfigProvider.get().applicationName),
// eslint-disable-next-line max-len
nls.localizeByDefault("Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global configuration, it will be ignored if set in a workspace or folder configuration."),
nls.localizeByDefault('Open a new untitled text file (only applies when opening an empty window).'),
nls.localizeByDefault('Open the Welcome page when opening an empty workbench.'),
],
};
service.updateSchemaProperty('workbench.startupEditor', property);
}
async onStart(app: FrontendApplication): Promise<void> {
this.stateService.reachedState('ready').then(async () => {
if (this.editorManager.all.length === 0) {
await this.preferenceService.ready;
const startupEditor = this.preferenceService.get('workbench.startupEditor');
switch (startupEditor) {
case 'welcomePage':
this.openView({ reveal: true, activate: true });
break;
case 'welcomePageInEmptyWorkbench':
if (!this.workspaceService.opened) {
this.openView({ reveal: true, activate: true });
}
break;
case 'newUntitledFile':
this.commandRegistry.executeCommand(CommonCommands.NEW_UNTITLED_TEXT_FILE.id);
break;
case 'readme':
await this.openReadme();
break;
}
}
});
}
protected async openReadme(): Promise<void> {
const roots = await this.workspaceService.roots;
const readmes = await Promise.all(roots.map(async folder => {
const folderStat = await this.fileService.resolve(folder.resource);
const fileArr = folderStat?.children?.sort((a, b) => a.name.localeCompare(b.name)) || [];
const filePath = fileArr.find(file => file.name.toLowerCase() === 'readme.md') || fileArr.find(file => file.name.toLowerCase().startsWith('readme'));
return filePath?.resource;
}));
const validReadmes = ArrayUtils.coalesce(readmes);
if (validReadmes.length) {
for (const readme of validReadmes) {
await this.previewContribution.open(readme);
}
} else {
// If no readme is found, show the welcome page.
this.openView({ reveal: true, activate: true });
}
}
override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(GettingStartedCommand, {
execute: () => this.openView({ reveal: true, activate: true }),
});
}
override registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(CommonMenus.HELP, {
commandId: GettingStartedCommand.id,
label: GettingStartedCommand.label,
order: 'a10'
});
}
}

View File

@@ -0,0 +1,34 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { GettingStartedContribution } from './getting-started-contribution';
import { ContainerModule, interfaces } from '@theia/core/shared/inversify';
import { GettingStartedWidget } from './getting-started-widget';
import { WidgetFactory, FrontendApplicationContribution, bindViewContribution, noopWidgetStatusBarContribution, WidgetStatusBarContribution } from '@theia/core/lib/browser';
import { bindGettingStartedPreferences } from '../common/getting-started-preferences';
import '../../src/browser/style/index.css';
export default new ContainerModule((bind: interfaces.Bind) => {
bindViewContribution(bind, GettingStartedContribution);
bind(FrontendApplicationContribution).toService(GettingStartedContribution);
bind(WidgetStatusBarContribution).toConstantValue(noopWidgetStatusBarContribution(GettingStartedWidget));
bind(GettingStartedWidget).toSelf();
bind(WidgetFactory).toDynamicValue(context => ({
id: GettingStartedWidget.ID,
createWidget: () => context.container.get<GettingStartedWidget>(GettingStartedWidget),
})).inSingletonScope();
bindGettingStartedPreferences(bind);
});

View File

@@ -0,0 +1,648 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { codicon, CommonCommands, Key, KeyCode, LabelProvider, LocalizedMarkdown, Message, ReactWidget } from '@theia/core/lib/browser';
import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { CommandRegistry, environment, isOSX, Path, PreferenceService } from '@theia/core/lib/common';
import { ApplicationInfo, ApplicationServer } from '@theia/core/lib/common/application-protocol';
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { nls } from '@theia/core/lib/common/nls';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import { KeymapsCommands } from '@theia/keymaps/lib/browser';
import { WorkspaceCommands, WorkspaceService } from '@theia/workspace/lib/browser';
import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
/**
* Default implementation of the `GettingStartedWidget`.
* The widget is displayed when there are currently no workspaces present.
* Some of the features displayed include:
* - `open` commands.
* - `recently used workspaces`.
* - `settings` commands.
* - `help` commands.
* - helpful links.
*/
@injectable()
export class GettingStartedWidget extends ReactWidget {
/**
* The widget `id`.
*/
static readonly ID = 'getting.started.widget';
/**
* The widget `label` which is used for display purposes.
*/
static readonly LABEL = nls.localizeByDefault('Welcome');
/**
* The `ApplicationInfo` for the application if available.
* Used in order to obtain the version number of the application.
*/
protected applicationInfo: ApplicationInfo | undefined;
/**
* The application name which is used for display purposes.
*/
protected applicationName = FrontendApplicationConfigProvider.get().applicationName;
protected home: string | undefined;
/**
* The recently used workspaces limit.
* Used in order to limit the number of recently used workspaces to display.
*/
protected recentLimit = 5;
/**
* The list of recently used workspaces.
*/
protected recentWorkspaces: string[] = [];
/**
* Indicates whether the "ai-core" extension is available.
*/
protected aiIsIncluded: boolean;
/**
* Collection of useful links to display for end users.
*/
protected readonly documentationUrl = 'https://www.theia-ide.org/docs/';
protected readonly compatibilityUrl = 'https://eclipse-theia.github.io/vscode-theia-comparator/status.html';
protected readonly extensionUrl = 'https://www.theia-ide.org/docs/authoring_extensions';
protected readonly pluginUrl = 'https://www.theia-ide.org/docs/authoring_plugins';
protected readonly userAIDocUrl = 'https://theia-ide.org/docs/user_ai/';
protected readonly theiaAIDocUrl = 'https://theia-ide.org/docs/theia_ai/';
protected readonly dataUsageTelemetryUrl = 'https://theia-ide.org/docs/data_usage_telemetry/';
protected readonly ghProjectUrl = 'https://github.com/eclipse-theia/theia/issues/new/choose';
@inject(ApplicationServer)
protected readonly appServer: ApplicationServer;
@inject(CommandRegistry)
protected readonly commandRegistry: CommandRegistry;
@inject(EnvVariablesServer)
protected readonly environments: EnvVariablesServer;
@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;
@inject(WindowService)
protected readonly windowService: WindowService;
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;
@inject(PreferenceService)
protected readonly preferenceService: PreferenceService;
@inject(MarkdownRenderer)
protected readonly markdownRenderer: MarkdownRenderer;
@postConstruct()
protected init(): void {
this.doInit();
}
protected async doInit(): Promise<void> {
this.id = GettingStartedWidget.ID;
this.title.label = GettingStartedWidget.LABEL;
this.title.caption = GettingStartedWidget.LABEL;
this.title.closable = true;
this.applicationInfo = await this.appServer.getApplicationInfo();
this.recentWorkspaces = await this.workspaceService.recentWorkspaces();
this.home = new URI(await this.environments.getHomeDirUri()).path.toString();
const extensions = await this.appServer.getExtensionsInfos();
this.aiIsIncluded = extensions.find(ext => ext.name === '@theia/ai-core') !== undefined;
this.update();
}
protected override onActivateRequest(msg: Message): void {
super.onActivateRequest(msg);
const elArr = this.node.getElementsByTagName('a');
if (elArr && elArr.length > 0) {
(elArr[0] as HTMLElement).focus();
}
}
/**
* Render the content of the widget.
*/
protected render(): React.ReactNode {
return <div className='gs-container'>
<div className='gs-content-container'>
{this.aiIsIncluded &&
<div className='gs-float shadow-pulse'>
{this.renderAIBanner()}
</div>
}
{this.renderHeader()}
<hr className='gs-hr' />
{this.aiIsIncluded &&
<div className='flex-grid'>
<div className='col'>
{this.renderNews()}
</div>
</div>
}
<div className='flex-grid'>
<div className='col'>
{this.renderStart()}
</div>
</div>
<div className='flex-grid'>
<div className='col'>
{this.renderRecentWorkspaces()}
</div>
</div>
<div className='flex-grid'>
<div className='col'>
{this.renderSettings()}
</div>
</div>
<div className='flex-grid'>
<div className='col'>
{this.renderHelp()}
</div>
</div>
<div className='flex-grid'>
<div className='col'>
{this.renderVersion()}
</div>
</div>
</div>
<div className='gs-preference-container'>
{this.renderPreferences()}
</div>
</div>;
}
/**
* Render the widget header.
* Renders the title `{applicationName} Getting Started`.
*/
protected renderHeader(): React.ReactNode {
return <div className='gs-header'>
<h1>{this.applicationName}<span className='gs-sub-header'>{' ' + GettingStartedWidget.LABEL}</span></h1>
</div>;
}
/**
* Render the `Start` section.
* Displays a collection of "start-to-work" related commands like `open` commands and some other.
*/
protected renderStart(): React.ReactNode {
const requireSingleOpen = isOSX || !environment.electron.is();
const createFile = <div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doCreateFile}
onKeyDown={this.doCreateFileEnter}>
{nls.localizeByDefault('New File...')}
</a>
</div>;
const open = requireSingleOpen && <div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpen}
onKeyDown={this.doOpenEnter}>
{nls.localizeByDefault('Open')}
</a>
</div>;
const openFile = !requireSingleOpen && <div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenFile}
onKeyDown={this.doOpenFileEnter}>
{nls.localizeByDefault('Open File')}
</a>
</div>;
const openFolder = !requireSingleOpen && <div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenFolder}
onKeyDown={this.doOpenFolderEnter}>
{nls.localizeByDefault('Open Folder')}
</a>
</div>;
const openWorkspace = (
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenWorkspace}
onKeyDown={this.doOpenWorkspaceEnter}>
{nls.localizeByDefault('Open Workspace')}
</a>
);
return <div className='gs-section'>
<h3 className='gs-section-header'><i className={codicon('folder-opened')}></i>{nls.localizeByDefault('Start')}</h3>
{createFile}
{open}
{openFile}
{openFolder}
{openWorkspace}
</div>;
}
/**
* Render the recently used workspaces section.
*/
protected renderRecentWorkspaces(): React.ReactNode {
const items = this.recentWorkspaces;
const paths = this.buildPaths(items);
const content = paths.slice(0, this.recentLimit).map((item, index) =>
<div className='gs-action-container' key={index}>
<a
role={'button'}
tabIndex={0}
onClick={() => this.open(new URI(items[index]))}
onKeyDown={(e: React.KeyboardEvent) => this.openEnter(e, new URI(items[index]))}>
{this.labelProvider.getName(new URI(items[index]))}
</a>
<span className='gs-action-details'>
{item}
</span>
</div>
);
// If the recently used workspaces list exceeds the limit, display `More...` which triggers the recently used workspaces quick-open menu upon selection.
const more = paths.length > this.recentLimit && <div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenRecentWorkspace}
onKeyDown={this.doOpenRecentWorkspaceEnter}>
{nls.localizeByDefault('More...')}
</a>
</div>;
return <div className='gs-section'>
<h3 className='gs-section-header'>
<i className={codicon('history')}></i>{nls.localizeByDefault('Recent')}
</h3>
{items.length > 0 ? content : <p className='gs-no-recent'>
{nls.localizeByDefault('You have no recent folders,') + ' '}
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenFolder}
onKeyDown={this.doOpenFolderEnter}>
{nls.localizeByDefault('open a folder')}
</a>
{' ' + nls.localizeByDefault('to start.')}
</p>}
{more}
</div>;
}
/**
* Render the settings section.
* Generally used to display useful links.
*/
protected renderSettings(): React.ReactNode {
return <div className='gs-section'>
<h3 className='gs-section-header'>
<i className={codicon('settings-gear')}></i>
{nls.localizeByDefault('Settings')}
</h3>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenPreferences}
onKeyDown={this.doOpenPreferencesEnter}>
{nls.localizeByDefault('Open Settings')}
</a>
</div>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={this.doOpenKeyboardShortcuts}
onKeyDown={this.doOpenKeyboardShortcutsEnter}>
{nls.localizeByDefault('Open Keyboard Shortcuts')}
</a>
</div>
</div>;
}
/**
* Render the help section.
*/
protected renderHelp(): React.ReactNode {
return <div className='gs-section'>
<h3 className='gs-section-header'>
<i className={codicon('question')}></i>
{nls.localizeByDefault('Help')}
</h3>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={() => this.doOpenExternalLink(this.documentationUrl)}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.documentationUrl)}>
{nls.localizeByDefault('Documentation')}
</a>
</div>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={() => this.doOpenExternalLink(this.compatibilityUrl)}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.compatibilityUrl)}>
{nls.localize('theia/getting-started/apiComparator', '{0} API Compatibility', 'VS Code')}
</a>
</div>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={() => this.doOpenExternalLink(this.extensionUrl)}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.extensionUrl)}>
{nls.localize('theia/getting-started/newExtension', 'Building a New Extension')}
</a>
</div>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={() => this.doOpenExternalLink(this.pluginUrl)}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.pluginUrl)}>
{nls.localize('theia/getting-started/newPlugin', 'Building a New Plugin')}
</a>
</div>
<div className='gs-action-container'>
<a
role={'button'}
tabIndex={0}
onClick={() => this.doOpenExternalLink(this.dataUsageTelemetryUrl)}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenExternalLinkEnter(e, this.dataUsageTelemetryUrl)}>
{nls.localize('theia/getting-started/telemetry', 'Data Usage & Telemetry')}
</a>
</div>
</div>;
}
/**
* Render the version section.
*/
protected renderVersion(): React.ReactNode {
return <div className='gs-section'>
<div className='gs-action-container'>
<p className='gs-sub-header' >
{this.applicationInfo ? nls.localizeByDefault('Version: {0}', this.applicationInfo.version) : ''}
</p>
</div>
</div>;
}
protected renderPreferences(): React.ReactNode {
return <WelcomePreferences preferenceService={this.preferenceService}></WelcomePreferences>;
}
protected renderNews(): React.ReactNode {
return <div className='gs-section'>
<h3 className='gs-section-header'>🚀 {nls.localize('theia/getting-started/ai/header', 'AI Support in the Theia IDE is available (Beta Version)!')} </h3>
<div className='gs-action-container'>
<a
role={'button'}
style={{ fontSize: 'var(--theia-ui-font-size2)' }}
tabIndex={0}
onClick={() => this.doOpenAIChatView()}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenAIChatViewEnter(e)}>
{nls.localize('theia/getting-started/ai/openAIChatView', 'Open the AI Chat View now to learn how to start!')}
</a>
</div>
</div>;
}
protected renderAIBanner(): React.ReactNode {
return <div className='gs-container gs-aifeature-container'>
<div className='flex-grid'>
<div className='col'>
<h3 className='gs-section-header'> 🚀 {nls.localize('theia/getting-started/ai/header', 'AI Support in the Theia IDE is available (Beta Version)!')} </h3>
<LocalizedMarkdown className='gs-action-container'
localizationKey='theia/getting-started/ai/features'
defaultMarkdown={`
Theia IDE now contains AI support, which offers early access to cutting-edge AI capabilities within your IDE.\\
Please note that these features are disabled by default, ensuring that users can opt-in at their discretion.
For those who choose to enable AI support, it is important to be aware that these may generate continuous
requests to the language models (LLMs) you provide access to. This might incur costs that you need to monitor closely.\\
For more details, please visit&nbsp;[the documentation]({0}).\\
\\
🚧 Please note that this feature is currently in a beta state and may undergo changes.
We welcome your feedback, contributions, and sponsorship! To support the ongoing development of the AI capabilities please visit the&nbsp;[Github Project]({1}).&nbsp;
Thank you for being part of our community!\\
The AI features are built on the framework Theia AI. If you want to build a custom AI-powered tool or IDE, Theia AI has been published as stable release.
Check out [the Theia AI documentation]({2})!
`}
args={[this.userAIDocUrl, this.ghProjectUrl, this.theiaAIDocUrl]}
markdownRenderer={this.markdownRenderer}
/>
<div className='gs-action-container'>
<a
role={'button'}
style={{ fontSize: 'var(--theia-ui-font-size2)' }}
tabIndex={0}
onClick={() => this.doOpenAIChatView()}
onKeyDown={(e: React.KeyboardEvent) => this.doOpenAIChatViewEnter(e)}>
{nls.localize('theia/getting-started/ai/openAIChatView', 'Open the AI Chat View now to learn how to start!')}
</a>
</div>
</div>
</div>
</div>;
}
protected doOpenAIChatView = () => this.commandRegistry.executeCommand('aiChat:toggle');
protected doOpenAIChatViewEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenAIChatView();
}
};
/**
* Build the list of workspace paths.
* @param workspaces {string[]} the list of workspaces.
* @returns {string[]} the list of workspace paths.
*/
protected buildPaths(workspaces: string[]): string[] {
const paths: string[] = [];
workspaces.forEach(workspace => {
const uri = new URI(workspace);
const pathLabel = this.labelProvider.getLongName(uri);
const path = this.home ? Path.tildify(pathLabel, this.home) : pathLabel;
paths.push(path);
});
return paths;
}
/**
* Trigger the create file command.
*/
protected doCreateFile = () => this.commandRegistry.executeCommand(CommonCommands.PICK_NEW_FILE.id);
protected doCreateFileEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doCreateFile();
}
};
/**
* Trigger the open command.
*/
protected doOpen = () => this.commandRegistry.executeCommand(WorkspaceCommands.OPEN.id);
protected doOpenEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpen();
}
};
/**
* Trigger the open file command.
*/
protected doOpenFile = () => this.commandRegistry.executeCommand(WorkspaceCommands.OPEN_FILE.id);
protected doOpenFileEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenFile();
}
};
/**
* Trigger the open folder command.
*/
protected doOpenFolder = () => this.commandRegistry.executeCommand(WorkspaceCommands.OPEN_FOLDER.id);
protected doOpenFolderEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenFolder();
}
};
/**
* Trigger the open workspace command.
*/
protected doOpenWorkspace = () => this.commandRegistry.executeCommand(WorkspaceCommands.OPEN_WORKSPACE.id);
protected doOpenWorkspaceEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenWorkspace();
}
};
/**
* Trigger the open recent workspace command.
*/
protected doOpenRecentWorkspace = () => this.commandRegistry.executeCommand(WorkspaceCommands.OPEN_RECENT_WORKSPACE.id);
protected doOpenRecentWorkspaceEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenRecentWorkspace();
}
};
/**
* Trigger the open preferences command.
* Used to open the preferences widget.
*/
protected doOpenPreferences = () => this.commandRegistry.executeCommand(CommonCommands.OPEN_PREFERENCES.id);
protected doOpenPreferencesEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenPreferences();
}
};
/**
* Trigger the open keyboard shortcuts command.
* Used to open the keyboard shortcuts widget.
*/
protected doOpenKeyboardShortcuts = () => this.commandRegistry.executeCommand(KeymapsCommands.OPEN_KEYMAPS.id);
protected doOpenKeyboardShortcutsEnter = (e: React.KeyboardEvent) => {
if (this.isEnterKey(e)) {
this.doOpenKeyboardShortcuts();
}
};
/**
* Open a workspace given its uri.
* @param uri {URI} the workspace uri.
*/
protected open = (uri: URI) => this.workspaceService.open(uri);
protected openEnter = (e: React.KeyboardEvent, uri: URI) => {
if (this.isEnterKey(e)) {
this.open(uri);
}
};
/**
* Open a link in an external window.
* @param url the link.
*/
protected doOpenExternalLink = (url: string) => this.windowService.openNewWindow(url, { external: true });
protected doOpenExternalLinkEnter = (e: React.KeyboardEvent, url: string) => {
if (this.isEnterKey(e)) {
this.doOpenExternalLink(url);
}
};
protected isEnterKey(e: React.KeyboardEvent): boolean {
return Key.ENTER.keyCode === KeyCode.createKeyCode(e.nativeEvent).key?.keyCode;
}
}
export interface PreferencesProps {
preferenceService: PreferenceService;
}
function WelcomePreferences(props: PreferencesProps): JSX.Element {
const [startupEditor, setStartupEditor] = React.useState<string>(
props.preferenceService.get('workbench.startupEditor', 'welcomePage')
);
React.useEffect(() => {
const prefListener = props.preferenceService.onPreferenceChanged(change => {
if (change.preferenceName === 'workbench.startupEditor') {
const prefValue = props.preferenceService.get<string>('workbench.startupEditor', 'none');
setStartupEditor(prefValue);
}
});
return () => prefListener.dispose();
}, [props.preferenceService]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.checked ? 'welcomePage' : 'none';
props.preferenceService.updateValue('workbench.startupEditor', newValue);
};
return (
<div className='gs-preference'>
<input
type="checkbox"
className="theia-input"
id="startupEditor"
onChange={handleChange}
checked={startupEditor === 'welcomePage' || startupEditor === 'welcomePageInEmptyWorkbench'}
/>
<label htmlFor="startupEditor">
{nls.localizeByDefault('Show welcome page on startup')}
</label>
</div>
);
}

View File

@@ -0,0 +1,136 @@
/********************************************************************************
* Copyright (C) 2018 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
********************************************************************************/
html,
body {
font-family: var(--theia-ui-font-family);
}
.col {
flex: 1;
align-items: center;
justify-content: center;
}
.flex-grid {
display: flex;
}
.gs-action-container {
line-height: 20px;
}
.gs-action-details {
padding-left: 5px;
color: var(--theia-descriptionForeground);
}
.gs-container {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.gs-content-container {
padding: 20px;
}
.gs-content-container a {
cursor: pointer;
}
.gs-header h1 {
flex: 1;
font-weight: 600;
}
.gs-hr {
background-color: var(--theia-contrastBorder);
height: 1px;
border: 0;
margin: 0px;
}
.gs-no-recent {
color: var(--theia-descriptionForeground);
}
.gs-section a {
border: none;
font-weight: 500;
text-decoration: none;
}
.gs-section a:hover {
text-decoration: underline;
}
.gs-section-header {
font-size: var(--theia-ui-font-size2);
font-weight: 600;
margin-bottom: 5px;
display: flex;
align-items: center;
}
.gs-section-header i {
padding-right: 5px;
}
.gs-sub-header {
color: var(--theia-descriptionForeground);
text-transform: capitalize;
font-weight: 400;
}
.gs-preference-container {
display: flex;
justify-content: center;
}
.gs-preference {
margin-bottom: 20px;
display: flex;
align-items: center;
}
.gs-float {
float: right;
width: 50%;
margin-top: 100px;
}
.gs-container.gs-aifeature-container {
border: 1px solid var(--theia-focusBorder);
padding: 15px;
}
.shadow-pulse {
animation: shadowPulse 2s infinite ease-in-out;
}
@keyframes shadowPulse {
0%,
100% {
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
50% {
box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
}
}

View File

@@ -0,0 +1,67 @@
// *****************************************************************************
// Copyright (C) 2023 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { interfaces } from '@theia/core/shared/inversify';
import {
createPreferenceProxy,
PreferenceContribution,
PreferenceProxy,
PreferenceSchema,
PreferenceService,
} from '@theia/core/lib/common/preferences';
import { nls } from '@theia/core/lib/common/nls';
export const GettingStartedPreferenceSchema: PreferenceSchema = {
properties: {
'workbench.startupEditor': {
type: 'string',
enum: ['none', 'welcomePage', 'readme', 'newUntitledFile', 'welcomePageInEmptyWorkbench'],
enumDescriptions: [
nls.localizeByDefault('Start without an editor.'),
nls.localize('theia/getting-started/startup-editor/welcomePage', 'Open the Welcome page, with content to aid in getting started with {0} and extensions.',
'Theia'),
// eslint-disable-next-line max-len
nls.localizeByDefault("Open the README when opening a folder that contains one, fallback to 'welcomePage' otherwise. Note: This is only observed as a global configuration, it will be ignored if set in a workspace or folder configuration."),
nls.localizeByDefault('Open a new untitled text file (only applies when opening an empty window).'),
nls.localizeByDefault('Open the Welcome page when opening an empty workbench.'),
],
default: 'welcomePage',
description: nls.localizeByDefault('Controls which editor is shown at startup, if none are restored from the previous session.')
},
}
};
export interface GettingStartedConfiguration {
'workbench.startupEditor': string;
}
export const GettingStartedPreferenceContribution = Symbol('GettingStartedPreferenceContribution');
export const GettingStartedPreferences = Symbol('GettingStartedPreferences');
export type GettingStartedPreferences = PreferenceProxy<GettingStartedConfiguration>;
export function createGettingStartedPreferences(preferences: PreferenceService, schema: PreferenceSchema = GettingStartedPreferenceSchema): GettingStartedPreferences {
return createPreferenceProxy(preferences, schema);
}
export function bindGettingStartedPreferences(bind: interfaces.Bind): void {
bind(GettingStartedPreferences).toDynamicValue(ctx => {
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
const contribution = ctx.container.get<PreferenceContribution>(GettingStartedPreferenceContribution);
return createGettingStartedPreferences(preferences, contribution.schema);
}).inSingletonScope();
bind(GettingStartedPreferenceContribution).toConstantValue({ schema: GettingStartedPreferenceSchema });
bind(PreferenceContribution).toService(GettingStartedPreferenceContribution);
}

View File

@@ -0,0 +1,22 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { ContainerModule, interfaces } from '@theia/core/shared/inversify';
import { bindGettingStartedPreferences } from '../common/getting-started-preferences';
export default new ContainerModule((bind: interfaces.Bind) => {
bindGettingStartedPreferences(bind);
});

View File

@@ -0,0 +1,28 @@
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* note: this bogus test file is required so that
we are able to run mocha unit tests on this
package, without having any actual unit tests in it.
This way a coverage report will be generated,
showing 0% coverage, instead of no report.
This file can be removed once we have real unit
tests in place. */
describe('getting-started package', () => {
it('support code coverage statistics', () => true);
});