diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 05ca59b26..b81e96bee 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -21,7 +21,7 @@ ], // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [5000], + "forwardPorts": [8080], // Use 'postCreateCommand' to run commands after the container is created. //"postCreateCommand": "yarn", diff --git a/.gitignore b/.gitignore index f91e599aa..0e07f1835 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ out node_modules .yalc yalc.lock +yarn-error.log diff --git a/.gitpod.yml b/.gitpod.yml index e784cce0b..b68bbaa99 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -13,7 +13,7 @@ tasks: echo "Please wait for 'yarn watch' to complete compilation, then run 'yarn serve'" echo "===========================================================================" ports: - - port: 5000 + - port: 8080 onOpen: open-browser github: prebuilds: diff --git a/README.md b/README.md index 4319a3030..7ed4dfdb4 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ git clone git@github.com:conwnet/github1s.git cd github1s yarn yarn watch -# The cli will automatically open http://localhost:5000 once the build is completed. -# You can visit http://localhost:5000/conwnet/github1s if it doesn't. +# The cli will automatically open http://localhost:8080 once the build is completed. +# You can visit http://localhost:8080/conwnet/github1s if it doesn't. ``` #### Local development with full VS Code build @@ -84,8 +84,8 @@ After the initial successful build, you could use the watch mode: cd github1s yarn yarn watch-with-vscode -# The cli will automatically open http://localhost:5000 once the build is completed. -# You can visit http://localhost:5000/conwnet/github1s if it doesn't. +# The cli will automatically open http://localhost:8080 once the build is completed. +# You can visit http://localhost:8080/conwnet/github1s if it doesn't. ``` ### ... or ... VS Code + Docker Development @@ -104,8 +104,8 @@ You can use the VS Code plugin [Remote-Containers](https://marketplace.visualstu ```bash yarn yarn watch -# The cli will automatically open http://localhost:5000 once the build is completed. -# You can visit http://localhost:5000/conwnet/github1s if it doesn't. +# The cli will automatically open http://localhost:8080 once the build is completed. +# You can visit http://localhost:8080/conwnet/github1s if it doesn't. ``` ### Format all codes diff --git a/docs/guide.md b/docs/guide.md index e9252aaf5..1f929d718 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -35,7 +35,7 @@ $ yarn $ yarn watch # or yarn build, it may take minutes, wait please ``` -Then, there will be a new directory named `dist` generated in the project root. The `yarn watch:dev-server` (part of `yarn watch` command) will automatically open http://localhost:5000 in the browser. +Then, there will be a new directory named `dist` generated in the project root. The `yarn watch:dev-server` (part of `yarn watch` command) will automatically open http://localhost:8080 in the browser. If you get a 404 error for some static files, please wait a minute for the building to complete. diff --git a/package.json b/package.json index 6b3992725..e59676467 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lib": "lib" }, "devDependencies": { - "@github1s/vscode-web": "0.9.0", + "@github1s/vscode-web": "0.10.0", "@typescript-eslint/eslint-plugin": "^5.40.1", "@typescript-eslint/parser": "^5.40.1", "chokidar": "^3.5.3", @@ -54,7 +54,7 @@ "webpack": "webpack --config webpack.config.js", "format": "prettier --write .", "eslint": "eslint --ext .ts --ext .js .", - "test:ci": "start-test watch:dev-server 5000 test", + "test:ci": "start-test watch:dev-server 8080 test", "test": "cd tests && yarn && yarn jest" }, "lint-staged": { diff --git a/scripts/serve-dist.js b/scripts/serve-dist.js deleted file mode 100755 index 6bd295489..000000000 --- a/scripts/serve-dist.js +++ /dev/null @@ -1,44 +0,0 @@ -const url = require('url'); -const http = require('http'); -const httpProxy = require('http-proxy'); - -const _proxyServer = httpProxy.createServer({ - ignorePath: true, - changeOrigin: false, -}); - -const handleProxyError = (error, req, res) => { - console.trace(error); - res.writeHead(500, { - 'Content-Type': 'application/json', - }); - res.end(JSON.stringify({ message: error.message })); -}; - -_proxyServer.on('error', handleProxyError); - -// proxy the request to vscode-unpkg.net -const vscodeUnpkgProxyHandler = (req, res, vscodeUnpkgMatches) => { - if (!vscodeUnpkgMatches) { - res.writeHead(404, { 'Content-Type': 'application/json' }); - return res.end( - JSON.stringify({ - error: `${req.url} Not Found`, - }) - ); - } - const publisher = vscodeUnpkgMatches[1]; - const restPartsPath = vscodeUnpkgMatches[2]; - const host = `${publisher}.vscode-unpkg.net`.toLowerCase(); - const target = `https://${host}/${publisher}/${restPartsPath}`; - const headers = { host }; - _proxyServer.web(req, res, { target, headers }); -}; - -const proxyServer = http.createServer((request, response) => { - const urlObj = url.parse(request.url); - const vscodeUnpkgMatches = urlObj.pathname.match(/^\/api\/vscode-unpkg\/([^/]+)\/(.*)/); - return vscodeUnpkgProxyHandler(request, response, vscodeUnpkgMatches); -}); - -module.exports = proxyServer; diff --git a/src/index.ts b/src/index.ts index 2e2d0c9ac..181218613 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,7 +29,7 @@ const resolvePlatformState = (): [Platform, string] => { const repository = pathParts.length >= 2 ? pathParts.slice(0, 2).join('/') : ''; return [Platform.Bitbucket, repository]; } - if (hostname.match(/^(.*\.)?bitbucket1s\.org$/i)) { + if (hostname.match(/^(.*\.)?npmjs1s\.com$/i)) { const trimmedParts = pathParts[0] === 'package' ? pathParts.slice(1) : pathParts; const packageParts = trimmedParts.slice(0, trimmedParts[0] && trimmedParts[0][0] === '@' ? 2 : 1); const repository = pathParts.length ? packageParts.join('/') || 'package' : ''; diff --git a/tests/__tests__/index.test.ts b/tests/__tests__/index.test.ts index 6390b6e9b..afe70a842 100644 --- a/tests/__tests__/index.test.ts +++ b/tests/__tests__/index.test.ts @@ -13,7 +13,7 @@ const matchImageSnapshotOptions: MatchImageSnapshotOptions = { let browser: Browser; let page: Page; -const BASE_URL = 'http://localhost:5000'; +const BASE_URL = 'http://localhost:8080'; beforeAll(async () => { browser = await chromium.launch(); diff --git a/tests/package.json b/tests/package.json index b8eac95c9..e0b3cacca 100644 --- a/tests/package.json +++ b/tests/package.json @@ -14,7 +14,7 @@ "typescript": "^4.1.3" }, "scripts": { - "test:ci": "start-server-and-test serve http://localhost:5000 test", + "test:ci": "start-server-and-test serve http://localhost:8080 test", "test": "jest", "test:watch": "jest --watchAll" }, diff --git a/vscode-web/.VERSION b/vscode-web/.VERSION index e168f31cb..1aa8d83eb 100644 --- a/vscode-web/.VERSION +++ b/vscode-web/.VERSION @@ -1 +1 @@ -1.77.3 \ No newline at end of file +1.78.2 \ No newline at end of file diff --git a/vscode-web/README.md b/vscode-web/README.md index faa51a250..3f0a94098 100644 --- a/vscode-web/README.md +++ b/vscode-web/README.md @@ -27,7 +27,7 @@ yarn build # Build the GitHub1s & other extensions yarn serve ``` -And visit the http://localhost:5000 to verify the change. Please revert any changes related to `yalc` before commit, i.e. the `package.json`, the `.yalc/` and `yalc.lock` files. +And visit the http://localhost:8080 to verify the change. Please revert any changes related to `yalc` before commit, i.e. the `package.json`, the `.yalc/` and `yalc.lock` files. ## Publish diff --git a/vscode-web/package.json b/vscode-web/package.json index fcb754c80..9370d0f71 100644 --- a/vscode-web/package.json +++ b/vscode-web/package.json @@ -1,6 +1,6 @@ { "name": "@github1s/vscode-web", - "version": "0.9.0", + "version": "0.10.0", "description": "VS Code web for GitHub1s", "author": "github1s", "license": "MIT", diff --git a/vscode-web/src/vs/editor/common/config/editorOptions.ts b/vscode-web/src/vs/editor/common/config/editorOptions.ts index ee80b6af7..41a24ab1e 100644 --- a/vscode-web/src/vs/editor/common/config/editorOptions.ts +++ b/vscode-web/src/vs/editor/common/config/editorOptions.ts @@ -240,6 +240,10 @@ export interface IEditorOptions { * Defaults to false. */ fontVariations?: boolean | string; + /** + * Controls whether to use default color decorations or not using the default document color provider + */ + defaultColorDecorators?: boolean; /** * Disable the use of `transform: translate3d(0px, 0px, 0px)` for the editor margin and lines layers. * The usage of `transform: translate3d(0px, 0px, 0px)` acts as a hint for browsers to create an extra layer. @@ -721,6 +725,12 @@ export interface IDiffEditorBaseOptions { * Defaults to true. */ enableSplitViewResizing?: boolean; + /** + * The default ratio when rendering side-by-side editors. + * Must be a number between 0 and 1, min sizes apply. + * Defaults to 0.5 + */ + splitViewDefaultRatio?: number; /** * Render the differences in two side-by-side editors. * Defaults to true. @@ -773,7 +783,12 @@ export interface IDiffEditorBaseOptions { /** * Diff Algorithm */ - diffAlgorithm?: 'smart' | 'experimental' | IDocumentDiffProvider; + diffAlgorithm?: 'legacy' | 'advanced' | IDocumentDiffProvider; + + /** + * Whether the diff editor aria label should be verbose. + */ + accessibilityVerbose?: boolean; } /** @@ -830,6 +845,7 @@ export interface IEnvironmentalOptions { readonly pixelRatio: number; readonly tabFocusMode: boolean; readonly accessibilitySupport: AccessibilitySupport; + readonly glyphMarginDecorationLaneCount: number; } /** @@ -1063,6 +1079,16 @@ class EditorIntOption extends SimpleEditorOption(value: any, defaultValue: T, minimum: number, maximum: number): number | T { + if (typeof value === 'undefined') { + return defaultValue; + } + const r = EditorFloatOption.float(value, defaultValue); + return EditorFloatOption.clamp(r, minimum, maximum); +} class EditorFloatOption extends SimpleEditorOption { @@ -1128,10 +1154,13 @@ class EditorStringOption extends SimpleEditorOption(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray): T { +export function stringSet(value: T | undefined, defaultValue: T, allowedValues: ReadonlyArray, renamedValues?: Record): T { if (typeof value !== 'string') { return defaultValue; } + if (renamedValues && value in renamedValues) { + return renamedValues[value]; + } if (allowedValues.indexOf(value) === -1) { return defaultValue; } @@ -2063,6 +2092,11 @@ export interface EditorLayoutInfo { */ readonly glyphMarginWidth: number; + /** + * The number of decoration lanes to render in the glyph margin. + */ + readonly glyphMarginDecorationLaneCount: number; + /** * Left position for the line numbers. */ @@ -2150,6 +2184,7 @@ export interface EditorLayoutInfoComputerEnv { readonly typicalHalfwidthCharacterWidth: number; readonly maxDigitWidth: number; readonly pixelRatio: number; + readonly glyphMarginDecorationLaneCount: number; } /** @@ -2217,7 +2252,8 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption { constructor() { - const defaults: EditorDropIntoEditorOptions = { enabled: true }; + const defaults: EditorDropIntoEditorOptions = { enabled: true, showDropSelector: 'afterDrop' }; super( EditorOption.dropIntoEditor, 'dropIntoEditor', defaults, { @@ -4724,6 +4774,19 @@ class EditorDropIntoEditor extends BaseEditorOption { - const promises = []; - for (const changed of e.event.changed) { - promises.push(this.addOrUpdateAccount(e.providerId, changed.account)); - } - for (const added of e.event.added) { - promises.push(this.addOrUpdateAccount(e.providerId, added.account)); - } - const result = await Promise.allSettled(promises); - for (const r of result) { - if (r.status === 'rejected') { - this.logService.error(r.reason); + for (const changed of [...e.event.changed, ...e.event.added]) { + try { + await this.addOrUpdateAccount(e.providerId, changed.account); + } catch (e) { + this.logService.error(e); } } for (const removed of e.event.removed) { @@ -437,13 +431,19 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { //#region groupedAccounts helpers private async addOrUpdateAccount(providerId: string, account: AuthenticationSessionAccount): Promise { - const accounts = this.groupedAccounts.get(providerId); - const existingAccount = accounts?.find(a => a.id === account.id); - if (existingAccount) { - if (existingAccount.label !== account.label) { - existingAccount.label = account.label; + let accounts = this.groupedAccounts.get(providerId); + if (accounts) { + const existingAccount = accounts.find(a => a.id === account.id); + if (existingAccount) { + // Update the label if it has changed + if (existingAccount.label !== account.label) { + existingAccount.label = account.label; + } + return; } - return; + } else { + accounts = []; + this.groupedAccounts.set(providerId, accounts); } const sessionFromEmbedder = await this.sessionFromEmbedder; @@ -459,11 +459,6 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { } } - if (!accounts) { - this.groupedAccounts.set(providerId, [{ ...account, canSignOut }]); - return; - } - accounts.push({ ...account, canSignOut }); } @@ -489,10 +484,11 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { const sessions = await this.authenticationService.getSessions(providerId); this.problematicProviders.delete(providerId); - const result = await Promise.allSettled(sessions.map(s => this.addOrUpdateAccount(providerId, s.account))); - for (const r of result) { - if (r.status === 'rejected') { - this.logService.error(r.reason); + for (const session of sessions) { + try { + await this.addOrUpdateAccount(providerId, session.account); + } catch (e) { + this.logService.error(e); } } } catch (e) { @@ -575,7 +571,7 @@ export class PlaceHolderViewContainerActivityAction extends ViewContainerActivit export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinnedAction { constructor(id: string, compositeBar: ICompositeBar) { - super({ id, name: id, cssClass: undefined }, compositeBar); + super({ id, name: id, classNames: undefined }, compositeBar); } setActivity(activity: IActivity): void { @@ -586,7 +582,7 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne export class PlaceHolderToggleCompositeBadgeAction extends ToggleCompositeBadgeAction { constructor(id: string, compositeBar: ICompositeBar) { - super({ id, name: id, cssClass: undefined }, compositeBar); + super({ id, name: id, classNames: undefined }, compositeBar); } setActivity(activity: IActivity): void { @@ -669,16 +665,6 @@ registerAction2( }); registerThemingParticipant((theme, collector) => { - const activityBarForegroundColor = theme.getColor(ACTIVITY_BAR_FOREGROUND); - if (activityBarForegroundColor) { - collector.addRule(` - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label.codicon, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .action-label.codicon, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label.codicon { - color: ${activityBarForegroundColor} !important; - } - `); - } const activityBarActiveBorderColor = theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER); if (activityBarActiveBorderColor) { diff --git a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 3e7c6e1ff..869ae6b03 100644 --- a/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/vscode-web/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -542,12 +542,10 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart preventLoopNavigation: true, })); - const iconClasses = logo?.icon ? 'home-bar-custom-icon' : ''; - homeBar.push(this._register(new ActivityAction({ id: 'workbench.actions.home', name: logo?.title || 'Home', - cssClass: iconClasses, + classNames: [logo?.icon ? 'home-bar-custom-icon' : ''], }))); const content = assertIsDefined(this.content); @@ -586,14 +584,14 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.globalActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.manage', name: localize('manage', "Manage"), - cssClass: ThemeIcon.asClassName(ActivitybarPart.GEAR_ICON), + classNames: ThemeIcon.asClassNameArray(ActivitybarPart.GEAR_ICON), })); if (this.accountsVisibilityPreference) { this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: localize('accounts', "Accounts"), - cssClass: ThemeIcon.asClassName(ActivitybarPart.ACCOUNTS_ICON) + classNames: ThemeIcon.asClassNameArray(ActivitybarPart.ACCOUNTS_ICON) })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); @@ -614,7 +612,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.accountsActivityAction = this._register(new ActivityAction({ id: 'workbench.actions.accounts', name: localize('accounts', "Accounts"), - cssClass: ThemeIcon.asClassName(Codicon.account) + classNames: ThemeIcon.asClassNameArray(Codicon.account) })); this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX }); } @@ -702,15 +700,16 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart } private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity { - let cssClass: string | undefined = undefined; + let classNames: string[] | undefined = undefined; let iconUrl: URI | undefined = undefined; if (URI.isUri(icon)) { iconUrl = icon; const cssUrl = asCSSUrl(icon); const hash = new StringSHA1(); hash.update(cssUrl); - cssClass = `activity-${id.replace(/\./g, '-')}-${hash.digest()}`; - const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`; + const iconId = `activity-${id.replace(/\./g, '-')}-${hash.digest()}`; + const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${iconId}`; + classNames = [iconId, 'uri-icon']; createCSSRule(iconClass, ` mask: ${cssUrl} no-repeat 50% 50%; mask-size: 24px; @@ -718,10 +717,10 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart -webkit-mask-size: 24px; `); } else if (ThemeIcon.isThemeIcon(icon)) { - cssClass = ThemeIcon.asClassName(icon); + classNames = ThemeIcon.asClassNameArray(icon); } - return { id, name, cssClass, iconUrl, keybindingId }; + return { id, name, classNames, iconUrl, keybindingId }; } private showOrHideViewContainer(viewContainer: ViewContainer): void { diff --git a/vscode-web/src/vs/workbench/browser/web.main.ts b/vscode-web/src/vs/workbench/browser/web.main.ts index 385285423..a98c22a2c 100644 --- a/vscode-web/src/vs/workbench/browser/web.main.ts +++ b/vscode-web/src/vs/workbench/browser/web.main.ts @@ -23,7 +23,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { Schemas, connectionTokenCookieName } from 'vs/base/common/network'; -import { IAnyWorkspaceIdentifier, IWorkspaceContextService, UNKNOWN_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/workspace/common/workspace'; +import { IAnyWorkspaceIdentifier, IWorkspaceContextService, UNKNOWN_EMPTY_WINDOW_WORKSPACE, isTemporaryWorkspace, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { onUnexpectedError } from 'vs/base/common/errors'; import { setFullscreen } from 'vs/base/browser/browser'; @@ -45,7 +45,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IndexedDBFileSystemProviderErrorDataClassification, IndexedDBFileSystemProvider, IndexedDBFileSystemProviderErrorData } from 'vs/platform/files/browser/indexedDBFileSystemProvider'; import { BrowserRequestService } from 'vs/workbench/services/request/browser/requestService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; +import { IUserDataInitializationService, IUserDataInitializer, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; import { UserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { IUserDataSyncStoreManagementService } from 'vs/platform/userDataSync/common/userDataSync'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -73,7 +73,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { dirname, joinPath } from 'vs/base/common/resources'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { DisposableTunnel, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; @@ -82,8 +82,12 @@ import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/co import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { BrowserUserDataProfilesService } from 'vs/platform/userDataProfile/browser/userDataProfile'; import { timeout } from 'vs/base/common/async'; -import { rendererLogId } from 'vs/workbench/common/logConstants'; +import { windowLogId } from 'vs/workbench/services/log/common/logConstants'; import { LogService } from 'vs/platform/log/common/logService'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces'; +import { UserDataProfileInitializer } from 'vs/workbench/services/userDataProfile/browser/userDataProfileInit'; +import { UserDataSyncInitializer } from 'vs/workbench/services/userDataSync/browser/userDataSyncInit'; export class BrowserMain extends Disposable { @@ -279,6 +283,10 @@ export class BrowserMain extends Disposable { const loggerService = new FileLoggerService(logLevel, logsPath, fileService); serviceCollection.set(ILoggerService, loggerService); + // Register File System Providers depending on IndexedDB support + // Register them early because they are needed for the profiles initialization + await this.registerIndexedDBFileSystemProviders(environmentService, fileService, bufferLogger, logService, loggerService, logsPath); + // URI Identity const uriIdentityService = new UriIdentityService(fileService); serviceCollection.set(IUriIdentityService, uriIdentityService); @@ -287,15 +295,14 @@ export class BrowserMain extends Disposable { const userDataProfilesService = new BrowserUserDataProfilesService(environmentService, fileService, uriIdentityService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); - const currentProfile = userDataProfilesService.getProfileForWorkspace(workspace) ?? userDataProfilesService.defaultProfile; + const currentProfile = await this.getCurrentProfile(workspace, userDataProfilesService, environmentService); const userDataProfileService = new UserDataProfileService(currentProfile, userDataProfilesService); serviceCollection.set(IUserDataProfileService, userDataProfileService); // Remote Agent const remoteAgentService = this._register(new RemoteAgentService(this.configuration.webSocketFactory, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService)); serviceCollection.set(IRemoteAgentService, remoteAgentService); - - await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, bufferLogger, logService, loggerService, logsPath); + this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService)); // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ @@ -363,7 +370,12 @@ export class BrowserMain extends Disposable { serviceCollection.set(ICredentialsService, credentialsService); // Userdata Initialize Service - const userDataInitializationService = new UserDataInitializationService(environmentService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService); + const userDataInitializers: IUserDataInitializer[] = []; + userDataInitializers.push(new UserDataSyncInitializer(environmentService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService)); + if (environmentService.options.profile) { + userDataInitializers.push(new UserDataProfileInitializer(environmentService, fileService, userDataProfileService, storageService, logService, uriIdentityService, requestService)); + } + const userDataInitializationService = new UserDataInitializationService(userDataInitializers); serviceCollection.set(IUserDataInitializationService, userDataInitializationService); try { @@ -394,8 +406,7 @@ export class BrowserMain extends Disposable { } } - private async registerFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IWorkbenchFileService, remoteAgentService: IRemoteAgentService, bufferLogger: BufferLogger, logService: ILogService, loggerService: ILoggerService, logsPath: URI): Promise { - + private async registerIndexedDBFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IWorkbenchFileService, bufferLogger: BufferLogger, logService: ILogService, loggerService: ILoggerService, logsPath: URI): Promise { // IndexedDB is used for logging and user data let indexedDB: IndexedDB | undefined; const userDataStore = 'vscode-userdata-store'; @@ -419,7 +430,7 @@ export class BrowserMain extends Disposable { fileService.registerProvider(logsPath.scheme, new InMemoryFileSystemProvider()); } - bufferLogger.logger = loggerService.createLogger(environmentService.logFile, { id: rendererLogId, name: localize('rendererLog', "Window") }); + bufferLogger.logger = loggerService.createLogger(environmentService.logFile, { id: windowLogId, name: localize('rendererLog', "Window") }); // User data let userDataProvider; @@ -433,9 +444,6 @@ export class BrowserMain extends Disposable { } fileService.registerProvider(Schemas.vscodeUserData, userDataProvider); - // Remote file system - this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService)); - // Local file access (if supported by browser) if (WebFileSystemAccess.supported(window)) { fileService.registerProvider(Schemas.file, new HTMLFileSystemProvider(indexedDB, handlesStore, logService)); @@ -507,6 +515,20 @@ export class BrowserMain extends Disposable { } private async createWorkspaceService(workspace: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + + // Temporary workspaces do not exist on startup because they are + // just in memory. As such, detect this case and eagerly create + // the workspace file empty so that it is a valid workspace. + + if (isWorkspaceIdentifier(workspace) && isTemporaryWorkspace(workspace.configPath)) { + try { + const emptyWorkspace: IStoredWorkspace = { folders: [] }; + await fileService.createFile(workspace.configPath, VSBuffer.fromString(JSON.stringify(emptyWorkspace, null, '\t')), { overwrite: false }); + } catch (error) { + // ignore if workspace file already exists + } + } + const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService); const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); @@ -522,6 +544,17 @@ export class BrowserMain extends Disposable { } } + private async getCurrentProfile(workspace: IAnyWorkspaceIdentifier, userDataProfilesService: BrowserUserDataProfilesService, environmentService: IBrowserWorkbenchEnvironmentService): Promise { + if (environmentService.options?.profile) { + const profile = userDataProfilesService.profiles.find(p => p.name === environmentService.options?.profile?.name); + if (profile) { + return profile; + } + return userDataProfilesService.createNamedProfile(environmentService.options?.profile?.name, undefined, workspace); + } + return userDataProfilesService.getProfileForWorkspace(workspace) ?? userDataProfilesService.defaultProfile; + } + private resolveWorkspace(): IAnyWorkspaceIdentifier { let workspace: IWorkspace | undefined = undefined; if (this.configuration.workspaceProvider) { diff --git a/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html b/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html index 27fc9c005..66b9c89b1 100644 --- a/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/vscode-web/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -10,8 +10,6 @@ - - @@ -35,6 +33,7 @@ const ID = searchParams.get('id'); const webviewOrigin = searchParams.get('origin'); const onElectron = searchParams.get('platform') === 'electron'; + const disableServiceWorker = searchParams.has('disableServiceWorker'); const expectedWorkerVersion = parseInt(searchParams.get('swVersion')); /** @@ -214,6 +213,10 @@ /** @type {Promise} */ const workerReady = new Promise((resolve, reject) => { + if (disableServiceWorker) { + return resolve(); + } + if (!areServiceWorkersEnabled()) { return reject(new Error('Service Workers are not enabled. Webviews will not work. Try disabling private/incognito mode.')); } @@ -259,13 +262,21 @@ // service worker already loaded & ready to receive messages postVersionMessage(currentController); } else { - console.log(`Found unexpected service worker controller. Found: ${currentController?.scriptURL}. Expected: ${swPath}`); + if (currentController) { + console.log(`Found unexpected service worker controller. Found: ${currentController.scriptURL}. Expected: ${swPath}. Waiting for controllerchange.`); + } else { + console.log(`No service worker controller found. Waiting for controllerchange.`); + } - // either there's no controlling service worker, or it's an old one: - // wait for it to change before posting the message + // Either there's no controlling service worker, or it's an old one. + // Wait for it to change before posting the message const onControllerChange = () => { navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange); - postVersionMessage(navigator.serviceWorker.controller); + if (navigator.serviceWorker.controller) { + postVersionMessage(navigator.serviceWorker.controller); + } else { + return reject(new Error('No controller found.')); + } }; navigator.serviceWorker.addEventListener('controllerchange', onControllerChange); } @@ -316,7 +327,7 @@ async signalReady() { /* below codes are changed by github1s */ - const parentOrigin = searchParams.get('parentOrigin'); + const parentOrigin = searchParams.get('parentOrigin') || ''; window.parent.postMessage({ target: ID, channel: 'webview-ready', data: {} }, parentOrigin, [this.channel.port2]); /* above codes are changed by github1s */ } @@ -406,26 +417,25 @@ reduceMotion: false, }; - hostMessaging.onMessage('did-load-resource', (_event, data) => { - navigator.serviceWorker.getRegistration().then(registration => { - assertIsDefined(registration.active).postMessage({ channel: 'did-load-resource', data }, data.data?.buffer ? [data.data.buffer] : []); + if (!disableServiceWorker) { + hostMessaging.onMessage('did-load-resource', (_event, data) => { + assertIsDefined(navigator.serviceWorker.controller).postMessage({ channel: 'did-load-resource', data }, data.data?.buffer ? [data.data.buffer] : []); }); - }); - hostMessaging.onMessage('did-load-localhost', (_event, data) => { - navigator.serviceWorker.getRegistration().then(registration => { - assertIsDefined(registration.active).postMessage({ channel: 'did-load-localhost', data }); + hostMessaging.onMessage('did-load-localhost', (_event, data) => { + assertIsDefined(navigator.serviceWorker.controller).postMessage({ channel: 'did-load-localhost', data }); }); - }); - navigator.serviceWorker.addEventListener('message', event => { - switch (event.data.channel) { - case 'load-resource': - case 'load-localhost': - hostMessaging.postMessage(event.data.channel, event.data); - return; - } - }); + navigator.serviceWorker.addEventListener('message', event => { + switch (event.data.channel) { + case 'load-resource': + case 'load-localhost': + hostMessaging.postMessage(event.data.channel, event.data); + return; + } + }); + } + /** * @param {HTMLDocument?} document * @param {HTMLElement?} body @@ -590,8 +600,10 @@ */ function isCopyPasteOrCut(e) { const hasMeta = e.ctrlKey || e.metaKey; - const shiftInsert = e.shiftKey && e.key.toLowerCase() === 'insert'; - return (hasMeta && ['c', 'v', 'x'].includes(e.key.toLowerCase())) || shiftInsert; + // 45: keyCode of "Insert" + const shiftInsert = e.shiftKey && e.keyCode === 45; + // 67, 86, 88: keyCode of "C", "V", "X" + return (hasMeta && [67, 86, 88].includes(e.keyCode)) || shiftInsert; } /** @@ -600,7 +612,8 @@ */ function isUndoRedo(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && ['z', 'y'].includes(e.key.toLowerCase()); + // 90, 89: keyCode of "Z", "Y" + return hasMeta && [90, 89].includes(e.keyCode); } /** @@ -609,7 +622,8 @@ */ function isPrint(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'p'; + // 80: keyCode of "P" + return hasMeta && e.keyCode === 80; } /** @@ -618,7 +632,8 @@ */ function isFindEvent(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'f'; + // 70: keyCode of "F" + return hasMeta && e.keyCode === 70; } /** @@ -627,7 +642,8 @@ */ function isSaveEvent(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 's'; + // 83: keyCode of "S" + return hasMeta && e.keyCode === 83; } /** @@ -636,7 +652,8 @@ */ function isCloseTab(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'w'; + // 87: keyCode of "W" + return hasMeta && e.keyCode === 87; } /** @@ -645,7 +662,8 @@ */ function isNewWindow(e) { const hasMeta = e.ctrlKey || e.metaKey; - return hasMeta && e.key.toLowerCase() === 'n'; + // 78: keyCode of "N" + return hasMeta && e.keyCode === 78; } let isHandlingScroll = false; diff --git a/webpack.config.js b/webpack.config.js index 003e8f915..91d86ca95 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -99,7 +99,7 @@ module.exports = (env, argv) => { ], performance: false, devServer: { - port: 5000, + port: 8080, liveReload: false, allowedHosts: 'all', static: { diff --git a/yarn.lock b/yarn.lock index 8aee93be8..19457f192 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31,10 +31,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@github1s/vscode-web@0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.9.0.tgz#4531745452dbd10be0fda8470818c5bf17fddc87" - integrity sha512-M/t0PExhgq09wgIYLnvcAlTdRJaR9m+gKP/sp7dmdkjTV9YQElfSCGMzEFfKN8fW3FASGunbZ2Hq79bpr0TCcA== +"@github1s/vscode-web@0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@github1s/vscode-web/-/vscode-web-0.10.0.tgz#ea0616b3f14f5321a73faf8796c857cd2d6b5d33" + integrity sha512-BaD1LvnphjZ4ZI5BupoDH5FDwBeccXke/tIsfX1DIOphz2YplrAkGETbA2+pFFx2/Y5lkmUvyt5Y+XgxHbhYQA== dependencies: "@vscode/iconv-lite-umd" "0.7.0" "@vscode/vscode-languagedetection" "1.0.21" @@ -4331,9 +4331,9 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.1.1.tgz#1e06fb4ca46e60d9da07e4f786ea370ed3c3cfec" - integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== + version "2.2.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@^20.2.2: version "20.2.9"