@opensumi/ide-electron-basic 2.21.6-rc-1669620508.0 → 2.21.6

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/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@opensumi/ide-electron-basic",
3
- "version": "2.21.6-rc-1669620508.0",
3
+ "version": "2.21.6",
4
4
  "files": [
5
- "lib",
6
- "src"
5
+ "lib"
7
6
  ],
8
7
  "license": "MIT",
9
8
  "main": "lib/index.js",
@@ -17,16 +16,16 @@
17
16
  "url": "git@github.com:opensumi/core.git"
18
17
  },
19
18
  "dependencies": {
20
- "@opensumi/ide-core-common": "2.21.6-rc-1669620508.0",
21
- "@opensumi/ide-core-node": "2.21.6-rc-1669620508.0",
22
- "@opensumi/ide-utils": "2.21.6-rc-1669620508.0"
19
+ "@opensumi/ide-core-common": "2.21.6",
20
+ "@opensumi/ide-core-node": "2.21.6",
21
+ "@opensumi/ide-utils": "2.21.6"
23
22
  },
24
23
  "devDependencies": {
25
- "@opensumi/ide-core-browser": "2.21.6-rc-1669620508.0",
24
+ "@opensumi/ide-core-browser": "2.21.6",
26
25
  "@opensumi/ide-dev-tool": "^1.3.1",
27
- "@opensumi/ide-editor": "2.21.6-rc-1669620508.0",
28
- "@opensumi/ide-overlay": "2.21.6-rc-1669620508.0",
29
- "@opensumi/ide-workspace": "2.21.6-rc-1669620508.0"
26
+ "@opensumi/ide-editor": "2.21.6",
27
+ "@opensumi/ide-overlay": "2.21.6",
28
+ "@opensumi/ide-workspace": "2.21.6"
30
29
  },
31
- "gitHead": "1b7f550f97a208a86aae59b75c73688b85c8cde1"
30
+ "gitHead": "a34917dfa6d851e067a431ad700812e58e8cc8a8"
32
31
  }
@@ -1,62 +0,0 @@
1
- import { Injectable, Autowired } from '@opensumi/di';
2
- import { IClipboardService, CLIPBOARD_FILE_TOKEN } from '@opensumi/ide-core-common';
3
- import { URI } from '@opensumi/ide-core-common';
4
- import { IElectronMainUIService } from '@opensumi/ide-core-common/lib/electron';
5
-
6
- export { CLIPBOARD_FILE_TOKEN };
7
-
8
- export const INativeClipboardService = Symbol('INativeClipboardService');
9
-
10
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
11
- export interface INativeClipboardService extends IClipboardService {}
12
-
13
- @Injectable()
14
- export class ElectronClipboardService implements INativeClipboardService {
15
- @Autowired(IElectronMainUIService)
16
- private electronMainUIService: IElectronMainUIService;
17
-
18
- async writeText(text: string): Promise<void> {
19
- return await this.electronMainUIService.writeClipboardText(text);
20
- }
21
- async readText(): Promise<string> {
22
- return await this.electronMainUIService.readClipboardText();
23
- }
24
- async writeResources(resources: URI[], field = CLIPBOARD_FILE_TOKEN): Promise<void> {
25
- try {
26
- const buffer = Buffer.from(JSON.stringify(resources.map((uri) => uri.toString())), 'utf8');
27
- return await this.electronMainUIService.writeClipboardBuffer(field, buffer);
28
- } catch {}
29
- }
30
- async readResources(field = CLIPBOARD_FILE_TOKEN): Promise<URI[]> {
31
- try {
32
- const buffer = await this.electronMainUIService.readClipboardBuffer(field);
33
- const list = JSON.parse(Buffer.from(buffer).toString('utf8'));
34
- if (
35
- !Array.isArray(list) ||
36
- !list.length ||
37
- !list.every((str) => typeof str === 'string' && URI.isUriString(str))
38
- ) {
39
- return [];
40
- }
41
- return list.map((str) => URI.parse(str));
42
- } catch {
43
- return [];
44
- }
45
- }
46
- async hasResources(field?: string | undefined): Promise<boolean> {
47
- try {
48
- const buffer = await this.electronMainUIService.readClipboardBuffer(field ?? CLIPBOARD_FILE_TOKEN);
49
- const list = JSON.parse(Buffer.from(buffer).toString('utf8'));
50
- if (
51
- !Array.isArray(list) ||
52
- !list.length ||
53
- !list.every((str) => typeof str === 'string' && URI.isUriString(str))
54
- ) {
55
- return false;
56
- }
57
- return true;
58
- } catch {
59
- return false;
60
- }
61
- }
62
- }
@@ -1,17 +0,0 @@
1
- import { Injectable, Autowired } from '@opensumi/di';
2
- import { IElectronNativeDialogService, electronEnv } from '@opensumi/ide-core-browser';
3
- import { IElectronMainUIService } from '@opensumi/ide-core-common/lib/electron';
4
-
5
- @Injectable()
6
- export class ElectronNativeDialogService implements IElectronNativeDialogService {
7
- @Autowired(IElectronMainUIService)
8
- electronMainUIService: IElectronMainUIService;
9
-
10
- async showOpenDialog(options: Electron.OpenDialogOptions): Promise<string[] | undefined> {
11
- return this.electronMainUIService.showOpenDialog(electronEnv.currentWindowId, options);
12
- }
13
-
14
- showSaveDialog(options: Electron.SaveDialogOptions): Promise<string | undefined> {
15
- return this.electronMainUIService.showSaveDialog(electronEnv.currentWindowId, options);
16
- }
17
- }
@@ -1,61 +0,0 @@
1
- .header {
2
- background: var(--menu-background);
3
- color: var(--titlebar-activeForeground);
4
- display: flex;
5
- align-items: center;
6
- text-align: center;
7
- line-height: 100%;
8
- font-size: 12px;
9
- user-select: none;
10
-
11
- .title_info {
12
- height: 100%;
13
- display: flex;
14
- align-items: center;
15
- -webkit-app-region: drag;
16
- flex-grow: 1;
17
- text-align: left;
18
- }
19
-
20
- :global .menu-bar {
21
- flex-shrink: 0;
22
- }
23
-
24
- :global .menubarWrapper {
25
- background-color: var(--kt-menubar-background);
26
- }
27
- }
28
-
29
- .windowActions {
30
- display: flex;
31
- align-items: center;
32
- -webkit-app-region: no-drag;
33
- user-select: none;
34
-
35
- > div {
36
- font-size: 16px;
37
- padding: 0 15px;
38
- height: 100%;
39
- width: 100%;
40
- display: flex;
41
- align-items: center;
42
-
43
- &:hover {
44
- background-color: hsla(0, 0%, 100%, 0.1);
45
- }
46
-
47
- &:hover:last-child {
48
- background-color: rgba(232, 17, 35, 0.9);
49
- color: white;
50
- }
51
- }
52
- }
53
-
54
- :global(.vs) {
55
- .windowActions {
56
- > div:not(:last-child):hover {
57
- background-color: rgba(0, 0, 0, 0.1);
58
- color: #696767;
59
- }
60
- }
61
- }
@@ -1,145 +0,0 @@
1
- import { Injectable, Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di';
2
- import {
3
- AppConfig,
4
- localize,
5
- replaceLocalizePlaceholder,
6
- Emitter,
7
- DisposableCollection,
8
- isMacintosh,
9
- } from '@opensumi/ide-core-browser';
10
- import { WorkbenchEditorService } from '@opensumi/ide-editor/lib/browser';
11
- import { basename, dirname, relative } from '@opensumi/ide-utils/lib/path';
12
- import { template } from '@opensumi/ide-utils/lib/strings';
13
-
14
- import { IElectronHeaderService } from '../../common/header';
15
-
16
- // "● "
17
- export const TITLE_DIRTY = '\u25cf ';
18
- export const SEPARATOR = ' - ';
19
-
20
- export let DEFAULT_TEMPLATE = '${dirty}${activeEditorShort}${separator}${rootName}';
21
-
22
- if (isMacintosh) {
23
- DEFAULT_TEMPLATE = '${activeEditorShort}${separator}${rootName}';
24
- }
25
-
26
- @Injectable()
27
- export class ElectronHeaderService implements IElectronHeaderService {
28
- disposableCollection = new DisposableCollection();
29
-
30
- @Autowired(WorkbenchEditorService)
31
- editorService: WorkbenchEditorService;
32
-
33
- @Autowired(INJECTOR_TOKEN)
34
- injector: Injector;
35
-
36
- @Autowired(AppConfig)
37
- appConfig: AppConfig;
38
-
39
- private _onTitleChanged = new Emitter<string>();
40
- onTitleChanged = this._onTitleChanged.event;
41
-
42
- private _appTitle: string;
43
-
44
- separator = SEPARATOR;
45
-
46
- private _titleTemplate = DEFAULT_TEMPLATE;
47
- get titleTemplate() {
48
- return this._titleTemplate;
49
- }
50
-
51
- set titleTemplate(value: string) {
52
- this._titleTemplate = value;
53
- this.updateAppTitle();
54
- }
55
-
56
- private _templateVariables = {} as Record<string, string | undefined>;
57
- setTemplateVariables(key: string, value: string | undefined) {
58
- this._templateVariables[key] = value;
59
- }
60
-
61
- constructor() {
62
- this.disposableCollection.push(
63
- this.editorService.onActiveResourceChange(() => {
64
- this.updateAppTitle();
65
- }),
66
- );
67
- }
68
-
69
- updateAppTitle() {
70
- this._onTitleChanged.fire(this.appTitle);
71
- }
72
-
73
- get appTitle() {
74
- const formatted = this.formatAppTitle();
75
-
76
- let result = formatted;
77
-
78
- if (this.appConfig.extensionDevelopmentHost) {
79
- result = `[${localize('workspace.development.title')}] ${result}`;
80
- }
81
- if (this.appConfig.isRemote) {
82
- result = `[${localize('common.remoteMode')}] ${result}`;
83
- }
84
-
85
- this._appTitle = result;
86
- return this._appTitle;
87
- }
88
-
89
- formatAppTitle() {
90
- const { appConfig } = this;
91
- const currentResource = this.editorService.currentResource;
92
- const currentEditor = this.editorService.currentEditor;
93
-
94
- const workspaceDir = appConfig.workspaceDir ? appConfig.workspaceDir : '';
95
- const workspaceBasename = basename(workspaceDir);
96
- const activeEditorFull = currentResource?.name ?? '';
97
- const activeEditorRelative = activeEditorFull && workspaceDir ? relative(workspaceDir, activeEditorFull) : '';
98
- const activeFolderFull = activeEditorFull ? dirname(activeEditorFull) : '';
99
- const activeFolderRelative = activeFolderFull && workspaceDir ? relative(workspaceDir, activeFolderFull) : '';
100
-
101
- const activeEditorShort = basename(activeEditorFull);
102
- const activeEditorMedium = activeEditorRelative;
103
- const activeEditorLong = activeEditorFull;
104
- const activeFolderShort = basename(activeFolderFull);
105
- const activeFolderMedium = activeFolderRelative;
106
- const activeFolderLong = activeFolderFull;
107
- const folderName = workspaceBasename;
108
- const folderPath = workspaceDir;
109
- const rootName = workspaceBasename;
110
- const rootPath = workspaceDir;
111
- const appName = replaceLocalizePlaceholder(appConfig.appName) ?? '';
112
- // TODO: 当前 OpenSumi 还不支持 Remote 名字
113
- const remoteName = '';
114
- const dirty = currentEditor?.currentDocumentModel?.dirty ? TITLE_DIRTY : '';
115
-
116
- const result = template(
117
- this.titleTemplate,
118
- {
119
- activeEditorShort,
120
- activeEditorMedium,
121
- activeEditorLong,
122
- activeFolderShort,
123
- activeFolderMedium,
124
- activeFolderLong,
125
- folderName,
126
- folderPath,
127
- rootName,
128
- rootPath,
129
- appName,
130
- remoteName,
131
- dirty,
132
- ...this._templateVariables,
133
- },
134
- {
135
- separator: this.separator,
136
- },
137
- );
138
-
139
- return result;
140
- }
141
-
142
- dispose() {
143
- this.disposableCollection.dispose();
144
- }
145
- }
@@ -1,263 +0,0 @@
1
- import { observer } from 'mobx-react-lite';
2
- import React, { useState, useEffect, useRef } from 'react';
3
-
4
- import {
5
- useInjectable,
6
- ComponentRenderer,
7
- ComponentRegistry,
8
- DomListener,
9
- electronEnv,
10
- IWindowService,
11
- useEventEffect,
12
- } from '@opensumi/ide-core-browser';
13
- import { getIcon } from '@opensumi/ide-core-browser';
14
- import { isMacintosh, Disposable } from '@opensumi/ide-core-browser';
15
- import { LAYOUT_VIEW_SIZE } from '@opensumi/ide-core-browser/lib/layout/constants';
16
- import { IElectronMainUIService } from '@opensumi/ide-core-common/lib/electron';
17
-
18
- import { IElectronHeaderService } from '../../common/header';
19
-
20
- import styles from './header.module.less';
21
-
22
- const useFullScreen = () => {
23
- const uiService: IElectronMainUIService = useInjectable(IElectronMainUIService);
24
- const [isFullScreen, setFullScreen] = useState<boolean>(false);
25
-
26
- useEffect(() => {
27
- uiService.isFullScreen(electronEnv.currentWindowId).then((fullScreen) => {
28
- setFullScreen(fullScreen);
29
- });
30
- const listener = uiService.on('fullScreenStatusChange', (windowId, fullScreen) => {
31
- if (windowId === electronEnv.currentWindowId) {
32
- setFullScreen(fullScreen);
33
- }
34
- });
35
- return () => {
36
- listener.dispose();
37
- };
38
- }, []);
39
- return {
40
- isFullScreen,
41
- };
42
- };
43
-
44
- const useMaximize = () => {
45
- const uiService: IElectronMainUIService = useInjectable(IElectronMainUIService);
46
- const [maximized, setMaximized] = useState(false);
47
-
48
- const getMaximized = async () => uiService.isMaximized(electronEnv.currentWindowId);
49
-
50
- useEffect(() => {
51
- const maximizeListener = uiService.on('maximizeStatusChange', (windowId, isMaximized) => {
52
- if (windowId === electronEnv.currentWindowId) {
53
- setMaximized(isMaximized);
54
- }
55
- });
56
- getMaximized().then((maximized) => {
57
- setMaximized(maximized);
58
- });
59
- return () => {
60
- maximizeListener.dispose();
61
- };
62
- }, []);
63
-
64
- return {
65
- maximized,
66
- getMaximized,
67
- };
68
- };
69
-
70
- // Big Sur increases title bar height
71
- const isNewMacHeaderBar = () => isMacintosh && parseFloat(electronEnv.osRelease) >= 20;
72
-
73
- export const HeaderBarLeftComponent = () => {
74
- const componentRegistry: ComponentRegistry = useInjectable(ComponentRegistry);
75
-
76
- if (isMacintosh) {
77
- return null;
78
- }
79
-
80
- const initialProps = {
81
- className: 'menubarWrapper',
82
- };
83
-
84
- return (
85
- <>
86
- <ComponentRenderer
87
- Component={componentRegistry.getComponentRegistryInfo('@opensumi/ide-menu-bar')!.views[0].component!}
88
- initialProps={initialProps}
89
- />
90
- </>
91
- );
92
- };
93
-
94
- export const HeaderBarRightComponent = () => {
95
- const { maximized } = useMaximize();
96
- const windowService: IWindowService = useInjectable(IWindowService);
97
-
98
- if (isMacintosh) {
99
- return null;
100
- }
101
-
102
- return (
103
- <div
104
- className={styles.windowActions}
105
- style={{
106
- height: isNewMacHeaderBar() ? LAYOUT_VIEW_SIZE.BIG_SUR_TITLEBAR_HEIGHT : LAYOUT_VIEW_SIZE.TITLEBAR_HEIGHT,
107
- }}
108
- >
109
- <div className={getIcon('min')} onClick={() => windowService.minimize()} />
110
- {maximized ? (
111
- <div className={getIcon('max')} onClick={() => windowService.unmaximize()} />
112
- ) : (
113
- <div className={getIcon('unmax')} onClick={() => windowService.maximize()} />
114
- )}
115
- <div className={getIcon('close1')} onClick={() => windowService.close()} />
116
- </div>
117
- );
118
- };
119
-
120
- interface ElectronHeaderBarPorps {
121
- LeftComponent?: React.FunctionComponent;
122
- RightComponent?: React.FunctionComponent;
123
- TitleComponent?: React.FunctionComponent<TitleBarProps>;
124
- Icon?: React.FunctionComponent;
125
- autoHide?: boolean;
126
- height?: number;
127
- }
128
-
129
- /**
130
- * autoHide: Hide the HeaderBar when the macOS full screen
131
- */
132
- export const ElectronHeaderBar = observer(
133
- ({
134
- LeftComponent,
135
- RightComponent,
136
- TitleComponent,
137
- autoHide = true,
138
- height = isNewMacHeaderBar() ? LAYOUT_VIEW_SIZE.BIG_SUR_TITLEBAR_HEIGHT : LAYOUT_VIEW_SIZE.TITLEBAR_HEIGHT,
139
- }: React.PropsWithChildren<ElectronHeaderBarPorps>) => {
140
- const windowService: IWindowService = useInjectable(IWindowService);
141
-
142
- const { isFullScreen } = useFullScreen();
143
- const { getMaximized } = useMaximize();
144
- if (!LeftComponent) {
145
- LeftComponent = HeaderBarLeftComponent;
146
- }
147
- if (!RightComponent) {
148
- RightComponent = HeaderBarRightComponent;
149
- }
150
- if (!TitleComponent) {
151
- TitleComponent = HeaderBarTitleComponent;
152
- }
153
-
154
- // in Mac, hide the header bar if it is in full screen mode
155
- if (isMacintosh && isFullScreen && autoHide) {
156
- return (
157
- <div>
158
- <TitleComponent hidden={true} />
159
- </div>
160
- );
161
- }
162
-
163
- return (
164
- <div
165
- className={styles.header}
166
- style={{ height }}
167
- onDoubleClick={async () => {
168
- if (await getMaximized()) {
169
- windowService.unmaximize();
170
- } else {
171
- windowService.maximize();
172
- }
173
- }}
174
- >
175
- <LeftComponent />
176
- <TitleComponent />
177
- <RightComponent />
178
- </div>
179
- );
180
- },
181
- );
182
-
183
- declare const ResizeObserver: any;
184
-
185
- export interface TitleInfo {
186
- currentResourceName?: string;
187
- workspaceName?: string;
188
- appName?: string;
189
- remoteMode?: boolean;
190
- extensionDevelopmentHost?: boolean;
191
- }
192
-
193
- export interface TitleBarProps {
194
- hidden?: boolean;
195
- }
196
-
197
- export const HeaderBarTitleComponent = observer(({ hidden }: TitleBarProps) => {
198
- const headerService = useInjectable(IElectronHeaderService) as IElectronHeaderService;
199
- const ref = useRef<HTMLDivElement>(null);
200
- const spanRef = useRef<HTMLSpanElement>(null);
201
- const [appTitle, setAppTitle] = useState(headerService.appTitle);
202
-
203
- useEventEffect(
204
- headerService.onTitleChanged,
205
- (v) => {
206
- setAppTitle(v);
207
- },
208
- [],
209
- );
210
-
211
- useEffect(() => {
212
- setPosition();
213
- const disposer = new Disposable();
214
-
215
- disposer.addDispose(
216
- new DomListener(window, 'resize', () => {
217
- setPosition();
218
- }),
219
- );
220
- if (ref.current && ref.current.previousElementSibling) {
221
- const resizeObserver = new ResizeObserver(setPosition);
222
- resizeObserver.observe(ref.current.previousElementSibling);
223
- disposer.addDispose({
224
- dispose: () => {
225
- resizeObserver.disconnect();
226
- },
227
- });
228
- }
229
- return disposer.dispose.bind(disposer);
230
- }, []);
231
-
232
- function setPosition() {
233
- // 在下一个 animationFrame 执行,此时 spanRef.current!.offsetWidth 的宽度才是正确的
234
- window.requestAnimationFrame(() => {
235
- if (ref.current && spanRef.current) {
236
- const windowWidth = window.innerWidth;
237
- let prevWidth = 0;
238
- let node = ref.current.previousElementSibling;
239
- while (node) {
240
- prevWidth += (node as HTMLElement).offsetWidth;
241
- node = node.previousElementSibling;
242
- }
243
- const left = Math.max(0, windowWidth * 0.5 - prevWidth - spanRef.current.offsetWidth * 0.5);
244
- ref.current.style.paddingLeft = left + 'px';
245
- }
246
- });
247
- }
248
-
249
- // 同时更新 document Title
250
- useEffect(() => {
251
- document.title = appTitle;
252
- }, [appTitle]);
253
-
254
- if (hidden) {
255
- return null;
256
- }
257
-
258
- return (
259
- <div className={styles.title_info} ref={ref}>
260
- <span ref={spanRef}>{appTitle}</span>
261
- </div>
262
- );
263
- });
@@ -1,391 +0,0 @@
1
- import { Provider, Injectable, Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di';
2
- import {
3
- BrowserModule,
4
- Domain,
5
- AppConfig,
6
- isOSX,
7
- ClientAppContribution,
8
- IElectronMainMenuService,
9
- localize,
10
- SlotLocation,
11
- IElectronNativeDialogService,
12
- CommandContribution,
13
- CommandRegistry,
14
- KeybindingContribution,
15
- KeybindingRegistry,
16
- isWindows,
17
- electronEnv,
18
- replaceLocalizePlaceholder,
19
- URI,
20
- ILogger,
21
- formatLocalize,
22
- IEventBus,
23
- Schemes,
24
- IClipboardService,
25
- } from '@opensumi/ide-core-browser';
26
- import { ComponentContribution, ComponentRegistry } from '@opensumi/ide-core-browser/lib/layout';
27
- import { IMenuRegistry, MenuContribution, MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
28
- import { IElectronMenuBarService } from '@opensumi/ide-core-browser/lib/menu/next/renderer/ctxmenu/electron';
29
- import { IElectronMainLifeCycleService, IElectronMainUIService } from '@opensumi/ide-core-common/lib/electron';
30
- import { IResourceOpenOptions } from '@opensumi/ide-editor';
31
- import {
32
- EditorGroupFileDropEvent,
33
- DragOverPosition,
34
- getSplitActionFromDragDrop,
35
- } from '@opensumi/ide-editor/lib/browser';
36
- import { IMessageService } from '@opensumi/ide-overlay/lib/common';
37
-
38
- import { IElectronHeaderService } from '../common/header';
39
-
40
- import { ElectronClipboardService } from './clipboard';
41
- import { ElectronNativeDialogService } from './dialog';
42
- import { ElectronHeaderService } from './header/header.service';
43
- import { ElectronHeaderBar } from './header/header.view';
44
- import { WelcomeContribution } from './welcome/contribution';
45
-
46
- @Injectable()
47
- export class ElectronBasicModule extends BrowserModule {
48
- providers: Provider[] = [
49
- {
50
- token: IElectronNativeDialogService,
51
- useClass: ElectronNativeDialogService,
52
- },
53
- {
54
- token: IElectronHeaderService,
55
- useClass: ElectronHeaderService,
56
- },
57
- ElectronBasicContribution,
58
- WelcomeContribution,
59
- ];
60
- }
61
-
62
- const nativeRoles = [
63
- {
64
- name: 'undo',
65
- key: 'ctrlcmd+z',
66
- when: '!editorFocus',
67
- },
68
- {
69
- name: 'redo',
70
- key: 'ctrlcmd+shift+z',
71
- when: '!editorFocus',
72
- },
73
- {
74
- name: 'copy',
75
- key: 'ctrlcmd+c',
76
- when: '!editorFocus',
77
- },
78
- {
79
- name: 'paste',
80
- key: 'ctrlcmd+v',
81
- when: '!editorFocus',
82
- },
83
- {
84
- name: 'selectAll',
85
- key: 'ctrlcmd+a',
86
- when: '!editorFocus',
87
- },
88
- {
89
- name: 'cut',
90
- key: 'ctrlcmd+x',
91
- when: '!editorFocus',
92
- },
93
- {
94
- name: 'toggleDevTools',
95
- key: 'alt+ctrlcmd+i',
96
- label: '%window.toggleDevTools%',
97
- alias: 'Toggle Developer Tools',
98
- },
99
- ];
100
-
101
- @Domain(ComponentContribution, ClientAppContribution, MenuContribution, CommandContribution, KeybindingContribution)
102
- export class ElectronBasicContribution
103
- implements
104
- KeybindingContribution,
105
- CommandContribution,
106
- ComponentContribution,
107
- ClientAppContribution,
108
- MenuContribution
109
- {
110
- @Autowired(AppConfig)
111
- config: AppConfig;
112
-
113
- @Autowired(INJECTOR_TOKEN)
114
- injector: Injector;
115
-
116
- @Autowired(IEventBus)
117
- eventBus: IEventBus;
118
-
119
- @Autowired(IElectronMenuBarService)
120
- private electronMenuBarService: IElectronMenuBarService;
121
-
122
- @Autowired(IElectronMainMenuService)
123
- private electronMainMenuService: IElectronMainMenuService;
124
-
125
- @Autowired(IElectronMainLifeCycleService)
126
- private electronMainLifeCycleService: IElectronMainLifeCycleService;
127
-
128
- @Autowired(IElectronMainUIService)
129
- private electronMainUIService: IElectronMainUIService;
130
-
131
- @Autowired(IMessageService)
132
- private messageService: IMessageService;
133
-
134
- @Autowired(ILogger)
135
- logger: ILogger;
136
-
137
- registerComponent(registry: ComponentRegistry) {
138
- const top = this.config.layoutConfig[SlotLocation.top];
139
- if (top && top.modules) {
140
- const index = top.modules.indexOf('@opensumi/ide-menu-bar');
141
- if (index !== -1) {
142
- top.modules.splice(index, 1, 'electron-header');
143
- }
144
- }
145
- registry.register(
146
- 'electron-header',
147
- {
148
- id: 'electron-header',
149
- component: ElectronHeaderBar,
150
- },
151
- {
152
- size: 27,
153
- containerId: 'electron-header',
154
- },
155
- );
156
- }
157
-
158
- registerMenus(menuRegistry: IMenuRegistry) {
159
- menuRegistry.registerMenuItem(MenuId.MenubarAppMenu, {
160
- command: {
161
- id: 'electron.about',
162
- label: localize('common.about'),
163
- },
164
- group: '0_about',
165
- nativeRole: 'about',
166
- });
167
-
168
- menuRegistry.registerMenuItem(MenuId.MenubarHelpMenu, {
169
- command: {
170
- id: 'electron.toggleDevTools',
171
- label: localize('window.toggleDevTools'),
172
- },
173
- nativeRole: 'toggledevtools',
174
- });
175
-
176
- menuRegistry.registerMenuItem(MenuId.MenubarHelpMenu, {
177
- command: {
178
- id: 'electron.reload',
179
- label: localize('window.reload'),
180
- },
181
- });
182
-
183
- menuRegistry.registerMenuItem(MenuId.ExplorerContext, {
184
- command: 'electron.revealInFinder',
185
- group: '12_electron',
186
- order: 3,
187
- });
188
- menuRegistry.registerMenuItem(MenuId.EditorTitleContext, {
189
- command: 'electron.revealInFinderTab',
190
- group: '2_open',
191
- order: 3,
192
- });
193
- }
194
-
195
- registerCommands(commands: CommandRegistry): void {
196
- nativeRoles.forEach((role) => {
197
- commands.registerCommand(
198
- {
199
- id: 'electron.' + role.name,
200
- label: replaceLocalizePlaceholder(role.label),
201
- alias: role.alias,
202
- },
203
- {
204
- execute: () => {
205
- this.electronMainMenuService.runNativeRoleAction(role.name);
206
- },
207
- },
208
- );
209
- });
210
-
211
- commands.registerCommand(
212
- {
213
- id: 'electron.zoomIn',
214
- label: localize('view.zoomIn'),
215
- alias: 'View: Zoom In',
216
- },
217
- {
218
- execute: () => {
219
- this.electronMainUIService.setZoomFactor(electronEnv.currentWebContentsId, {
220
- delta: 0.1,
221
- });
222
- },
223
- },
224
- );
225
-
226
- commands.registerCommand(
227
- {
228
- id: 'electron.zoomOut',
229
- label: localize('view.zoomOut'),
230
- alias: 'View: Zoom Out',
231
- },
232
- {
233
- execute: () => {
234
- this.electronMainUIService.setZoomFactor(electronEnv.currentWebContentsId, {
235
- delta: -0.1,
236
- });
237
- },
238
- },
239
- );
240
-
241
- commands.registerCommand(
242
- {
243
- id: 'electron.zoomReset',
244
- label: localize('view.zoomReset'),
245
- alias: 'View: Zoom Reset',
246
- },
247
- {
248
- execute: () => {
249
- this.electronMainUIService.setZoomFactor(electronEnv.currentWebContentsId, {
250
- value: 1,
251
- });
252
- },
253
- },
254
- );
255
-
256
- commands.registerCommand(
257
- {
258
- id: 'electron.reload',
259
- label: localize('window.reload'),
260
- alias: 'Reload Window',
261
- },
262
- {
263
- execute: () => {
264
- this.electronMainLifeCycleService.reloadWindow(electronEnv.currentWindowId);
265
- },
266
- },
267
- );
268
-
269
- commands.registerCommand(
270
- {
271
- id: 'electron.revealInFinder',
272
- label: localize('explorer.electron.revealInFinder'),
273
- },
274
- {
275
- execute: (uri: URI) => {
276
- if (uri && uri.scheme === Schemes.file) {
277
- this.electronMainUIService.revealInFinder(uri.codeUri.fsPath);
278
- }
279
- },
280
- },
281
- );
282
-
283
- commands.registerCommand(
284
- {
285
- id: 'electron.revealInFinderTab',
286
- label: localize('explorer.electron.revealInFinder'),
287
- },
288
- {
289
- execute: ({ uri }: { uri?: URI } = {}) => {
290
- if (uri && uri.scheme === Schemes.file) {
291
- this.electronMainUIService.revealInFinder(uri.codeUri.fsPath);
292
- }
293
- },
294
- },
295
- );
296
-
297
- commands.registerCommand(
298
- {
299
- id: 'electron.openInSystemTerminal',
300
- label: localize('explorer.electron.openInSystemTerminal'),
301
- },
302
- {
303
- execute: (uri: URI) => {
304
- if (uri && uri.scheme === Schemes.file) {
305
- try {
306
- this.electronMainUIService.revealInSystemTerminal(uri.codeUri.fsPath);
307
- } catch (e) {
308
- this.logger.error(e);
309
- this.messageService.error(
310
- formatLocalize('explorer.electron.openInSystemTerminal.error', uri.displayName, e.message),
311
- );
312
- }
313
- }
314
- },
315
- },
316
- );
317
- }
318
-
319
- registerKeybindings(keybindings: KeybindingRegistry) {
320
- nativeRoles.forEach((role) => {
321
- if (role.key) {
322
- keybindings.registerKeybinding({
323
- command: 'electron.' + role.name,
324
- keybinding: role.key,
325
- when: role.when,
326
- priority: Number.MIN_SAFE_INTEGER, // 永远在最后被命中
327
- });
328
- }
329
- });
330
-
331
- keybindings.registerKeybinding({
332
- command: 'electron.reload',
333
- keybinding: 'shift+ctrlcmd+r',
334
- });
335
-
336
- keybindings.registerKeybinding({
337
- command: 'electron.zoomIn',
338
- keybinding: isWindows ? 'alt+=' : 'ctrlcmd+=',
339
- });
340
-
341
- keybindings.registerKeybinding({
342
- command: 'electron.zoomOut',
343
- keybinding: isWindows ? 'alt+-' : 'ctrlcmd+-',
344
- });
345
- keybindings.registerKeybinding({
346
- command: 'electron.zoomReset',
347
- keybinding: isWindows ? 'alt+numpad0' : 'ctrlcmd+numpad0',
348
- });
349
- }
350
-
351
- onStart() {
352
- if (isOSX) {
353
- this.electronMenuBarService.start();
354
- }
355
-
356
- // 注册drag drop file的行为
357
- this.eventBus.on(EditorGroupFileDropEvent, async (event) => {
358
- const payload = event.payload;
359
- // fileList 只能这样遍历
360
- for (let i = 0; i < payload.files.length; i++) {
361
- const file = payload.files[i];
362
- let group = event.payload.group;
363
- if (file.path) {
364
- const fileURI = URI.file(file.path);
365
- const options: IResourceOpenOptions = {
366
- index: event.payload.tabIndex !== -1 ? event.payload.tabIndex : undefined,
367
- };
368
- // 只有第一个才split
369
- if (i === 0 && event.payload.position && event.payload.position !== DragOverPosition.CENTER) {
370
- options.split = getSplitActionFromDragDrop(event.payload.position);
371
- }
372
- // 只有最后一个才可能为 preview
373
- if (i < payload.files.length - 1) {
374
- options.preview = false;
375
- }
376
- const res = await group.open(fileURI, options);
377
- if (res) {
378
- // split后当前group会变动
379
- group = res.group;
380
- }
381
- }
382
- }
383
- });
384
-
385
- // override broswer modules
386
- this.injector.overrideProviders({
387
- token: IClipboardService,
388
- useClass: ElectronClipboardService,
389
- });
390
- }
391
- }
@@ -1,4 +0,0 @@
1
- export interface IWelcomeMetaData {
2
- recentWorkspaces: string[];
3
- recentFiles: string[];
4
- }
@@ -1,64 +0,0 @@
1
- import { Autowired } from '@opensumi/di';
2
- import { Domain, URI, localize, ClientAppContribution, RecentFilesManager } from '@opensumi/ide-core-browser';
3
- import { ResourceService, IResource, WorkbenchEditorService } from '@opensumi/ide-editor';
4
- import {
5
- BrowserEditorContribution,
6
- EditorComponentRegistry,
7
- EditorComponentRenderMode,
8
- } from '@opensumi/ide-editor/lib/browser';
9
- import { IWorkspaceService } from '@opensumi/ide-workspace';
10
-
11
- import { IWelcomeMetaData } from './common';
12
- import { EditorWelcomeComponent } from './welcome';
13
-
14
- @Domain(BrowserEditorContribution, ClientAppContribution)
15
- export class WelcomeContribution implements BrowserEditorContribution, ClientAppContribution {
16
- @Autowired(IWorkspaceService)
17
- private readonly workspaceService: IWorkspaceService;
18
-
19
- @Autowired(WorkbenchEditorService)
20
- private readonly editorService: WorkbenchEditorService;
21
-
22
- @Autowired(RecentFilesManager)
23
- private readonly recentFilesManager: RecentFilesManager;
24
-
25
- registerEditorComponent(registry: EditorComponentRegistry) {
26
- registry.registerEditorComponent({
27
- uid: 'welcome',
28
- scheme: 'welcome',
29
- component: EditorWelcomeComponent,
30
- renderMode: EditorComponentRenderMode.ONE_PER_WORKBENCH,
31
- });
32
- registry.registerEditorComponentResolver('welcome', (resource, results) => {
33
- results.push({
34
- type: 'component',
35
- componentId: 'welcome',
36
- });
37
- });
38
- }
39
-
40
- registerResource(service: ResourceService) {
41
- service.registerResourceProvider({
42
- scheme: 'welcome',
43
- provideResource: async (uri: URI): Promise<IResource<IWelcomeMetaData>> =>
44
- Promise.all([
45
- this.workspaceService.getMostRecentlyUsedWorkspaces(),
46
- this.recentFilesManager.getMostRecentlyOpenedFiles(),
47
- ]).then(([workspaces, files]) => ({
48
- uri,
49
- name: localize('welcome.title'),
50
- icon: '',
51
- metadata: {
52
- recentWorkspaces: workspaces || [],
53
- recentFiles: files || [],
54
- },
55
- })),
56
- });
57
- }
58
-
59
- onDidStart() {
60
- if (!this.workspaceService.workspace) {
61
- this.editorService.open(new URI('welcome://'));
62
- }
63
- }
64
- }
@@ -1,13 +0,0 @@
1
- .welcome {
2
- h1 {
3
- color: var(--foreground);
4
- }
5
- padding: 10px 20px;
6
- > div {
7
- margin-bottom: 20px;
8
- }
9
-
10
- .recentRow {
11
- padding: 2px;
12
- }
13
- }
@@ -1,57 +0,0 @@
1
- import React from 'react';
2
-
3
- import {
4
- useInjectable,
5
- localize,
6
- FileUri,
7
- URI,
8
- CommandService,
9
- FILE_COMMANDS,
10
- IWindowService,
11
- } from '@opensumi/ide-core-browser';
12
- import { ReactEditorComponent } from '@opensumi/ide-editor/lib/browser';
13
-
14
- import { IWelcomeMetaData } from './common';
15
- import styles from './welcome.module.less';
16
-
17
- export const EditorWelcomeComponent: ReactEditorComponent<IWelcomeMetaData> = ({ resource }) => {
18
- const commandService: CommandService = useInjectable(CommandService);
19
- const windowService: IWindowService = useInjectable(IWindowService);
20
-
21
- return (
22
- <div className={styles.welcome}>
23
- <div>
24
- <h1>{localize('welcome.quickStart')}</h1>
25
- <div>
26
- <a
27
- onClick={() => {
28
- commandService.executeCommand(FILE_COMMANDS.OPEN_FOLDER.id, { newWindow: false });
29
- }}
30
- >
31
- {localize('file.open.folder')}
32
- </a>
33
- </div>
34
- </div>
35
- <div>
36
- <h1>{localize('welcome.recent.workspace')}</h1>
37
- {resource.metadata!.recentWorkspaces.map((workspace) => {
38
- let workspacePath = workspace;
39
- if (workspace.startsWith('file://')) {
40
- workspacePath = FileUri.fsPath(workspace);
41
- }
42
- return (
43
- <div key={workspace} className={styles.recentRow}>
44
- <a
45
- onClick={() => {
46
- windowService.openWorkspace(new URI(workspace), { newWindow: false });
47
- }}
48
- >
49
- {workspacePath}
50
- </a>
51
- </div>
52
- );
53
- })}
54
- </div>
55
- </div>
56
- );
57
- };
@@ -1,40 +0,0 @@
1
- import { Event } from '@opensumi/ide-core-browser';
2
- export const IElectronHeaderService = Symbol('IElectronHeaderService');
3
-
4
- export interface IElectronHeaderService {
5
- /**
6
- * 如果你的 template 中包含自定义变量,请先通过 setTemplateVariables 注入变量后,再调用该函数。
7
- */
8
- titleTemplate: string;
9
- /**
10
- * 占位符 ${separator} 的值
11
- */
12
- separator: string;
13
- appTitle: string;
14
- onTitleChanged: Event<string>;
15
- /**
16
- * 可以让集成方定义可替换的 variables。
17
- * 如小程序开发者工具注入了一个 ${projectName},含义为用户当前项目的名字。
18
- */
19
- setTemplateVariables(key: string, value: string | undefined): void;
20
-
21
- /**
22
- * 默认支持的变量有下面这些,也支持集成方通过 setTemplateVariables 自己注入
23
- * Controls the window title based on the active editor. Variables are substituted based on the context:
24
- * - `${activeEditorShort}`: the file name (e.g. myFile.txt).
25
- * - `${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt).
26
- * - `${activeEditorLong}`: the full path of the file (e.g. /Users/Development/myFolder/myFileFolder/myFile.txt).
27
- * - `${activeFolderShort}`: the name of the folder the file is contained in (e.g. myFileFolder).
28
- * - `${activeFolderMedium}`: the path of the folder the file is contained in, relative to the workspace folder (e.g. myFolder/myFileFolder).
29
- * - `${activeFolderLong}`: the full path of the folder the file is contained in (e.g. /Users/Development/myFolder/myFileFolder).
30
- * - `${folderName}`: name of the workspace folder the file is contained in (e.g. myFolder).
31
- * - `${folderPath}`: file path of the workspace folder the file is contained in (e.g. /Users/Development/myFolder).
32
- * - `${rootName}`: name of the opened workspace or folder (e.g. myFolder or myWorkspace).
33
- * - `${rootPath}`: file path of the opened workspace or folder (e.g. /Users/Development/myWorkspace).
34
- * - `${appName}`: e.g. VS Code.
35
- * - `${remoteName}`: e.g. SSH
36
- * - `${dirty}`: an indicator for when the active editor has unsaved changes.
37
- * - `${separator}`: a conditional separator (" - ") that only shows when surrounded by variables with values or static text.
38
- */
39
- formatAppTitle(): string;
40
- }