diff --git a/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx b/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx
new file mode 100644
index 00000000..3f70ee12
--- /dev/null
+++ b/website/src/components/PlaygroundFeatures/FillRemainingHeight.tsx
@@ -0,0 +1,32 @@
+import { useLayoutEffect, useState } from "react";
+// On mount, measures the window height and current vertical offset, and
+// renders children into a div that stretches to the bottom of the viewport.
+export default function FillRemainingHeight({ children, minHeight }) {
+ const [containerRef, setContainerRef] = useState(null);
+ const [height, setHeight] = useState(null);
+ useLayoutEffect(() => {
+ if (containerRef == null) {
+ return;
+ }
+
+ function updateSize() {
+ const verticalOffset = containerRef.getBoundingClientRect().y;
+ const available = Math.max(
+ window.innerHeight - verticalOffset,
+ minHeight,
+ );
+ setHeight(available);
+ }
+
+ updateSize();
+
+ window.addEventListener("resize", updateSize);
+ return () => window.removeEventListener("resize", updateSize);
+ }, [containerRef, minHeight]);
+
+ return (
+
+ {height != null && children}
+
+ );
+}
diff --git a/website/src/components/PlaygroundFeatures/defaultState.ts b/website/src/components/PlaygroundFeatures/defaultState.ts
index cbe4894c..767de741 100644
--- a/website/src/components/PlaygroundFeatures/defaultState.ts
+++ b/website/src/components/PlaygroundFeatures/defaultState.ts
@@ -42,6 +42,7 @@ export const DEFAULT_STATE: State = {
outputOption: "sdl",
showGratsDirectives: false,
},
+ ts: null,
gratsResult: null,
VERSION: URL_VERSION,
};
diff --git a/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx b/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx
deleted file mode 100644
index 2b5ca922..00000000
--- a/website/src/components/PlaygroundFeatures/editors/CodegenOutputView.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { EditorState } from "@codemirror/state";
-import { EditorView, keymap, lineNumbers } from "@codemirror/view";
-import { defaultKeymap } from "@codemirror/commands";
-import { typescriptLanguage } from "@codemirror/lang-javascript";
-import {
- defaultHighlightStyle,
- syntaxHighlighting,
-} from "@codemirror/language";
-import { getTypeScriptOutputString, onSelectorChange } from "../store";
-import { Theme } from "./theme";
-import store from "../store";
-
-export default function OutputView() {
- const [ref, setRef] = useState(null);
- useEffect(() => {
- if (ref != null) {
- createOutputView(store, ref);
- }
- }, [ref]);
- return (
-
- );
-}
-
-export function createOutputView(store, right: HTMLDivElement) {
- const outputState = EditorState.create({
- doc: getTypeScriptOutputString(store.getState()),
- extensions: [
- Theme,
- keymap.of(defaultKeymap),
- lineNumbers(),
- typescriptLanguage,
- EditorState.readOnly.of(true),
- EditorView.lineWrapping,
- syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
- ],
- });
-
- const rightView = new EditorView({
- state: outputState,
- parent: right,
- });
-
- // When the output changes, update the output view
- onSelectorChange(store, getTypeScriptOutputString, (output) => {
- rightView.dispatch({
- changes: {
- from: 0,
- to: rightView.state.doc.length,
- insert: output,
- },
- });
- });
-}
diff --git a/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx b/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx
deleted file mode 100644
index 20079b3b..00000000
--- a/website/src/components/PlaygroundFeatures/editors/GraphQLOutputView.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { EditorState } from "@codemirror/state";
-import { EditorView, keymap, lineNumbers } from "@codemirror/view";
-import { defaultKeymap } from "@codemirror/commands";
-import { graphqlLanguage } from "cm6-graphql";
-import {
- defaultHighlightStyle,
- syntaxHighlighting,
-} from "@codemirror/language";
-import { getGraphQLOutputString, onSelectorChange } from "../store";
-import { Theme } from "./theme";
-import store from "../store";
-
-export default function OutputView() {
- const [ref, setRef] = useState(null);
- useEffect(() => {
- if (ref != null) {
- createOutputView(store, ref);
- }
- }, [ref]);
- return (
-
- );
-}
-
-export function createOutputView(store, right: HTMLDivElement) {
- const outputState = EditorState.create({
- doc: getGraphQLOutputString(store.getState()),
- extensions: [
- Theme,
- keymap.of(defaultKeymap),
- lineNumbers(),
- graphqlLanguage,
- EditorState.readOnly.of(true),
- EditorView.lineWrapping,
- syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
- ],
- });
-
- const rightView = new EditorView({
- state: outputState,
- parent: right,
- });
-
- // When the output changes, update the output view
- onSelectorChange(store, getGraphQLOutputString, (output) => {
- rightView.dispatch({
- changes: {
- from: 0,
- to: rightView.state.doc.length,
- insert: output,
- },
- });
- });
-}
diff --git a/website/src/components/PlaygroundFeatures/editors/InputView.tsx b/website/src/components/PlaygroundFeatures/editors/InputView.tsx
deleted file mode 100644
index 0350addc..00000000
--- a/website/src/components/PlaygroundFeatures/editors/InputView.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import React, { useState, useEffect } from "react";
-import { EditorState, Compartment } from "@codemirror/state";
-import { EditorView, keymap, lineNumbers } from "@codemirror/view";
-import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
-import { typescriptLanguage } from "@codemirror/lang-javascript";
-import * as ts from "typescript";
-import {
- defaultHighlightStyle,
- syntaxHighlighting,
-} from "@codemirror/language";
-import { createLinter } from "../linter";
-import lzstring from "lz-string";
-import { createDefaultMapFromCDN } from "@typescript/vfs";
-import { getConfig, getDoc, getView, onSelectorChange } from "../store";
-import { createSelector } from "reselect";
-import { Theme } from "./theme";
-import store, { useUrlState } from "../store";
-import { GratsConfig } from "grats";
-
-export default function InputView() {
- useEffect(() => {
- store.dispatch({ type: "SET_STATE_FROM_URL" });
- }, []);
- useUrlState(store);
- const [ref, setRef] = useState
(null);
- const fsMap = useFsMap();
- useEffect(() => {
- if (ref != null && fsMap != null) {
- createInputView(store, fsMap, ref);
- }
- }, [ref, fsMap]);
- return (
-
- );
-}
-
-function useFsMap() {
- const [fsMap, setFsMap] = useState
+ }
+ >
+