use monaco editor in terminal accessible buffer (#174400)

This commit is contained in:
Megan Rogge 2023-02-16 11:22:28 -06:00 committed by GitHub
parent 524f4227ca
commit 855ba20c38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 151 additions and 106 deletions

View file

@ -87,13 +87,13 @@
"vscode-proxy-agent": "^0.12.0", "vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0", "vscode-regexpp": "^3.1.0",
"vscode-textmate": "8.0.0", "vscode-textmate": "8.0.0",
"xterm": "5.2.0-beta.28", "xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7", "xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0", "xterm-addon-search": "0.11.0",
"xterm-addon-serialize": "0.9.0", "xterm-addon-serialize": "0.9.0",
"xterm-addon-unicode11": "0.5.0", "xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.15.0-beta.7", "xterm-addon-webgl": "0.15.0-beta.7",
"xterm-headless": "5.2.0-beta.28", "xterm-headless": "5.2.0-beta.29",
"yauzl": "^2.9.2", "yauzl": "^2.9.2",
"yazl": "^2.4.3" "yazl": "^2.4.3"
}, },

View file

@ -24,13 +24,13 @@
"vscode-proxy-agent": "^0.12.0", "vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0", "vscode-regexpp": "^3.1.0",
"vscode-textmate": "8.0.0", "vscode-textmate": "8.0.0",
"xterm": "5.2.0-beta.28", "xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7", "xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0", "xterm-addon-search": "0.11.0",
"xterm-addon-serialize": "0.9.0", "xterm-addon-serialize": "0.9.0",
"xterm-addon-unicode11": "0.5.0", "xterm-addon-unicode11": "0.5.0",
"xterm-addon-webgl": "0.15.0-beta.7", "xterm-addon-webgl": "0.15.0-beta.7",
"xterm-headless": "5.2.0-beta.28", "xterm-headless": "5.2.0-beta.29",
"yauzl": "^2.9.2", "yauzl": "^2.9.2",
"yazl": "^2.4.3" "yazl": "^2.4.3"
}, },

View file

@ -11,7 +11,7 @@
"tas-client-umd": "0.1.6", "tas-client-umd": "0.1.6",
"vscode-oniguruma": "1.7.0", "vscode-oniguruma": "1.7.0",
"vscode-textmate": "8.0.0", "vscode-textmate": "8.0.0",
"xterm": "5.2.0-beta.28", "xterm": "5.2.0-beta.29",
"xterm-addon-canvas": "0.4.0-beta.7", "xterm-addon-canvas": "0.4.0-beta.7",
"xterm-addon-search": "0.11.0", "xterm-addon-search": "0.11.0",
"xterm-addon-unicode11": "0.5.0", "xterm-addon-unicode11": "0.5.0",

View file

@ -88,7 +88,7 @@ xterm-addon-webgl@0.15.0-beta.7:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af" resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af"
integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w== integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w==
xterm@5.2.0-beta.28: xterm@5.2.0-beta.29:
version "5.2.0-beta.28" version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.28.tgz#852347e4eaf5aae7d82c90592a42adc9daab2a79" resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.29.tgz#99764aff5cd8cdb4335f5d59466b134cfcb45e3e"
integrity sha512-aLDxCuqjWHjvnhfWfkxy/y6coNrC+QIhbDe2sdfLPkrxhK6KnYE6qiZD5jXUIQXeq0KmSDcYi/esuKujvobC2A== integrity sha512-zx5RKcQqo78bza4R/m3WtxAJCBAF4U61fy6cxqb1PkqXF9/qdYlySUCVOauMxv+6n6cAxt3EQWwLlgvbvQBbsw==

View file

@ -866,15 +866,15 @@ xterm-addon-webgl@0.15.0-beta.7:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af" resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af"
integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w== integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w==
xterm-headless@5.2.0-beta.28: xterm-headless@5.2.0-beta.29:
version "5.2.0-beta.28" version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.28.tgz#d2c149da51ef138f46268b755c4fdc4202eb771c" resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.29.tgz#dd08312fdb4292c217e685d9e2e8b1957364e298"
integrity sha512-4XcjBhFwuyjpz2ubESwp75UceySOOKdJszKyyxOQ3/7L937uiVEBBLc8T231XU8lSwWUU7czyNjYyCfpszY4+Q== integrity sha512-1P4urIeDTkl2C+zGb4WUnKJMACZMPGYHwVXMjkB0WhMISbkt6M34MH9ljxHhnL99dHwlx2Lvi6wvhnpyZucWCg==
xterm@5.2.0-beta.28: xterm@5.2.0-beta.29:
version "5.2.0-beta.28" version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.28.tgz#852347e4eaf5aae7d82c90592a42adc9daab2a79" resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.29.tgz#99764aff5cd8cdb4335f5d59466b134cfcb45e3e"
integrity sha512-aLDxCuqjWHjvnhfWfkxy/y6coNrC+QIhbDe2sdfLPkrxhK6KnYE6qiZD5jXUIQXeq0KmSDcYi/esuKujvobC2A== integrity sha512-zx5RKcQqo78bza4R/m3WtxAJCBAF4U61fy6cxqb1PkqXF9/qdYlySUCVOauMxv+6n6cAxt3EQWwLlgvbvQBbsw==
yallist@^4.0.0: yallist@^4.0.0:
version "4.0.0" version "4.0.0"

View file

@ -112,8 +112,7 @@ export const enum TerminalSettingId {
ShellIntegrationDecorationsEnabled = 'terminal.integrated.shellIntegration.decorationsEnabled', ShellIntegrationDecorationsEnabled = 'terminal.integrated.shellIntegration.decorationsEnabled',
ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history', ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history',
ShellIntegrationSuggestEnabled = 'terminal.integrated.shellIntegration.suggestEnabled', ShellIntegrationSuggestEnabled = 'terminal.integrated.shellIntegration.suggestEnabled',
SmoothScrolling = 'terminal.integrated.smoothScrolling', SmoothScrolling = 'terminal.integrated.smoothScrolling'
AccessibleBufferContentEditable = 'terminal.integrated.accessibleBufferContentEditable'
} }
export const enum TerminalLogConstants { export const enum TerminalLogConstants {

View file

@ -1053,7 +1053,7 @@ export interface IXtermTerminal {
/** /**
* Focuses the accessible buffer, updating its contents * Focuses the accessible buffer, updating its contents
*/ */
focusAccessibleBuffer(): void; focusAccessibleBuffer(): Promise<void>;
} }
export interface IInternalXtermTerminal { export interface IInternalXtermTerminal {

View file

@ -423,7 +423,7 @@ export function registerTerminalActions() {
}); });
} }
async run(accessor: ServicesAccessor): Promise<void> { async run(accessor: ServicesAccessor): Promise<void> {
accessor.get(ITerminalService).activeInstance?.xterm?.focusAccessibleBuffer(); await accessor.get(ITerminalService).activeInstance?.xterm?.focusAccessibleBuffer();
} }
}); });
registerAction2(class extends Action2 { registerAction2(class extends Action2 {

View file

@ -38,8 +38,16 @@ import { Emitter } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { SuggestAddon } from 'vs/workbench/contrib/terminal/browser/xterm/suggestAddon'; import { SuggestAddon } from 'vs/workbench/contrib/terminal/browser/xterm/suggestAddon';
import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ITextModel } from 'vs/editor/common/model';
import { IModelService } from 'vs/editor/common/services/model';
import { StringBuilder } from 'vs/editor/common/core/stringBuilder';
import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget';
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration';
import { LinkDetector } from 'vs/editor/contrib/links/browser/links';
import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard';
const enum RenderConstants { const enum RenderConstants {
/** /**
@ -281,7 +289,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
}); });
} }
focusAccessibleBuffer(): void { async focusAccessibleBuffer(): Promise<void> {
this._accessibileBuffer?.focus(); this._accessibileBuffer?.focus();
} }
@ -312,7 +320,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
if (!this._container) { if (!this._container) {
this.raw.open(container); this.raw.open(container);
} }
this._accessibileBuffer = this._instantiationService.createInstance(AccessibleBuffer, this.raw, this.getFont(), this._capabilities); this._accessibileBuffer = this._instantiationService.createInstance(AccessibleBuffer, this, this._capabilities);
// TODO: Move before open to the DOM renderer doesn't initialize // TODO: Move before open to the DOM renderer doesn't initialize
if (this._shouldLoadWebgl()) { if (this._shouldLoadWebgl()) {
this._enableWebglRenderer(); this._enableWebglRenderer();
@ -763,76 +771,126 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, II
this.raw.write(data); this.raw.write(data);
} }
} }
const enum ACCESSIBLE_BUFFER { Scheme = 'terminal-accessible-buffer' }
class AccessibleBuffer extends DisposableStore { class AccessibleBuffer extends DisposableStore {
private _accessibleBuffer: HTMLElement;
private _accessibleBuffer: HTMLElement | undefined; private _bufferEditor: CodeEditorWidget;
private _bufferElementFragment: DocumentFragment | undefined; private _editorContainer: HTMLElement;
private _registered: boolean = false;
private _font: ITerminalFont;
constructor( constructor(
private readonly _terminal: RawXtermTerminal, private readonly _terminal: XtermTerminal,
private readonly _font: ITerminalFont,
private readonly _capabilities: ITerminalCapabilityStore, private readonly _capabilities: ITerminalCapabilityStore,
@IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IInstantiationService private readonly _instantiationService: IInstantiationService,
@IConfigurationService private readonly _configurationService: IConfigurationService @IModelService private readonly _modelService: IModelService,
@IConfigurationService configurationService: IConfigurationService
) { ) {
super(); super();
this.add(this._terminal.registerBufferElementProvider({ provideBufferElements: () => this.focus() })); const codeEditorWidgetOptions: ICodeEditorWidgetOptions = {
isSimpleWidget: true,
contributions: EditorExtensionsRegistry.getSomeEditorContributions([LinkDetector.ID, SelectionClipboardContributionID])
};
this._font = this._terminal.getFont();
const editorOptions: IEditorConstructionOptions = {
...getSimpleEditorOptions(),
lineDecorationsWidth: 6,
dragAndDrop: true,
cursorWidth: 1,
fontSize: this._font.fontSize,
lineHeight: this._font.charHeight ? this._font.charHeight * this._font.lineHeight : 1,
fontFamily: this._font.fontFamily,
wrappingStrategy: 'advanced',
wrappingIndent: 'none',
padding: { top: 2, bottom: 2 },
quickSuggestions: false,
scrollbar: { alwaysConsumeMouseWheel: false },
renderWhitespace: 'none',
dropIntoEditor: { enabled: true },
accessibilitySupport: configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'),
cursorBlinking: configurationService.getValue('terminal.integrated.cursorBlinking'),
readOnly: true
};
this._accessibleBuffer = this._terminal.raw.element!.querySelector('.xterm-accessible-buffer') as HTMLElement;
this._editorContainer = document.createElement('div');
this._bufferEditor = this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, editorOptions, codeEditorWidgetOptions);
this.add(configurationService.onDidChangeConfiguration(e => {
if (e.affectedKeys.has(TerminalSettingId.FontFamily)) {
this._font = this._terminal.getFont();
}
}));
} }
focus(): DocumentFragment { async focus(): Promise<void> {
if (!this._bufferElementFragment) { await this._updateBufferEditor();
this._bufferElementFragment = document.createDocumentFragment(); // Updates xterm's accessibleBufferActive property
} // such that mouse events do not cause the terminal buffer
this._accessibleBuffer = this._terminal.element?.querySelector('.xterm-accessible-buffer') as HTMLElement || undefined; // to steal the focus
if (!this._accessibleBuffer) {
return this._bufferElementFragment;
}
// see https://github.com/microsoft/vscode/issues/173532
const accessibleBufferContentEditable = isLinux ? 'on' : this._configurationService.getValue(TerminalSettingId.AccessibleBufferContentEditable);
this._accessibleBuffer.contentEditable = accessibleBufferContentEditable === 'on' || (accessibleBufferContentEditable === 'auto' && !this._accessibilityService.isScreenReaderOptimized()) ? 'true' : 'false';
// The viewport is undefined when this is focused, so we cannot get the cell height from that. Instead, estimate using the font.
const lineHeight = this._font?.charHeight ? this._font.charHeight * this._font.lineHeight + 'px' : '';
this._accessibleBuffer.style.lineHeight = lineHeight;
const commands = this._capabilities.get(TerminalCapability.CommandDetection)?.commands;
if (!commands?.length) {
const noContent = document.createElement('div');
const noContentLabel = localize('terminal.integrated.noContent', "No terminal content available for this session.");
noContent.textContent = noContentLabel;
this._bufferElementFragment.replaceChildren(noContent);
this._accessibleBuffer.focus();
return this._bufferElementFragment;
}
let header;
let replaceChildren = true;
for (const command of commands) {
header = document.createElement('h2');
// without this, the text area gets focused when keyboard shortcuts are used
header.tabIndex = -1;
header.textContent = command.command.replace(new RegExp(' ', 'g'), '\xA0');
if (command.exitCode !== 0) {
header.textContent += ` exited with code ${command.exitCode}`;
}
const output = document.createElement('div');
// without this, the text area gets focused when keyboard shortcuts are used
output.tabIndex = -1;
output.textContent = command.getOutput()?.replace(new RegExp(' ', 'g'), '\xA0') || '';
if (replaceChildren) {
this._bufferElementFragment.replaceChildren(header, output);
replaceChildren = false;
} else {
this._bufferElementFragment.appendChild(header);
this._bufferElementFragment.appendChild(output);
}
}
this._accessibleBuffer.focus(); this._accessibleBuffer.focus();
if (this._accessibleBuffer.contentEditable === 'true') { this._bufferEditor.focus();
document.execCommand('selectAll', false, undefined); }
document.getSelection()?.collapseToEnd();
} else if (header) { private async _updateBufferEditor(): Promise<void> {
// focus the cursor line's header if (!this._registered) {
header.tabIndex = 0; // Registration is delayed until focus so the capability has time to have been added
this.add(this._terminal.raw.registerBufferElementProvider({ provideBufferElements: () => this._editorContainer }));
this._registered = true;
} }
return this._bufferElementFragment; // When this is created, the element isn't yet attached so the dimensions are tiny
this._bufferEditor.layout({ width: this._accessibleBuffer.clientWidth, height: this._accessibleBuffer.clientHeight });
const commandDetection = this._capabilities.has(TerminalCapability.CommandDetection);
const fragment = commandDetection ? this._getShellIntegrationContent() : this._getAllContent();
const model = await this._getTextModel(URI.from({ scheme: ACCESSIBLE_BUFFER.Scheme, fragment }));
if (model) {
this._bufferEditor.setModel(model);
}
}
async _getTextModel(resource: URI): Promise<ITextModel | null> {
const existing = this._modelService.getModel(resource);
if (existing && !existing.isDisposed()) {
return existing;
}
return this._modelService.createModel(resource.fragment, null, resource, false);
}
private _getShellIntegrationContent(): string {
const commands = this._capabilities.get(TerminalCapability.CommandDetection)?.commands;
const sb = new StringBuilder(10000);
let content = localize('terminal.integrated.noContent', "No terminal content available for this session. Run some commands to create content.");
if (!commands?.length) {
return content;
}
for (const command of commands) {
sb.appendString(command.command.replace(new RegExp(' ', 'g'), '\xA0'));
if (command.exitCode !== 0) {
sb.appendString(` exited with code ${command.exitCode}`);
}
sb.appendString('\n');
sb.appendString(command.getOutput()?.replace(new RegExp(' ', 'g'), '\xA0') || '');
}
content = sb.build();
return content;
}
private _getAllContent(): string {
const lines: string[] = [];
let currentLine: string = '';
const buffer = this._terminal.raw.buffer.active;
const end = buffer.length;
for (let i = 0; i < end; i++) {
const line = buffer.getLine(i);
if (!line) {
continue;
}
const isWrapped = buffer.getLine(i + 1)?.isWrapped;
currentLine += line.translateToString(!isWrapped);
if (!isWrapped || i === end - 1) {
lines.push(currentLine.replace(new RegExp(' ', 'g'), '\xA0'));
currentLine = '';
}
}
return lines.join('\n');
} }
} }

View file

@ -253,7 +253,6 @@ export interface ITerminalConfiguration {
}; };
useWslProfiles: boolean; useWslProfiles: boolean;
altClickMovesCursor: boolean; altClickMovesCursor: boolean;
accessibleBufferContentEditable: 'auto' | 'on' | 'off';
macOptionIsMeta: boolean; macOptionIsMeta: boolean;
macOptionClickForcesSelection: boolean; macOptionClickForcesSelection: boolean;
gpuAcceleration: 'auto' | 'on' | 'canvas' | 'off'; gpuAcceleration: 'auto' | 'on' | 'canvas' | 'off';

View file

@ -579,17 +579,6 @@ const terminalConfiguration: IConfigurationNode = {
markdownDescription: localize('terminal.integrated.smoothScrolling', "Controls whether the terminal will scroll using an animation."), markdownDescription: localize('terminal.integrated.smoothScrolling', "Controls whether the terminal will scroll using an animation."),
type: 'boolean', type: 'boolean',
default: false default: false
},
[TerminalSettingId.AccessibleBufferContentEditable]: {
markdownDescription: localize('terminal.integrated.accessibleBufferContentEditable', "Controls whether the accessible buffer is marks as a `contenteditable` element. This adds a text cursor to the buffer, allowing selection with the keyboard without a screen reader. Screen reader users will typically want to leave this as `auto` or `off` which will treat the buffer similar to a document. By default, on Linux, this will be set to `on` so that it works when using Orca."),
type: 'string',
enum: ['auto', 'on', 'off'],
enumDescriptions: [
localize('accessibleBufferContentEditable.auto', "Automatically enable when a screen reader is not detected."),
localize('accessibleBufferContentEditable.on', "Always on."),
localize('accessibleBufferContentEditable.off', "Always off.")
],
default: 'auto'
} }
} }
}; };

View file

@ -11863,15 +11863,15 @@ xterm-addon-webgl@0.15.0-beta.7:
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af" resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.15.0-beta.7.tgz#ab247b499f61e8eebff92e08ec5ca999d87e06af"
integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w== integrity sha512-7WCI/D6uFNp3y9TeTsbSo1h7gCy4h/yP2lWn8ZEjCaiGvO11DbKMq17fbiwaR3YmGWXoRKkcLaNIiqxFnjKO4w==
xterm-headless@5.2.0-beta.28: xterm-headless@5.2.0-beta.29:
version "5.2.0-beta.28" version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.28.tgz#d2c149da51ef138f46268b755c4fdc4202eb771c" resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-5.2.0-beta.29.tgz#dd08312fdb4292c217e685d9e2e8b1957364e298"
integrity sha512-4XcjBhFwuyjpz2ubESwp75UceySOOKdJszKyyxOQ3/7L937uiVEBBLc8T231XU8lSwWUU7czyNjYyCfpszY4+Q== integrity sha512-1P4urIeDTkl2C+zGb4WUnKJMACZMPGYHwVXMjkB0WhMISbkt6M34MH9ljxHhnL99dHwlx2Lvi6wvhnpyZucWCg==
xterm@5.2.0-beta.28: xterm@5.2.0-beta.29:
version "5.2.0-beta.28" version "5.2.0-beta.29"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.28.tgz#852347e4eaf5aae7d82c90592a42adc9daab2a79" resolved "https://registry.yarnpkg.com/xterm/-/xterm-5.2.0-beta.29.tgz#99764aff5cd8cdb4335f5d59466b134cfcb45e3e"
integrity sha512-aLDxCuqjWHjvnhfWfkxy/y6coNrC+QIhbDe2sdfLPkrxhK6KnYE6qiZD5jXUIQXeq0KmSDcYi/esuKujvobC2A== integrity sha512-zx5RKcQqo78bza4R/m3WtxAJCBAF4U61fy6cxqb1PkqXF9/qdYlySUCVOauMxv+6n6cAxt3EQWwLlgvbvQBbsw==
y18n@^3.2.1: y18n@^3.2.1:
version "3.2.2" version "3.2.2"