1import { applyThemeToElement, attachGitHubWorkspace, createIndexedDBFileSystem, parseGitHubSpec, type WorkbenchTheme } from 'minwebide';
2import { openNumblWorkbench, openStartingFile } from './ide';
3import './landing.css';
5// The #/github/<spec> route: a GitHub repository as a workspace of its own.
6// <spec> is anything parseGitHubSpec accepts — owner/repo, owner/repo@ref, or
7// a URL-encoded github.com URL. The URL is the identity: nothing is added to
8// the project registry. The first visit imports into a per-repo IndexedDB
9// database; later visits reopen that local copy, edits included, with the
10// Source Control view tracking changes against the imported commit (and
11// offering "Reload from GitHub" to start fresh).
13/** Handles a #/github/<spec> route. Returns a disposable view (the IDE, or an error screen). */
14export async function openGitHubRoute(container: HTMLElement, specText: string, theme: WorkbenchTheme): Promise<{ dispose(): void }> {
15 let fs: Awaited<ReturnType<typeof createIndexedDBFileSystem>> | undefined;
16 let ide: Awaited<ReturnType<typeof openNumblWorkbench>> | undefined;
17 try {
18 const spec = parseGitHubSpec(specText);
19 const name = `${spec.owner}/${spec.repo}`;
20 document.title = `${name} — numbl web IDE`;
21 const dbName = `numbl-web-ide-gh-${spec.owner}-${spec.repo}${spec.ref ? `-${spec.ref}` : ''}${spec.dir ? `-${spec.dir}` : ''}`
22 .toLowerCase().replace(/[^a-z0-9._-]/g, '-');
24 fs = await createIndexedDBFileSystem({ dbName });
25 ide = await openNumblWorkbench(container, fs, name, theme);
26 ide.workbench.statusBar.removeItem('branding');
27 ide.workbench.statusBar.setItem('project', 'left', 'Projects', {
28 icon: 'arrow-left',
29 title: 'Back to projects',
30 onClick: () => { location.hash = '#/'; },
31 });
33 // imports on first visit (status bar progress + GitHub output channel);
34 // the README is left to openStartingFile, which prefers numbl entry points
35 const view = await attachGitHubWorkspace(ide.workbench, fs, spec, { autoOpenReadme: false, appName: 'numbl web IDE' });
36 await openStartingFile(fs, ide.workbench);
38 return {
39 dispose() {
40 view.dispose();
41 ide!.dispose();
42 fs!.dispose();
43 },
44 };
45 } catch (error) {
46 ide?.dispose();
47 fs?.dispose();
48 container.textContent = '';
49 const message = error instanceof Error ? error.message : String(error);
50 return renderErrorScreen(container, theme, `Could not open repository: ${message}`);
51 }
52}
54function renderErrorScreen(container: HTMLElement, theme: WorkbenchTheme, text: string): { dispose(): void } {
55 const root = document.createElement('div');
56 root.className = 'landing';
57 applyThemeToElement(theme, root);
58 const inner = document.createElement('div');
59 inner.className = 'landing-inner';
60 const message = document.createElement('p');
61 message.className = 'landing-subtitle';
62 message.textContent = text;
63 inner.appendChild(message);
64 const back = document.createElement('a');
65 back.className = 'landing-link';
66 back.href = '#/';
67 back.textContent = 'Back to projects';
68 inner.appendChild(back);
69 root.appendChild(inner);
70 container.appendChild(root);
71 return { dispose: () => root.remove() };
72}