docs: add open in IDX support (#57099)

This change adds a menu to the in-browser code editor on adev and adds the option for open in IDX.

PR Close #57099
This commit is contained in:
marktechson 2024-07-23 11:34:36 -05:00 committed by Dylan Hunn
parent 56816bbdb2
commit ca8bd5be99
12 changed files with 273 additions and 3 deletions

View file

@ -80,6 +80,7 @@ APPLICATION_DEPS = [
"@npm//@lezer/javascript",
"@npm//@lezer/common",
"@npm//@stackblitz/sdk",
"@npm//open-in-idx",
"@npm//@xterm/xterm",
"@npm//@xterm/addon-fit",
"@npm//algoliasearch",

View file

@ -68,11 +68,23 @@
<button
class="adev-editor-download-button"
type="button"
(click)="openCurrentCodeInStackBlitz()"
aria-label="Open current code in editor in StackBlitz"
aria-label="Open current code in editor in an online editor"
[cdkMenuTriggerFor]="launcherMenu"
>
<docs-icon>launch</docs-icon>
</button>
<!-- launcher dropdown window -->
<ng-template #launcherMenu>
<div class="adev-editor-dropdown" cdkMenu>
<button cdkMenuItem (click)="openCurrentSolutionInIDX()">
<span>Open in IDX </span>
<img class="icon" src="assets/images/tutorials/common/idx_logo.svg" height="32">
</button>
<button cdkMenuItem (click)="openCurrentCodeInStackBlitz()">
Open in StackBlitz
</button>
</div>
</ng-template>
<button
class="adev-editor-download-button"
type="button"

View file

@ -186,3 +186,34 @@
}
}
}
.adev-editor-dropdown {
border: 1px solid var(--senary-contrast);
border-radius: 0.25rem;
padding: 0;
transform: translateY(-0.7rem);
button {
background: var(--page-background);
font-size: 0.875rem;
width: 100%;
text-align: left;
padding-block: 0.5rem;
color: var(--quaternary-contrast);
transition: color 0.3s ease, background 0.3s ease;
font-weight: 400;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background: var(--senary-contrast);
color: var(--primary-contrast);
}
.icon {
margin: initial;
padding: initial;
width: auto;
}
}
}

View file

@ -31,6 +31,8 @@ import {DiagnosticWithLocation, DiagnosticsState} from './services/diagnostics-s
import {DownloadManager} from '../download-manager.service';
import {StackBlitzOpener} from '../stackblitz-opener.service';
import {ClickOutside, IconComponent} from '@angular/docs';
import {CdkMenu, CdkMenuItem, CdkMenuTrigger} from '@angular/cdk/menu';
import {IDXLauncher} from '../idx-launcher.service';
export const REQUIRED_FILES = new Set([
'src/main.ts',
@ -46,7 +48,16 @@ const ANGULAR_DEV = 'https://angular.dev';
templateUrl: './code-editor.component.html',
styleUrls: ['./code-editor.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIf, NgFor, MatTabsModule, IconComponent, ClickOutside],
imports: [
NgIf,
NgFor,
MatTabsModule,
IconComponent,
ClickOutside,
CdkMenu,
CdkMenuItem,
CdkMenuTrigger,
],
})
export class CodeEditor implements AfterViewInit, OnDestroy {
@ViewChild('codeEditorWrapper') private codeEditorWrapperRef!: ElementRef<HTMLDivElement>;
@ -78,6 +89,7 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
private readonly diagnosticsState = inject(DiagnosticsState);
private readonly downloadManager = inject(DownloadManager);
private readonly stackblitzOpener = inject(StackBlitzOpener);
private readonly idxLauncher = inject(IDXLauncher);
private readonly title = inject(Title);
private readonly location = inject(Location);
private readonly embeddedTutorialManager = inject(EmbeddedTutorialManager);
@ -117,6 +129,9 @@ export class CodeEditor implements AfterViewInit, OnDestroy {
this.codeMirrorEditor.disable();
}
openCurrentSolutionInIDX(): void {
this.idxLauncher.openCurrentSolutionInIDX();
}
async openCurrentCodeInStackBlitz(): Promise<void> {
const title = this.title.getTitle();

View file

@ -0,0 +1,51 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {EnvironmentInjector, Injectable, inject} from '@angular/core';
import {injectAsync} from '../core/services/inject-async';
import * as IDX from 'open-in-idx';
@Injectable({
providedIn: 'root',
})
export class IDXLauncher {
private readonly environmentInjector = inject(EnvironmentInjector);
async openCurrentSolutionInIDX(): Promise<void> {
const nodeRuntimeSandbox = await injectAsync(this.environmentInjector, () =>
import('./node-runtime-sandbox.service').then((c) => c.NodeRuntimeSandbox),
);
const runtimeFiles = await nodeRuntimeSandbox.getSolutionFiles();
const workspaceFiles: Record<string, string> = {};
for (let i = 0; i < runtimeFiles.length; i++) {
const file = runtimeFiles[i];
//don't include config.json, BUILD.bazel, package-lock.json, package.json.template
const doNotAllowList = [
'config.json',
'BUILD.bazel',
'package-lock.json',
'package.json.template',
];
const path = file.path.replace(/^\//, '');
//don't include binary formats
if (!doNotAllowList.includes(path) && typeof file.content === 'string') {
if (path === 'idx/dev.nix') {
workspaceFiles['.idx/dev.nix'] = file.content as string;
} else {
workspaceFiles[path] = file.content as string;
}
}
}
IDX.newAdhocWorkspace({files: workspaceFiles});
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1,-2.417,0)">
<path d="M20.084,23.5C20.084,22.81 19.524,22.25 18.834,22.25L12.167,22.25C11.477,22.25 10.917,22.81 10.917,23.5C10.917,24.19 11.477,24.75 12.167,24.75L18.834,24.75C19.524,24.75 20.084,24.19 20.084,23.5Z" style="fill:rgb(137,100,232);"/>
<path d="M22.583,19.75C22.583,19.06 22.023,18.5 21.333,18.5L20.083,18.5C19.393,18.5 18.833,19.06 18.833,19.75C18.833,20.44 19.393,21 20.083,21L21.333,21C22.023,21 22.583,20.44 22.583,19.75Z" style="fill:rgb(23,184,119);"/>
<path d="M17.583,19.75C17.583,19.06 17.023,18.5 16.333,18.5L15.083,18.5C14.393,18.5 13.833,19.06 13.833,19.75C13.833,20.44 14.393,21 15.083,21L16.333,21C17.023,21 17.583,20.44 17.583,19.75Z" style="fill:rgb(23,184,119);"/>
<path d="M22.167,16C22.167,15.31 21.607,14.75 20.917,14.75L18,14.75C17.31,14.75 16.75,15.31 16.75,16C16.75,16.69 17.31,17.25 18,17.25L20.917,17.25C21.607,17.25 22.167,16.69 22.167,16Z" style="fill:rgb(255,162,62);"/>
<path d="M25.917,16C25.917,15.31 25.357,14.75 24.667,14.75C23.977,14.75 23.417,15.31 23.417,16C23.417,16.69 23.977,17.25 24.667,17.25C25.357,17.25 25.917,16.69 25.917,16Z" style="fill:rgb(255,162,62);"/>
<path d="M23,12.25C23,11.56 22.44,11 21.75,11L15.083,11C14.393,11 13.833,11.56 13.833,12.25C13.833,12.94 14.393,13.5 15.083,13.5L21.75,13.5C22.44,13.5 23,12.94 23,12.25Z" style="fill:rgb(37,166,233);"/>
<path d="M20.084,8.5C20.084,7.81 19.524,7.25 18.834,7.25L15.917,7.25C15.227,7.25 14.667,7.81 14.667,8.5C14.667,9.19 15.227,9.75 15.917,9.75L18.834,9.75C19.524,9.75 20.084,9.19 20.084,8.5Z" style="fill:rgb(137,100,232);"/>
<path d="M13.417,8.5C13.417,7.81 12.857,7.25 12.167,7.25C11.477,7.25 10.917,7.81 10.917,8.5C10.917,9.19 11.477,9.75 12.167,9.75C12.857,9.75 13.417,9.19 13.417,8.5Z" style="fill:rgb(137,100,232);"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,35 @@
# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
# Which nixpkgs channel to use.
channel = "stable-23.11"; # or "unstable"
# Use https://search.nixos.org/packages to find packages
packages = [
pkgs.nodejs_18
];
# Sets environment variables in the workspace
env = {};
idx = {
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
extensions = [
"angular.ng-template"
];
workspace = {
# Runs when a workspace is first created with this \`dev.nix\` file
onCreate = {
npm-install = "npm install --no-audit --prefer-offline";
};
# To run something each time the environment is rebuilt, use the \`onStart\` hook
};
# Enable previews and customize configuration
previews = {
enable = true;
previews = {
web = {
command = ["npm" "run" "start" "--" "--port" "$PORT" "--host" "0.0.0.0"];
manager = "web";
};
};
};
};
}

View file

@ -0,0 +1,35 @@
# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
# Which nixpkgs channel to use.
channel = "stable-23.11"; # or "unstable"
# Use https://search.nixos.org/packages to find packages
packages = [
pkgs.nodejs_18
];
# Sets environment variables in the workspace
env = {};
idx = {
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
extensions = [
"angular.ng-template"
];
workspace = {
# Runs when a workspace is first created with this \`dev.nix\` file
onCreate = {
npm-install = "npm install --no-audit --prefer-offline";
};
# To run something each time the environment is rebuilt, use the \`onStart\` hook
};
# Enable previews and customize configuration
previews = {
enable = true;
previews = {
web = {
command = ["npm" "run" "start" "--" "--port" "$PORT" "--host" "0.0.0.0"];
manager = "web";
};
};
};
};
}

View file

@ -0,0 +1,35 @@
# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
# Which nixpkgs channel to use.
channel = "stable-23.11"; # or "unstable"
# Use https://search.nixos.org/packages to find packages
packages = [
pkgs.nodejs_18
];
# Sets environment variables in the workspace
env = {};
idx = {
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
extensions = [
"angular.ng-template"
];
workspace = {
# Runs when a workspace is first created with this \`dev.nix\` file
onCreate = {
npm-install = "npm install --no-audit --prefer-offline";
};
# To run something each time the environment is rebuilt, use the \`onStart\` hook
};
# Enable previews and customize configuration
previews = {
enable = true;
previews = {
web = {
command = ["npm" "run" "start" "--" "--port" "$PORT" "--host" "0.0.0.0"];
manager = "web";
};
};
};
};
}

View file

@ -0,0 +1,35 @@
# To learn more about how to use Nix to configure your environment
# see: https://developers.google.com/idx/guides/customize-idx-env
{ pkgs, ... }: {
# Which nixpkgs channel to use.
channel = "stable-23.11"; # or "unstable"
# Use https://search.nixos.org/packages to find packages
packages = [
pkgs.nodejs_18
];
# Sets environment variables in the workspace
env = {};
idx = {
# Search for the extensions you want on https://open-vsx.org/ and use "publisher.id"
extensions = [
"angular.ng-template"
];
workspace = {
# Runs when a workspace is first created with this \`dev.nix\` file
onCreate = {
npm-install = "npm install --no-audit --prefer-offline";
};
# To run something each time the environment is rebuilt, use the \`onStart\` hook
};
# Enable previews and customize configuration
previews = {
enable = true;
previews = {
web = {
command = ["npm" "run" "start" "--" "--port" "$PORT" "--host" "0.0.0.0"];
manager = "web";
};
};
};
};
}

View file

@ -126,6 +126,7 @@
"memo-decorator": "^2.0.1",
"ngx-flamegraph": "0.0.12",
"ngx-progressbar": "^11.1.0",
"open-in-idx": "^0.1.1",
"protractor": "^7.0.0",
"reflect-metadata": "^0.2.0",
"requirejs": "^2.3.6",

View file

@ -13260,6 +13260,11 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
open-in-idx@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/open-in-idx/-/open-in-idx-0.1.1.tgz#390cb0985146a461f5a662ed1fd0db3fce55b2f0"
integrity sha512-4Cks2eY4bnWpBP/fEj1deRrVYbHME36g0w4/IFDG4iwnkD7CwmK9HrF3A3LR/RKHs5AXUMj49YxnwdIxEizDpA==
open@8.4.2:
version "8.4.2"
resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"