import { attachGitHubSourceControl, createWorkbench, type Workbench, type WorkbenchTheme, type WorkspaceFileSystem } from 'minwebide'; import { createMipSystem } from './numbl/mipSystem'; import { createNumblRunner } from './numbl/runner'; import { openProjectFileSystem, touchProject, type ProjectInfo } from './projects'; export interface NumblWorkbench { readonly workbench: Workbench; dispose(): void; } /** * Assembles the numbl workbench (mip system + runner) on a file system. * Shared by project IDEs and GitHub repo IDEs; does not own `fs` — the * caller disposes it. */ export async function openNumblWorkbench(container: HTMLElement, fs: WorkspaceFileSystem, workspaceName: string, theme: WorkbenchTheme): Promise { const mip = await createMipSystem(); // fetch/refresh mip core in the background; runs await it (and say so // in the output channel if they actually have to wait) void mip.ensureCore().catch(() => { /* retried on the next run */ }); const workbench = createWorkbench(container, { fileSystem: fs, theme, workspaceName, }); const numbl = createNumblRunner(fs, workbench, mip); workbench.registerRunner(numbl.runner); return { workbench, dispose() { numbl.dispose(); workbench.dispose(); mip.dispose(); }, }; } /** Opens the most useful starting file, if any. */ export async function openStartingFile(fs: WorkspaceFileSystem, workbench: Workbench): Promise { for (const path of ['/waves.m', '/main.m', '/README.md']) { const uri = fs.root.with({ path }); if (await fs.fileService.exists(uri)) { await workbench.openFile(uri); return; } } } /** Opens the IDE for a project. Returns a disposable view. */ export async function openIde(container: HTMLElement, project: ProjectInfo, theme: WorkbenchTheme): Promise<{ dispose(): void }> { touchProject(project.id); document.title = `${project.name} — numbl web IDE`; const fs = await openProjectFileSystem(project.id); const ide = await openNumblWorkbench(container, fs, project.name, theme); // the project indicator: click to go back to the project list ide.workbench.statusBar.setItem('project', 'left', project.name, { icon: 'folder-opened', title: 'Back to projects', onClick: () => { location.hash = '#/'; }, }); // replace the default branding item with the project indicator ide.workbench.statusBar.removeItem('branding'); // source control: publish this project to a new GitHub repo, or — once // published — track changes and push const sourceControl = await attachGitHubSourceControl(ide.workbench, fs, { appName: 'numbl web IDE', defaultRepoName: project.name, // after publishing, the repo's own route is the canonical place to work onPublished: ({ owner, repo }) => { location.hash = `#/github/${owner}/${repo}`; }, }); await openStartingFile(fs, ide.workbench); return { dispose() { sourceControl.dispose(); ide.dispose(); fs.dispose(); }, }; }