/ concept-collection / stan-web-ide
Sign in
concept-collection / stan-web-ide
stan-web-ide / src / ide.ts
111 lines · 4.1 KBBlameHistoryRaw
1import { attachGitHubSourceControl, createWorkbench, type Workbench, type WorkbenchTheme, type WorkspaceFileSystem } from 'minwebide';
2import { createCsvTableProvider } from './csvTable';
3import { openProjectFileSystem, touchProject, type ProjectInfo } from './projects';
4import { createResultsViewProvider } from './stan/resultsView';
5import { createStanRunner } from './stan/runner';
6import { createSampleEditorProvider } from './stan/sampleEditor';
7import { showServerDialog } from './stan/serverDialog';
8import { getServerUrl, onDidChangeServerUrl, probeServer } from './stan/settings';
10export interface StanWorkbench {
11 readonly workbench: Workbench;
12 dispose(): void;
15/**
16 * Assembles the Stan workbench (runner, custom editors, compile-server status
17 * item) on a file system. Shared by project IDEs and GitHub repo IDEs; does
18 * not own `fs` — the caller disposes it.
19 */
20export async function openStanWorkbench(container: HTMLElement, fs: WorkspaceFileSystem, workspaceName: string, theme: WorkbenchTheme): Promise<StanWorkbench> {
21 const workbench = createWorkbench(container, {
22 fileSystem: fs,
23 theme,
24 workspaceName,
25 });
26 const stan = createStanRunner(fs, (path) => workbench.openFile(fs.root.with({ path })));
27 workbench.registerRunner(stan.runner);
28 workbench.registerCustomEditor(createSampleEditorProvider(fs, workbench, { stop: stan.stop }));
29 workbench.registerCustomEditor(createResultsViewProvider(fs));
30 workbench.registerCustomEditor(createCsvTableProvider());
32 // compile-server status: shows connectivity, click to change the URL
33 let disposed = false;
34 const refreshServerItem = async () => {
35 const url = getServerUrl();
36 workbench.statusBar.setItem('stan-server', 'right', 'Stan server: checking...', {
37 icon: 'server',
38 title: `${url}\nClick to change the compilation server`,
39 onClick: () => showServerDialog(container),
40 });
41 const ok = await probeServer(url);
42 if (disposed || url !== getServerUrl()) {
43 return;
44 }
45 workbench.statusBar.setItem('stan-server', 'right', `Stan server: ${ok ? 'connected' : 'offline'}`, {
46 icon: ok ? 'server' : 'warning',
47 title: `${url}${ok ? 'connected' : 'not reachable'}\nClick to change the compilation server`,
48 onClick: () => showServerDialog(container),
49 });
50 };
51 void refreshServerItem();
52 const serverListener = onDidChangeServerUrl(() => void refreshServerItem());
54 return {
55 workbench,
56 dispose() {
57 disposed = true;
58 serverListener.dispose();
59 stan.dispose();
60 workbench.dispose();
61 },
62 };
65/** Opens the most useful starting file, if any. */
66export async function openStartingFile(fs: WorkspaceFileSystem, workbench: Workbench): Promise<void> {
67 for (const path of ['/fit.sample', '/main.stan', '/README.md']) {
68 const uri = fs.root.with({ path });
69 if (await fs.fileService.exists(uri)) {
70 await workbench.openFile(uri);
71 return;
72 }
73 }
76/** Opens the IDE for a project. Returns a disposable view. */
77export async function openIde(container: HTMLElement, project: ProjectInfo, theme: WorkbenchTheme): Promise<{ dispose(): void }> {
78 touchProject(project.id);
79 document.title = `${project.name} — stan web IDE`;
81 const fs = await openProjectFileSystem(project.id);
82 const ide = await openStanWorkbench(container, fs, project.name, theme);
84 // the project indicator: click to go back to the project list
85 ide.workbench.statusBar.setItem('project', 'left', project.name, {
86 icon: 'folder-opened',
87 title: 'Back to projects',
88 onClick: () => { location.hash = '#/'; },
89 });
90 // replace the default branding item with the project indicator
91 ide.workbench.statusBar.removeItem('branding');
93 // source control: publish this project to a new GitHub repo, or — once
94 // published — track changes and push
95 const sourceControl = await attachGitHubSourceControl(ide.workbench, fs, {
96 appName: 'stan web IDE',
97 defaultRepoName: project.name,
98 // after publishing, the repo's own route is the canonical place to work
99 onPublished: ({ owner, repo }) => { location.hash = `#/github/${owner}/${repo}`; },
100 });
102 await openStartingFile(fs, ide.workbench);
104 return {
105 dispose() {
106 sourceControl.dispose();
107 ide.dispose();
108 fs.dispose();
109 },
110 };
moveopenescclose