;
23 | };
24 | };
25 |
26 | export type AcrRoleState = {
27 | hasAcrPull: boolean;
28 | };
29 |
30 | export function createClusterId(clusterKey: ClusterKey): string {
31 | return `${clusterKey.resourceGroup}/${clusterKey.clusterName}`;
32 | }
33 |
--------------------------------------------------------------------------------
/webview-ui/src/AttachAcrToCluster/state/update/acrDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { ClusterKey } from "../../../../../src/webview-contract/webviewDefinitions/attachAcrToCluster";
2 | import { newLoaded, newLoading } from "../../../utilities/lazy";
3 | import { AcrReferenceData, createClusterId } from "../stateTypes";
4 |
5 | export function setRoleAssignmentLoading(data: AcrReferenceData, clusterKey: ClusterKey): AcrReferenceData {
6 | return {
7 | ...data,
8 | assignedRoleDefinitions: {
9 | ...data.assignedRoleDefinitions,
10 | [createClusterId(clusterKey)]: newLoading(),
11 | },
12 | };
13 | }
14 |
15 | export function updateRoleAssignments(
16 | data: AcrReferenceData,
17 | clusterKey: ClusterKey,
18 | hasAcrPull: boolean,
19 | ): AcrReferenceData {
20 | return {
21 | ...data,
22 | assignedRoleDefinitions: {
23 | ...data.assignedRoleDefinitions,
24 | [createClusterId(clusterKey)]: newLoaded({ hasAcrPull }),
25 | },
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/webview-ui/src/AutomatedDeployments/AutomatedDeployments.module.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | width: 32rem;
3 | }
4 |
5 | .inputContainer {
6 | display: grid;
7 | grid-template-columns: 14rem auto;
8 | column-gap: 1rem;
9 | row-gap: 0.5rem;
10 | }
11 |
12 | .inputContainer .label {
13 | grid-column: 1 / 2;
14 | margin: 0.2rem 0;
15 | }
16 |
17 | .inputContainer .control {
18 | grid-column: 2 / 3;
19 | margin: 0.2rem 0;
20 | width: 20rem;
21 | }
22 |
23 | .inputContainer .controlSupplement {
24 | grid-column: 2 / 3;
25 | margin-top: -0.7rem; /* grid row gap + control border */
26 | margin-bottom: 0.2rem;
27 | }
28 |
29 | .inputContainer .fullWidth {
30 | grid-column: 1 / 3;
31 | }
32 |
33 | .inputContainer .validationMessage {
34 | grid-column: 2 / 3;
35 | margin-top: -0.5rem;
36 | margin-bottom: 1rem;
37 | }
38 |
39 | .iconButton {
40 | text-align: left;
41 | width: auto;
42 | color: var(--vscode-textLink-foreground);
43 | }
44 |
45 | .buttonContainer {
46 | margin-top: 1rem;
47 | margin-bottom: 1rem;
48 | display: flex;
49 | flex-direction: row;
50 | column-gap: 0.5rem;
51 | }
52 |
53 | .inlineIcon {
54 | vertical-align: middle;
55 | margin-left: 0.3rem;
56 | margin-right: 0.3rem;
57 | }
58 |
59 | .sideControl {
60 | grid-column: 3 / 4;
61 | }
62 |
63 | .label {
64 | grid-column: 1 / 2;
65 | }
66 |
67 | .midControl {
68 | grid-column: 2 / 3;
69 | }
70 |
--------------------------------------------------------------------------------
/webview-ui/src/AzureServiceOperator/Progress.tsx:
--------------------------------------------------------------------------------
1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2 | import { faCheckCircle, faTimesCircle } from "@fortawesome/free-solid-svg-icons";
3 | import styles from "./AzureServiceOperator.module.css";
4 | import { ProgressStep } from "./ProgressStep";
5 | import { InstallStep, InstallStepStatus } from "./helpers/state";
6 |
7 | export type StepWithDescription = {
8 | step: InstallStep;
9 | description: string;
10 | };
11 |
12 | export interface ProgressProps {
13 | steps: StepWithDescription[];
14 | }
15 |
16 | export function Progress(props: ProgressProps) {
17 | const succeeded = props.steps[props.steps.length - 1].step.status === InstallStepStatus.Succeeded;
18 | const failed = props.steps.some((s) => s.step.status === InstallStepStatus.Failed);
19 | const heading = succeeded ? "Successfully Installed ASO" : failed ? "Failed to Install ASO" : "Progress";
20 |
21 | return (
22 |
23 |
24 | {succeeded && }
25 | {failed && }
26 | {heading}
27 |
28 | {props.steps.map((s) => (
29 |
30 | ))}
31 |
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/webview-ui/src/AzureServiceOperator/helpers/inputs.ts:
--------------------------------------------------------------------------------
1 | import { InstallSettingsParams } from "../../../../src/webview-contract/webviewDefinitions/azureServiceOperator";
2 | import { ASOState } from "./state";
3 |
4 | export function getRequiredInputs(state: ASOState): InstallSettingsParams | null {
5 | const { appId, appSecret, cloudName, selectedSubscription, tenantId } = state;
6 | if (!appId) return null;
7 | if (!appSecret) return null;
8 | if (!cloudName) return null;
9 | if (!selectedSubscription) return null;
10 | if (!tenantId) return null;
11 | return { appId, appSecret, cloudName, subscriptionId: selectedSubscription.id, tenantId };
12 | }
13 |
--------------------------------------------------------------------------------
/webview-ui/src/ClusterProperties/ConfirmationDialog.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./ClusterProperties.module.css";
3 |
4 | export interface ConfirmationDialogProps {
5 | title: string;
6 | message: React.ReactNode;
7 | confirmLabel: string;
8 | cancelLabel: string;
9 | isOpen: boolean;
10 | onConfirm: () => void;
11 | onCancel: () => void;
12 | }
13 |
14 | export function ConfirmationDialog(props: ConfirmationDialogProps) {
15 | if (!props.isOpen) {
16 | return null;
17 | }
18 |
19 | return (
20 |
21 |
22 |
{props.title}
23 |
{props.message}
24 |
25 |
26 | {props.cancelLabel}
27 |
28 |
29 | {props.confirmLabel}
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/webview-ui/src/CreateCluster/Success.tsx:
--------------------------------------------------------------------------------
1 | interface SuccessProps {
2 | portalClusterUrl: string;
3 | name: string;
4 | }
5 |
6 | export function Success(props: SuccessProps) {
7 | return (
8 | <>
9 | Cluster {props.name} was created successfully
10 |
11 | Click here to view your cluster in the Azure Portal.
12 |
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/Detector.tsx:
--------------------------------------------------------------------------------
1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/detector";
2 | import { SingleDetector } from "./SingleDetector";
3 | import { useStateManagement } from "../utilities/state";
4 | import { stateUpdater, vscode } from "./state";
5 |
6 | export function Detector(initialState: InitialState) {
7 | const { state } = useStateManagement(stateUpdater, initialState, vscode);
8 |
9 | return (
10 | <>
11 | {state.name}
12 | {state.description && state.description !== "test" && {state.description}
}
13 | To perform more checks on your cluster, visit AKS Diagnostics .
14 |
15 | {state.detectors.map((detector) => (
16 |
17 | ))}
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/SingleDetector.tsx:
--------------------------------------------------------------------------------
1 | import { SingleDataset, SingleDetectorARMResponse } from "../../../src/webview-contract/webviewDefinitions/detector";
2 | import { InsightsRenderer } from "./renderers/InsightsRenderer";
3 | import { MarkdownRenderer } from "./renderers/MarkdownRenderer";
4 | import { Status, getOverallStatus } from "./utils";
5 | import styles from "./Detector.module.css";
6 |
7 | export function SingleDetector(detector: SingleDetectorARMResponse) {
8 | const status = getOverallStatus(detector);
9 | const panelClassNames = getPanelClassNames(status);
10 |
11 | return (
12 |
13 |
14 |
{detector.properties.metadata.name}
15 |
16 |
17 | {detector.properties.dataset.map(getRenderer).filter((r) => r !== null)}
18 |
19 |
20 | );
21 | }
22 |
23 | function getRenderer(dataset: SingleDataset, index: number) {
24 | switch (dataset.renderingProperties.type) {
25 | case 7:
26 | return ;
27 | case 9:
28 | return ;
29 | default:
30 | return null;
31 | }
32 | }
33 |
34 | function getPanelClassNames(status: Status) {
35 | switch (status) {
36 | case Status.Success:
37 | return `${styles.detectorPanel} ${styles.success}`;
38 | case Status.Warning:
39 | return `${styles.detectorPanel} ${styles.warning}`;
40 | default:
41 | return `${styles.detectorPanel} ${styles.error}`;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/renderers/Error.tsx:
--------------------------------------------------------------------------------
1 | export interface ErrorProps {
2 | message: string;
3 | data: unknown;
4 | }
5 |
6 | export function Error(props: ErrorProps) {
7 | return (
8 | <>
9 | Rendering Error
10 | {props.message}
11 | Data
12 | {JSON.stringify(props.data, null, 2)}
13 | >
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/renderers/MarkdownRenderer.tsx:
--------------------------------------------------------------------------------
1 | import ReactMarkdown from "react-markdown";
2 | import rehypeRaw from "rehype-raw";
3 | import { SingleDataset } from "../../../../src/webview-contract/webviewDefinitions/detector";
4 | import { getMarkdownComponents } from "./common";
5 |
6 | const markdownComponents = getMarkdownComponents();
7 |
8 | export function MarkdownRenderer(props: SingleDataset) {
9 | return (
10 | <>
11 | {props.renderingProperties.title}
12 | {props.table.rows.map((r, i) => (
13 |
14 | {String(r[0])}
15 |
16 | ))}
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/renderers/README.md:
--------------------------------------------------------------------------------
1 | # Detector Renderer Components
2 |
3 | This folder contains renderers for each `type` of `dataset`.
4 |
5 | The output of a detector is structured like:
6 |
7 | ```json
8 | {
9 | "properties": {
10 | "dataset": [
11 | {
12 | "renderingProperties": {
13 | "type": 7
14 | },
15 | "table": {
16 | "columns": [...],
17 | "rows": [...]
18 | }
19 | },
20 | // ...
21 | ],
22 | // ...
23 | },
24 | // ...
25 | }
26 | ```
27 |
28 | I.e. it contains a collection of `dataset`s, each of which contain metadata about how they are rendered. Specifically, they contain a `renderingProperties.type` value,
29 | which informs how the data in the `table` structure should be rendered.
30 |
31 | Some common types are:
32 | - 7 ("insights"): A status value and message, with zero or more name/value pairs of extra data.
33 | - 9 ("markdown"): One or more blobs of markdown.
34 | - 2 ("time series"): Numeric data over time, to be rendered as a chart.
35 | - 10 ("detector"): Used in 'category' detectors - contains no data, just references to other detectors.
36 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/renderers/common.tsx:
--------------------------------------------------------------------------------
1 | import { Components } from "react-markdown";
2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3 | import {
4 | faCheckCircle,
5 | faExclamationCircle,
6 | faExclamationTriangle,
7 | faInfoCircle,
8 | } from "@fortawesome/free-solid-svg-icons";
9 | import styles from "../Detector.module.css";
10 |
11 | /**
12 | * Gets a mapping that converts rendered markdown components to custom React elements,
13 | * so that we can apply our own styling or icons to the markdown that comes from the detectors.
14 | */
15 | export function getMarkdownComponents(): Components {
16 | return {
17 | table: ({ node, ...props }) => ,
18 | span: ({ node, ...props }) => {
19 | return (
20 | <>
21 | {getIcons((node?.properties?.className as string[]) || [])}
22 |
23 | >
24 | );
25 | },
26 | };
27 | }
28 |
29 | function getIcons(classNames: string[]) {
30 | return classNames.map(getIcon).filter((i) => i !== null);
31 | }
32 |
33 | function getIcon(className: string) {
34 | switch (className) {
35 | case "fa-exclamation-triangle":
36 | return ;
37 | case "fa-exclamation-circle":
38 | return ;
39 | case "fa-check-circle":
40 | return ;
41 | case "fa-info-circle":
42 | return ;
43 | default:
44 | return null;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/webview-ui/src/Detector/state.ts:
--------------------------------------------------------------------------------
1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/detector";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 | import { getWebviewMessageContext } from "../utilities/vscode";
4 |
5 | export type EventDef = Record;
6 |
7 | export type DetectorState = InitialState;
8 |
9 | export const stateUpdater: WebviewStateUpdater<"detector", EventDef, DetectorState> = {
10 | createState: (initialState) => ({ ...initialState }),
11 | vscodeMessageHandler: {},
12 | eventHandler: {},
13 | };
14 |
15 | export const vscode = getWebviewMessageContext<"detector">({});
16 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/DraftValidate/DraftValidate.tsx:
--------------------------------------------------------------------------------
1 | import { InitialState } from "../../../../src/webview-contract/webviewDefinitions/draft/draftValidate";
2 | import { useStateManagement } from "../../utilities/state";
3 | import { stateUpdater, vscode } from "./state";
4 | import { useEffect } from "react";
5 |
6 | export function DraftValidate(initialState: InitialState) {
7 | const { state } = useStateManagement(stateUpdater, initialState, vscode);
8 |
9 | //Request the validation results from the backend once when the component is mounted.
10 | useEffect(() => {
11 | vscode.postCreateDraftValidateRequest("");
12 | }, []);
13 |
14 | return (
15 | <>
16 | Draft Validate
17 | {state.validationResults}
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/DraftValidate/state.ts:
--------------------------------------------------------------------------------
1 | import { WebviewStateUpdater } from "../../utilities/state";
2 | import { getWebviewMessageContext } from "../../utilities/vscode";
3 |
4 | export type EventDef = {
5 | //Defines the events that can originate from the webview and be sent to the backend (ToVsCodeMsgDef).
6 | draftValidateRequest: string;
7 | };
8 |
9 | export type DraftValidateState = {
10 | validationResults: string;
11 | };
12 |
13 | export const stateUpdater: WebviewStateUpdater<"draftValidate", EventDef, DraftValidateState> = {
14 | createState: (initialState) => ({
15 | validationResults: initialState.validationResults,
16 | }),
17 | vscodeMessageHandler: {
18 | // This handler updates the state when a message from the extension
19 | validationResult: (state, response) => ({
20 | ...state,
21 | validationResults: response.result,
22 | }),
23 | },
24 | eventHandler: {
25 | draftValidateRequest: (state) => ({
26 | ...state,
27 | }),
28 | },
29 | };
30 |
31 | export const vscode = getWebviewMessageContext<"draftValidate">({
32 | createDraftValidateRequest: null,
33 | });
34 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/index.ts:
--------------------------------------------------------------------------------
1 | import { DraftDockerfile } from "./DraftDockerfile/DraftDockerfile";
2 | import { DraftDeployment } from "./DraftDeployment/DraftDeployment";
3 | import { DraftWorkflow } from "./DraftWorkflow/DraftWorkflow";
4 | import { DraftValidate } from "./DraftValidate/DraftValidate";
5 |
6 | export { DraftDockerfile, DraftDeployment, DraftWorkflow, DraftValidate };
7 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/state/stateTypes.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AcrKey,
3 | ClusterKey,
4 | GitHubRepo,
5 | RepositoryKey,
6 | Subscription,
7 | } from "../../../../src/webview-contract/webviewDefinitions/draft/types";
8 | import { Lazy } from "../../utilities/lazy";
9 |
10 | export type AzureReferenceData = {
11 | subscriptions: Lazy;
12 | };
13 |
14 | export type SubscriptionReferenceData = {
15 | subscription: Subscription;
16 | acrs: Lazy;
17 | clusters: Lazy;
18 | };
19 |
20 | export type AcrReferenceData = {
21 | key: AcrKey;
22 | repositories: Lazy;
23 | };
24 |
25 | export type RepositoryReferenceData = {
26 | key: RepositoryKey;
27 | tags: Lazy;
28 | };
29 |
30 | export type ClusterReferenceData = {
31 | key: ClusterKey;
32 | namespaces: Lazy;
33 | };
34 |
35 | export type GitHubReferenceData = {
36 | repositories: GitHubRepositoryReferenceData[];
37 | };
38 |
39 | export type GitHubRepositoryReferenceData = {
40 | repository: GitHubRepo;
41 | branches: Lazy;
42 | };
43 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/state/update/acrRepoDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { newLoaded, newLoading } from "../../../utilities/lazy";
2 | import { RepositoryReferenceData } from "../stateTypes";
3 |
4 | export function setTagsLoading(data: RepositoryReferenceData): RepositoryReferenceData {
5 | return { ...data, tags: newLoading() };
6 | }
7 |
8 | export function updateTags(data: RepositoryReferenceData, tags: string[]): RepositoryReferenceData {
9 | return {
10 | ...data,
11 | tags: newLoaded(tags),
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/state/update/clusterDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { newLoaded, newLoading } from "../../../utilities/lazy";
2 | import { ClusterReferenceData } from "../stateTypes";
3 |
4 | export function setNamespacesLoading(data: ClusterReferenceData): ClusterReferenceData {
5 | return {
6 | ...data,
7 | namespaces: newLoading(),
8 | };
9 | }
10 |
11 | export function updateNamespaces(data: ClusterReferenceData, namespaces: string[]): ClusterReferenceData {
12 | return {
13 | ...data,
14 | namespaces: newLoaded(namespaces),
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/state/update/gitHubReferenceDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { GitHubRepoKey } from "../../../../../src/webview-contract/webviewDefinitions/draft/types";
2 | import { replaceItem } from "../../../utilities/array";
3 | import { GitHubReferenceData, GitHubRepositoryReferenceData } from "../stateTypes";
4 | import * as RepoDataUpdate from "./gitHubRepoDataUpdate";
5 |
6 | export function setBranchesLoading(data: GitHubReferenceData, repoKey: GitHubRepoKey): GitHubReferenceData {
7 | return updateRepository(data, repoKey, (repoData) => RepoDataUpdate.setBranchesLoading(repoData));
8 | }
9 |
10 | export function updateBranches(
11 | data: GitHubReferenceData,
12 | repoKey: GitHubRepoKey,
13 | branches: string[],
14 | ): GitHubReferenceData {
15 | return updateRepository(data, repoKey, (repoData) => RepoDataUpdate.updateBranches(repoData, branches));
16 | }
17 |
18 | function updateRepository(
19 | data: GitHubReferenceData,
20 | repoKey: GitHubRepoKey,
21 | updater: (data: GitHubRepositoryReferenceData) => GitHubRepositoryReferenceData,
22 | ): GitHubReferenceData {
23 | return {
24 | ...data,
25 | repositories: replaceItem(
26 | data.repositories,
27 | (data) =>
28 | data.repository.gitHubRepoOwner === repoKey.gitHubRepoOwner &&
29 | data.repository.gitHubRepoName === repoKey.gitHubRepoName,
30 | updater,
31 | ),
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/webview-ui/src/Draft/state/update/gitHubRepoDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { newLoaded, newLoading } from "../../../utilities/lazy";
2 | import { GitHubRepositoryReferenceData } from "../stateTypes";
3 |
4 | export function setBranchesLoading(data: GitHubRepositoryReferenceData): GitHubRepositoryReferenceData {
5 | return {
6 | ...data,
7 | branches: newLoading(),
8 | };
9 | }
10 |
11 | export function updateBranches(data: GitHubRepositoryReferenceData, branches: string[]): GitHubRepositoryReferenceData {
12 | return {
13 | ...data,
14 | branches: newLoaded(branches),
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/webview-ui/src/FleetProperties/state.ts:
--------------------------------------------------------------------------------
1 | import { FleetInfo, InitialState } from "../../../src/webview-contract/webviewDefinitions/fleetProperties";
2 | import { Lazy, newLoaded, newLoading, newNotLoaded } from "../utilities/lazy";
3 | import { WebviewStateUpdater } from "../utilities/state";
4 | import { getWebviewMessageContext } from "../utilities/vscode";
5 |
6 | export type CreateFleetState = InitialState & {
7 | fleetInfo: Lazy;
8 | fleetOperationRequested: boolean;
9 | errorMessage: string | null;
10 | };
11 |
12 | export type EventDef = {
13 | setPropertiesLoading: void;
14 | setFleetOperationRequested: void;
15 | };
16 |
17 | export const stateUpdater: WebviewStateUpdater<"fleetProperties", EventDef, CreateFleetState> = {
18 | createState: (initialState) => ({
19 | ...initialState,
20 | fleetInfo: newNotLoaded(),
21 | fleetOperationRequested: false,
22 | errorMessage: null,
23 | }),
24 | vscodeMessageHandler: {
25 | getPropertiesResponse: (state, fleetInfo) => ({
26 | ...state,
27 | fleetInfo: newLoaded(fleetInfo),
28 | fleetOperationRequested: false,
29 | }),
30 | errorNotification: (state, err) => ({ ...state, errorMessage: err }),
31 | },
32 | eventHandler: {
33 | setPropertiesLoading: (state) => ({ ...state, fleetInfo: newLoading() }),
34 | setFleetOperationRequested: (state) => ({ ...state, fleetOperationRequested: true }),
35 | },
36 | };
37 |
38 | export const vscode = getWebviewMessageContext<"fleetProperties">({
39 | getPropertiesRequest: null,
40 | refreshRequest: null,
41 | });
42 |
--------------------------------------------------------------------------------
/webview-ui/src/InspektorGadget/GadgetSelector.tsx:
--------------------------------------------------------------------------------
1 | import { GadgetCategory } from "./helpers/gadgets/types";
2 | import { configuredGadgetResources } from "./helpers/gadgets";
3 | import { CustomDropdown } from "../components/CustomDropdown";
4 | import { CustomDropdownOption } from "../components/CustomDropdownOption";
5 | import { useState } from "react";
6 | export interface GadgetSelectorProps {
7 | category: GadgetCategory;
8 | id: string;
9 | className?: string;
10 | required?: boolean;
11 | onResourceChanged: (resource: string | null) => void;
12 | }
13 |
14 | export function GadgetSelector(props: GadgetSelectorProps) {
15 | function handleResourceChange(value: string) {
16 | const resource = value ? value : null;
17 | setSelectedNode(value);
18 | props.onResourceChanged(resource);
19 | }
20 |
21 | const configuredResources = configuredGadgetResources[props.category];
22 | const [selectedNode, setSelectedNode] = useState("");
23 |
24 | return (
25 |
26 |
27 | {Object.keys(configuredResources).map((resource) => (
28 |
29 | ))}
30 |
31 | );
32 | }
33 |
--------------------------------------------------------------------------------
/webview-ui/src/InspektorGadget/TraceItemSortSelector.tsx:
--------------------------------------------------------------------------------
1 | import { FormEvent } from "react";
2 | import { ItemProperty, SortSpecifier, fromSortString, toSortString } from "./helpers/gadgets/types";
3 |
4 | type ChangeEvent = Event | FormEvent;
5 |
6 | export interface TraceItemSortSelectorProps {
7 | id: string;
8 | className?: string;
9 | required?: boolean;
10 | sortSpecifiers: SortSpecifier[];
11 | allProperties: ItemProperty[];
12 | onSortSpecifiersChange: (sortSpecifiers: SortSpecifier[]) => void;
13 | }
14 |
15 | export function TraceItemSortSelector(props: TraceItemSortSelectorProps) {
16 | function handleSortStringChange(e: ChangeEvent) {
17 | const input = e.currentTarget as HTMLInputElement;
18 | const sortSpecifiers = fromSortString(input.value, props.allProperties);
19 | props.onSortSpecifiersChange(sortSpecifiers);
20 | }
21 |
22 | const sortString = toSortString(props.sortSpecifiers);
23 | const allowedIdentifiers = props.allProperties.map((p) => p.identifier).sort();
24 | const title = `Allowed properties:\n${allowedIdentifiers.join("\n")}`;
25 | return (
26 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/webview-ui/src/Kaito/kaito-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Azure/vscode-aks-tools/2588f09d3fef5dae2ab2ddf56e347c3e59daab23/webview-ui/src/Kaito/kaito-image.png
--------------------------------------------------------------------------------
/webview-ui/src/Kaito/state.ts:
--------------------------------------------------------------------------------
1 | import { InitialState, ProgressEventType } from "../../../src/webview-contract/webviewDefinitions/kaito";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 | import { getWebviewMessageContext } from "../utilities/vscode";
4 |
5 | export type EventDef = Record;
6 |
7 | export type KaitoState = InitialState & {
8 | operationDescription: string;
9 | kaitoInstallStatus: ProgressEventType;
10 | errors: string | undefined;
11 | };
12 |
13 | export const stateUpdater: WebviewStateUpdater<"kaito", EventDef, KaitoState> = {
14 | createState: (initialState) => ({
15 | ...initialState,
16 | operationDescription: "",
17 | kaitoInstallStatus: ProgressEventType.NotStarted,
18 | errors: undefined,
19 | }),
20 | vscodeMessageHandler: {
21 | kaitoInstallProgressUpdate: (state, args) => ({
22 | ...state,
23 | operationDescription: args.operationDescription,
24 | kaitoInstallStatus: args.event,
25 | errors: args.errorMessage,
26 | }),
27 | getWorkspaceResponse: (state, args) => ({
28 | ...state,
29 | workspace: args.workspace,
30 | }),
31 | },
32 | eventHandler: {},
33 | };
34 |
35 | export const vscode = getWebviewMessageContext<"kaito">({
36 | installKaitoRequest: null,
37 | generateWorkspaceRequest: null,
38 | });
39 |
--------------------------------------------------------------------------------
/webview-ui/src/KaitoManage/state.ts:
--------------------------------------------------------------------------------
1 | import { WebviewStateUpdater } from "../utilities/state";
2 | import { getWebviewMessageContext } from "../utilities/vscode";
3 | import { ModelState } from "../../../src/webview-contract/webviewDefinitions/kaitoManage";
4 |
5 | export type EventDef = Record;
6 |
7 | export type MonitorState = {
8 | clusterName: string;
9 | models: ModelState[];
10 | };
11 |
12 | export const vscode = getWebviewMessageContext<"kaitoManage">({
13 | monitorUpdateRequest: null,
14 | deleteWorkspaceRequest: null,
15 | redeployWorkspaceRequest: null,
16 | getLogsRequest: null,
17 | testWorkspaceRequest: null,
18 | });
19 |
20 | export const stateUpdater: WebviewStateUpdater<"kaitoManage", EventDef, MonitorState> = {
21 | createState: (initialState) => ({
22 | ...initialState,
23 | }),
24 | vscodeMessageHandler: {
25 | monitorUpdate: (state, args) => ({
26 | ...state,
27 | models: args.models,
28 | }),
29 | },
30 | eventHandler: {},
31 | };
32 |
--------------------------------------------------------------------------------
/webview-ui/src/KaitoModels/state.ts:
--------------------------------------------------------------------------------
1 | import { WebviewStateUpdater } from "../utilities/state";
2 | import { getWebviewMessageContext } from "../utilities/vscode";
3 |
4 | export type EventDef = Record;
5 |
6 | export type DeploymentState = {
7 | clusterName: string;
8 | modelName: string;
9 | workspaceExists: boolean;
10 | resourceReady: boolean | null;
11 | inferenceReady: boolean | null;
12 | workspaceReady: boolean | null;
13 | age: number;
14 | };
15 |
16 | export const vscode = getWebviewMessageContext<"kaitoModels">({
17 | generateCRDRequest: null,
18 | deployKaitoRequest: null,
19 | resetStateRequest: null,
20 | cancelRequest: null,
21 | kaitoManageRedirectRequest: null,
22 | });
23 |
24 | export const stateUpdater: WebviewStateUpdater<"kaitoModels", EventDef, DeploymentState> = {
25 | createState: (initialState) => ({
26 | ...initialState,
27 | modelName: "",
28 | workspaceExists: false,
29 | resourceReady: null,
30 | inferenceReady: null,
31 | workspaceReady: null,
32 | age: 0,
33 | }),
34 | vscodeMessageHandler: {
35 | deploymentProgressUpdate: (state, args) => ({
36 | ...state,
37 | modelName: args.modelName,
38 | workspaceExists: args.workspaceExists,
39 | resourceReady: args.resourceReady,
40 | inferenceReady: args.inferenceReady,
41 | workspaceReady: args.workspaceReady,
42 | age: args.age,
43 | }),
44 | },
45 | eventHandler: {},
46 | };
47 |
--------------------------------------------------------------------------------
/webview-ui/src/KaitoTest/state.ts:
--------------------------------------------------------------------------------
1 | import { getWebviewMessageContext } from "../utilities/vscode";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 |
4 | export const vscode = getWebviewMessageContext<"kaitoTest">({
5 | queryRequest: null,
6 | });
7 |
8 | export type EventDef = Record;
9 |
10 | export type TestState = {
11 | clusterName: string;
12 | modelName: string;
13 | output: string;
14 | };
15 |
16 | export const stateUpdater: WebviewStateUpdater<"kaitoTest", EventDef, TestState> = {
17 | createState: (initialState) => ({
18 | ...initialState,
19 | }),
20 | vscodeMessageHandler: {
21 | testUpdate: (state, args) => ({
22 | ...state,
23 | modelName: args.modelName,
24 | output: args.output,
25 | }),
26 | },
27 | eventHandler: {},
28 | };
29 |
--------------------------------------------------------------------------------
/webview-ui/src/Kubectl/CommandInput.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./Kubectl.module.css";
2 | import { FormEvent } from "react";
3 |
4 | type ChangeEvent = Event | FormEvent;
5 |
6 | export interface CommandInputProps {
7 | command: string;
8 | matchesExisting: boolean;
9 | onCommandUpdate: (command: string) => void;
10 | onRunCommand: (command: string) => void;
11 | onSaveRequest: () => void;
12 | }
13 |
14 | export function CommandInput(props: CommandInputProps) {
15 | function handleCommandChange(e: ChangeEvent) {
16 | const input = e.currentTarget as HTMLInputElement;
17 | props.onCommandUpdate(input.value);
18 | }
19 |
20 | function onKeyPress(e: React.KeyboardEvent) {
21 | if (e.nativeEvent instanceof KeyboardEvent) {
22 | if (e.code === "Enter") {
23 | props.onRunCommand(props.command);
24 | }
25 | }
26 | }
27 |
28 | const canRun = props.command.trim().length > 0;
29 | return (
30 |
31 |
32 | Command
33 |
34 |
42 |
43 | props.onRunCommand(props.command)}>
44 | Run
45 |
46 | {!props.matchesExisting && Save }
47 |
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/webview-ui/src/Kubectl/CommandOutput.tsx:
--------------------------------------------------------------------------------
1 | import styles from "./Kubectl.module.css";
2 | import { EventHandlers } from "../utilities/state";
3 | import { EventDef } from "./helpers/state";
4 | import { ProgressRing } from "../components/ProgressRing";
5 |
6 | export interface CommandOutputProps {
7 | isCommandRunning: boolean;
8 | output: string | null;
9 | errorMessage: string | null;
10 | eventHandlers: EventHandlers;
11 | }
12 |
13 | export function CommandOutput(props: CommandOutputProps) {
14 | const hasOutput = props.output !== undefined;
15 | const hasError = props.errorMessage !== undefined;
16 |
17 | return (
18 | <>
19 | {props.isCommandRunning && }
20 | {hasOutput && {props.output} }
21 | {hasError && {props.errorMessage} }
22 | >
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/ErrorView.tsx:
--------------------------------------------------------------------------------
1 | import { KustomizeConfig } from "../../../src/webview-contract/webviewDefinitions/periscope";
2 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
3 | import { faTimesCircle } from "@fortawesome/free-solid-svg-icons";
4 | import styles from "./Periscope.module.css";
5 |
6 | export interface ErrorViewProps {
7 | clusterName: string;
8 | message: string;
9 | config: KustomizeConfig;
10 | }
11 |
12 | export function ErrorView(props: ErrorViewProps) {
13 | return (
14 | <>
15 |
16 | AKS Periscope failed to run on '{props.clusterName}'. Please see the error below for more details.
17 |
18 | Periscope settings (from VS Code Configuration)
19 |
20 | GitHub organisation (containing aks-periscope repo with Kustomize base)
21 | {props.config.repoOrg}
22 |
23 | Container registry (containing Periscope image to deploy)
24 | {props.config.containerRegistry}
25 |
26 | Image version (tag for {props.config.containerRegistry}/aks/periscope image)
27 | {props.config.imageVersion}
28 |
29 | Release tag (for {props.config.repoOrg}/aks-periscope GitHub repo)
30 | {props.config.releaseTag}
31 |
32 |
33 | {props.message}
34 | >
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/NoDiagnosticSettingView.tsx:
--------------------------------------------------------------------------------
1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2 | import { faTimesCircle } from "@fortawesome/free-solid-svg-icons";
3 | import styles from "./Periscope.module.css";
4 |
5 | export interface NoDiagnosticSettingViewProps {
6 | clusterName: string;
7 | }
8 |
9 | export function NoDiagnosticSettingView(props: NoDiagnosticSettingViewProps) {
10 | return (
11 | <>
12 |
13 |
14 | We didn’t find any storage account associated with `{props.clusterName}`. Please use the Diagnostics
15 | settings in the Azure Portal to configure a storage account for your cluster and try again.
16 |
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/NodeActions.tsx:
--------------------------------------------------------------------------------
1 | import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2 | import { faClone, faDownload } from "@fortawesome/free-solid-svg-icons";
3 |
4 | export interface NodeActionsProps {
5 | runId: string;
6 | nodeName: string;
7 | containerUrl: string;
8 | shareableSas: string;
9 | isUploaded: boolean;
10 | }
11 |
12 | export function NodeActions(props: NodeActionsProps) {
13 | const shareableLink = `${props.containerUrl}/${props.runId}/${props.nodeName}/${props.nodeName}.zip${props.shareableSas}`;
14 |
15 | function copyShareLink(e: React.MouseEvent) {
16 | e.stopPropagation();
17 | navigator.clipboard.writeText(shareableLink);
18 | }
19 |
20 | return (
21 | <>
22 |
23 |
24 | Copy 7-Day Shareable Link
25 |
26 |
27 | {props.isUploaded && (
28 |
29 |
30 | Download Zip
31 |
32 | )}
33 | >
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/NodeLogs.tsx:
--------------------------------------------------------------------------------
1 | import { PodLogs } from "../../../src/webview-contract/webviewDefinitions/periscope";
2 |
3 | export interface NodeLogsProps {
4 | node: string;
5 | podLogs: PodLogs[];
6 | }
7 |
8 | export function NodeLogs(props: NodeLogsProps) {
9 | return (
10 | <>
11 | {props.node} Node Logs
12 | {(props.podLogs || []).map((podLog) => (
13 |
14 |
{podLog.podName}
15 |
{podLog.logs}
16 |
17 | ))}
18 | >
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/Periscope.module.css:
--------------------------------------------------------------------------------
1 | table.nodelist {
2 | border-collapse: collapse;
3 | width: 100%;
4 | }
5 |
6 | table.nodelist th {
7 | height: 50px;
8 | text-align: left;
9 | }
10 |
11 | table.nodelist td {
12 | padding: 5px;
13 | }
14 |
15 | table.nodelist tbody tr.selected {
16 | background-color: var(--vscode-list-activeSelectionBackground);
17 | color: var(--vscode-list-activeSelectionForeground);
18 | }
19 |
20 | table.nodelist tbody tr:hover {
21 | background-color: var(--vscode-list-hoverBackground);
22 | color: var(--vscode-list-hoverForeground);
23 | cursor: pointer;
24 | }
25 |
26 | .actionsContainer {
27 | display: flex;
28 | margin: auto;
29 | align-items: center;
30 | justify-content: left;
31 | }
32 |
33 | .errorIndicator {
34 | color: var(--vscode-testing-iconFailed);
35 | padding-right: 5px;
36 | }
37 |
38 | .successIndicator {
39 | color: var(--vscode-testing-iconPassed);
40 | padding-right: 5px;
41 | }
42 |
43 | dl.settinglist {
44 | display: flex;
45 | flex-flow: row wrap;
46 | }
47 |
48 | dl.settinglist dt {
49 | flex-basis: 50%;
50 | padding: 2px 4px;
51 | font-weight: bold;
52 | }
53 |
54 | dl.settinglist dd {
55 | padding: 2px 4px;
56 | }
57 |
--------------------------------------------------------------------------------
/webview-ui/src/Periscope/state.ts:
--------------------------------------------------------------------------------
1 | import { InitialState, NodeUploadStatus, PodLogs } from "../../../src/webview-contract/webviewDefinitions/periscope";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 | import { getWebviewMessageContext } from "../utilities/vscode";
4 |
5 | export type PeriscopeState = InitialState & {
6 | nodeUploadStatuses: NodeUploadStatus[];
7 | selectedNode: string;
8 | nodePodLogs: PodLogs[] | null;
9 | };
10 |
11 | export type EventDef = {
12 | setSelectedNode: string;
13 | };
14 |
15 | export const stateUpdater: WebviewStateUpdater<"periscope", EventDef, PeriscopeState> = {
16 | createState: (initialState) => ({
17 | ...initialState,
18 | nodeUploadStatuses: initialState.nodes.map((n) => ({ nodeName: n, isUploaded: false })),
19 | selectedNode: "",
20 | nodePodLogs: null,
21 | }),
22 | vscodeMessageHandler: {
23 | nodeLogsResponse: (state, args) => ({ ...state, nodePodLogs: args.logs }),
24 | uploadStatusResponse: (state, args) => ({ ...state, nodeUploadStatuses: args.uploadStatuses }),
25 | },
26 | eventHandler: {
27 | setSelectedNode: (state, node) => ({ ...state, selectedNode: node, nodePodLogs: null }),
28 | },
29 | };
30 |
31 | export const vscode = getWebviewMessageContext<"periscope">({
32 | nodeLogsRequest: null,
33 | uploadStatusRequest: null,
34 | });
35 |
--------------------------------------------------------------------------------
/webview-ui/src/RetinaCapture/DeleteNodeExplorerDialog.tsx:
--------------------------------------------------------------------------------
1 | import { Dialog } from "../components/Dialog";
2 | import styles from "./RetinaCapture.module.css";
3 |
4 | export interface DeleteNodeExplorerDialogProps {
5 | isShown: boolean;
6 | nodes: string[];
7 | onCancel: () => void;
8 | onAccept: (nodeName: string) => void;
9 | }
10 |
11 | export function DeleteNodeExplorerDialog(props: DeleteNodeExplorerDialogProps) {
12 | function handleYes() {
13 | props.onAccept(props.nodes.join(","));
14 | }
15 |
16 | function handleNo() {
17 | props.onCancel();
18 | }
19 |
20 | return (
21 | props.onCancel()}>
22 | Delete Node Explorer
23 |
24 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/webview-ui/src/RetinaCapture/RetinaCapture.module.css:
--------------------------------------------------------------------------------
1 | .content {
2 | display: grid;
3 | grid-template-columns: 6rem 50rem;
4 | grid-gap: 1rem;
5 | align-items: center;
6 | }
7 |
8 | .inputContainer {
9 | display: grid;
10 | grid-template-columns: 8rem 20rem 8rem;
11 | grid-gap: 1rem;
12 | }
13 |
14 | .buttonContainer {
15 | margin-top: 1rem;
16 | display: flex;
17 | flex-direction: row;
18 | column-gap: 0.5rem;
19 | }
20 |
21 | .checkboxLabel {
22 | position: relative;
23 | top: -0.25rem;
24 | left: 0.2rem;
25 | }
26 |
27 | .checkboxLabel:hover {
28 | cursor: pointer;
29 | }
30 |
31 | .buttonDiv {
32 | margin-top: 1rem;
33 | display: flex;
34 | }
35 |
--------------------------------------------------------------------------------
/webview-ui/src/RetinaCapture/state.ts:
--------------------------------------------------------------------------------
1 | import { InitialState } from "../../../src/webview-contract/webviewDefinitions/retinaCapture";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 | import { getWebviewMessageContext } from "../utilities/vscode";
4 |
5 | export type EventDef = Record;
6 |
7 | export type RetinaState = InitialState;
8 |
9 | export const stateUpdater: WebviewStateUpdater<"retinaCapture", EventDef, RetinaState> = {
10 | createState: (initialState) => ({
11 | ...initialState,
12 | }),
13 | vscodeMessageHandler: {},
14 | eventHandler: {},
15 | };
16 |
17 | export const vscode = getWebviewMessageContext<"retinaCapture">({
18 | deleteRetinaNodeExplorer: undefined,
19 | handleCaptureFileDownload: undefined,
20 | });
21 |
--------------------------------------------------------------------------------
/webview-ui/src/TCPDump/TcpDump.module.css:
--------------------------------------------------------------------------------
1 | .content {
2 | display: grid;
3 | grid-template-columns: 6rem 50rem;
4 | grid-gap: 1rem;
5 | align-items: center;
6 | }
7 |
8 | .filterContent {
9 | margin-left: 1rem;
10 | display: grid;
11 | grid-template-columns: 8rem 47rem;
12 | grid-gap: 1rem;
13 | align-items: center;
14 | }
15 |
16 | .content > .label,
17 | .filterContent > .label {
18 | grid-column: 1 / 2;
19 | }
20 |
21 | .content > .control,
22 | .filterContent > .control {
23 | grid-column: 2 / 3;
24 | }
25 |
26 | .content > .numberControl,
27 | .filterContent > .numberControl {
28 | grid-column: 2 / 3;
29 | max-width: 4rem;
30 | }
31 |
32 | .content > .controlDropdown {
33 | grid-column: 2 / 3;
34 | max-width: 30rem;
35 | }
36 |
37 | .filterContent > .controlDropdown {
38 | grid-column: 2 / 3;
39 | max-width: 27rem;
40 | }
41 |
42 | .content > .controlButton,
43 | .filterContent > .controlButton {
44 | grid-column: 2 / 3;
45 | max-width: 8rem;
46 | }
47 |
48 | .content > .fullWidth,
49 | .filterContent > .fullWidth {
50 | grid-column: 1 / 3;
51 | }
52 |
53 | table.capturelist {
54 | border-collapse: collapse;
55 | width: 100%;
56 | }
57 |
58 | table.capturelist th {
59 | height: 50px;
60 | text-align: left;
61 | }
62 |
63 | table.capturelist td {
64 | padding: 5px;
65 | text-align: left;
66 | }
67 |
68 | button span {
69 | margin-right: 0.5rem;
70 | }
71 |
72 | .pcapFilterInput {
73 | width: 27rem;
74 | }
75 |
--------------------------------------------------------------------------------
/webview-ui/src/TCPDump/protocols.ts:
--------------------------------------------------------------------------------
1 | export const transportLayerProtocols = ["TCP", "UDP", "ICMP"];
2 | export type TransportLayerProtocol = (typeof transportLayerProtocols)[number];
3 |
4 | export const applicationLayerProtocols = ["HTTP", "HTTPS", "DNS"] as const;
5 | export type ApplicationLayerProtocol = (typeof applicationLayerProtocols)[number];
6 |
7 | export type ProtocolMapping = Record<
8 | ApplicationLayerProtocol,
9 | {
10 | port: number;
11 | protocol: TransportLayerProtocol | null;
12 | }
13 | >;
14 |
15 | export const protocolMapping: ProtocolMapping = {
16 | HTTP: { port: 80, protocol: "TCP" },
17 | HTTPS: { port: 443, protocol: "TCP" },
18 | DNS: { port: 53, protocol: null }, // TODO: Do we do like NSGs and use "DNS (TCP)" and "DNS (UDP)"?
19 | };
20 |
--------------------------------------------------------------------------------
/webview-ui/src/TCPDump/state/dataLoading.ts:
--------------------------------------------------------------------------------
1 | import { NodeName } from "../../../../src/webview-contract/webviewDefinitions/tcpDump";
2 | import { isNotLoaded } from "../../utilities/lazy";
3 | import { EventHandlers } from "../../utilities/state";
4 | import { EventDef, NodeReferenceData, NodeState, ReferenceData, vscode } from "../state";
5 |
6 | export type EventHandlerFunc = (eventHandlers: EventHandlers) => void;
7 |
8 | export function loadAllNodes(referenceData: ReferenceData, updates: EventHandlerFunc[]): void {
9 | if (isNotLoaded(referenceData.nodes)) {
10 | vscode.postGetAllNodes();
11 | updates.push((e) => e.onSetLoadingNodes());
12 | }
13 | }
14 |
15 | export function loadFilterPods(nodeReferenceData: NodeReferenceData, updates: EventHandlerFunc[]): void {
16 | if (isNotLoaded(nodeReferenceData.filterPods)) {
17 | vscode.postGetFilterPodsForNode({ node: nodeReferenceData.node });
18 | updates.push((e) => e.onSetLoadingFilterPods({ node: nodeReferenceData.node }));
19 | }
20 | }
21 |
22 | export function loadCaptureInterfaces(nodeState: NodeState, node: NodeName, updates: EventHandlerFunc[]): void {
23 | if (isNotLoaded(nodeState.captureInterfaces)) {
24 | vscode.postGetInterfaces({ node });
25 | updates.push((e) => e.onSetLoadingInterfaces({ node }));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/webview-ui/src/TCPDump/state/nodeReferenceDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { FilterPod } from "../../../../src/webview-contract/webviewDefinitions/tcpDump";
2 | import { newLoaded, newLoading } from "../../utilities/lazy";
3 | import { NodeReferenceData } from "../state";
4 |
5 | export function setFilterPodsLoading(data: NodeReferenceData): NodeReferenceData {
6 | return { ...data, filterPods: newLoading() };
7 | }
8 |
9 | export function updateFilterPods(data: NodeReferenceData, pods: FilterPod[]): NodeReferenceData {
10 | return {
11 | ...data,
12 | filterPods: newLoaded(pods),
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/webview-ui/src/TCPDump/state/referenceDataUpdate.ts:
--------------------------------------------------------------------------------
1 | import { FilterPod, NodeName } from "../../../../src/webview-contract/webviewDefinitions/tcpDump";
2 | import { replaceItem, updateValues } from "../../utilities/array";
3 | import { map as lazyMap, newLoaded, newLoading, newNotLoaded, orDefault } from "../../utilities/lazy";
4 | import { ReferenceData, NodeReferenceData } from "../state";
5 | import * as NodeReferenceDataUpdate from "./nodeReferenceDataUpdate";
6 |
7 | export function setNodesLoading(data: ReferenceData): ReferenceData {
8 | return { ...data, nodes: newLoading() };
9 | }
10 |
11 | export function updateNodes(data: ReferenceData, nodes: NodeName[]): ReferenceData {
12 | const existingNodes = orDefault(data.nodes, []);
13 | const updatedNodes = updateValues(
14 | existingNodes,
15 | nodes,
16 | (node, item) => node === item.node,
17 | (node) => ({
18 | node,
19 | filterPods: newNotLoaded(),
20 | }),
21 | );
22 |
23 | return {
24 | ...data,
25 | nodes: newLoaded(updatedNodes),
26 | };
27 | }
28 |
29 | export function setFilterPodsLoading(data: ReferenceData, node: NodeName): ReferenceData {
30 | return updateNode(data, node, (data) => NodeReferenceDataUpdate.setFilterPodsLoading(data));
31 | }
32 |
33 | export function updateFilterPods(data: ReferenceData, node: NodeName, pods: FilterPod[]): ReferenceData {
34 | return updateNode(data, node, (data) => NodeReferenceDataUpdate.updateFilterPods(data, pods));
35 | }
36 |
37 | function updateNode(
38 | data: ReferenceData,
39 | node: NodeName,
40 | updater: (data: NodeReferenceData) => NodeReferenceData,
41 | ): ReferenceData {
42 | return {
43 | ...data,
44 | nodes: lazyMap(data.nodes, (nodes) => replaceItem(nodes, (data) => data.node === node, updater)),
45 | };
46 | }
47 |
--------------------------------------------------------------------------------
/webview-ui/src/TestStyleViewer/state.ts:
--------------------------------------------------------------------------------
1 | import { CssRule, InitialState } from "../../../src/webview-contract/webviewDefinitions/testStyleViewer";
2 | import { WebviewStateUpdater } from "../utilities/state";
3 | import { getWebviewMessageContext } from "../utilities/vscode";
4 |
5 | export type State = InitialState & {
6 | cssVars: string[];
7 | cssRules: CssRule[];
8 | };
9 |
10 | export type EventDef = {
11 | cssVarsUpdate: string[];
12 | cssRulesUpdate: CssRule[];
13 | };
14 |
15 | export const stateUpdater: WebviewStateUpdater<"style", EventDef, State> = {
16 | createState: (initialState) => ({
17 | ...initialState,
18 | cssVars: [],
19 | cssRules: [],
20 | }),
21 | vscodeMessageHandler: {},
22 | eventHandler: {
23 | cssVarsUpdate: (state, cssVars) => ({ ...state, cssVars }),
24 | cssRulesUpdate: (state, cssRules) => ({ ...state, cssRules }),
25 | },
26 | };
27 |
28 | export const vscode = getWebviewMessageContext<"style">({
29 | reportCssRules: null,
30 | reportCssVars: null,
31 | });
32 |
--------------------------------------------------------------------------------
/webview-ui/src/components/CustomDropdownOption.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./CustomDropdown.module.css";
3 |
4 | export interface CustomDropdownOptionProps extends Omit, "onClick"> {
5 | id?: string;
6 | value: string;
7 | label: string;
8 | className?: string;
9 | onClick?: (value: string) => void;
10 | }
11 |
12 | // eslint-disable-next-line @typescript-eslint/naming-convention
13 | export const CustomDropdownOption: React.FC = ({ id, value, label, className, onClick }) => {
14 | const handleClick = () => {
15 | if (onClick) {
16 | onClick(value);
17 | }
18 | };
19 |
20 | return (
21 |
22 | {label}
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/webview-ui/src/components/Dialog.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 |
3 | interface DialogProps {
4 | isShown: boolean;
5 | onCancel: () => void;
6 | }
7 |
8 | export function Dialog(props: React.PropsWithChildren) {
9 | const dialogRef = useRef(null);
10 |
11 | useEffect(() => {
12 | document.body.addEventListener("click", handleDocumentClick);
13 | return () => document.removeEventListener("click", handleDocumentClick);
14 | });
15 |
16 | useEffect(() => {
17 | const elem = dialogRef.current;
18 | elem?.addEventListener("close", handleClose);
19 | return () => elem?.removeEventListener("close", handleClose);
20 | });
21 |
22 | useEffect(() => {
23 | if (props.isShown && !dialogRef.current!.hasAttribute("open")) {
24 | dialogRef.current!.showModal();
25 | } else if (!props.isShown && dialogRef.current!.hasAttribute("open")) {
26 | dialogRef.current!.close();
27 | }
28 | }, [props.isShown, dialogRef]);
29 |
30 | function handleClose() {
31 | if (props.isShown) {
32 | props.onCancel();
33 | }
34 | }
35 |
36 | function handleDocumentClick(e: MouseEvent) {
37 | if (e.target === dialogRef.current) {
38 | e.preventDefault();
39 | e.stopPropagation();
40 | dialogRef.current!.close();
41 | }
42 | }
43 |
44 | return {props.children} ;
45 | }
46 |
--------------------------------------------------------------------------------
/webview-ui/src/components/InlineAction.module.css:
--------------------------------------------------------------------------------
1 | .actionItem {
2 | display: grid;
3 | grid-template-columns: auto auto;
4 | justify-content: stretch;
5 | }
6 |
7 | .actionItem .actionDescription {
8 | grid-column: 1 / 2;
9 | }
10 |
11 | .actionItem .actionButtons {
12 | grid-column: 2 / 3;
13 | display: flex;
14 | flex-direction: row;
15 | justify-content: flex-end;
16 | align-items: center;
17 | gap: 0.5rem;
18 | }
19 |
20 | .successIndicator {
21 | color: var(--vscode-testing-iconPassed);
22 | }
23 |
24 | .inlineIcon {
25 | vertical-align: middle;
26 | margin-left: 0.3rem;
27 | margin-right: 0.3rem;
28 | }
29 |
--------------------------------------------------------------------------------
/webview-ui/src/components/NodeSelector.tsx:
--------------------------------------------------------------------------------
1 | import { CustomDropdown } from "./CustomDropdown";
2 | import { CustomDropdownOption } from "./CustomDropdownOption";
3 | import { useState } from "react";
4 |
5 | export interface NodeSelectorProps {
6 | nodes: string[];
7 | id: string;
8 | className?: string;
9 | required?: boolean;
10 | onNodeChanged: (node: string | null) => void;
11 | }
12 |
13 | export function NodeSelector(props: NodeSelectorProps) {
14 | const [selectedNode, setSelectedNode] = useState("");
15 |
16 | function handleNodeChange(node: string) {
17 | setSelectedNode(node);
18 | props.onNodeChanged(node);
19 | }
20 |
21 | return (
22 |
23 |
24 | {props.nodes.map((node) => (
25 |
26 | ))}
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/webview-ui/src/components/ProgressRing.module.css:
--------------------------------------------------------------------------------
1 | .spinner {
2 | border: 3px solid var(--vscode-progressBar-background);
3 | border-top: 3px solid transparent;
4 | border-left: 3px solid transparent;
5 | border-radius: 50%;
6 | animation: spin 0.6s linear infinite;
7 | }
8 |
9 | @keyframes spin {
10 | to {
11 | transform: rotate(360deg);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/webview-ui/src/components/ProgressRing.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from "./ProgressRing.module.css";
3 |
4 | interface ProgressRingProps {
5 | size?: number;
6 | className?: string;
7 | }
8 | // eslint-disable-next-line @typescript-eslint/naming-convention
9 | export const ProgressRing: React.FC = ({ size = "1rem", className }) => {
10 | return
;
11 | };
12 |
--------------------------------------------------------------------------------
/webview-ui/src/components/TextWithDropdown.module.css:
--------------------------------------------------------------------------------
1 | .indicator {
2 | cursor: pointer;
3 | margin-right: -2px;
4 | }
5 |
6 | .listbox {
7 | position: absolute;
8 | width: 100%;
9 | margin: 0;
10 | background: var(--vscode-dropdown-background);
11 | border: 1px solid var(--vscode-focusBorder);
12 | border-radius: 2px;
13 | box-sizing: border-box;
14 | display: inline-flex;
15 | flex-direction: column;
16 | left: 0px;
17 | max-height: 200px;
18 | padding: 0px;
19 | overflow-y: auto;
20 | z-index: 1;
21 | }
22 |
23 | .listbox.hidden {
24 | display: none;
25 | }
26 |
27 | .listboxItem {
28 | flex-shrink: 0;
29 | cursor: pointer;
30 | padding: 2px;
31 | }
32 |
33 | .listboxItem:hover {
34 | background: var(--vscode-list-activeSelectionBackground);
35 | }
36 |
37 | .listboxItem.selected {
38 | background: var(--vscode-list-activeSelectionBackground);
39 | }
40 |
41 | .inputField {
42 | position: relative;
43 | width: 100%;
44 | }
45 |
46 | .selectedValue {
47 | width: 100%;
48 | padding-right: 1.8rem !important;
49 | box-sizing: border-box;
50 | }
51 | .selectedValue:hover {
52 | cursor: pointer;
53 | }
54 |
55 | .dropDownButton {
56 | position: absolute;
57 | right: 8px;
58 | top: 50%;
59 | transform: translateY(-50%);
60 | pointer-events: none;
61 | }
62 |
--------------------------------------------------------------------------------
/webview-ui/src/icons/ArrowIcon.tsx:
--------------------------------------------------------------------------------
1 | import { SvgProps } from "./svgProps";
2 |
3 | export function ArrowIcon(props: SvgProps) {
4 | return (
5 |
6 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/webview-ui/src/icons/svgProps.ts:
--------------------------------------------------------------------------------
1 | export type SvgProps = {
2 | style?: React.CSSProperties;
3 | className?: string;
4 | };
5 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/TestScenarioSelector/TestScenarioSelector.module.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | height: 100%;
3 | width: 15rem;
4 | position: fixed;
5 | padding: 1rem 0 0 0;
6 | margin: 0;
7 | top: 0;
8 | left: 0;
9 | overflow-x: hidden;
10 | box-sizing: border-box;
11 | }
12 |
13 | .contentLink {
14 | padding: 0.2rem 1rem;
15 | text-decoration: none;
16 | display: block;
17 | }
18 |
19 | .contentLink:hover {
20 | background-color: var(--vscode-list-hoverBackground);
21 | color: var(--vscode-list-hoverForeground);
22 | }
23 |
24 | .contentLink.selected {
25 | background-color: var(--vscode-list-activeSelectionBackground);
26 | color: var(--vscode-list-activeSelectionForeground);
27 | }
28 |
29 | .main {
30 | margin-left: 15rem;
31 | }
32 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/components/FilePicker.module.css:
--------------------------------------------------------------------------------
1 | ul.nodeList {
2 | list-style-type: none;
3 | padding: 0;
4 | padding-left: 0;
5 | margin: 0;
6 | }
7 |
8 | ul.nodeList ul.nodeList {
9 | padding-left: 1rem;
10 | border-left: 1px solid var(--vscode-menu-border);
11 | }
12 |
13 | ul.nodeList > li {
14 | margin: 0.2rem;
15 | }
16 |
17 | ul.nodeList > li > .item {
18 | padding: 0.2rem;
19 | }
20 |
21 | ul.nodeList > li > .item.selected {
22 | background-color: var(--vscode-list-activeSelectionBackground);
23 | color: var(--vscode-list-activeSelectionForeground);
24 | }
25 |
26 | ul.nodeList > li > .item:hover {
27 | cursor: pointer;
28 | background-color: var(--vscode-list-hoverBackground);
29 | color: var(--vscode-list-hoverForeground);
30 | }
31 |
32 | .expander {
33 | cursor: pointer;
34 | padding-right: 0.3rem;
35 | }
36 |
37 | .inputContainer {
38 | display: grid;
39 | grid-template-columns: auto auto;
40 | grid-auto-rows: min-content;
41 | grid-gap: 1rem;
42 | padding-bottom: 0.5rem;
43 | padding-top: 0.5rem;
44 | }
45 |
46 | .inputContainer .label {
47 | grid-column: 1 / 2;
48 | }
49 |
50 | .inputContainer .control {
51 | grid-column: 2 / 3;
52 | }
53 |
54 | .buttonContainer {
55 | margin-top: 1rem;
56 | display: flex;
57 | flex-direction: row;
58 | column-gap: 0.5rem;
59 | }
60 |
61 | .title {
62 | margin: 0rem 0 0.5rem 0;
63 | }
64 |
65 | .itemSpan {
66 | position: relative;
67 | top: -0.1rem;
68 | }
69 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/draft/index.ts:
--------------------------------------------------------------------------------
1 | import { getDraftDeploymentScenarios } from "./draftDeploymentTests";
2 | import { getDraftDockerfileScenarios } from "./draftDockerfileTests";
3 | import { getDraftWorkflowScenarios } from "./draftWorkflowTests";
4 |
5 | export { getDraftDeploymentScenarios, getDraftDockerfileScenarios, getDraftWorkflowScenarios };
6 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/draft/testData/gitHubData.ts:
--------------------------------------------------------------------------------
1 | export type GitHubRepoData = {
2 | forkName: string;
3 | ownerName: string;
4 | repoName: string;
5 | isFork: boolean;
6 | defaultBranch: string;
7 | branches: string[];
8 | };
9 |
10 | export function getGitHubRepoData(): GitHubRepoData[] {
11 | return [
12 | {
13 | forkName: "upstream",
14 | ownerName: "Contoso",
15 | repoName: "aks-store-demo",
16 | isFork: false,
17 | defaultBranch: "main",
18 | branches: ["main", "feature1", "feature2"],
19 | },
20 | {
21 | forkName: "origin",
22 | ownerName: "developer",
23 | repoName: "aks-store-demo",
24 | isFork: true,
25 | defaultBranch: "main",
26 | branches: ["main", "feature1", "feature2"],
27 | },
28 | {
29 | forkName: "other-remote",
30 | ownerName: "otherdev",
31 | repoName: "aks-store-demo",
32 | isFork: true,
33 | defaultBranch: "main",
34 | branches: ["main", "feature1", "feature2"],
35 | },
36 | ];
37 | }
38 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/kaitoTestTests.tsx:
--------------------------------------------------------------------------------
1 | import { MessageHandler, MessageSink } from "../../../src/webview-contract/messaging";
2 | import {
3 | InitialState,
4 | ToVsCodeMsgDef,
5 | ToWebViewMsgDef,
6 | } from "../../../src/webview-contract/webviewDefinitions/kaitoTest";
7 | import { KaitoTest } from "../KaitoTest/KaitoTest";
8 | import { stateUpdater } from "../KaitoTest/state";
9 | import { Scenario } from "../utilities/manualTest";
10 |
11 | export function getKaitoTestScenarios() {
12 | const initialState: InitialState = {
13 | clusterName: "ai-service",
14 | modelName: "falcon-7b-instruct",
15 | output: "",
16 | };
17 |
18 | function getMessageHandler(webview: MessageSink): MessageHandler {
19 | return {
20 | queryRequest: (params) => {
21 | console.log("queryRequest", params);
22 | webview.postTestUpdate({
23 | clusterName: initialState.clusterName,
24 | modelName: initialState.modelName,
25 | output: "What is the meaning of life?\n\nThe meaning of life is to be happy. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim i",
26 | });
27 | },
28 | };
29 | }
30 |
31 | return [
32 | Scenario.create(
33 | "kaitoTest",
34 | "Test Page",
35 | () => ,
36 | getMessageHandler,
37 | stateUpdater.vscodeMessageHandler,
38 | ),
39 | ];
40 | }
41 |
--------------------------------------------------------------------------------
/webview-ui/src/manualTest/testStyleViewerTests.tsx:
--------------------------------------------------------------------------------
1 | import { MessageHandler } from "../../../src/webview-contract/messaging";
2 | import {
3 | CssRule,
4 | InitialState,
5 | ToVsCodeMsgDef,
6 | } from "../../../src/webview-contract/webviewDefinitions/testStyleViewer";
7 | import { Scenario } from "./../utilities/manualTest";
8 | import { TestStyleViewer } from "./../TestStyleViewer/TestStyleViewer";
9 | import { stateUpdater } from "../TestStyleViewer/state";
10 |
11 | export function getTestStyleViewerScenarios() {
12 | const messageHandler: MessageHandler = {
13 | reportCssRules: (args) => handleReportCssRules(args.rules),
14 | reportCssVars: (args) => handleReportCssVars(args.cssVars),
15 | };
16 |
17 | function handleReportCssVars(cssVars: string[]) {
18 | console.log(cssVars.join("\n"));
19 | }
20 |
21 | function handleReportCssRules(rules: CssRule[]) {
22 | console.log(rules.map((r) => r.text).join("\n"));
23 | }
24 |
25 | const initialState: InitialState = {
26 | isVSCode: false,
27 | };
28 |
29 | return [
30 | Scenario.create(
31 | "style",
32 | "",
33 | () => ,
34 | () => messageHandler,
35 | stateUpdater.vscodeMessageHandler,
36 | ),
37 | ];
38 | }
39 |
--------------------------------------------------------------------------------
/webview-ui/src/utilities/lazy.ts:
--------------------------------------------------------------------------------
1 | enum LoadingState {
2 | NotLoaded,
3 | Loading,
4 | Loaded,
5 | }
6 |
7 | export type NotLoaded = {
8 | loadingState: LoadingState.NotLoaded;
9 | };
10 |
11 | export type Loading = {
12 | loadingState: LoadingState.Loading;
13 | };
14 |
15 | export type Loaded = {
16 | loadingState: LoadingState.Loaded;
17 | value: T;
18 | };
19 |
20 | export type Lazy = NotLoaded | Loading | Loaded;
21 |
22 | export function isNotLoaded(l: Lazy): l is NotLoaded {
23 | return l.loadingState === LoadingState.NotLoaded;
24 | }
25 |
26 | export function isLoading(l: Lazy): l is Loading {
27 | return l.loadingState === LoadingState.Loading;
28 | }
29 |
30 | export function isLoaded(l: Lazy): l is Loaded {
31 | return l.loadingState === LoadingState.Loaded;
32 | }
33 |
34 | export function newNotLoaded(): NotLoaded {
35 | return { loadingState: LoadingState.NotLoaded };
36 | }
37 |
38 | export function newLoading(): Loading {
39 | return { loadingState: LoadingState.Loading };
40 | }
41 |
42 | export function newLoaded(value: T): Loaded {
43 | return { loadingState: LoadingState.Loaded, value };
44 | }
45 |
46 | export function isLazy(value: T | Lazy): value is Lazy {
47 | return value instanceof Object && "loadingState" in value;
48 | }
49 |
50 | export function asLazy(item: Lazy | T): Lazy {
51 | if (isLazy(item)) {
52 | return item;
53 | }
54 |
55 | return newLoaded(item);
56 | }
57 |
58 | export function orDefault(lazy: Lazy, fallback: T): T {
59 | return isLoaded(lazy) ? lazy.value : fallback;
60 | }
61 |
62 | export function map(l: Lazy, fn: (f: T1) => T2): Lazy {
63 | if (!isLoaded(l)) {
64 | return l;
65 | }
66 |
67 | return newLoaded(fn(l.value));
68 | }
69 |
--------------------------------------------------------------------------------
/webview-ui/src/utilities/manualTest.ts:
--------------------------------------------------------------------------------
1 | import { JSX } from "react";
2 | import { CommandKeys, MessageHandler, MessageSink } from "../../../src/webview-contract/messaging";
3 | import { ContentId, ToVsCodeMsgDef, ToWebviewMsgDef } from "../../../src/webview-contract/webviewTypes";
4 | import { getTestVscodeMessageContext } from "./vscode";
5 |
6 | /**
7 | * Represents scenarios for manual testing webviews in a browser.
8 | *
9 | * The same Webview can be set up with different initial data or message handlers.
10 | */
11 | export class Scenario {
12 | private constructor(
13 | readonly name: string,
14 | readonly factory: () => JSX.Element,
15 | ) {}
16 |
17 | static create(
18 | contentId: T,
19 | description: string,
20 | factory: () => JSX.Element,
21 | getHandler: (webview: MessageSink>) => MessageHandler>,
22 | cmdKeys: CommandKeys>,
23 | ): Scenario {
24 | const name = description ? `${contentId} (${description})` : contentId;
25 | return new Scenario(name, () => {
26 | const context = getTestVscodeMessageContext(cmdKeys);
27 | // Set up the subscription before creating the element
28 | context.subscribeToMessages(getHandler(context));
29 | return factory();
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/webview-ui/src/utilities/maybe.ts:
--------------------------------------------------------------------------------
1 | export type Just = {
2 | hasValue: true;
3 | value: T;
4 | };
5 |
6 | export type Nothing = {
7 | hasValue: false;
8 | };
9 |
10 | export type Maybe = Just | Nothing;
11 |
12 | export function just(value: T): Just {
13 | return {
14 | hasValue: true,
15 | value,
16 | };
17 | }
18 |
19 | export function nothing(): Nothing {
20 | return { hasValue: false };
21 | }
22 |
23 | export function hasValue(m: Maybe): m is Just {
24 | return m.hasValue;
25 | }
26 |
27 | export function isNothing(m: Maybe): m is Nothing {
28 | return !m.hasValue;
29 | }
30 |
31 | export function map(m: Maybe, fn: (value: T) => U): Maybe {
32 | return hasValue(m) ? just(fn(m.value)) : nothing();
33 | }
34 |
35 | export function orDefault(m: Maybe, fallback: T): T {
36 | return hasValue(m) ? m.value : fallback;
37 | }
38 |
39 | export function asNullable(m: Maybe): T | null {
40 | return m.hasValue ? m.value : null;
41 | }
42 |
--------------------------------------------------------------------------------
/webview-ui/src/utilities/runtimeTypes.ts:
--------------------------------------------------------------------------------
1 | export function isObject(value: unknown): value is object {
2 | return typeof value === "object" && value !== null && value.constructor.name === "Object";
3 | }
4 |
5 | export function isArray(value: unknown): value is unknown[] {
6 | return typeof value === "object" && value !== null && value.constructor.name === "Array";
7 | }
8 |
--------------------------------------------------------------------------------
/webview-ui/src/utilities/time.ts:
--------------------------------------------------------------------------------
1 | export async function delay(ms: number): Promise {
2 | return new Promise((resolve) => setTimeout(resolve, ms));
3 | }
4 |
--------------------------------------------------------------------------------
/webview-ui/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/webview-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ES2022"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ES2022",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/webview-ui/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/webview-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 | import checker from "vite-plugin-checker";
4 | import eslintPlugin from "vite-plugin-eslint";
5 | import { resolve } from "path";
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | react(),
11 | eslintPlugin({
12 | cache: false,
13 | include: ["./src/**/*.ts", "./src/**/*.tsx"],
14 | exclude: [],
15 | }),
16 | // By default, vite doesn't output typescript errors.
17 | // This plugin causes errors to be printed to stdout/stderr, meaning they
18 | // can be picked up by the problem matcher defined in .vscode/tasks.json.
19 | // https://github.com/vitejs/vite/issues/4393#issuecomment-890996317
20 | checker({ typescript: true }),
21 | ],
22 | base: "./",
23 | server: {
24 | port: 3000,
25 | },
26 | build: {
27 | rollupOptions: {
28 | input: {
29 | main: resolve(__dirname, "src/main.tsx"),
30 | },
31 | output: {
32 | entryFileNames: `assets/[name].js`,
33 | chunkFileNames: `assets/[name].js`,
34 | assetFileNames: `assets/[name].[ext]`,
35 | },
36 | },
37 | },
38 | });
39 |
--------------------------------------------------------------------------------