@theia/core 1.47.1 → 1.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/i18n/nls.cs.json +4 -0
- package/i18n/nls.de.json +4 -0
- package/i18n/nls.es.json +4 -0
- package/i18n/nls.fr.json +4 -0
- package/i18n/nls.hu.json +4 -0
- package/i18n/nls.it.json +4 -0
- package/i18n/nls.ja.json +4 -0
- package/i18n/nls.json +4 -0
- package/i18n/nls.pl.json +4 -0
- package/i18n/nls.pt-br.json +4 -0
- package/i18n/nls.pt-pt.json +4 -0
- package/i18n/nls.ru.json +4 -0
- package/i18n/nls.zh-cn.json +4 -0
- package/lib/browser/color-application-contribution.d.ts +4 -2
- package/lib/browser/color-application-contribution.d.ts.map +1 -1
- package/lib/browser/color-application-contribution.js +11 -1
- package/lib/browser/color-application-contribution.js.map +1 -1
- package/lib/browser/common-frontend-contribution.d.ts +6 -0
- package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/common-frontend-contribution.js +43 -1
- package/lib/browser/common-frontend-contribution.js.map +1 -1
- package/lib/browser/decorations-service.js +4 -5
- package/lib/browser/decorations-service.js.map +1 -1
- package/lib/browser/keybinding.d.ts.map +1 -1
- package/lib/browser/keybinding.js +3 -0
- package/lib/browser/keybinding.js.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.d.ts.map +1 -1
- package/lib/browser/menu/browser-context-menu-renderer.js +2 -1
- package/lib/browser/menu/browser-context-menu-renderer.js.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts +1 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +2 -2
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/secondary-window-handler.d.ts +12 -11
- package/lib/browser/secondary-window-handler.d.ts.map +1 -1
- package/lib/browser/secondary-window-handler.js +16 -23
- package/lib/browser/secondary-window-handler.js.map +1 -1
- package/lib/browser/shell/application-shell.d.ts +3 -1
- package/lib/browser/shell/application-shell.d.ts.map +1 -1
- package/lib/browser/shell/application-shell.js +28 -20
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts +1 -0
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.d.ts.map +1 -1
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js +12 -7
- package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.js.map +1 -1
- package/lib/browser/styling-service.d.ts +3 -2
- package/lib/browser/styling-service.d.ts.map +1 -1
- package/lib/browser/styling-service.js +11 -2
- package/lib/browser/styling-service.js.map +1 -1
- package/lib/browser/window/default-secondary-window-service.d.ts +1 -1
- package/lib/browser/window/default-secondary-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-secondary-window-service.js +22 -25
- package/lib/browser/window/default-secondary-window-service.js.map +1 -1
- package/lib/browser/window/default-window-service.d.ts +1 -0
- package/lib/browser/window/default-window-service.d.ts.map +1 -1
- package/lib/browser/window/default-window-service.js +3 -0
- package/lib/browser/window/default-window-service.js.map +1 -1
- package/lib/browser/window/test/mock-window-service.d.ts +1 -0
- package/lib/browser/window/test/mock-window-service.d.ts.map +1 -1
- package/lib/browser/window/test/mock-window-service.js +1 -0
- package/lib/browser/window/test/mock-window-service.js.map +1 -1
- package/lib/browser/window/window-service.d.ts +10 -2
- package/lib/browser/window/window-service.d.ts.map +1 -1
- package/lib/browser/window/window-service.js.map +1 -1
- package/lib/common/nls.js +3 -1
- package/lib/common/nls.js.map +1 -1
- package/lib/common/quick-pick-service.d.ts +1 -0
- package/lib/common/quick-pick-service.d.ts.map +1 -1
- package/lib/common/quick-pick-service.js.map +1 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.js +3 -1
- package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
- package/lib/electron-browser/preload.js +2 -2
- package/lib/electron-browser/preload.js.map +1 -1
- package/lib/electron-browser/window/electron-secondary-window-service.d.ts +1 -1
- package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-secondary-window-service.js +4 -8
- package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
- package/lib/electron-browser/window/electron-window-service.d.ts +3 -1
- package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
- package/lib/electron-browser/window/electron-window-service.js +12 -2
- package/lib/electron-browser/window/electron-window-service.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +1 -1
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +9 -1
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/package.json +6 -6
- package/src/browser/color-application-contribution.ts +11 -2
- package/src/browser/common-frontend-contribution.ts +44 -1
- package/src/browser/decorations-service.ts +4 -4
- package/src/browser/keybinding.ts +3 -0
- package/src/browser/menu/browser-context-menu-renderer.ts +2 -1
- package/src/browser/menu/browser-menu-plugin.ts +2 -2
- package/src/browser/secondary-window-handler.ts +21 -22
- package/src/browser/shell/application-shell.ts +25 -18
- package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.tsx +15 -10
- package/src/browser/styling-service.ts +12 -3
- package/src/browser/window/default-secondary-window-service.ts +24 -26
- package/src/browser/window/default-window-service.ts +4 -0
- package/src/browser/window/test/mock-window-service.ts +1 -0
- package/src/browser/window/window-service.ts +12 -2
- package/src/common/nls.ts +3 -1
- package/src/common/quick-pick-service.ts +1 -0
- package/src/electron-browser/menu/electron-context-menu-renderer.ts +3 -1
- package/src/electron-browser/preload.ts +2 -2
- package/src/electron-browser/window/electron-secondary-window-service.ts +4 -8
- package/src/electron-browser/window/electron-window-service.ts +15 -3
- package/src/electron-common/electron-api.ts +1 -1
- package/src/electron-main/electron-api-main.ts +8 -1
|
@@ -82,37 +82,14 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
createSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
|
|
85
|
-
const win = this.doCreateSecondaryWindow(widget, shell);
|
|
86
|
-
if (win) {
|
|
87
|
-
this.secondaryWindows.push(win);
|
|
88
|
-
win.addEventListener('close', () => {
|
|
89
|
-
const extIndex = this.secondaryWindows.indexOf(win);
|
|
90
|
-
if (extIndex > -1) {
|
|
91
|
-
this.secondaryWindows.splice(extIndex, 1);
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return win;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
protected findWindow<T>(windowName: string): Window | undefined {
|
|
99
|
-
for (const w of this.secondaryWindows) {
|
|
100
|
-
if (w.name === windowName) {
|
|
101
|
-
return w;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return undefined;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined {
|
|
108
|
-
let options;
|
|
109
85
|
const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget);
|
|
110
|
-
options = `popup=1,width=${width},height=${height},left=${left},top=${top}`;
|
|
86
|
+
let options = `popup=1,width=${width},height=${height},left=${left},top=${top}`;
|
|
111
87
|
if (this.preferenceService.get('window.secondaryWindowAlwaysOnTop')) {
|
|
112
88
|
options += ',alwaysOnTop=true';
|
|
113
89
|
}
|
|
114
90
|
const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined;
|
|
115
91
|
if (newWindow) {
|
|
92
|
+
this.secondaryWindows.push(newWindow);
|
|
116
93
|
newWindow.addEventListener('DOMContentLoaded', () => {
|
|
117
94
|
newWindow.addEventListener('beforeunload', evt => {
|
|
118
95
|
const saveable = Saveable.get(widget);
|
|
@@ -124,17 +101,38 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService {
|
|
|
124
101
|
}
|
|
125
102
|
}, { capture: true });
|
|
126
103
|
|
|
127
|
-
newWindow.addEventListener('
|
|
104
|
+
newWindow.addEventListener('unload', () => {
|
|
128
105
|
const saveable = Saveable.get(widget);
|
|
129
106
|
shell.closeWidget(widget.id, {
|
|
130
107
|
save: !!saveable && saveable.dirty && saveable.autoSave !== 'off'
|
|
131
108
|
});
|
|
109
|
+
|
|
110
|
+
const extIndex = this.secondaryWindows.indexOf(newWindow);
|
|
111
|
+
if (extIndex > -1) {
|
|
112
|
+
this.secondaryWindows.splice(extIndex, 1);
|
|
113
|
+
};
|
|
132
114
|
});
|
|
115
|
+
this.windowCreated(newWindow, widget, shell);
|
|
133
116
|
});
|
|
134
117
|
}
|
|
135
118
|
return newWindow;
|
|
136
119
|
}
|
|
137
120
|
|
|
121
|
+
protected windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void {
|
|
122
|
+
newWindow.addEventListener('unload', () => {
|
|
123
|
+
shell.closeWidget(widget.id);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected findWindow<T>(windowName: string): Window | undefined {
|
|
128
|
+
for (const w of this.secondaryWindows) {
|
|
129
|
+
if (w.name === windowName) {
|
|
130
|
+
return w;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
138
136
|
protected findSecondaryWindowCoordinates(widget: ExtractableWidget): (number | undefined)[] {
|
|
139
137
|
const clientBounds = widget.node.getBoundingClientRect();
|
|
140
138
|
const preference = this.preferenceService.get('window.secondaryWindowPlacement');
|
|
@@ -57,6 +57,10 @@ export class DefaultWindowService implements WindowService, FrontendApplicationC
|
|
|
57
57
|
this.openNewWindow(`#${DEFAULT_WINDOW_HASH}`);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
focus(): void {
|
|
61
|
+
window.focus();
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
/**
|
|
61
65
|
* Returns a list of actions that {@link FrontendApplicationContribution}s would like to take before shutdown
|
|
62
66
|
* It is expected that this will succeed - i.e. return an empty array - at most once per session. If no vetoes are received
|
|
@@ -21,6 +21,7 @@ import { WindowService } from '../window-service';
|
|
|
21
21
|
export class MockWindowService implements WindowService {
|
|
22
22
|
openNewWindow(): undefined { return undefined; }
|
|
23
23
|
openNewDefaultWindow(): void { }
|
|
24
|
+
focus(): void { }
|
|
24
25
|
reload(): void { }
|
|
25
26
|
isSafeToShutDown(): Promise<boolean> { return Promise.resolve(true); }
|
|
26
27
|
setSafeToShutDown(): void { }
|
|
@@ -18,6 +18,11 @@ import { StopReason } from '../../common/frontend-application-state';
|
|
|
18
18
|
import { Event } from '../../common/event';
|
|
19
19
|
import { NewWindowOptions, WindowSearchParams } from '../../common/window';
|
|
20
20
|
|
|
21
|
+
export interface WindowReloadOptions {
|
|
22
|
+
search?: WindowSearchParams,
|
|
23
|
+
hash?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
/**
|
|
22
27
|
* Service for opening new browser windows.
|
|
23
28
|
*/
|
|
@@ -35,7 +40,12 @@ export interface WindowService {
|
|
|
35
40
|
* Opens a new default window.
|
|
36
41
|
* - In electron and in the browser it will open the default window without a pre-defined content.
|
|
37
42
|
*/
|
|
38
|
-
openNewDefaultWindow(params?:
|
|
43
|
+
openNewDefaultWindow(params?: WindowReloadOptions): void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Reveal and focuses the current window
|
|
47
|
+
*/
|
|
48
|
+
focus(): void;
|
|
39
49
|
|
|
40
50
|
/**
|
|
41
51
|
* Fires when the `window` unloads. The unload event is inevitable. On this event, the frontend application can save its state and release resource.
|
|
@@ -64,5 +74,5 @@ export interface WindowService {
|
|
|
64
74
|
/**
|
|
65
75
|
* Reloads the window according to platform.
|
|
66
76
|
*/
|
|
67
|
-
reload(params?:
|
|
77
|
+
reload(params?: WindowReloadOptions): void;
|
|
68
78
|
}
|
package/src/common/nls.ts
CHANGED
|
@@ -83,7 +83,9 @@ class LocalizationKeyProvider {
|
|
|
83
83
|
|
|
84
84
|
private preferredKeys = new Set([
|
|
85
85
|
// We only want the `File` translation used in the menu
|
|
86
|
-
'vscode/fileActions.contribution/filesCategory'
|
|
86
|
+
'vscode/fileActions.contribution/filesCategory',
|
|
87
|
+
// Needed for `Close Editor` translation
|
|
88
|
+
'vscode/editor.contribution/closeEditor'
|
|
87
89
|
]);
|
|
88
90
|
private data = this.buildData();
|
|
89
91
|
|
|
@@ -161,6 +161,7 @@ export interface QuickPick<T extends QuickPickItemOrSeparator> extends QuickInpu
|
|
|
161
161
|
matchOnDescription: boolean;
|
|
162
162
|
matchOnDetail: boolean;
|
|
163
163
|
keepScrollPosition: boolean;
|
|
164
|
+
buttons: ReadonlyArray<QuickInputButton>;
|
|
164
165
|
readonly onDidAccept: Event<{ inBackground: boolean } | undefined>;
|
|
165
166
|
readonly onDidChangeValue: Event<string>;
|
|
166
167
|
readonly onDidTriggerButton: Event<QuickInputButton>;
|
|
@@ -104,11 +104,13 @@ export class ElectronContextMenuRenderer extends BrowserContextMenuRenderer {
|
|
|
104
104
|
const menu = this.electronMenuFactory.createElectronContextMenu(menuPath, args, context, contextKeyService, skipSingleRootNode);
|
|
105
105
|
const { x, y } = coordinateFromAnchor(anchor);
|
|
106
106
|
|
|
107
|
+
const windowName = options.context?.ownerDocument.defaultView?.Window.name;
|
|
108
|
+
|
|
107
109
|
const menuHandle = window.electronTheiaCore.popup(menu, x, y, () => {
|
|
108
110
|
if (onHide) {
|
|
109
111
|
onHide();
|
|
110
112
|
}
|
|
111
|
-
});
|
|
113
|
+
}, windowName);
|
|
112
114
|
// native context menu stops the event loop, so there is no keyboard events
|
|
113
115
|
this.context.resetAltPressed();
|
|
114
116
|
return new ElectronContextMenuAccess(menuHandle);
|
|
@@ -81,11 +81,11 @@ const api: TheiaCoreAPI = {
|
|
|
81
81
|
},
|
|
82
82
|
attachSecurityToken: (endpoint: string) => ipcRenderer.invoke(CHANNEL_ATTACH_SECURITY_TOKEN, endpoint),
|
|
83
83
|
|
|
84
|
-
popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise<number> {
|
|
84
|
+
popup: async function (menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise<number> {
|
|
85
85
|
const menuId = nextMenuId++;
|
|
86
86
|
const handlers = new Map<number, () => void>();
|
|
87
87
|
commandHandlers.set(menuId, handlers);
|
|
88
|
-
const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y);
|
|
88
|
+
const handle = await ipcRenderer.invoke(CHANNEL_OPEN_POPUP, menuId, convertMenu(menu, handlers), x, y, windowName);
|
|
89
89
|
const closeListener = () => {
|
|
90
90
|
ipcRenderer.removeListener(CHANNEL_ON_CLOSE_POPUP, closeListener);
|
|
91
91
|
commandHandlers.delete(menuId);
|
|
@@ -24,16 +24,12 @@ export class ElectronSecondaryWindowService extends DefaultSecondaryWindowServic
|
|
|
24
24
|
window.electronTheiaCore.focusWindow(win.name);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
protected override
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
window.electronTheiaCore.setMenuBarVisible(false, w.name);
|
|
31
|
-
window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(w.name, () => this.canClose(widget, shell));
|
|
32
|
-
}
|
|
33
|
-
return w;
|
|
27
|
+
protected override windowCreated(newWindow: Window, widget: ExtractableWidget, shell: ApplicationShell): void {
|
|
28
|
+
window.electronTheiaCore.setMenuBarVisible(false, newWindow.name);
|
|
29
|
+
window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(newWindow.name, () => this.canClose(widget, shell));
|
|
34
30
|
}
|
|
35
31
|
private async canClose(widget: ExtractableWidget, shell: ApplicationShell): Promise<boolean> {
|
|
36
|
-
await shell.closeWidget(widget.id
|
|
32
|
+
await shell.closeWidget(widget.id);
|
|
37
33
|
return widget.isDisposed;
|
|
38
34
|
}
|
|
39
35
|
}
|
|
@@ -21,6 +21,7 @@ import { ElectronMainWindowService } from '../../electron-common/electron-main-w
|
|
|
21
21
|
import { ElectronWindowPreferences } from './electron-window-preferences';
|
|
22
22
|
import { ConnectionCloseService } from '../../common/messaging/connection-management';
|
|
23
23
|
import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider';
|
|
24
|
+
import { WindowReloadOptions } from '../../browser/window/window-service';
|
|
24
25
|
|
|
25
26
|
@injectable()
|
|
26
27
|
export class ElectronWindowService extends DefaultWindowService {
|
|
@@ -56,6 +57,9 @@ export class ElectronWindowService extends DefaultWindowService {
|
|
|
56
57
|
this.delegate.openNewDefaultWindow(params);
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
override focus(): void {
|
|
61
|
+
window.electronTheiaCore.focusWindow(window.name);
|
|
62
|
+
}
|
|
59
63
|
@postConstruct()
|
|
60
64
|
protected init(): void {
|
|
61
65
|
// Update the default zoom level on startup when the preferences event is fired.
|
|
@@ -86,12 +90,20 @@ export class ElectronWindowService extends DefaultWindowService {
|
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
override reload(params?:
|
|
93
|
+
override reload(params?: WindowReloadOptions): void {
|
|
90
94
|
if (params) {
|
|
91
|
-
const
|
|
92
|
-
|
|
95
|
+
const newLocation = new URL(location.href);
|
|
96
|
+
if (params.search) {
|
|
97
|
+
const query = Object.entries(params.search).map(([name, value]) => `${name}=${value}`).join('&');
|
|
98
|
+
newLocation.search = query;
|
|
99
|
+
}
|
|
100
|
+
if (params.hash) {
|
|
101
|
+
newLocation.hash = '#' + params.hash;
|
|
102
|
+
}
|
|
103
|
+
location.assign(newLocation);
|
|
93
104
|
} else {
|
|
94
105
|
window.electronTheiaCore.requestReload();
|
|
95
106
|
}
|
|
96
107
|
}
|
|
97
108
|
}
|
|
109
|
+
|
|
@@ -50,7 +50,7 @@ export interface TheiaCoreAPI {
|
|
|
50
50
|
setMenuBarVisible(visible: boolean, windowName?: string): void;
|
|
51
51
|
setMenu(menu: MenuDto[] | undefined): void;
|
|
52
52
|
|
|
53
|
-
popup(menu: MenuDto[], x: number, y: number, onClosed: () => void): Promise<number>;
|
|
53
|
+
popup(menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise<number>;
|
|
54
54
|
closePopup(handle: number): void;
|
|
55
55
|
|
|
56
56
|
focusWindow(name: string): void;
|
|
@@ -115,7 +115,7 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
// popup menu
|
|
118
|
-
ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y) => {
|
|
118
|
+
ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y, windowName?: string) => {
|
|
119
119
|
const zoom = event.sender.getZoomFactor();
|
|
120
120
|
// TODO: Remove the offset once Electron fixes https://github.com/electron/electron/issues/31641
|
|
121
121
|
const offset = process.platform === 'win32' ? 0 : 2;
|
|
@@ -124,7 +124,14 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
|
|
|
124
124
|
y = Math.round(y * zoom) + offset;
|
|
125
125
|
const popup = Menu.buildFromTemplate(this.fromMenuDto(event.sender, menuId, menu));
|
|
126
126
|
this.openPopups.set(menuId, popup);
|
|
127
|
+
let electronWindow: BrowserWindow | undefined;
|
|
128
|
+
if (windowName) {
|
|
129
|
+
electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
|
|
130
|
+
} else {
|
|
131
|
+
electronWindow = BrowserWindow.fromWebContents(event.sender) || undefined;
|
|
132
|
+
}
|
|
127
133
|
popup.popup({
|
|
134
|
+
window: electronWindow,
|
|
128
135
|
callback: () => {
|
|
129
136
|
this.openPopups.delete(menuId);
|
|
130
137
|
event.sender.send(CHANNEL_ON_CLOSE_POPUP, menuId);
|