mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-09 16:24:45 +01:00
terminal: support tab title templates for extension terminals (#296404)
* terminal: support tab title templates for extension terminals * terminal: omit undefined tabTitle from profile quick pick * refactor: rename terminal title properties to titleTemplate for consistency * proposed api check.
This commit is contained in:
@@ -449,6 +449,9 @@ const _allApiProposals = {
|
||||
terminalShellEnv: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellEnv.d.ts',
|
||||
},
|
||||
terminalTitle: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalTitle.d.ts',
|
||||
},
|
||||
testObserver: {
|
||||
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts',
|
||||
},
|
||||
|
||||
@@ -671,6 +671,13 @@ export interface IShellLaunchConfig {
|
||||
* This allows extensions to control shell integration for terminals they create.
|
||||
*/
|
||||
shellIntegrationNonce?: string;
|
||||
|
||||
/**
|
||||
* A title template string that supports the same variables as the
|
||||
* `terminal.integrated.tabs.title` setting. When set, this overrides the config-based
|
||||
* title template for this terminal instance.
|
||||
*/
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
export interface ITerminalTabAction {
|
||||
@@ -686,6 +693,7 @@ export interface ICreateContributedTerminalProfileOptions {
|
||||
color?: string;
|
||||
location?: TerminalLocation | { viewColumn: number; preserveState?: boolean } | { splitActiveTerminal: boolean };
|
||||
cwd?: string | URI;
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
export enum TerminalLocation {
|
||||
@@ -713,6 +721,7 @@ export interface IShellLaunchConfigDto {
|
||||
isFeatureTerminal?: boolean;
|
||||
tabActions?: ITerminalTabAction[];
|
||||
shellIntegrationEnvironmentReporting?: boolean;
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -962,6 +971,7 @@ export interface ITerminalProfileContribution {
|
||||
id: string;
|
||||
icon?: URI | { light: URI; dark: URI } | string;
|
||||
color?: string;
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
export interface IExtensionTerminalProfile extends ITerminalProfileContribution {
|
||||
|
||||
@@ -173,7 +173,8 @@ export class MainThreadTerminalService extends Disposable implements MainThreadT
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal,
|
||||
useShellEnvironment: launchConfig.useShellEnvironment,
|
||||
isTransient: launchConfig.isTransient,
|
||||
shellIntegrationNonce: launchConfig.shellIntegrationNonce
|
||||
shellIntegrationNonce: launchConfig.shellIntegrationNonce,
|
||||
titleTemplate: launchConfig.titleTemplate,
|
||||
};
|
||||
const terminal = Promises.withAsyncBody<ITerminalInstance>(async r => {
|
||||
const terminal = await this._terminalService.createTerminal({
|
||||
@@ -415,7 +416,8 @@ export class MainThreadTerminalService extends Disposable implements MainThreadT
|
||||
cwd: terminalInstance.shellLaunchConfig.cwd,
|
||||
env: terminalInstance.shellLaunchConfig.env,
|
||||
hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser,
|
||||
tabActions: terminalInstance.shellLaunchConfig.tabActions
|
||||
tabActions: terminalInstance.shellLaunchConfig.tabActions,
|
||||
titleTemplate: terminalInstance.shellLaunchConfig.titleTemplate
|
||||
};
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.instanceId, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto);
|
||||
}
|
||||
|
||||
@@ -941,6 +941,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
},
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | vscode.ExtensionTerminalOptions | string, shellPath?: string, shellArgs?: readonly string[] | string): vscode.Terminal {
|
||||
if (typeof nameOrOptions === 'object') {
|
||||
if ('titleTemplate' in nameOrOptions && nameOrOptions.titleTemplate !== undefined) {
|
||||
checkProposedApiEnabled(extension, 'terminalTitle');
|
||||
}
|
||||
if ('pty' in nameOrOptions) {
|
||||
return extHostTerminalService.createExtensionTerminal(nameOrOptions);
|
||||
}
|
||||
|
||||
@@ -626,6 +626,7 @@ export interface TerminalLaunchConfig {
|
||||
location?: TerminalLocation | { viewColumn: number; preserveFocus?: boolean } | { parentTerminal: ExtHostTerminalIdentifier } | { splitActiveTerminal: boolean };
|
||||
isTransient?: boolean;
|
||||
shellIntegrationNonce?: string;
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js';
|
||||
import { ISerializedTerminalInstanceContext } from '../../contrib/terminal/common/terminal.js';
|
||||
import { isWindows } from '../../../base/common/platform.js';
|
||||
import { hasKey } from '../../../base/common/types.js';
|
||||
import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
|
||||
|
||||
@@ -193,11 +194,12 @@ export class ExtHostTerminal extends Disposable {
|
||||
location: internalOptions?.location || this._serializeParentTerminal(options.location, internalOptions?.resolvedExtHostIdentifier),
|
||||
isTransient: options.isTransient ?? undefined,
|
||||
shellIntegrationNonce: options.shellIntegrationNonce ?? undefined,
|
||||
titleTemplate: options.titleTemplate ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public async createExtensionTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, internalOptions?: ITerminalInternalOptions, parentTerminal?: ExtHostTerminalIdentifier, iconPath?: TerminalIcon, color?: ThemeColor, shellIntegrationNonce?: string): Promise<number> {
|
||||
public async createExtensionTerminal(location?: TerminalLocation | vscode.TerminalEditorLocationOptions | vscode.TerminalSplitLocationOptions, internalOptions?: ITerminalInternalOptions, parentTerminal?: ExtHostTerminalIdentifier, iconPath?: TerminalIcon, color?: ThemeColor, shellIntegrationNonce?: string, titleTemplate?: string): Promise<number> {
|
||||
if (typeof this._id !== 'string') {
|
||||
throw new Error('Terminal has already been created');
|
||||
}
|
||||
@@ -209,6 +211,7 @@ export class ExtHostTerminal extends Disposable {
|
||||
location: internalOptions?.location || this._serializeParentTerminal(location, parentTerminal),
|
||||
isTransient: true,
|
||||
shellIntegrationNonce: shellIntegrationNonce ?? undefined,
|
||||
titleTemplate: titleTemplate ?? undefined,
|
||||
});
|
||||
// At this point, the id has been set via `$acceptTerminalOpened`
|
||||
if (typeof this._id === 'string') {
|
||||
@@ -423,7 +426,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
private readonly _bufferer: TerminalDataBufferer;
|
||||
private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
|
||||
private readonly _completionProviders: Map<string, vscode.TerminalCompletionProvider<vscode.TerminalCompletionItem>> = new Map();
|
||||
private readonly _profileProviders: Map<string, vscode.TerminalProfileProvider> = new Map();
|
||||
private readonly _profileProviders: Map<string, { provider: vscode.TerminalProfileProvider; extension: IExtensionDescription }> = new Map();
|
||||
private readonly _quickFixProviders: Map<string, vscode.TerminalQuickFixProvider> = new Map();
|
||||
private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
|
||||
private readonly _terminalLinkCancellationSource: Map<number, CancellationTokenSource> = new Map();
|
||||
@@ -513,7 +516,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, generateUuid(), options, options.name);
|
||||
const p = new ExtHostPseudoterminal(options.pty);
|
||||
terminal.createExtensionTerminal(options.location, internalOptions, this._serializeParentTerminal(options, internalOptions).resolvedExtHostIdentifier, asTerminalIcon(options.iconPath), asTerminalColor(options.color), options.shellIntegrationNonce).then(id => {
|
||||
terminal.createExtensionTerminal(options.location, internalOptions, this._serializeParentTerminal(options, internalOptions).resolvedExtHostIdentifier, asTerminalIcon(options.iconPath), asTerminalColor(options.color), options.shellIntegrationNonce, options.titleTemplate).then(id => {
|
||||
const disposable = this._setupExtHostProcessListeners(id, p);
|
||||
this._terminalProcessDisposables[id] = disposable;
|
||||
});
|
||||
@@ -634,7 +637,8 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
shellArgs: shellLaunchConfigDto.args,
|
||||
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
|
||||
env: shellLaunchConfigDto.env,
|
||||
hideFromUser: shellLaunchConfigDto.hideFromUser
|
||||
hideFromUser: shellLaunchConfigDto.hideFromUser,
|
||||
titleTemplate: shellLaunchConfigDto.titleTemplate
|
||||
};
|
||||
const terminal = new ExtHostTerminal(this._proxy, id, creationOptions, name);
|
||||
this._terminals.push(terminal);
|
||||
@@ -749,7 +753,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
if (this._profileProviders.has(id)) {
|
||||
throw new Error(`Terminal profile provider "${id}" already registered`);
|
||||
}
|
||||
this._profileProviders.set(id, provider);
|
||||
this._profileProviders.set(id, { provider, extension });
|
||||
this._proxy.$registerProfileProvider(id, extension.identifier.value);
|
||||
return new VSCodeDisposable(() => {
|
||||
this._profileProviders.delete(id);
|
||||
@@ -842,7 +846,11 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
|
||||
public async $createContributedProfileTerminal(id: string, options: ICreateContributedTerminalProfileOptions): Promise<void> {
|
||||
const token = new CancellationTokenSource().token;
|
||||
let profile = await this._profileProviders.get(id)?.provideTerminalProfile(token);
|
||||
const profileProviderData = this._profileProviders.get(id);
|
||||
if (!profileProviderData) {
|
||||
throw new Error(`No terminal profile provider registered for id "${id}"`);
|
||||
}
|
||||
let profile = await profileProviderData.provider.provideTerminalProfile(token);
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
@@ -854,11 +862,19 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
throw new Error(`No terminal profile options provided for id "${id}"`);
|
||||
}
|
||||
|
||||
if (hasKey(profile.options, { pty: true })) {
|
||||
this.createExtensionTerminal(profile.options, options);
|
||||
if (profile.options.titleTemplate !== undefined) {
|
||||
checkProposedApiEnabled(profileProviderData.extension, 'terminalTitle');
|
||||
}
|
||||
|
||||
const profileOptions = options.titleTemplate && !profile.options.titleTemplate
|
||||
? { ...profile.options, titleTemplate: options.titleTemplate }
|
||||
: profile.options;
|
||||
|
||||
if (hasKey(profileOptions, { pty: true })) {
|
||||
this.createExtensionTerminal(profileOptions, options);
|
||||
return;
|
||||
}
|
||||
this.createTerminalFromOptions(profile.options, options);
|
||||
this.createTerminalFromOptions(profileOptions, options);
|
||||
}
|
||||
|
||||
public registerLinkProvider(provider: vscode.TerminalLinkProvider): vscode.Disposable {
|
||||
|
||||
@@ -518,7 +518,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
|
||||
// When a custom pty is used set the name immediately so it gets passed over to the exthost
|
||||
// and is available when Pseudoterminal.open fires.
|
||||
if (this.shellLaunchConfig.customPtyImplementation) {
|
||||
if (this.shellLaunchConfig.customPtyImplementation && !this._shellLaunchConfig.titleTemplate) {
|
||||
this._setTitle(this._shellLaunchConfig.name, TitleEventSource.Api);
|
||||
}
|
||||
|
||||
@@ -1471,7 +1471,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (this._shellLaunchConfig.name) {
|
||||
if (this._shellLaunchConfig.name && !this._shellLaunchConfig.titleTemplate) {
|
||||
this._setTitle(this._shellLaunchConfig.name, TitleEventSource.Api);
|
||||
} else {
|
||||
// Listen to xterm.js' sequence title change event, trigger this async to ensure
|
||||
@@ -1483,7 +1483,13 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
||||
}
|
||||
});
|
||||
});
|
||||
this._setTitle(this._shellLaunchConfig.executable, TitleEventSource.Process);
|
||||
// When a title template is provided, use the name as the initial process name
|
||||
// so it can be referenced via ${process} in the template
|
||||
if (this._shellLaunchConfig.titleTemplate && this._shellLaunchConfig.name) {
|
||||
this._setTitle(this._shellLaunchConfig.name, TitleEventSource.Process);
|
||||
} else {
|
||||
this._setTitle(this._shellLaunchConfig.executable, TitleEventSource.Process);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this._register(processManager.onProcessExit(exitCode => this._onProcessExit(exitCode)));
|
||||
@@ -2633,7 +2639,8 @@ export class TerminalLabelComputer extends Disposable {
|
||||
}
|
||||
|
||||
refreshLabel(instance: Pick<ITerminalInstance, 'shellLaunchConfig' | 'shellType' | 'cwd' | 'fixedCols' | 'fixedRows' | 'initialCwd' | 'processName' | 'sequence' | 'userHome' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'>, reset?: boolean): void {
|
||||
this._title = this.computeLabel(instance, this._terminalConfigurationService.config.tabs.title, TerminalLabelType.Title, reset);
|
||||
const titleTemplate = instance.shellLaunchConfig.titleTemplate ?? this._terminalConfigurationService.config.tabs.title;
|
||||
this._title = this.computeLabel(instance, titleTemplate, TerminalLabelType.Title, reset);
|
||||
this._description = this.computeLabel(instance, this._terminalConfigurationService.config.tabs.description, TerminalLabelType.Description);
|
||||
if (this._title !== instance.title || this._description !== instance.description || reset) {
|
||||
this._onDidChangeLabel.fire({ title: this._title, description: this._description });
|
||||
|
||||
@@ -77,16 +77,29 @@ export class TerminalProfileQuickpick {
|
||||
await this._configurationService.updateValue(defaultProfileKey, result.profileName, ConfigurationTarget.USER);
|
||||
} else if (type === 'createInstance') {
|
||||
if (hasKey(result.profile, { id: true })) {
|
||||
const config: {
|
||||
extensionIdentifier: string;
|
||||
id: string;
|
||||
title: string;
|
||||
titleTemplate?: string;
|
||||
options: {
|
||||
icon: IExtensionTerminalProfile['icon'];
|
||||
color: IExtensionTerminalProfile['color'];
|
||||
};
|
||||
} = {
|
||||
extensionIdentifier: result.profile.extensionIdentifier,
|
||||
id: result.profile.id,
|
||||
title: result.profile.title,
|
||||
options: {
|
||||
icon: result.profile.icon,
|
||||
color: result.profile.color,
|
||||
}
|
||||
};
|
||||
if (result.profile.titleTemplate !== undefined) {
|
||||
config.titleTemplate = result.profile.titleTemplate;
|
||||
}
|
||||
return {
|
||||
config: {
|
||||
extensionIdentifier: result.profile.extensionIdentifier,
|
||||
id: result.profile.id,
|
||||
title: result.profile.title,
|
||||
options: {
|
||||
icon: result.profile.icon,
|
||||
color: result.profile.color,
|
||||
}
|
||||
},
|
||||
config,
|
||||
keyMods: result.keyMods
|
||||
};
|
||||
} else {
|
||||
@@ -177,7 +190,8 @@ export class TerminalProfileQuickpick {
|
||||
title: contributed.title,
|
||||
icon: contributed.icon,
|
||||
id: contributed.id,
|
||||
color: contributed.color
|
||||
color: contributed.color,
|
||||
titleTemplate: contributed.titleTemplate
|
||||
},
|
||||
profileName: contributed.title,
|
||||
iconClasses
|
||||
|
||||
@@ -233,7 +233,8 @@ export class TerminalProfileService extends Disposable implements ITerminalProfi
|
||||
icon: args.options.icon,
|
||||
id: args.id,
|
||||
title: args.title,
|
||||
color: args.options.color
|
||||
color: args.options.color,
|
||||
titleTemplate: args.titleTemplate
|
||||
};
|
||||
|
||||
(profilesConfig as { [key: string]: ITerminalProfileObject })[args.title] = newProfile;
|
||||
@@ -271,5 +272,6 @@ function contributedProfilesEqual(one: IExtensionTerminalProfile, other: IExtens
|
||||
one.color === other.color &&
|
||||
one.icon === other.icon &&
|
||||
one.id === other.id &&
|
||||
one.title === other.title;
|
||||
one.title === other.title &&
|
||||
one.titleTemplate === other.titleTemplate;
|
||||
}
|
||||
|
||||
@@ -256,7 +256,8 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
await this.createContributedTerminalProfile(result.config.extensionIdentifier, result.config.id, {
|
||||
icon: result.config.options?.icon,
|
||||
color: result.config.options?.color,
|
||||
location: !!(keyMods?.alt && activeInstance) ? { splitActiveTerminal: true } : defaultLocation
|
||||
location: !!(keyMods?.alt && activeInstance) ? { splitActiveTerminal: true } : defaultLocation,
|
||||
titleTemplate: result.config.titleTemplate,
|
||||
});
|
||||
return;
|
||||
} else if (result.config && hasKey(result.config, { profileName: true })) {
|
||||
@@ -1032,6 +1033,7 @@ export class TerminalService extends Disposable implements ITerminalService {
|
||||
color: contributedProfile.color,
|
||||
location,
|
||||
cwd: shellLaunchConfig.cwd,
|
||||
titleTemplate: contributedProfile.titleTemplate,
|
||||
});
|
||||
const instanceHost = resolvedLocation === TerminalLocation.Editor ? this._terminalEditorService : this._terminalGroupService;
|
||||
// TODO@meganrogge: This returns undefined in the remote & web smoke tests but the function
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface ITerminalProfileResolverService {
|
||||
export const ShellIntegrationExitCode = 633;
|
||||
|
||||
export interface IRegisterContributedProfileArgs {
|
||||
extensionIdentifier: string; id: string; title: string; options: ICreateContributedTerminalProfileOptions;
|
||||
extensionIdentifier: string; id: string; title: string; options: ICreateContributedTerminalProfileOptions; titleTemplate?: string;
|
||||
}
|
||||
|
||||
export const ITerminalProfileService = createDecorator<ITerminalProfileService>('terminalProfileService');
|
||||
@@ -693,6 +693,10 @@ export const terminalContributionsDescriptor: IExtensionPointDescriptor<ITermina
|
||||
}
|
||||
}]
|
||||
},
|
||||
titleTemplate: {
|
||||
description: nls.localize('vscode.extension.contributes.terminal.profiles.titleTemplate', "A title template string for the terminal tab. Supports variables like $\{sequence}, $\{process}, $\{cwd}, etc. Overrides the default terminal.integrated.tabs.title setting for terminals created with this profile."),
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -404,6 +404,12 @@ suite('Workbench - TerminalInstance', () => {
|
||||
strictEqual(terminalLabelComputer.title, 'my-title');
|
||||
strictEqual(terminalLabelComputer.description, 'folder');
|
||||
});
|
||||
test('should use shellLaunchConfig.titleTemplate as template when set', () => {
|
||||
const terminalLabelComputer = createLabelComputer({ terminal: { integrated: { tabs: { separator: ' - ', title: '${process}', description: '${cwd}' } } } });
|
||||
terminalLabelComputer.refreshLabel(createInstance({ capabilities, sequence: 'my-sequence', processName: 'zsh', shellLaunchConfig: { titleTemplate: '${sequence}' } }));
|
||||
strictEqual(terminalLabelComputer.title, 'my-sequence');
|
||||
strictEqual(terminalLabelComputer.description, 'cwd');
|
||||
});
|
||||
test('should provide cwdFolder for all cwds only when in multi-root', () => {
|
||||
const terminalLabelComputer = createLabelComputer({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${cwdFolder}', description: '${cwdFolder}' } } } });
|
||||
terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: ROOT_1 }) } as IWorkspaceFolder, cwd: ROOT_1 }));
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/xyz
|
||||
|
||||
export interface TerminalOptions {
|
||||
/**
|
||||
* A title template string for the terminal tab. This supports the same variables as the
|
||||
* `terminal.integrated.tabs.title` setting, such as `${sequence}`, `${process}`, `${cwd}`,
|
||||
* `${cwdFolder}`, `${workspaceFolderName}`, etc. When set, this overrides the default title
|
||||
* behavior (which uses the `name` as a static title) and instead uses the template for
|
||||
* dynamic title resolution.
|
||||
*
|
||||
* For example, setting `titleTemplate` to `"${sequence}"` allows the terminal's escape sequence
|
||||
* title to be used as the tab title.
|
||||
*/
|
||||
titleTemplate?: string;
|
||||
}
|
||||
|
||||
export interface ExtensionTerminalOptions {
|
||||
/**
|
||||
* A title template string for the terminal tab. This supports the same variables as the
|
||||
* `terminal.integrated.tabs.title` setting, such as `${sequence}`, `${process}`, `${cwd}`,
|
||||
* `${cwdFolder}`, `${workspaceFolderName}`, etc. When set, this overrides the default title
|
||||
* behavior (which uses the `name` as a static title) and instead uses the template for
|
||||
* dynamic title resolution.
|
||||
*
|
||||
* For example, setting `titleTemplate` to `"${sequence}"` allows the terminal's escape sequence
|
||||
* title to be used as the tab title.
|
||||
*/
|
||||
titleTemplate?: string;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user