diff --git a/common/changes/@typespec/html-program-viewer/feature-themable-html-program-viewer_2023-11-09-20-22.json b/common/changes/@typespec/html-program-viewer/feature-themable-html-program-viewer_2023-11-09-20-22.json
new file mode 100644
index 0000000000..076b3e344f
--- /dev/null
+++ b/common/changes/@typespec/html-program-viewer/feature-themable-html-program-viewer_2023-11-09-20-22.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@typespec/html-program-viewer",
+ "comment": "Add ability to change colors of the program viewer using css variables. A `ColorProvider` component is also provided for convenience.",
+ "type": "none"
+ }
+ ],
+ "packageName": "@typespec/html-program-viewer"
+}
diff --git a/common/changes/@typespec/playground/feature-themable-html-program-viewer_2023-11-09-20-22.json b/common/changes/@typespec/playground/feature-themable-html-program-viewer_2023-11-09-20-22.json
new file mode 100644
index 0000000000..3007c546a8
--- /dev/null
+++ b/common/changes/@typespec/playground/feature-themable-html-program-viewer_2023-11-09-20-22.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@typespec/playground",
+ "comment": "Configure the program viewer to respect the color theme",
+ "type": "none"
+ }
+ ],
+ "packageName": "@typespec/playground"
+}
\ No newline at end of file
diff --git a/packages/html-program-viewer/src/color-provider.tsx b/packages/html-program-viewer/src/color-provider.tsx
new file mode 100644
index 0000000000..8cc881cd8f
--- /dev/null
+++ b/packages/html-program-viewer/src/color-provider.tsx
@@ -0,0 +1,19 @@
+import { FunctionComponent, ReactNode, useMemo } from "react";
+import { ColorPalette, ColorVariable, ColorsVariables } from "./constants.js";
+
+export interface ColorProviderProps {
+ colors: Partial;
+ children?: ReactNode;
+}
+
+export const ColorProvider: FunctionComponent = ({ children, colors }) => {
+ const cssVariables = useMemo(() => {
+ const colorArray: [ColorVariable, string][] = Object.entries(colors) as any;
+ return Object.fromEntries(
+ colorArray.map(([key, value]) => {
+ return [ColorsVariables[key], value];
+ })
+ );
+ }, [colors]);
+ return {children}
;
+};
diff --git a/packages/html-program-viewer/src/common.tsx b/packages/html-program-viewer/src/common.tsx
index a3f86fa4f2..b0c5cc8f6d 100644
--- a/packages/html-program-viewer/src/common.tsx
+++ b/packages/html-program-viewer/src/common.tsx
@@ -1,46 +1,8 @@
-import { css } from "@emotion/react";
-import { FunctionComponent, PropsWithChildren, ReactElement } from "react";
+import { FunctionComponent, ReactElement } from "react";
import { Colors } from "./constants.js";
-export interface ItemProps {
- title: string;
- id?: string;
- hide?: boolean;
-}
-
-const ItemStyles = css({
- border: "1px solid #c5c5c5",
-});
-
-const ItemTitleStyles = css({
- border: "1px solid #c5c5c5",
- backgroundColor: "#dedede",
- padding: "2px 5px",
-});
-
-const ItemContentStyles = css({
- padding: "1rem",
-});
-
-export const Item: FunctionComponent> = ({
- id,
- title,
- hide,
- children,
-}) => {
- if (hide) {
- return ;
- }
- return (
-
- );
-};
-
export const Literal: FunctionComponent<{ children: any }> = ({ children }) => (
- {children}
+ {children}
);
export const KeyValueSection: FunctionComponent<{ children: ReactElement | ReactElement[] }> = ({
diff --git a/packages/html-program-viewer/src/constants.ts b/packages/html-program-viewer/src/constants.ts
index 220c9e5bda..d2c9d87f85 100644
--- a/packages/html-program-viewer/src/constants.ts
+++ b/packages/html-program-viewer/src/constants.ts
@@ -1,4 +1,28 @@
-export const Colors = {
- typeKind: "#7a3e9d",
+const variablePrefix = `--tsp-tgv`;
+export const ColorsVariables = {
+ background: `${variablePrefix}-background`,
+ dataKey: `${variablePrefix}-data-key`,
+ indentationGuide: `${variablePrefix}-indentation-guide`,
+ literal: `${variablePrefix}-literal`,
+ property: `${variablePrefix}-property`,
+ ref: `${variablePrefix}-ref`,
+ typeKind: `${variablePrefix}-type-kind`,
+ typeName: `${variablePrefix}-type-name`,
+} as const;
+
+export type ColorVariable = keyof typeof ColorsVariables;
+export type ColorPalette = Record;
+export const DefaultColors: ColorPalette = {
+ background: "#f3f3f3",
+ dataKey: "#333333",
indentationGuide: "#777",
+ literal: "#5da713",
+ property: "#9c5d27",
+ ref: "#268bd2",
+ typeKind: "#7a3e9d",
+ typeName: "#333333",
};
+
+export const Colors: typeof ColorsVariables = Object.fromEntries(
+ Object.entries(ColorsVariables).map(([k, v]) => [k, `var(${v}, ${(DefaultColors as any)[k]})`])
+) as any;
diff --git a/packages/html-program-viewer/src/index.ts b/packages/html-program-viewer/src/index.ts
index 1789ee445b..dd9bb34234 100644
--- a/packages/html-program-viewer/src/index.ts
+++ b/packages/html-program-viewer/src/index.ts
@@ -1,2 +1,4 @@
+export { ColorProvider, ColorProviderProps } from "./color-provider.js";
+export type { ColorPalette } from "./constants.js";
export * from "./emitter.js";
export { TypeSpecProgramViewer } from "./ui.js";
diff --git a/packages/html-program-viewer/src/type-ui-base.tsx b/packages/html-program-viewer/src/type-ui-base.tsx
index 2ec0b8ac90..7b54fed8f6 100644
--- a/packages/html-program-viewer/src/type-ui-base.tsx
+++ b/packages/html-program-viewer/src/type-ui-base.tsx
@@ -24,7 +24,7 @@ export interface TypeUIBaseProps {
const TypeNameStyles = css({
display: "inline",
- color: "#333333",
+ color: Colors.typeName,
});
export const TypeUIBase: FunctionComponent = (props) => {
@@ -32,7 +32,7 @@ export const TypeUIBase: FunctionComponent = (props) => {
const properties = props.properties.map((prop) => {
return (
-
+
{prop.name}
: {prop.value}
diff --git a/packages/html-program-viewer/src/ui.tsx b/packages/html-program-viewer/src/ui.tsx
index 40097b138c..e1fcf10490 100644
--- a/packages/html-program-viewer/src/ui.tsx
+++ b/packages/html-program-viewer/src/ui.tsx
@@ -18,6 +18,7 @@ import {
import React, { FunctionComponent, ReactElement, useContext } from "react";
import ReactDOMServer from "react-dom/server";
import { KeyValueSection, Literal } from "./common.js";
+import { Colors } from "./constants.js";
import { inspect } from "./inspect.js";
import { TypeUIBase, TypeUIBaseProperty } from "./type-ui-base.js";
import { getIdForType, isNamedUnion } from "./utils.js";
@@ -39,7 +40,7 @@ export interface TypeSpecProgramViewerProps {
const ProgramViewerStyles = css({
fontFamily: "monospace",
- backgroundColor: "#f3f3f3",
+ backgroundColor: Colors.background,
li: {
margin: 0,
listStyle: "none",
@@ -347,7 +348,7 @@ const NamedTypeRef: FunctionComponent<{ type: NamedType }> = ({ type }) => {
return (
= ({ type }) => {
{entries.map(([k, v], i) => (
-
{k.toString()}:
{" "}
+
{k.toString()}:
{" "}
{inspect(v)}
))}
diff --git a/packages/playground/src/react/output-view.tsx b/packages/playground/src/react/output-view.tsx
index 144b21a224..76955dd503 100644
--- a/packages/playground/src/react/output-view.tsx
+++ b/packages/playground/src/react/output-view.tsx
@@ -1,7 +1,7 @@
import { css } from "@emotion/react";
import { tokens } from "@fluentui/react-components";
import { Diagnostic, Program } from "@typespec/compiler";
-import { TypeSpecProgramViewer } from "@typespec/html-program-viewer";
+import { ColorPalette, ColorProvider, TypeSpecProgramViewer } from "@typespec/html-program-viewer";
import { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
import { ErrorTab, InternalCompilerError } from "./error-tab.js";
import { FileOutput } from "./file-output.js";
@@ -159,7 +159,7 @@ const OutputContent: FunctionComponent = ({
overflow: "scroll",
}}
>
- {program && }
+ {program && }
);
}
@@ -185,3 +185,25 @@ const ErrorTabCountStyles = css({
padding: "0 5px",
borderRadius: "20px",
});
+
+interface TypeGraphViewerProps {
+ program: Program;
+}
+const TypeGraphViewer = ({ program }: TypeGraphViewerProps) => {
+ return (
+
+
+
+ );
+};
+
+const TypeGraphColors: ColorPalette = {
+ background: tokens.colorNeutralBackground1,
+ typeKind: tokens.colorPaletteBerryForeground2,
+ typeName: tokens.colorNeutralForeground2,
+ dataKey: tokens.colorNeutralForeground2,
+ ref: tokens.colorBrandForeground1,
+ literal: tokens.colorPaletteLightGreenForeground2,
+ indentationGuide: tokens.colorNeutralForeground4,
+ property: tokens.colorPaletteMarigoldForeground2,
+};