@typespec/playground 0.1.0-alpha.3-dev.7 → 0.1.0-alpha.3-dev.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/index.css +58 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.js +867 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/viewers.js +29 -0
- package/dist/react/viewers.js.map +1 -0
- package/dist/src/browser-host.d.ts +4 -4
- package/dist/src/browser-host.d.ts.map +1 -1
- package/dist/src/core.d.ts +12 -2
- package/dist/src/core.d.ts.map +1 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/react/editor-command-bar.d.ts +2 -2
- package/dist/src/react/editor-command-bar.d.ts.map +1 -1
- package/dist/src/react/editor.d.ts +4 -0
- package/dist/src/react/editor.d.ts.map +1 -1
- package/dist/src/react/playground.d.ts +1 -3
- package/dist/src/react/playground.d.ts.map +1 -1
- package/dist/src/react/settings/compiler-settings.d.ts +1 -1
- package/dist/src/react/settings/compiler-settings.d.ts.map +1 -1
- package/dist/src/react/settings/linter-form.d.ts +1 -1
- package/dist/src/react/settings/linter-form.d.ts.map +1 -1
- package/dist/src/react/standalone.d.ts +2 -0
- package/dist/src/react/standalone.d.ts.map +1 -1
- package/dist/src/react/types.d.ts +1 -6
- package/dist/src/react/types.d.ts.map +1 -1
- package/dist/src/react/typespec-editor.d.ts +3 -3
- package/dist/src/react/typespec-editor.d.ts.map +1 -1
- package/dist/src/react/viewers.d.ts +1 -0
- package/dist/src/react/viewers.d.ts.map +1 -1
- package/dist/src/services.d.ts +1 -1
- package/dist/src/services.d.ts.map +1 -1
- package/dist/src/types.d.ts +10 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/vite/index.d.ts.map +1 -1
- package/dist/src/vite/types.d.ts +4 -0
- package/dist/src/vite/types.d.ts.map +1 -1
- package/dist/{src/services.js → state-storage-ad8a9d39.js} +278 -10
- package/dist/state-storage-ad8a9d39.js.map +1 -0
- package/dist/{src/vite → vite}/index.js +14 -9
- package/dist/vite/index.js.map +1 -0
- package/package.json +34 -10
- package/dist/src/browser-host.js +0 -129
- package/dist/src/browser-host.js.map +0 -1
- package/dist/src/core.js +0 -17
- package/dist/src/core.js.map +0 -1
- package/dist/src/index.js +0 -6
- package/dist/src/index.js.map +0 -1
- package/dist/src/manifest.js +0 -3
- package/dist/src/manifest.js.map +0 -1
- package/dist/src/monaco-worker.js +0 -30
- package/dist/src/monaco-worker.js.map +0 -1
- package/dist/src/react/diagnostic-list.js +0 -16
- package/dist/src/react/diagnostic-list.js.map +0 -1
- package/dist/src/react/editor-command-bar.js +0 -15
- package/dist/src/react/editor-command-bar.js.map +0 -1
- package/dist/src/react/editor.js +0 -37
- package/dist/src/react/editor.js.map +0 -1
- package/dist/src/react/emitter-dropdown.js +0 -13
- package/dist/src/react/emitter-dropdown.js.map +0 -1
- package/dist/src/react/error-tab.js +0 -20
- package/dist/src/react/error-tab.js.map +0 -1
- package/dist/src/react/file-output.js +0 -31
- package/dist/src/react/file-output.js.map +0 -1
- package/dist/src/react/footer.js +0 -29
- package/dist/src/react/footer.js.map +0 -1
- package/dist/src/react/hooks.js +0 -50
- package/dist/src/react/hooks.js.map +0 -1
- package/dist/src/react/index.js +0 -4
- package/dist/src/react/index.js.map +0 -1
- package/dist/src/react/output-tabs.js +0 -38
- package/dist/src/react/output-tabs.js.map +0 -1
- package/dist/src/react/output-view.js +0 -98
- package/dist/src/react/output-view.js.map +0 -1
- package/dist/src/react/playground.js +0 -182
- package/dist/src/react/playground.js.map +0 -1
- package/dist/src/react/samples-dropdown.js +0 -15
- package/dist/src/react/samples-dropdown.js.map +0 -1
- package/dist/src/react/settings/compiler-settings.js +0 -42
- package/dist/src/react/settings/compiler-settings.js.map +0 -1
- package/dist/src/react/settings/emitter-options-form.js +0 -39
- package/dist/src/react/settings/emitter-options-form.js.map +0 -1
- package/dist/src/react/settings/linter-form.js +0 -33
- package/dist/src/react/settings/linter-form.js.map +0 -1
- package/dist/src/react/split-pane/index.js +0 -2
- package/dist/src/react/split-pane/index.js.map +0 -1
- package/dist/src/react/split-pane/pane.js +0 -5
- package/dist/src/react/split-pane/pane.js.map +0 -1
- package/dist/src/react/split-pane/sash-content.js +0 -7
- package/dist/src/react/split-pane/sash-content.js.map +0 -1
- package/dist/src/react/split-pane/sash.js +0 -23
- package/dist/src/react/split-pane/sash.js.map +0 -1
- package/dist/src/react/split-pane/split-pane.js +0 -138
- package/dist/src/react/split-pane/split-pane.js.map +0 -1
- package/dist/src/react/split-pane/split-pane.module.css +0 -58
- package/dist/src/react/standalone.js +0 -63
- package/dist/src/react/standalone.js.map +0 -1
- package/dist/src/react/swagger-ui.js +0 -19
- package/dist/src/react/swagger-ui.js.map +0 -1
- package/dist/src/react/types.js +0 -2
- package/dist/src/react/types.js.map +0 -1
- package/dist/src/react/typespec-editor.js +0 -30
- package/dist/src/react/typespec-editor.js.map +0 -1
- package/dist/src/react/viewers.js +0 -8
- package/dist/src/react/viewers.js.map +0 -1
- package/dist/src/services.js.map +0 -1
- package/dist/src/state-storage.js +0 -81
- package/dist/src/state-storage.js.map +0 -1
- package/dist/src/types.js +0 -2
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils.d.ts +0 -8
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.js +0 -14
- package/dist/src/utils.js.map +0 -1
- package/dist/src/vite/index.js.map +0 -1
- package/dist/src/vite/types.js +0 -2
- package/dist/src/vite/types.js.map +0 -1
- package/dist/vite.config.d.ts +0 -3
- package/dist/vite.config.d.ts.map +0 -1
- package/dist/vite.config.js +0 -38
- package/dist/vite.config.js.map +0 -1
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from '@emotion/react/jsx-runtime';
|
|
2
|
+
import debounce from 'debounce';
|
|
3
|
+
import { editor, Uri, MarkerSeverity, KeyMod, KeyCode } from 'monaco-editor';
|
|
4
|
+
import { useCallback, useMemo, useState, useEffect, useRef, useId as useId$1 } from 'react';
|
|
5
|
+
import { CompletionItemTag } from 'vscode-languageserver';
|
|
6
|
+
import { g as getMarkerLocation, c as createBrowserHost, a as registerMonacoLanguage, r as registerMonacoDefaultWorkers, b as createUrlStateStorage } from '../state-storage-ad8a9d39.js';
|
|
7
|
+
import { Select, useId, Label, RadioGroup, Radio, Input, Switch, Checkbox, Link, Toolbar, Tooltip, ToolbarButton, Dialog, DialogTrigger, DialogSurface, DialogBody, mergeClasses, useToastController, Toast, ToastTitle, ToastBody, FluentProvider, webLightTheme, Toaster } from '@fluentui/react-components';
|
|
8
|
+
import { Save16Regular, Broom16Filled, Settings24Regular, Bug16Regular } from '@fluentui/react-icons';
|
|
9
|
+
import { css } from '@emotion/react';
|
|
10
|
+
import { MANIFEST } from '@typespec/compiler';
|
|
11
|
+
import { TypeSpecProgramViewer } from '@typespec/html-program-viewer';
|
|
12
|
+
import { createRoot } from 'react-dom/client';
|
|
13
|
+
import 'vscode-languageserver-textdocument';
|
|
14
|
+
import 'lzutf8';
|
|
15
|
+
|
|
16
|
+
const EmitterDropdown = ({ emitters, onSelectedEmitterChange, selectedEmitter, }) => {
|
|
17
|
+
const options = emitters.map((emitterName) => {
|
|
18
|
+
return jsx("option", { children: emitterName }, emitterName);
|
|
19
|
+
});
|
|
20
|
+
const handleSelected = useCallback((evt) => {
|
|
21
|
+
onSelectedEmitterChange(evt.target.value);
|
|
22
|
+
}, [onSelectedEmitterChange]);
|
|
23
|
+
return (jsxs(Select, { className: "sample-dropdown", onChange: handleSelected, value: selectedEmitter, children: [jsx("option", { value: "", disabled: true, children: "Select emitter..." }), options] }));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const SamplesDropdown = ({ samples, selectedSampleName, onSelectedSampleNameChange, }) => {
|
|
27
|
+
const options = Object.keys(samples).map((sample) => {
|
|
28
|
+
return jsx("option", { children: sample }, sample);
|
|
29
|
+
});
|
|
30
|
+
const handleSelected = useCallback((evt) => {
|
|
31
|
+
if (samples[evt.target.value]) {
|
|
32
|
+
onSelectedSampleNameChange(evt.target.value);
|
|
33
|
+
}
|
|
34
|
+
}, [onSelectedSampleNameChange]);
|
|
35
|
+
return (jsxs(Select, { className: "sample-dropdown", onChange: handleSelected, value: selectedSampleName !== null && selectedSampleName !== void 0 ? selectedSampleName : "", children: [jsx("option", { value: "", disabled: true, children: "Select sample..." }), options] }));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const EmitterOptionsForm = ({ library, options, optionsChanged, }) => {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
const emitterOptionsSchema = (_b = (_a = library.emitter) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.properties;
|
|
41
|
+
if (emitterOptionsSchema === undefined) {
|
|
42
|
+
return jsx(Fragment, { children: "\"No options\"" });
|
|
43
|
+
}
|
|
44
|
+
const entries = Object.entries(emitterOptionsSchema);
|
|
45
|
+
const handleChange = useCallback(({ name, value }) => {
|
|
46
|
+
optionsChanged({
|
|
47
|
+
...options,
|
|
48
|
+
[library.name]: {
|
|
49
|
+
...options[library.name],
|
|
50
|
+
[name]: value,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}, [options, optionsChanged]);
|
|
54
|
+
return (jsx("div", { children: entries.map(([key, value]) => {
|
|
55
|
+
var _a;
|
|
56
|
+
return (jsx("div", { css: { margin: "16px 0" }, children: jsx(JsonSchemaPropertyInput, { emitterOptions: (_a = options[library.name]) !== null && _a !== void 0 ? _a : {}, name: key, prop: value, onChange: handleChange }) }, key));
|
|
57
|
+
}) }));
|
|
58
|
+
};
|
|
59
|
+
const JsonSchemaPropertyInput = ({ emitterOptions, name, prop, onChange, }) => {
|
|
60
|
+
var _a;
|
|
61
|
+
const prettyName = useMemo(() => name[0].toUpperCase() + name.slice(1).replace(/-/g, " "), [name]);
|
|
62
|
+
const inputId = useId("input");
|
|
63
|
+
const value = (_a = emitterOptions[name]) !== null && _a !== void 0 ? _a : prop.default;
|
|
64
|
+
const handleChange = useCallback((_, data) => onChange({ name, value: "value" in data ? data.value : data.checked }), [onChange]);
|
|
65
|
+
switch (prop.type) {
|
|
66
|
+
case "boolean":
|
|
67
|
+
return jsx(Switch, { label: prettyName, checked: value, onChange: handleChange });
|
|
68
|
+
case "string":
|
|
69
|
+
default:
|
|
70
|
+
return (jsxs("div", { children: [jsx("div", { children: jsx(Label, { htmlFor: inputId, title: name, children: prettyName }) }), jsx("div", { children: prop.enum ? (jsx(RadioGroup, { layout: "horizontal", id: inputId, value: value, onChange: handleChange, children: prop.enum.map((x) => (jsx(Radio, { value: x, label: x }, x))) })) : (jsx(Input, { id: inputId, value: value, onChange: handleChange })) })] }));
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const LinterForm = ({ libraries, linterRuleSet, onLinterRuleSetChanged, }) => {
|
|
75
|
+
const rulesets = libraries.flatMap((lib) => {
|
|
76
|
+
var _a, _b, _c;
|
|
77
|
+
return Object.keys((_c = (_b = (_a = lib.definition) === null || _a === void 0 ? void 0 : _a.linter) === null || _b === void 0 ? void 0 : _b.ruleSets) !== null && _c !== void 0 ? _c : {}).map((x) => `${lib.name}/${x}`);
|
|
78
|
+
});
|
|
79
|
+
if (rulesets.length === 0) {
|
|
80
|
+
return jsx(Fragment, { children: "No ruleset available" });
|
|
81
|
+
}
|
|
82
|
+
const handleChange = (ruleSet, checked) => {
|
|
83
|
+
var _a;
|
|
84
|
+
const ruleSets = (_a = linterRuleSet.extends) !== null && _a !== void 0 ? _a : [];
|
|
85
|
+
const updatedRuleSets = checked
|
|
86
|
+
? [...ruleSets, ruleSet]
|
|
87
|
+
: ruleSets.filter((x) => x !== ruleSet);
|
|
88
|
+
onLinterRuleSetChanged({
|
|
89
|
+
extends: updatedRuleSets,
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
return (jsx(Fragment, { children: rulesets.map((ruleSet) => {
|
|
93
|
+
var _a;
|
|
94
|
+
return (jsx(RuleSetCheckbox, { ruleSet: ruleSet, checked: (_a = linterRuleSet.extends) === null || _a === void 0 ? void 0 : _a.includes(ruleSet), onChange: handleChange }, ruleSet));
|
|
95
|
+
}) }));
|
|
96
|
+
};
|
|
97
|
+
const RuleSetCheckbox = ({ ruleSet, checked, onChange }) => {
|
|
98
|
+
const handleChange = useCallback((_, data) => {
|
|
99
|
+
onChange(ruleSet, !!data.checked);
|
|
100
|
+
}, [ruleSet, checked, onChange]);
|
|
101
|
+
return jsx(Checkbox, { label: ruleSet, checked: checked, onChange: handleChange });
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const CompilerSettings = ({ selectedEmitter, libraries, options, onOptionsChanged, }) => {
|
|
105
|
+
var _a, _b;
|
|
106
|
+
const emitterOptionsChanged = useCallback((emitterOptions) => {
|
|
107
|
+
onOptionsChanged({
|
|
108
|
+
...options,
|
|
109
|
+
options: emitterOptions,
|
|
110
|
+
});
|
|
111
|
+
}, [options]);
|
|
112
|
+
const library = useTypeSpecLibrary(selectedEmitter);
|
|
113
|
+
const linterRuleSet = (_a = options.linterRuleSet) !== null && _a !== void 0 ? _a : {};
|
|
114
|
+
const linterRuleSetChanged = useCallback((linterRuleSet) => {
|
|
115
|
+
onOptionsChanged({
|
|
116
|
+
...options,
|
|
117
|
+
linterRuleSet,
|
|
118
|
+
});
|
|
119
|
+
}, [options]);
|
|
120
|
+
return (jsxs("div", { css: { padding: 10 }, children: [jsx("h2", { children: "Settings" }), jsxs(Fragment, { children: ["Emitter: ", selectedEmitter] }), jsx("h3", { children: "Options" }), library && (jsx(EmitterOptionsForm, { library: library, options: (_b = options.options) !== null && _b !== void 0 ? _b : {}, optionsChanged: emitterOptionsChanged })), jsx("h3", { children: "Linter" }), jsx(LinterForm, { libraries: libraries, linterRuleSet: linterRuleSet, onLinterRuleSetChanged: linterRuleSetChanged })] }));
|
|
121
|
+
};
|
|
122
|
+
function useTypeSpecLibrary(name) {
|
|
123
|
+
const [lib, setLib] = useState();
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
setLib(undefined);
|
|
126
|
+
import(/* @vite-ignore */ name)
|
|
127
|
+
.then((module) => {
|
|
128
|
+
if (module.$lib === undefined) {
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.error(`Couldn't load library ${name} missing $lib export`);
|
|
131
|
+
}
|
|
132
|
+
setLib(module.$lib);
|
|
133
|
+
})
|
|
134
|
+
.catch((e) => {
|
|
135
|
+
// eslint-disable-next-line no-console
|
|
136
|
+
console.error("Failed to load library", name);
|
|
137
|
+
});
|
|
138
|
+
}, [name]);
|
|
139
|
+
return lib;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const EditorCommandBar = ({ documentationUrl, saveCode, formatCode, newIssue, libraries, selectedEmitter, onSelectedEmitterChange, compilerOptions: emitterOptions, onCompilerOptionsChange, samples, selectedSampleName, onSelectedSampleNameChange, }) => {
|
|
143
|
+
const documentation = documentationUrl ? (jsx("label", { children: jsx(Link, { href: documentationUrl, target: "_blank", children: "Docs" }) })) : undefined;
|
|
144
|
+
const bugButton = newIssue ? jsx(FileBugButton, { onClick: newIssue }) : undefined;
|
|
145
|
+
return (jsx("div", { css: { borderBottom: "1px solid #f5f5f5" }, children: jsxs(Toolbar, { children: [jsx(Tooltip, { content: "Save", relationship: "description", withArrow: true, children: jsx(ToolbarButton, { "aria-label": "Save", icon: jsx(Save16Regular, {}), onClick: saveCode }) }), jsx(Tooltip, { content: "Format", relationship: "description", withArrow: true, children: jsx(ToolbarButton, { "aria-label": "Format", icon: jsx(Broom16Filled, {}), onClick: formatCode }) }), samples && (jsx(SamplesDropdown, { samples: samples, selectedSampleName: selectedSampleName, onSelectedSampleNameChange: onSelectedSampleNameChange })), jsx(EmitterDropdown, { emitters: libraries.filter((x) => x.isEmitter).map((x) => x.name), onSelectedEmitterChange: onSelectedEmitterChange, selectedEmitter: selectedEmitter }), jsxs(Dialog, { children: [jsx(DialogTrigger, { children: jsx(ToolbarButton, { icon: jsx(Settings24Regular, {}) }) }), jsx(DialogSurface, { children: jsx(DialogBody, { children: jsx(CompilerSettings, { libraries: libraries, selectedEmitter: selectedEmitter, options: emitterOptions, onOptionsChanged: onCompilerOptionsChange }) }) })] }), documentation, jsx("div", { css: { flex: "1" } }), bugButton] }) }));
|
|
146
|
+
};
|
|
147
|
+
const FileBugButton = ({ onClick }) => {
|
|
148
|
+
return (jsx(Tooltip, { content: "File Bug Report", relationship: "description", withArrow: true, children: jsx(ToolbarButton, { appearance: "subtle", "aria-label": "File Bug Report", icon: jsx(Bug16Regular, {}), onClick: onClick, children: "File bug" }) }));
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const Editor = ({ model, options, actions, onMount }) => {
|
|
152
|
+
const editorContainerRef = useRef(null);
|
|
153
|
+
const editorRef = useRef(null);
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
editorRef.current = editor.create(editorContainerRef.current, {
|
|
156
|
+
model,
|
|
157
|
+
automaticLayout: true,
|
|
158
|
+
...options,
|
|
159
|
+
});
|
|
160
|
+
onMount === null || onMount === void 0 ? void 0 : onMount({ editor: editorRef.current });
|
|
161
|
+
}, []);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
const disposables = [];
|
|
164
|
+
for (const command of actions !== null && actions !== void 0 ? actions : []) {
|
|
165
|
+
disposables.push(editorRef.current.addAction(command));
|
|
166
|
+
}
|
|
167
|
+
return () => {
|
|
168
|
+
disposables.forEach((x) => x.dispose());
|
|
169
|
+
};
|
|
170
|
+
}, [actions]);
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (editorRef.current) {
|
|
173
|
+
editorRef.current.setModel(model);
|
|
174
|
+
}
|
|
175
|
+
}, [model]);
|
|
176
|
+
return (jsx("div", { className: "monaco-editor-container", css: { width: "100%", height: "100%", overflow: "hidden" }, ref: editorContainerRef }));
|
|
177
|
+
};
|
|
178
|
+
function useMonacoModel(uri, language) {
|
|
179
|
+
return useMemo(() => {
|
|
180
|
+
var _a;
|
|
181
|
+
const monacoUri = Uri.parse(uri);
|
|
182
|
+
return (_a = editor.getModel(monacoUri)) !== null && _a !== void 0 ? _a : editor.createModel("", language, monacoUri);
|
|
183
|
+
}, [uri, language]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const Footer = () => {
|
|
187
|
+
const prItem = MANIFEST.pr ? (jsxs(FooterItem, { link: `https://github.com/microsoft/typespec/pull/${MANIFEST.pr}`, children: [jsx("span", { children: "PR " }), jsx("span", { children: MANIFEST.pr })] })) : (jsx(Fragment, {}));
|
|
188
|
+
return (jsxs("div", { css: [
|
|
189
|
+
{
|
|
190
|
+
gridArea: "footer",
|
|
191
|
+
gridRow: "3",
|
|
192
|
+
backgroundColor: "#007acc",
|
|
193
|
+
display: "flex",
|
|
194
|
+
fontSize: "14px",
|
|
195
|
+
},
|
|
196
|
+
MANIFEST.pr ? { backgroundColor: "#ce662a" } : {},
|
|
197
|
+
], children: [prItem, jsxs(FooterItem, { children: [jsx("span", { children: "TypeSpec Version " }), jsx("span", { children: MANIFEST.version })] }), jsxs(FooterItem, { link: `https://github.com/microsoft/typespec/commit/${MANIFEST.commit}`, children: [jsx("span", { children: "Commit " }), jsx("span", { children: MANIFEST.commit.slice(0, 6) })] })] }));
|
|
198
|
+
};
|
|
199
|
+
const FooterItem = ({ children, link }) => {
|
|
200
|
+
return link ? (jsx("a", { css: FooterItemStyles, href: link, target: "_blank", children: children })) : (jsx("div", { css: FooterItemStyles, children: children }));
|
|
201
|
+
};
|
|
202
|
+
const FooterItemStyles = css({
|
|
203
|
+
textDecoration: "none",
|
|
204
|
+
color: "#fefefe",
|
|
205
|
+
borderRight: "1px solid #d5d5d5",
|
|
206
|
+
padding: "0 5px",
|
|
207
|
+
"&:hover": {
|
|
208
|
+
backgroundColor: "#063a5c",
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Hook to manage a value that could be either controlled or uncontrolled, such as a checked state or
|
|
214
|
+
* text box string.
|
|
215
|
+
* @param controlledValue - The controlled value passed in the props. This value will always be used if provided,
|
|
216
|
+
* and the internal state will be updated to reflect it.
|
|
217
|
+
* @param defaultUncontrolledValue - Initial value for the internal state in the uncontrolled case.
|
|
218
|
+
* @returns An array of the current value and an updater callback. Like `React.useState`, the updater
|
|
219
|
+
* callback always has the same identity, and it can take either a new value, or a function which
|
|
220
|
+
* is passed the previous value and returns the new value.
|
|
221
|
+
* @see https://reactjs.org/docs/uncontrolled-components.html
|
|
222
|
+
*/
|
|
223
|
+
function useControllableValue(controlledValue, defaultUncontrolledValue, onChange) {
|
|
224
|
+
const [value, setValue] = useState(defaultUncontrolledValue);
|
|
225
|
+
const isControlled = useMemo(() => controlledValue !== undefined, []);
|
|
226
|
+
const currentValue = isControlled ? controlledValue : value;
|
|
227
|
+
// Duplicate the current value and onChange in refs so they're accessible from
|
|
228
|
+
// setValueOrCallOnChange without creating a new callback every time
|
|
229
|
+
const valueRef = useRef(currentValue);
|
|
230
|
+
const onChangeRef = useRef(onChange);
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
valueRef.current = currentValue;
|
|
233
|
+
onChangeRef.current = onChange;
|
|
234
|
+
});
|
|
235
|
+
// To match the behavior of the setter returned by React.useState, this callback's identity
|
|
236
|
+
// should never change. This means it MUST NOT directly reference variables that can change.
|
|
237
|
+
const setValueOrCallOnChange = useMemo(() => (update) => {
|
|
238
|
+
// Assuming here that TValue is not a function, because a controllable value will typically
|
|
239
|
+
// be something a user can enter as input
|
|
240
|
+
const newValue = typeof update === "function" ? update(valueRef.current) : update;
|
|
241
|
+
if (onChangeRef.current) {
|
|
242
|
+
onChangeRef.current(newValue);
|
|
243
|
+
}
|
|
244
|
+
if (!isControlled) {
|
|
245
|
+
setValue(newValue);
|
|
246
|
+
}
|
|
247
|
+
}, []);
|
|
248
|
+
return [currentValue, setValueOrCallOnChange];
|
|
249
|
+
}
|
|
250
|
+
function useAsyncMemo(callback, defaultValue, deps) {
|
|
251
|
+
const [value, setValue] = useState(defaultValue);
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
callback()
|
|
254
|
+
.then(setValue)
|
|
255
|
+
// eslint-disable-next-line no-console
|
|
256
|
+
.catch(() => console.error("Failed to load async memo"));
|
|
257
|
+
}, deps);
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const DiagnosticList = ({ diagnostics }) => {
|
|
262
|
+
if (diagnostics.length === 0) {
|
|
263
|
+
return jsx("div", { className: "center", children: "No errors" });
|
|
264
|
+
}
|
|
265
|
+
return (jsx("div", { css: { height: "100%", overflow: "auto" }, children: diagnostics.map((x, i) => {
|
|
266
|
+
return jsx(DiagnosticItem, { diagnostic: x }, i);
|
|
267
|
+
}) }));
|
|
268
|
+
};
|
|
269
|
+
const DiagnosticItem = ({ diagnostic }) => {
|
|
270
|
+
return (jsxs("div", { css: { display: "flex" }, children: [jsx("div", { css: [{ padding: "0 5px" }, diagnostic.severity === "error" ? errorColor : warningColor], children: diagnostic.severity }), jsx("div", { css: { padding: "0 5px", color: "#333333" }, children: diagnostic.code }), jsx("div", { css: { padding: "0 5px" }, children: diagnostic.message })] }));
|
|
271
|
+
};
|
|
272
|
+
const errorColor = css({ color: "#cc2222" });
|
|
273
|
+
const warningColor = css({ color: "orange" });
|
|
274
|
+
|
|
275
|
+
const ErrorTab = ({ internalCompilerError, diagnostics, }) => {
|
|
276
|
+
return (jsxs(Fragment, { children: [internalCompilerError && jsx(InternalCompilerError, { error: internalCompilerError }), diagnostics && jsx(DiagnosticList, { diagnostics: diagnostics })] }));
|
|
277
|
+
};
|
|
278
|
+
const InternalCompilerError = ({ error }) => {
|
|
279
|
+
return (jsx("div", { css: { CenterStyles }, children: jsxs("div", { css: {
|
|
280
|
+
border: "1px solid #cc2222",
|
|
281
|
+
padding: "10px",
|
|
282
|
+
margin: "20px",
|
|
283
|
+
}, children: [jsx("h3", { children: "Internal Compiler error" }), jsx("div", { children: "File issue at https://github.com/microsoft/typespec" }), jsx("hr", {}), jsx("div", { children: error.stack })] }) }));
|
|
284
|
+
};
|
|
285
|
+
const CenterStyles = css({
|
|
286
|
+
display: "flex",
|
|
287
|
+
height: "100%",
|
|
288
|
+
alignItems: "center",
|
|
289
|
+
justifyContent: "center",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Display a file output using different viewers.
|
|
294
|
+
*/
|
|
295
|
+
const FileOutput = ({ filename, content, viewers }) => {
|
|
296
|
+
if (viewers.length === 0) {
|
|
297
|
+
return jsx(Fragment, { children: "No viewers" });
|
|
298
|
+
}
|
|
299
|
+
else if (viewers.length === 1) {
|
|
300
|
+
return viewers[0].render({ filename, content });
|
|
301
|
+
}
|
|
302
|
+
const [selected, setSelected] = useState(viewers[0].key);
|
|
303
|
+
const handleSelected = useCallback((_, data) => {
|
|
304
|
+
setSelected(data.value);
|
|
305
|
+
}, [selected]);
|
|
306
|
+
const selectedRender = useMemo(() => {
|
|
307
|
+
var _a;
|
|
308
|
+
return (_a = viewers.find((x) => x.key === selected)) === null || _a === void 0 ? void 0 : _a.render;
|
|
309
|
+
}, [selected, viewers]);
|
|
310
|
+
return (jsxs("div", { css: { width: "100%", height: "100%", overflow: "hidden" }, children: [jsx(Select, { value: selected, onChange: handleSelected, css: DropdownStyle, children: viewers.map(({ key, label }) => (jsx("option", { value: key, children: label }, key))) }), selectedRender && selectedRender({ filename, content })] }));
|
|
311
|
+
};
|
|
312
|
+
const DropdownStyle = css({
|
|
313
|
+
margin: "0.5rem 1.5rem",
|
|
314
|
+
position: "absolute",
|
|
315
|
+
"z-index": 1,
|
|
316
|
+
right: 0,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const OutputTabs = ({ tabs, selected, onSelect }) => {
|
|
320
|
+
const [leftTabs, rightTabs] = chunk(tabs, (x) => x.align === "left");
|
|
321
|
+
return (jsxs("div", { css: { display: "flex", borderBottom: "1px solid #c5c5c5" }, children: [leftTabs.map((tab) => {
|
|
322
|
+
return (jsx(OutputTab, { tab: tab, selected: selected === tab.id, onSelect: onSelect }, tab.id));
|
|
323
|
+
}), jsx("span", { css: { flex: 1, borderRight: "1px solid #ccc" } }), rightTabs.map((tab) => {
|
|
324
|
+
return (jsx(OutputTab, { tab: tab, selected: selected === tab.id, onSelect: onSelect }, tab.id));
|
|
325
|
+
})] }));
|
|
326
|
+
};
|
|
327
|
+
const OutputTab = ({ tab, selected, onSelect }) => {
|
|
328
|
+
return (jsx("a", { css: [
|
|
329
|
+
{
|
|
330
|
+
height: "26px",
|
|
331
|
+
padding: "0 5px",
|
|
332
|
+
borderRight: "1px solid #ccc",
|
|
333
|
+
borderTop: "none",
|
|
334
|
+
borderBottom: "none",
|
|
335
|
+
color: "#000",
|
|
336
|
+
textDecoration: "none",
|
|
337
|
+
cursor: "pointer",
|
|
338
|
+
},
|
|
339
|
+
selected ? { fontWeight: "bold", backgroundColor: "#eee" } : {},
|
|
340
|
+
], onClick: () => onSelect(tab.id), children: tab.name }));
|
|
341
|
+
};
|
|
342
|
+
function chunk(items, condition) {
|
|
343
|
+
const match = [];
|
|
344
|
+
const unMatch = [];
|
|
345
|
+
for (const item of items) {
|
|
346
|
+
if (condition(item)) {
|
|
347
|
+
match.push(item);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
unMatch.push(item);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return [match, unMatch];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const TypeSpecEditor = ({ actions, options, ...other }) => {
|
|
357
|
+
const resolvedOptions = {
|
|
358
|
+
"semanticHighlighting.enabled": true,
|
|
359
|
+
automaticLayout: true,
|
|
360
|
+
tabSize: 2,
|
|
361
|
+
minimap: {
|
|
362
|
+
enabled: false,
|
|
363
|
+
},
|
|
364
|
+
...options,
|
|
365
|
+
};
|
|
366
|
+
return jsx(Editor, { actions: actions, options: resolvedOptions, ...other });
|
|
367
|
+
};
|
|
368
|
+
const OutputEditor = ({ filename, value, }) => {
|
|
369
|
+
if (filename === "") {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const options = {
|
|
373
|
+
readOnly: true,
|
|
374
|
+
automaticLayout: true,
|
|
375
|
+
minimap: {
|
|
376
|
+
enabled: false,
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
const model = useMonacoModel(filename);
|
|
380
|
+
model.setValue(value);
|
|
381
|
+
return jsx(Editor, { model: model, options: options });
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const OutputView = ({ compilationState, viewers }) => {
|
|
385
|
+
if (compilationState === undefined) {
|
|
386
|
+
return jsx(Fragment, {});
|
|
387
|
+
}
|
|
388
|
+
if ("internalCompilerError" in compilationState) {
|
|
389
|
+
return jsx(InternalCompilerError, { error: compilationState.internalCompilerError });
|
|
390
|
+
}
|
|
391
|
+
return jsx(OutputViewInternal, { compilationResult: compilationState, viewers: viewers });
|
|
392
|
+
};
|
|
393
|
+
const OutputViewInternal = ({ compilationResult, viewers }) => {
|
|
394
|
+
const { program, outputFiles } = compilationResult;
|
|
395
|
+
const [viewSelection, setViewSelection] = useState({
|
|
396
|
+
type: "file",
|
|
397
|
+
filename: "",
|
|
398
|
+
content: "",
|
|
399
|
+
});
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
if (viewSelection.type === "file") {
|
|
402
|
+
if (outputFiles.length > 0) {
|
|
403
|
+
const fileStillThere = outputFiles.find((x) => x === viewSelection.filename);
|
|
404
|
+
void loadOutputFile(fileStillThere !== null && fileStillThere !== void 0 ? fileStillThere : outputFiles[0]);
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
setViewSelection({ type: "file", filename: viewSelection.filename, content: "" });
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}, [program, outputFiles]);
|
|
411
|
+
async function loadOutputFile(path) {
|
|
412
|
+
const contents = await program.host.readFile("./tsp-output/" + path);
|
|
413
|
+
setViewSelection({ type: "file", filename: path, content: contents.text });
|
|
414
|
+
}
|
|
415
|
+
const diagnostics = program.diagnostics;
|
|
416
|
+
const tabs = useMemo(() => {
|
|
417
|
+
return [
|
|
418
|
+
...outputFiles.map((x) => ({
|
|
419
|
+
align: "left",
|
|
420
|
+
name: x,
|
|
421
|
+
id: x,
|
|
422
|
+
})),
|
|
423
|
+
{ id: "type-graph", name: "Type Graph", align: "right" },
|
|
424
|
+
{
|
|
425
|
+
id: "errors",
|
|
426
|
+
name: jsx(ErrorTabLabel, { diagnostics: diagnostics }),
|
|
427
|
+
align: "right",
|
|
428
|
+
},
|
|
429
|
+
];
|
|
430
|
+
}, [outputFiles, diagnostics]);
|
|
431
|
+
const handleTabSelection = useCallback((tabId) => {
|
|
432
|
+
if (tabId === "type-graph") {
|
|
433
|
+
setViewSelection({ type: "type-graph" });
|
|
434
|
+
}
|
|
435
|
+
else if (tabId === "errors") {
|
|
436
|
+
setViewSelection({ type: "errors" });
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
void loadOutputFile(tabId);
|
|
440
|
+
}
|
|
441
|
+
}, []);
|
|
442
|
+
return (jsxs(Fragment, { children: [jsx(OutputTabs, { tabs: tabs, selected: viewSelection.type === "file" ? viewSelection.filename : viewSelection.type, onSelect: handleTabSelection }), jsx("div", { className: "output-content", css: { width: "100%", height: "100%", overflow: "hidden" }, children: jsx(OutputContent, { viewSelection: viewSelection, program: program, viewers: viewers }) })] }));
|
|
443
|
+
};
|
|
444
|
+
const rawFileViewer = {
|
|
445
|
+
key: "raw",
|
|
446
|
+
label: "File",
|
|
447
|
+
render: ({ filename, content }) => (jsx(OutputEditor, { filename: filename, value: content })),
|
|
448
|
+
};
|
|
449
|
+
const OutputContent = ({ viewSelection, program, viewers, }) => {
|
|
450
|
+
const resolvedViewers = useMemo(() => [rawFileViewer, ...(viewers !== null && viewers !== void 0 ? viewers : [])], [viewers]);
|
|
451
|
+
switch (viewSelection.type) {
|
|
452
|
+
case "file":
|
|
453
|
+
return (jsx(FileOutput, { filename: viewSelection.filename, content: viewSelection.content, viewers: resolvedViewers }));
|
|
454
|
+
case "errors":
|
|
455
|
+
return jsx(ErrorTab, { diagnostics: program === null || program === void 0 ? void 0 : program.diagnostics });
|
|
456
|
+
default:
|
|
457
|
+
return (jsx("div", { css: {
|
|
458
|
+
height: "100%",
|
|
459
|
+
overflow: "scroll",
|
|
460
|
+
}, children: program && jsx(TypeSpecProgramViewer, { program: program }) }));
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const ErrorTabLabel = ({ diagnostics }) => {
|
|
464
|
+
const errorCount = diagnostics ? diagnostics.length : 0;
|
|
465
|
+
return (jsxs("div", { children: ["Errors ", errorCount > 0 ? jsx("span", { css: ErrorTabCountStyles, children: errorCount }) : ""] }));
|
|
466
|
+
};
|
|
467
|
+
const ErrorTabCountStyles = css({
|
|
468
|
+
backgroundColor: "#cc2222",
|
|
469
|
+
color: "#f5f5f5",
|
|
470
|
+
padding: "0 5px",
|
|
471
|
+
borderRadius: "20px",
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
function Pane({ children, ...props }) {
|
|
475
|
+
return jsx("div", { ...props, children: children });
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
var style = {"split-pane":"split-pane-module_split-pane__XCqR8","split-disabled":"split-pane-module_split-disabled__fZ0Sg","split-pane-dragging":"split-pane-module_split-pane-dragging__mQJnZ","react-split-vertical":"split-pane-module_react-split-vertical__WTUQ1","react-split-horizontal":"split-pane-module_react-split-horizontal__Fpls-","sash":"split-pane-module_sash__hNWH6","sash-disabled":"split-pane-module_sash-disabled__z6uCV","sash-vertical":"split-pane-module_sash-vertical__S8HbH","sash-horizontal":"split-pane-module_sash-horizontal__BTUaN","sash-content":"split-pane-module_sash-content__DLAt4","sash-content-dragging":"split-pane-module_sash-content-dragging__NkKQP","pane":"split-pane-module_pane__m1zQB"};
|
|
479
|
+
|
|
480
|
+
const SashContent = ({ className, children, dragging, ...others }) => {
|
|
481
|
+
return (jsx("div", { className: mergeClasses(style["sash-content"], dragging && style["sash-content-dragging"], className), ...others, children: children }));
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const Sash = ({ className, render, onDragStart, onDragging, onDragEnd, onReset, ...others }) => {
|
|
485
|
+
const [draging, setDrag] = useState(false);
|
|
486
|
+
const handleMouseMove = (e) => {
|
|
487
|
+
onDragging(e);
|
|
488
|
+
};
|
|
489
|
+
const handleMouseUp = (e) => {
|
|
490
|
+
setDrag(false);
|
|
491
|
+
onDragEnd(e);
|
|
492
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
493
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
494
|
+
};
|
|
495
|
+
return (jsx("div", { className: mergeClasses(style["sash"], className), onMouseDown: (e) => {
|
|
496
|
+
setDrag(true);
|
|
497
|
+
onDragStart(e);
|
|
498
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
499
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
500
|
+
}, onDoubleClick: onReset, ...others, children: render(draging) }));
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const SplitPane = ({ children, sizes: propSizes, initialSizes: defaultSizes, allowResize = true, split = "vertical", className: wrapClassName, sashRender = (_, active) => jsx(SashContent, { dragging: active }), resizerSize = 4, performanceMode = false, onChange = () => null, onDragStart = () => null, onDragEnd = () => null, ...others }) => {
|
|
504
|
+
var _a;
|
|
505
|
+
const [resolvedPropSize, updateSizes] = useControllableValue(propSizes, defaultSizes, onChange);
|
|
506
|
+
const axis = useRef({ x: 0, y: 0 });
|
|
507
|
+
const wrapper = useRef(null);
|
|
508
|
+
const cacheSizes = useRef({ sizes: [], sashPosSizes: [] });
|
|
509
|
+
const [wrapperRect, setWrapperRect] = useState({});
|
|
510
|
+
const [isDragging, setDragging] = useState(false);
|
|
511
|
+
useEffect(() => {
|
|
512
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
513
|
+
var _a, _b;
|
|
514
|
+
setWrapperRect((_b = (_a = wrapper === null || wrapper === void 0 ? void 0 : wrapper.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) !== null && _b !== void 0 ? _b : {});
|
|
515
|
+
});
|
|
516
|
+
resizeObserver.observe(wrapper.current);
|
|
517
|
+
return () => {
|
|
518
|
+
resizeObserver.disconnect();
|
|
519
|
+
};
|
|
520
|
+
}, []);
|
|
521
|
+
const { sizeName, splitPos, splitAxis } = useMemo(() => ({
|
|
522
|
+
sizeName: split === "vertical" ? "width" : "height",
|
|
523
|
+
splitPos: split === "vertical" ? "left" : "top",
|
|
524
|
+
splitAxis: split === "vertical" ? "x" : "y",
|
|
525
|
+
}), [split]);
|
|
526
|
+
const wrapSize = (_a = wrapperRect[sizeName]) !== null && _a !== void 0 ? _a : 0;
|
|
527
|
+
// Get limit sizes via children
|
|
528
|
+
const paneLimitSizes = useMemo(() => children.map((childNode) => {
|
|
529
|
+
const limits = [0, Infinity];
|
|
530
|
+
if ((childNode === null || childNode === void 0 ? void 0 : childNode.type) === Pane) {
|
|
531
|
+
const { minSize, maxSize } = childNode.props;
|
|
532
|
+
limits[0] = assertsSize(minSize, wrapSize, 0);
|
|
533
|
+
limits[1] = assertsSize(maxSize, wrapSize);
|
|
534
|
+
}
|
|
535
|
+
return limits;
|
|
536
|
+
}), [children, wrapSize]);
|
|
537
|
+
const sizes = useMemo(function () {
|
|
538
|
+
let count = 0;
|
|
539
|
+
let curSum = 0;
|
|
540
|
+
const res = children.map((_, index) => {
|
|
541
|
+
const size = assertsSize(resolvedPropSize[index], wrapSize);
|
|
542
|
+
size === Infinity ? count++ : (curSum += size);
|
|
543
|
+
return size;
|
|
544
|
+
});
|
|
545
|
+
// resize or illegal size input,recalculate pane sizes
|
|
546
|
+
if (curSum > wrapSize || (!count && curSum < wrapSize)) {
|
|
547
|
+
const cacheNum = (curSum - wrapSize) / curSum;
|
|
548
|
+
return res.map((size) => {
|
|
549
|
+
return size === Infinity ? 0 : size - size * cacheNum;
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (count > 0) {
|
|
553
|
+
const average = (wrapSize - curSum) / count;
|
|
554
|
+
return res.map((size) => {
|
|
555
|
+
return size === Infinity ? average : size;
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return res;
|
|
559
|
+
}, [...resolvedPropSize, children.length, wrapSize]);
|
|
560
|
+
const sashPosSizes = useMemo(() => sizes.reduce((a, b) => [...a, a[a.length - 1] + b], [0]), [...sizes]);
|
|
561
|
+
const dragStart = useCallback((e) => {
|
|
562
|
+
var _a, _b;
|
|
563
|
+
(_b = (_a = document === null || document === void 0 ? void 0 : document.body) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.add(style["split-disabled"]);
|
|
564
|
+
axis.current = { x: e.pageX, y: e.pageY };
|
|
565
|
+
cacheSizes.current = { sizes, sashPosSizes };
|
|
566
|
+
setDragging(true);
|
|
567
|
+
onDragStart(e);
|
|
568
|
+
}, [onDragStart, sizes, sashPosSizes]);
|
|
569
|
+
const resetPosition = useCallback(() => {
|
|
570
|
+
updateSizes(defaultSizes);
|
|
571
|
+
}, [defaultSizes, updateSizes]);
|
|
572
|
+
const dragEnd = useCallback((e) => {
|
|
573
|
+
var _a, _b;
|
|
574
|
+
(_b = (_a = document === null || document === void 0 ? void 0 : document.body) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.remove(style["split-disabled"]);
|
|
575
|
+
axis.current = { x: e.pageX, y: e.pageY };
|
|
576
|
+
cacheSizes.current = { sizes, sashPosSizes };
|
|
577
|
+
setDragging(false);
|
|
578
|
+
onDragEnd(e);
|
|
579
|
+
}, [onDragEnd, sizes, sashPosSizes]);
|
|
580
|
+
const onDragging = useCallback(function (e, i) {
|
|
581
|
+
const curAxis = { x: e.pageX, y: e.pageY };
|
|
582
|
+
let distanceX = curAxis[splitAxis] - axis.current[splitAxis];
|
|
583
|
+
const leftBorder = -Math.min(sizes[i] - paneLimitSizes[i][0], paneLimitSizes[i + 1][1] - sizes[i + 1]);
|
|
584
|
+
const rightBorder = Math.min(sizes[i + 1] - paneLimitSizes[i + 1][0], paneLimitSizes[i][1] - sizes[i]);
|
|
585
|
+
if (distanceX < leftBorder) {
|
|
586
|
+
distanceX = leftBorder;
|
|
587
|
+
}
|
|
588
|
+
if (distanceX > rightBorder) {
|
|
589
|
+
distanceX = rightBorder;
|
|
590
|
+
}
|
|
591
|
+
const nextSizes = [...sizes];
|
|
592
|
+
nextSizes[i] += distanceX;
|
|
593
|
+
nextSizes[i + 1] -= distanceX;
|
|
594
|
+
updateSizes(nextSizes);
|
|
595
|
+
}, [paneLimitSizes, onChange]);
|
|
596
|
+
const paneFollow = !(performanceMode && isDragging);
|
|
597
|
+
const paneSizes = paneFollow ? sizes : cacheSizes.current.sizes;
|
|
598
|
+
const panePoses = paneFollow ? sashPosSizes : cacheSizes.current.sashPosSizes;
|
|
599
|
+
return (jsxs("div", { className: mergeClasses(style["split-pane"], split === "vertical" && style["split-pane-vertical"], split === "horizontal" && style["split-pane-horizontal"], isDragging && style["split-pane-dragging"], wrapClassName), ref: wrapper, ...others, children: [children.map((childNode, childIndex) => {
|
|
600
|
+
const isPane = childNode.type === Pane;
|
|
601
|
+
const paneProps = isPane ? childNode.props : {};
|
|
602
|
+
return (jsx(Pane, { className: mergeClasses(style["pane"], paneProps.className), style: {
|
|
603
|
+
...paneProps.style,
|
|
604
|
+
[sizeName]: paneSizes[childIndex],
|
|
605
|
+
[splitPos]: panePoses[childIndex],
|
|
606
|
+
}, children: isPane ? paneProps.children : childNode }, childIndex));
|
|
607
|
+
}), sashPosSizes.slice(1, -1).map((posSize, index) => (jsx(Sash, { className: mergeClasses(!allowResize && style["sash-disabled"], split === "vertical" ? style["sash-vertical"] : style["sash-horizontal"]), style: {
|
|
608
|
+
[sizeName]: resizerSize,
|
|
609
|
+
[splitPos]: posSize - resizerSize / 2,
|
|
610
|
+
}, render: sashRender.bind(null, index), onReset: resetPosition, onDragStart: dragStart, onDragging: (e) => onDragging(e, index), onDragEnd: dragEnd }, index)))] }));
|
|
611
|
+
};
|
|
612
|
+
/**
|
|
613
|
+
* Convert size to absolute number or Infinity
|
|
614
|
+
* SplitPane allows sizes in string and number, but the state sizes only support number,
|
|
615
|
+
* so convert string and number to number in here
|
|
616
|
+
* 'auto' -> divide the remaining space equally
|
|
617
|
+
* 'xxx px' -> xxx
|
|
618
|
+
* 'xxx%' -> wrapper.size * xxx/100
|
|
619
|
+
* xxx -> xxx
|
|
620
|
+
*/
|
|
621
|
+
function assertsSize(size, sum, defaultValue = Infinity) {
|
|
622
|
+
if (typeof size === "undefined")
|
|
623
|
+
return defaultValue;
|
|
624
|
+
if (typeof size === "number")
|
|
625
|
+
return size;
|
|
626
|
+
if (size.endsWith("%"))
|
|
627
|
+
return sum * (+size.replace("%", "") / 100);
|
|
628
|
+
if (size.endsWith("px"))
|
|
629
|
+
return +size.replace("px", "");
|
|
630
|
+
return defaultValue;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const Playground = (props) => {
|
|
634
|
+
var _a, _b, _c, _d;
|
|
635
|
+
const { host, onSave } = props;
|
|
636
|
+
const editorRef = useRef(undefined);
|
|
637
|
+
const [selectedEmitter, onSelectedEmitterChange] = useControllableValue(props.emitter, props.defaultEmitter, props.onEmitterChange);
|
|
638
|
+
const [compilerOptions, onCompilerOptionsChange] = useControllableValue(props.compilerOptions, (_a = props.defaultCompilerOptions) !== null && _a !== void 0 ? _a : {}, props.onCompilerOptionsChange);
|
|
639
|
+
const [selectedSampleName, onSelectedSampleNameChange] = useControllableValue(props.sampleName, props.defaultSampleName, props.onSampleNameChange);
|
|
640
|
+
const [content, setContent] = useState(props.defaultContent);
|
|
641
|
+
const isSampleUntouched = useMemo(() => {
|
|
642
|
+
var _a, _b;
|
|
643
|
+
return Boolean(selectedSampleName && content === ((_b = (_a = props.samples) === null || _a === void 0 ? void 0 : _a[selectedSampleName]) === null || _b === void 0 ? void 0 : _b.content));
|
|
644
|
+
}, [content, selectedSampleName, props.samples]);
|
|
645
|
+
const typespecModel = useMonacoModel("inmemory://test/main.tsp", "typespec");
|
|
646
|
+
const [compilationState, setCompilationState] = useState(undefined);
|
|
647
|
+
const doCompile = useCallback(async () => {
|
|
648
|
+
const content = typespecModel.getValue();
|
|
649
|
+
setContent(content);
|
|
650
|
+
const typespecCompiler = host.compiler;
|
|
651
|
+
const state = await compile(host, content, selectedEmitter, compilerOptions);
|
|
652
|
+
setCompilationState(state);
|
|
653
|
+
if ("program" in state) {
|
|
654
|
+
const markers = state.program.diagnostics.map((diag) => ({
|
|
655
|
+
...getMarkerLocation(typespecCompiler, diag.target),
|
|
656
|
+
message: diag.message,
|
|
657
|
+
severity: diag.severity === "error" ? MarkerSeverity.Error : MarkerSeverity.Warning,
|
|
658
|
+
tags: diag.code === "deprecated" ? [CompletionItemTag.Deprecated] : undefined,
|
|
659
|
+
}));
|
|
660
|
+
editor.setModelMarkers(typespecModel, "owner", markers !== null && markers !== void 0 ? markers : []);
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
editor.setModelMarkers(typespecModel, "owner", []);
|
|
664
|
+
}
|
|
665
|
+
}, [host, selectedEmitter, compilerOptions, typespecModel, setContent]);
|
|
666
|
+
const updateTypeSpec = useCallback((value) => {
|
|
667
|
+
if (typespecModel.getValue() !== value) {
|
|
668
|
+
typespecModel.setValue(value);
|
|
669
|
+
}
|
|
670
|
+
}, [typespecModel]);
|
|
671
|
+
useEffect(() => {
|
|
672
|
+
var _a;
|
|
673
|
+
updateTypeSpec((_a = props.defaultContent) !== null && _a !== void 0 ? _a : "");
|
|
674
|
+
}, []);
|
|
675
|
+
useEffect(() => {
|
|
676
|
+
if (selectedSampleName && props.samples) {
|
|
677
|
+
const config = props.samples[selectedSampleName];
|
|
678
|
+
if (config.content) {
|
|
679
|
+
updateTypeSpec(config.content);
|
|
680
|
+
if (config.preferredEmitter) {
|
|
681
|
+
onSelectedEmitterChange(config.preferredEmitter);
|
|
682
|
+
}
|
|
683
|
+
if (config.compilerOptions) {
|
|
684
|
+
onCompilerOptionsChange(config.compilerOptions);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}, [updateTypeSpec, selectedSampleName]);
|
|
689
|
+
useEffect(() => {
|
|
690
|
+
const debouncer = debounce(() => doCompile(), 200);
|
|
691
|
+
const disposable = typespecModel.onDidChangeContent(debouncer);
|
|
692
|
+
return () => {
|
|
693
|
+
debouncer.clear();
|
|
694
|
+
disposable.dispose();
|
|
695
|
+
};
|
|
696
|
+
}, [typespecModel, doCompile]);
|
|
697
|
+
useEffect(() => {
|
|
698
|
+
void doCompile();
|
|
699
|
+
}, [doCompile]);
|
|
700
|
+
const saveCode = useCallback(() => {
|
|
701
|
+
if (onSave) {
|
|
702
|
+
onSave({
|
|
703
|
+
content: typespecModel.getValue(),
|
|
704
|
+
emitter: selectedEmitter,
|
|
705
|
+
options: compilerOptions,
|
|
706
|
+
sampleName: isSampleUntouched ? selectedSampleName : undefined,
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
}, [
|
|
710
|
+
typespecModel,
|
|
711
|
+
onSave,
|
|
712
|
+
selectedEmitter,
|
|
713
|
+
compilerOptions,
|
|
714
|
+
selectedSampleName,
|
|
715
|
+
isSampleUntouched,
|
|
716
|
+
]);
|
|
717
|
+
const formatCode = useCallback(() => {
|
|
718
|
+
var _a, _b;
|
|
719
|
+
void ((_b = (_a = editorRef.current) === null || _a === void 0 ? void 0 : _a.getAction("editor.action.formatDocument")) === null || _b === void 0 ? void 0 : _b.run());
|
|
720
|
+
}, [typespecModel]);
|
|
721
|
+
const newIssue = useCallback(async () => {
|
|
722
|
+
var _a;
|
|
723
|
+
saveCode();
|
|
724
|
+
const bodyPayload = encodeURIComponent(`\n\n\n[Playground Link](${document.location.href})`);
|
|
725
|
+
const url = `${(_a = props === null || props === void 0 ? void 0 : props.links) === null || _a === void 0 ? void 0 : _a.githubIssueUrl}?body=${bodyPayload}`;
|
|
726
|
+
window.open(url, "_blank");
|
|
727
|
+
}, [saveCode, typespecModel]);
|
|
728
|
+
const typespecEditorActions = useMemo(() => [
|
|
729
|
+
// ctrl/cmd+S => save
|
|
730
|
+
{ id: "save", label: "Save", keybindings: [KeyMod.CtrlCmd | KeyCode.KeyS], run: saveCode },
|
|
731
|
+
], [saveCode]);
|
|
732
|
+
const onTypeSpecEditorMount = useCallback(({ editor }) => {
|
|
733
|
+
editorRef.current = editor;
|
|
734
|
+
}, []);
|
|
735
|
+
const libraries = useMemo(() => Object.values(host.libraries), [host.libraries]);
|
|
736
|
+
return (jsxs("div", { css: {
|
|
737
|
+
display: "grid",
|
|
738
|
+
gridTemplateColumns: "1",
|
|
739
|
+
gridTemplateRows: "1fr auto",
|
|
740
|
+
gridTemplateAreas: '"typespeceditor"\n "footer"',
|
|
741
|
+
width: "100%",
|
|
742
|
+
height: "100%",
|
|
743
|
+
overflow: "hidden",
|
|
744
|
+
fontFamily: `"Segoe UI", Tahoma, Geneva, Verdana, sans-serif`,
|
|
745
|
+
}, children: [jsxs(SplitPane, { initialSizes: ["50%", "50%"], css: { gridArea: "typespeceditor", width: "100%", height: "100%", overflow: "hidden" }, children: [jsxs(Pane, { children: [jsx(EditorCommandBar, { libraries: libraries, selectedEmitter: selectedEmitter, onSelectedEmitterChange: onSelectedEmitterChange, compilerOptions: compilerOptions, onCompilerOptionsChange: onCompilerOptionsChange, samples: props.samples, selectedSampleName: selectedSampleName, onSelectedSampleNameChange: onSelectedSampleNameChange, saveCode: saveCode, formatCode: formatCode, newIssue: ((_b = props === null || props === void 0 ? void 0 : props.links) === null || _b === void 0 ? void 0 : _b.githubIssueUrl) ? newIssue : undefined, documentationUrl: (_c = props.links) === null || _c === void 0 ? void 0 : _c.documentationUrl }), jsx(TypeSpecEditor, { model: typespecModel, actions: typespecEditorActions, onMount: onTypeSpecEditorMount })] }), jsx(Pane, { children: jsx(OutputView, { compilationState: compilationState, viewers: (_d = props.emitterViewers) === null || _d === void 0 ? void 0 : _d[selectedEmitter] }) })] }), jsx(Footer, {})] }));
|
|
746
|
+
};
|
|
747
|
+
const outputDir = "./tsp-output";
|
|
748
|
+
async function compile(host, content, selectedEmitter, options) {
|
|
749
|
+
var _a;
|
|
750
|
+
await host.writeFile("main.tsp", content);
|
|
751
|
+
await emptyOutputDir(host);
|
|
752
|
+
try {
|
|
753
|
+
const typespecCompiler = host.compiler;
|
|
754
|
+
const program = await typespecCompiler.compile(host, "main.tsp", {
|
|
755
|
+
...options,
|
|
756
|
+
options: {
|
|
757
|
+
...options.options,
|
|
758
|
+
[selectedEmitter]: {
|
|
759
|
+
...(_a = options.options) === null || _a === void 0 ? void 0 : _a[selectedEmitter],
|
|
760
|
+
"emitter-output-dir": "tsp-output",
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
outputDir: "tsp-output",
|
|
764
|
+
emit: [selectedEmitter],
|
|
765
|
+
});
|
|
766
|
+
const outputFiles = await findOutputFiles(host);
|
|
767
|
+
return { program, outputFiles };
|
|
768
|
+
}
|
|
769
|
+
catch (error) {
|
|
770
|
+
// eslint-disable-next-line no-console
|
|
771
|
+
console.error("Internal compiler error", error);
|
|
772
|
+
return { internalCompilerError: error };
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
async function findOutputFiles(host) {
|
|
776
|
+
const files = [];
|
|
777
|
+
async function addFiles(dir) {
|
|
778
|
+
const items = await host.readDir(outputDir + dir);
|
|
779
|
+
for (const item of items) {
|
|
780
|
+
const itemPath = `${dir}/${item}`;
|
|
781
|
+
if ((await host.stat(outputDir + itemPath)).isDirectory()) {
|
|
782
|
+
await addFiles(itemPath);
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
files.push(dir === "" ? item : `${dir}/${item}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
await addFiles("");
|
|
790
|
+
return files;
|
|
791
|
+
}
|
|
792
|
+
async function emptyOutputDir(host) {
|
|
793
|
+
// empty output directory
|
|
794
|
+
const dirs = await host.readDir("./tsp-output");
|
|
795
|
+
for (const file of dirs) {
|
|
796
|
+
const path = "./tsp-output/" + file;
|
|
797
|
+
const uri = Uri.parse(host.pathToFileURL(path));
|
|
798
|
+
const model = editor.getModel(uri);
|
|
799
|
+
if (model) {
|
|
800
|
+
model.dispose();
|
|
801
|
+
}
|
|
802
|
+
await host.rm(path, { recursive: true });
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
async function createReactPlayground(config) {
|
|
807
|
+
const host = await createBrowserHost(config.libraries, config.importConfig);
|
|
808
|
+
await registerMonacoLanguage(host);
|
|
809
|
+
registerMonacoDefaultWorkers();
|
|
810
|
+
const stateStorage = createStandalonePlaygroundStateStorage();
|
|
811
|
+
const initialState = stateStorage.load();
|
|
812
|
+
const App = () => {
|
|
813
|
+
const toasterId = useId$1();
|
|
814
|
+
const { dispatchToast } = useToastController(toasterId);
|
|
815
|
+
const options = useMemo(() => {
|
|
816
|
+
var _a;
|
|
817
|
+
return ({
|
|
818
|
+
...config,
|
|
819
|
+
host,
|
|
820
|
+
libraries: config.libraries,
|
|
821
|
+
defaultContent: initialState.content,
|
|
822
|
+
defaultEmitter: (_a = initialState.emitter) !== null && _a !== void 0 ? _a : config.defaultEmitter,
|
|
823
|
+
defaultCompilerOptions: initialState.options,
|
|
824
|
+
defaultSampleName: initialState.sampleName,
|
|
825
|
+
onSave: (value) => {
|
|
826
|
+
stateStorage.save(value);
|
|
827
|
+
void navigator.clipboard.writeText(window.location.toString());
|
|
828
|
+
dispatchToast(jsxs(Toast, { children: [jsx(ToastTitle, { children: "Saved!" }), jsx(ToastBody, { children: "Playground link has been copied to the clipboard." })] }), { intent: "success" });
|
|
829
|
+
},
|
|
830
|
+
});
|
|
831
|
+
}, [dispatchToast]);
|
|
832
|
+
return (jsxs(FluentProvider, { theme: webLightTheme, children: [jsx(Toaster, { toasterId: toasterId }), jsx("div", { css: { height: "100vh" }, children: jsx(Playground, { ...options }) })] }));
|
|
833
|
+
};
|
|
834
|
+
return jsx(App, {});
|
|
835
|
+
}
|
|
836
|
+
async function renderReactPlayground(config) {
|
|
837
|
+
const app = await createReactPlayground(config);
|
|
838
|
+
const root = createRoot(document.getElementById("root"));
|
|
839
|
+
root.render(app);
|
|
840
|
+
}
|
|
841
|
+
function createStandalonePlaygroundStateStorage() {
|
|
842
|
+
const stateStorage = createUrlStateStorage({
|
|
843
|
+
content: {
|
|
844
|
+
queryParam: "c",
|
|
845
|
+
compress: "lz-base64",
|
|
846
|
+
},
|
|
847
|
+
emitter: {
|
|
848
|
+
queryParam: "e",
|
|
849
|
+
},
|
|
850
|
+
options: {
|
|
851
|
+
type: "object",
|
|
852
|
+
queryParam: "options",
|
|
853
|
+
},
|
|
854
|
+
sampleName: {
|
|
855
|
+
queryParam: "sample",
|
|
856
|
+
},
|
|
857
|
+
});
|
|
858
|
+
return {
|
|
859
|
+
load: stateStorage.load,
|
|
860
|
+
save(data) {
|
|
861
|
+
stateStorage.save(data.sampleName ? { sampleName: data.sampleName, options: data.options } : data);
|
|
862
|
+
},
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export { Playground, createReactPlayground, renderReactPlayground };
|
|
867
|
+
//# sourceMappingURL=index.js.map
|