21efcb6stan web IDE: run Stan sampling in the browserJeremy Magland 1import { loadBuiltinTheme, registerBuiltinLanguages } from 'minwebide';
2import { openIde } from './ide';
3import { renderLanding } from './landing';
4import { registerStanLanguage } from './stan/language';
5import { registerStanLsp } from './stan/lsp';
6import { getProject } from './projects';
8// Routes:
9// #/ project picker (landing page)
10// #/project/<id> the IDE, opened on that project's file system
12async function start(): Promise<void> {
13 // dev never uses a service worker (isolation comes from server headers) —
14 // unregister any stale coi-serviceworker left over from a production
15 // build or an earlier version, since a controlling stale SW can block
16 // module workers with mismatched COEP headers
17 if (import.meta.env.DEV && 'serviceWorker' in navigator) {
18 const registrations = await navigator.serviceWorker.getRegistrations();
19 if (registrations.length > 0) {
20 await Promise.all(registrations.map(r => r.unregister()));
21 if (navigator.serviceWorker.controller) {
22 location.reload();
23 return;
24 }
25 }
26 }
28 const app = document.getElementById('app')!;
30 // one-time global setup: theme + languages are shared by all views;
31 // Stan registers last so it owns .stan (and .sample maps to YAML)
32 const theme = await loadBuiltinTheme('dark_modern');
33 await registerBuiltinLanguages(theme);
34 registerStanLanguage();
35 // the Stan language server (diagnostics, hover, completion, format) runs
36 // in one worker for the whole session, across projects
37 registerStanLsp();
39 let current: { dispose(): void } | undefined;
40 let navigating = false;
42 const route = async () => {
43 if (navigating) {
44 return;
45 }
46 navigating = true;
47 try {
48 current?.dispose();
49 current = undefined;
50 app.textContent = '';
52 const match = location.hash.match(/^#\/project\/([a-z0-9]+)/i);
53 if (match) {
54 const project = getProject(match[1]);
55 if (project) {
56 current = await openIde(app, project, theme);
57 return;
58 }
59 // unknown project id: fall through to the landing page
60 history.replaceState(null, '', '#/');
61 }
62 current = renderLanding(app, theme);
63 } finally {
64 navigating = false;
65 }
66 };
68 window.addEventListener('hashchange', route);
69 await route();
70}
72start();