mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-23 10:51:30 +00:00
Compare commits
2 Commits
iframe-htm
...
user/swvis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6852515b7c | ||
|
|
b1862bb566 |
4683
package-lock.json
generated
4683
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,7 @@
|
|||||||
"applicationinsights": "1.8.0",
|
"applicationinsights": "1.8.0",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"bootstrap": "3.4.1",
|
"bootstrap": "3.4.1",
|
||||||
|
"botframework-webchat": "4.10.1",
|
||||||
"canvas": "2.6.1",
|
"canvas": "2.6.1",
|
||||||
"clean-webpack-plugin": "0.1.19",
|
"clean-webpack-plugin": "0.1.19",
|
||||||
"copy-webpack-plugin": "6.0.2",
|
"copy-webpack-plugin": "6.0.2",
|
||||||
|
|||||||
@@ -78,3 +78,4 @@ ko.components.register("upload-file-pane", new PaneComponents.UploadFilePaneComp
|
|||||||
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
ko.components.register("string-input-pane", new PaneComponents.StringInputPaneComponent());
|
||||||
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
ko.components.register("setup-notebooks-pane", new PaneComponents.SetupNotebooksPaneComponent());
|
||||||
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
ko.components.register("github-repos-pane", new PaneComponents.GitHubReposPaneComponent());
|
||||||
|
ko.components.register("support-pane", new PaneComponents.SupportPaneComponent());
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import * as _ from "underscore";
|
||||||
|
import React from "react";
|
||||||
|
import ReactWebChat, { createDirectLine } from "botframework-webchat";
|
||||||
|
|
||||||
|
export interface SupportPaneComponentProps {
|
||||||
|
directLineToken: string;
|
||||||
|
userToken: string;
|
||||||
|
subId: string;
|
||||||
|
rg:string;
|
||||||
|
accName:string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SupportPaneComponent extends React.Component<SupportPaneComponentProps> {
|
||||||
|
private readonly userId: string = _.uniqueId();
|
||||||
|
|
||||||
|
|
||||||
|
constructor(props: SupportPaneComponentProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const styleOptions = {
|
||||||
|
bubbleBackground: "rgba(0, 0, 255, .1)",
|
||||||
|
bubbleFromUserBackground: "rgba(0, 255, 0, .1)"
|
||||||
|
};
|
||||||
|
|
||||||
|
const directLine = createDirectLine({ token: this.props.directLineToken });
|
||||||
|
const dl =
|
||||||
|
{
|
||||||
|
...directLine,
|
||||||
|
postActivity: (activity: any) => {
|
||||||
|
// Add whatever needs to be added.
|
||||||
|
activity.channelData.token = this.props.userToken;
|
||||||
|
activity.channelData.subId = this.props.subId;
|
||||||
|
activity.channelData.rg = this.props.rg;
|
||||||
|
activity.channelData.accName = this.props.accName;
|
||||||
|
|
||||||
|
//activity.channelData.MyKey = "hello";
|
||||||
|
return directLine.postActivity(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ReactWebChat directLine={dl} userid={this.userId} styleOptions={styleOptions}/>;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import * as ko from "knockout";
|
||||||
|
import * as React from "react";
|
||||||
|
import { ReactAdapter } from "../../../Bindings/ReactBindingHandler";
|
||||||
|
import Explorer from "../../Explorer";
|
||||||
|
import { SupportPaneComponent } from "./SupportPaneComponent";
|
||||||
|
import { userContext } from "../../../UserContext";
|
||||||
|
|
||||||
|
export interface SupportPaneComponentParams {
|
||||||
|
directLineAccessToken: string;
|
||||||
|
userToken: string;
|
||||||
|
subId: string;
|
||||||
|
rg: string;
|
||||||
|
accName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SupportPaneComponentAdapter implements ReactAdapter {
|
||||||
|
public parameters: ko.Observable<SupportPaneComponentParams>;
|
||||||
|
|
||||||
|
constructor(private container: Explorer) {
|
||||||
|
|
||||||
|
this.parameters = ko.observable<SupportPaneComponentParams>({
|
||||||
|
directLineAccessToken: this.container.conversationToken(),
|
||||||
|
userToken: this.container.userToken(),
|
||||||
|
subId: this.container.subId(),
|
||||||
|
rg: this.container.rg(),
|
||||||
|
accName: this.container.accName()
|
||||||
|
});
|
||||||
|
this.container.conversationToken.subscribe(accessToken => {
|
||||||
|
this.parameters().directLineAccessToken = accessToken;
|
||||||
|
this.forceRender();
|
||||||
|
});
|
||||||
|
this.container.userToken.subscribe(userToken => {
|
||||||
|
this.parameters().userToken = userToken;
|
||||||
|
this.forceRender();
|
||||||
|
});
|
||||||
|
this.container.subId.subscribe(subId => {
|
||||||
|
this.parameters().subId = subId;
|
||||||
|
this.forceRender();
|
||||||
|
});
|
||||||
|
this.container.rg.subscribe(rg => {
|
||||||
|
this.parameters().rg = rg;
|
||||||
|
this.forceRender();
|
||||||
|
});
|
||||||
|
this.container.accName.subscribe(accName => {
|
||||||
|
this.parameters().accName = accName;
|
||||||
|
this.forceRender();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderComponent(): JSX.Element {
|
||||||
|
return <SupportPaneComponent
|
||||||
|
directLineToken={this.parameters().directLineAccessToken}
|
||||||
|
userToken={this.parameters().userToken}
|
||||||
|
subId={this.parameters().subId}
|
||||||
|
rg={this.parameters().rg}
|
||||||
|
accName={this.parameters().accName}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public forceRender(): void {
|
||||||
|
this.parameters.valueHasMutated();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,6 +69,7 @@ import { RouteHandler } from "../RouteHandlers/RouteHandler";
|
|||||||
import { SaveQueryPane } from "./Panes/SaveQueryPane";
|
import { SaveQueryPane } from "./Panes/SaveQueryPane";
|
||||||
import { SettingsPane } from "./Panes/SettingsPane";
|
import { SettingsPane } from "./Panes/SettingsPane";
|
||||||
import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
|
import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane";
|
||||||
|
import { SupportPane } from "./Panes/SupportPane";
|
||||||
import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter";
|
import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter";
|
||||||
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter";
|
||||||
import { StringInputPane } from "./Panes/StringInputPane";
|
import { StringInputPane } from "./Panes/StringInputPane";
|
||||||
@@ -175,6 +176,11 @@ export default class Explorer {
|
|||||||
public isAuthWithResourceToken: ko.Observable<boolean>;
|
public isAuthWithResourceToken: ko.Observable<boolean>;
|
||||||
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
public isResourceTokenCollectionNodeSelected: ko.Computed<boolean>;
|
||||||
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
private resourceTreeForResourceToken: ResourceTreeAdapterForResourceToken;
|
||||||
|
public conversationToken: ko.Observable<string>;
|
||||||
|
public userToken: ko.Observable<string>;
|
||||||
|
public subId: ko.Observable<string>;
|
||||||
|
public rg: ko.Observable<string>;
|
||||||
|
public accName: ko.Observable<string>;
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
public isTabsContentExpanded: ko.Observable<boolean>;
|
public isTabsContentExpanded: ko.Observable<boolean>;
|
||||||
@@ -195,6 +201,7 @@ export default class Explorer {
|
|||||||
public newVertexPane: NewVertexPane;
|
public newVertexPane: NewVertexPane;
|
||||||
public cassandraAddCollectionPane: CassandraAddCollectionPane;
|
public cassandraAddCollectionPane: CassandraAddCollectionPane;
|
||||||
public settingsPane: SettingsPane;
|
public settingsPane: SettingsPane;
|
||||||
|
public supportPane: SupportPane;
|
||||||
public executeSprocParamsPane: ExecuteSprocParamsPane;
|
public executeSprocParamsPane: ExecuteSprocParamsPane;
|
||||||
public renewAdHocAccessPane: RenewAdHocAccessPane;
|
public renewAdHocAccessPane: RenewAdHocAccessPane;
|
||||||
public uploadItemsPane: UploadItemsPane;
|
public uploadItemsPane: UploadItemsPane;
|
||||||
@@ -325,10 +332,16 @@ export default class Explorer {
|
|||||||
this.hasStorageAnalyticsAfecFeature = ko.observable(false);
|
this.hasStorageAnalyticsAfecFeature = ko.observable(false);
|
||||||
this.hasStorageAnalyticsAfecFeature.subscribe((enabled: boolean) => this.refreshCommandBarButtons());
|
this.hasStorageAnalyticsAfecFeature.subscribe((enabled: boolean) => this.refreshCommandBarButtons());
|
||||||
this.isSynapseLinkUpdating = ko.observable<boolean>(false);
|
this.isSynapseLinkUpdating = ko.observable<boolean>(false);
|
||||||
|
this.conversationToken = ko.observable<string>();
|
||||||
|
this.userToken = ko.observable<string>();
|
||||||
|
this.subId = ko.observable<string>();
|
||||||
|
this.rg = ko.observable<string>();
|
||||||
|
this.accName = ko.observable<string>();
|
||||||
this.isAccountReady.subscribe(async (isAccountReady: boolean) => {
|
this.isAccountReady.subscribe(async (isAccountReady: boolean) => {
|
||||||
if (isAccountReady) {
|
if (isAccountReady) {
|
||||||
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true);
|
this.isAuthWithResourceToken() ? this.refreshDatabaseForResourceToken() : this.refreshAllDatabases(true);
|
||||||
RouteHandler.getInstance().initHandler();
|
RouteHandler.getInstance().initHandler();
|
||||||
|
this.generateConversationToken();
|
||||||
this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint());
|
this.notebookWorkspaceManager = new NotebookWorkspaceManager(this.armEndpoint());
|
||||||
this.arcadiaWorkspaces = ko.observableArray();
|
this.arcadiaWorkspaces = ko.observableArray();
|
||||||
this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint());
|
this._arcadiaManager = new ArcadiaResourceManager(this.armEndpoint());
|
||||||
@@ -702,6 +715,12 @@ export default class Explorer {
|
|||||||
container: this
|
container: this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.supportPane = new SupportPane({
|
||||||
|
id: "supportpane",
|
||||||
|
visible: ko.observable<boolean>(false),
|
||||||
|
container: this
|
||||||
|
});
|
||||||
|
|
||||||
this.executeSprocParamsPane = new ExecuteSprocParamsPane({
|
this.executeSprocParamsPane = new ExecuteSprocParamsPane({
|
||||||
id: "executesprocparamspane",
|
id: "executesprocparamspane",
|
||||||
visible: ko.observable<boolean>(false),
|
visible: ko.observable<boolean>(false),
|
||||||
@@ -1592,6 +1611,52 @@ export default class Explorer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async generateConversationToken() {
|
||||||
|
const response = await fetch("https://directline.botframework.com/v3/directline/tokens/generate", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
[Constants.HttpHeaders.authorization]: "Bearer BSjLmJJHZRA.PxahjJGCNOKl7q9tiodWyVcqJOIzG894vAAqCme639o",
|
||||||
|
Accept: "application/json",
|
||||||
|
[Constants.HttpHeaders.contentType]: "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
"user": {
|
||||||
|
"id": `dl_${_.uniqueId()}`,
|
||||||
|
"name": this.getUserName()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(await response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenResponse: { conversationId: string; token: string; expires_in: number } = await response.json();
|
||||||
|
this.conversationToken(tokenResponse?.token);
|
||||||
|
if (tokenResponse?.expires_in) {
|
||||||
|
setTimeout(() => this.generateConversationToken(), (tokenResponse?.expires_in - 1000) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getUserName() {
|
||||||
|
const accessToken = userContext?.authorizationToken;
|
||||||
|
if (!accessToken) {
|
||||||
|
return "Cosmos DB User";
|
||||||
|
}
|
||||||
|
|
||||||
|
let name;
|
||||||
|
try {
|
||||||
|
const tokenPayload = decryptJWTToken(accessToken);
|
||||||
|
if (tokenPayload && tokenPayload.hasOwnProperty("name")) {
|
||||||
|
name = tokenPayload.name;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _getArcadiaWorkspaces(): Promise<ArcadiaWorkspaceItem[]> {
|
private async _getArcadiaWorkspaces(): Promise<ArcadiaWorkspaceItem[]> {
|
||||||
try {
|
try {
|
||||||
const workspaces = await this._arcadiaManager.listWorkspacesAsync([userContext.subscriptionId]);
|
const workspaces = await this._arcadiaManager.listWorkspacesAsync([userContext.subscriptionId]);
|
||||||
@@ -1940,6 +2005,11 @@ export default class Explorer {
|
|||||||
resourceGroup: inputs.resourceGroup,
|
resourceGroup: inputs.resourceGroup,
|
||||||
subscriptionId: inputs.subscriptionId
|
subscriptionId: inputs.subscriptionId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.userToken(userContext.authorizationToken);
|
||||||
|
this.subId(userContext.subscriptionId);
|
||||||
|
this.rg(userContext.resourceGroup);
|
||||||
|
this.accName(userContext.databaseAccount.name);
|
||||||
TelemetryProcessor.traceSuccess(
|
TelemetryProcessor.traceSuccess(
|
||||||
Action.LoadDatabaseAccount,
|
Action.LoadDatabaseAccount,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import SynapseIcon from "../../../../images/synapse-link.svg";
|
|||||||
import { configContext, Platform } from "../../../ConfigContext";
|
import { configContext, Platform } from "../../../ConfigContext";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||||
|
import { AuthType } from "../../../AuthType";
|
||||||
|
|
||||||
export class CommandBarComponentButtonFactory {
|
export class CommandBarComponentButtonFactory {
|
||||||
private static counter: number = 0;
|
private static counter: number = 0;
|
||||||
@@ -178,6 +179,21 @@ export class CommandBarComponentButtonFactory {
|
|||||||
buttons.push(settingsPaneButton);
|
buttons.push(settingsPaneButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.authType === AuthType.AAD) {
|
||||||
|
const label = "Chat Assistant";
|
||||||
|
const supportPaneButton: CommandButtonComponentProps = {
|
||||||
|
iconSrc: FeedbackIcon,
|
||||||
|
iconAlt: label,
|
||||||
|
onCommandClick: () => container.supportPane.open(),
|
||||||
|
commandButtonLabel: null,
|
||||||
|
ariaLabel: label,
|
||||||
|
tooltipText: label,
|
||||||
|
hasPopup: true,
|
||||||
|
disabled: false
|
||||||
|
};
|
||||||
|
buttons.push(supportPaneButton);
|
||||||
|
}
|
||||||
|
|
||||||
if (container.isHostedDataExplorerEnabled()) {
|
if (container.isHostedDataExplorerEnabled()) {
|
||||||
const label = "Open Full Screen";
|
const label = "Open Full Screen";
|
||||||
const fullScreenButton: CommandButtonComponentProps = {
|
const fullScreenButton: CommandButtonComponentProps = {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import UploadFilePaneTemplate from "./UploadFilePane.html";
|
|||||||
import StringInputPaneTemplate from "./StringInputPane.html";
|
import StringInputPaneTemplate from "./StringInputPane.html";
|
||||||
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
import SetupNotebooksPaneTemplate from "./SetupNotebooksPane.html";
|
||||||
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
import GitHubReposPaneTemplate from "./GitHubReposPane.html";
|
||||||
|
import SupportPaneTemplate from "./SupportPane.html";
|
||||||
|
|
||||||
export class PaneComponent {
|
export class PaneComponent {
|
||||||
constructor(data: any) {
|
constructor(data: any) {
|
||||||
@@ -224,3 +225,12 @@ export class GitHubReposPaneComponent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SupportPaneComponent {
|
||||||
|
constructor() {
|
||||||
|
return {
|
||||||
|
viewModel: PaneComponent,
|
||||||
|
template: SupportPaneTemplate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
35
src/Explorer/Panes/SupportPane.html
Normal file
35
src/Explorer/Panes/SupportPane.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<div data-bind="visible: visible, event: { keydown: onPaneKeyDown }">
|
||||||
|
<div class="contextual-pane-out" data-bind="click: cancel, clickBubble: false"></div>
|
||||||
|
<div class="contextual-pane" id="supportpane">
|
||||||
|
<!-- Save Query form -- Start -->
|
||||||
|
<div class="contextual-pane-in">
|
||||||
|
<div class="paneContentContainer">
|
||||||
|
<!-- Save Query header - Start -->
|
||||||
|
<div class="firstdivbg headerline">
|
||||||
|
<span role="heading" aria-level="2" data-bind="text: title"></span>
|
||||||
|
<div
|
||||||
|
class="closeImg"
|
||||||
|
role="button"
|
||||||
|
aria-label="Close pane"
|
||||||
|
tabindex="0"
|
||||||
|
data-bind="click: cancel, event: { keypress: onCloseKeyPress }"
|
||||||
|
>
|
||||||
|
<img src="../../../images/close-black.svg" title="Close" alt="Close" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Save Query header - End -->
|
||||||
|
|
||||||
|
<!-- Save Query inputs - Start -->
|
||||||
|
<div class="paneMainContent">
|
||||||
|
<div class="pkPadding" style="height: 100%;" data-bind="react: supportPaneComponentAdapter"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Save Query form - Start -->
|
||||||
|
<!-- Loader - Start -->
|
||||||
|
<div class="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" data-bind="visible: isExecuting">
|
||||||
|
<img class="dataExplorerLoader" src="/LoadingIndicator_3Squares.gif" />
|
||||||
|
</div>
|
||||||
|
<!-- Loader - End -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
28
src/Explorer/Panes/SupportPane.ts
Normal file
28
src/Explorer/Panes/SupportPane.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import { ContextualPaneBase } from "./ContextualPaneBase";
|
||||||
|
import { SupportPaneComponentAdapter } from "../Controls/SupportPaneComponent/SupportPaneComponentAdapter";
|
||||||
|
|
||||||
|
export class SupportPane extends ContextualPaneBase {
|
||||||
|
public supportPaneComponentAdapter: SupportPaneComponentAdapter;
|
||||||
|
|
||||||
|
constructor(options: ViewModels.PaneOptions) {
|
||||||
|
super(options);
|
||||||
|
this.title("Cosmos DB Chat Assistant");
|
||||||
|
this.resetData();
|
||||||
|
this.supportPaneComponentAdapter = new SupportPaneComponentAdapter(this.container);
|
||||||
|
}
|
||||||
|
|
||||||
|
public open() {
|
||||||
|
super.open();
|
||||||
|
this.supportPaneComponentAdapter.forceRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public close() {
|
||||||
|
super.close();
|
||||||
|
this.supportPaneComponentAdapter.forceRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public submit() {
|
||||||
|
// override default behavior because this is not a form
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,10 @@
|
|||||||
<title>Azure Cosmos DB</title>
|
<title>Azure Cosmos DB</title>
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="flexContainer">
|
<div class="flexContainer">
|
||||||
<div id="divExplorer" class="flexContainer hideOverflows" style="display: none">
|
<div id="divExplorer" class="flexContainer hideOverflows" style="display: none">
|
||||||
<!-- Main Command Bar - Start -->
|
<!-- Main Command Bar - Start -->
|
||||||
@@ -289,6 +291,7 @@
|
|||||||
<upload-file-pane params="{data: uploadFilePane}"></upload-file-pane>
|
<upload-file-pane params="{data: uploadFilePane}"></upload-file-pane>
|
||||||
<string-input-pane params="{data: stringInputPane}"></string-input-pane>
|
<string-input-pane params="{data: stringInputPane}"></string-input-pane>
|
||||||
<setup-notebooks-pane params="{data: setupNotebooksPane}"></setup-notebooks-pane>
|
<setup-notebooks-pane params="{data: setupNotebooksPane}"></setup-notebooks-pane>
|
||||||
|
<support-pane params="{data: supportPane}"></support-pane>
|
||||||
|
|
||||||
<!-- ko if: isGitHubPaneEnabled -->
|
<!-- ko if: isGitHubPaneEnabled -->
|
||||||
<github-repos-pane params="{data: gitHubReposPane}"></github-repos-pane>
|
<github-repos-pane params="{data: gitHubReposPane}"></github-repos-pane>
|
||||||
|
|||||||
Reference in New Issue
Block a user