Skip to content

Commit 21776a3

Browse files
Inline submit — no page navigation, everything in background (#5)
* Replace redirect flow with inline submit via Cloudflare Worker The previous flow took users off the page to a GitHub issue form. Now everything happens in the background: 1. User pastes URL, clicks Request 2. Frontend POSTs to /api/request (Cloudflare Worker) 3. Worker creates the GitHub issue (triggers auto-add pipeline) 4. User sees inline success message with their docs link Changes: - Add worker/ with Cloudflare Worker that proxies to GitHub Issues API - Rewrite frontend JS to use fetch() with loading/success/error states - Remove nav "Request a Repo" button (the inline form is the entry point) - Remove .request-btn CSS, add .submit-feedback state classes Worker deployment: cd worker && npx wrangler secret put GITHUB_TOKEN npx wrangler deploy Route repos.supermodeltools.com/api/* to the worker in Cloudflare. * Redirect to loading page after Generate, auto-refresh when docs ready UX flow: 1. User pastes URL, clicks "Generate" (renamed from Request) 2. API fires in background, user is redirected to the docs URL 3. Since docs aren't deployed yet, GitHub Pages serves 404.html 4. 404.html detects the repo path and shows a loading page: - Supermodel eye logo with scanning animation - Animated progress steps (Forking → Analyzing → Building → Deploying) - Polls the URL every 5s for up to 10 minutes 5. When docs deploy, poll detects real content and page auto-reloads 6. User sees the full architecture docs For genuine 404s (not repo paths), shows a standard 404 page. Changes: - generate-index.go: add generate404() + notFoundTemplate - Rename "Request" → "Generate" in button text - Frontend JS redirects to docs_url after successful API call - 404.html: loading animation, step progress, polling logic * Skeleton loading page instead of 404 — no 404 ever shown After clicking Generate, the user lands on /generating/{name} which is served by the Cloudflare Worker as a real 200 page. It mirrors the actual arch-docs layout with shimmer placeholders: - Same header with repo name, nav links, "All Repos" back link - Skeleton hero with shimmer stat counters - Skeleton chart panels (Architecture Overview, Composition) - Skeleton taxonomy grids (Node Types, Domains, Languages) - Floating status bar: "Generating docs — analyzing codebase..." The status bar cycles through stages (forking, analyzing, building graphs, mapping architecture, deploying) on realistic timers. Polls the real docs URL every 5s. When docs are ready, the status bar flashes "Ready!" and redirects to the real page. The user sees what the page WILL look like, never sees a 404, and is seamlessly transitioned to the real docs when ready. Also: - Renamed "Request" → "Generate" everywhere - 404.html now just redirects to homepage (for genuine broken links)
1 parent 676f876 commit 21776a3

File tree

3 files changed

+506
-32
lines changed

3 files changed

+506
-32
lines changed

generate-index.go

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ func main() {
5959
os.Exit(1)
6060
}
6161

62+
if err := generate404(); err != nil {
63+
fmt.Fprintf(os.Stderr, "Error generating 404.html: %v\n", err)
64+
os.Exit(1)
65+
}
66+
6267
// Copy CNAME and static root files to site directory
6368
if cname, err := os.ReadFile("CNAME"); err == nil {
6469
os.WriteFile("site/CNAME", cname, 0644)
@@ -71,7 +76,14 @@ func main() {
7176
for _, cat := range cfg.Categories {
7277
totalRepos += len(cat.Repos)
7378
}
74-
fmt.Printf("Generated index.html and sitemap.xml (%d repos)\n", totalRepos)
79+
fmt.Printf("Generated site (%d repos)\n", totalRepos)
80+
}
81+
82+
func generate404() error {
83+
return os.WriteFile("site/404.html", []byte(`<!DOCTYPE html>
84+
<html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=/"><title>Redirecting…</title></head>
85+
<body><p>Redirecting to <a href="/">homepage</a>…</p></body></html>
86+
`), 0644)
7587
}
7688

7789
func generateSitemap(cfg Config) error {
@@ -196,18 +208,6 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
196208
.site-nav { display: flex; gap: 16px; align-items: center; }
197209
.site-nav a { color: var(--text-muted); font-size: 14px; font-weight: 500; white-space: nowrap; }
198210
.site-nav a:hover { color: var(--text); text-decoration: none; }
199-
.request-btn {
200-
color: var(--accent-light) !important;
201-
border: 1px solid var(--accent);
202-
border-radius: var(--radius);
203-
padding: 6px 12px;
204-
transition: background 0.2s, color 0.2s;
205-
}
206-
.request-btn:hover {
207-
background: var(--accent);
208-
color: #fff !important;
209-
text-decoration: none !important;
210-
}
211211
.hero {
212212
padding: 64px 0 48px;
213213
text-align: center;
@@ -395,14 +395,21 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
395395
pointer-events: auto;
396396
}
397397
.submit-btn.active:hover { background: var(--accent-light); }
398-
.submit-preview {
399-
margin-top: 8px;
398+
.submit-btn.loading {
399+
opacity: 0.6;
400+
pointer-events: none;
401+
}
402+
.submit-feedback {
403+
margin-top: 10px;
400404
font-size: 13px;
401-
color: var(--green);
402405
font-family: var(--mono);
403406
display: none;
404407
}
405-
.submit-preview.visible { display: block; }
408+
.submit-feedback.visible { display: block; }
409+
.submit-feedback.preview { color: var(--text-muted); }
410+
.submit-feedback.success { color: var(--green); }
411+
.submit-feedback.success a { color: var(--green); text-decoration: underline; }
412+
.submit-feedback.error { color: var(--red); }
406413
@media (max-width: 768px) {
407414
.container { padding: 0 16px; }
408415
.hero { padding: 40px 0 32px; }
@@ -432,7 +439,6 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
432439
<a href="https://supermodeltools.com">Website</a>
433440
<a href="https://github.com/supermodeltools">GitHub</a>
434441
<a href="https://x.com/supermodeltools">X</a>
435-
<a href="https://github.com/supermodeltools/supermodeltools.github.io/issues/new?template=request-repo.yml" class="request-btn">+ Request a Repo</a>
436442
</nav>
437443
</div>
438444
</header>
@@ -462,9 +468,9 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
462468
<div class="submit-label">Don't see your repo? Paste a URL to generate arch docs:</div>
463469
<div class="submit-row">
464470
<input type="text" class="submit-input" id="submit-url" placeholder="https://github.com/owner/repo" autocomplete="off" spellcheck="false">
465-
<button class="submit-btn" id="submit-btn" type="button">Request</button>
471+
<button class="submit-btn" id="submit-btn" type="button">Generate</button>
466472
</div>
467-
<div class="submit-preview" id="submit-preview"></div>
473+
<div class="submit-feedback" id="submit-feedback"></div>
468474
</div>
469475
</div>
470476
@@ -510,10 +516,10 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
510516
var noResults = document.getElementById('no-results');
511517
var submitInput = document.getElementById('submit-url');
512518
var submitBtn = document.getElementById('submit-btn');
513-
var submitPreview = document.getElementById('submit-preview');
519+
var feedback = document.getElementById('submit-feedback');
514520
var noResultsRequest = document.getElementById('no-results-request');
515521
516-
var issueBase = 'https://github.com/supermodeltools/supermodeltools.github.io/issues/new?template=request-repo.yml';
522+
var API_URL = '/api/request';
517523
518524
// --- Search ---
519525
searchInput.addEventListener('input', function() {
@@ -546,36 +552,65 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
546552
return null;
547553
}
548554
555+
function showFeedback(msg, type) {
556+
feedback.className = 'submit-feedback visible ' + type;
557+
feedback.innerHTML = msg;
558+
}
559+
549560
submitInput.addEventListener('input', function() {
550561
var parsed = parseRepo(this.value);
551562
if (parsed) {
552563
var name = parsed.split('/')[1];
553-
submitPreview.textContent = '\u2192 Docs will be at repos.supermodeltools.com/' + name + '/';
554-
submitPreview.classList.add('visible');
564+
showFeedback('\u2192 repos.supermodeltools.com/' + name + '/', 'preview');
555565
submitBtn.classList.add('active');
556566
} else {
557-
submitPreview.classList.remove('visible');
567+
feedback.className = 'submit-feedback';
558568
submitBtn.classList.remove('active');
559569
}
560570
});
561571
562-
function submitRequest() {
572+
async function submitRequest() {
563573
var parsed = parseRepo(submitInput.value);
564574
if (!parsed) return;
575+
565576
var repoUrl = 'https://github.com/' + parsed;
566577
var name = parsed.split('/')[1];
567-
var url = issueBase
568-
+ '&repo_url=' + encodeURIComponent(repoUrl)
569-
+ '&title=' + encodeURIComponent('[Repo Request] ' + name);
570-
window.open(url, '_blank');
578+
579+
// Loading state
580+
submitBtn.classList.add('loading');
581+
submitBtn.textContent = 'Generating...';
582+
showFeedback('Setting up ' + name + '...', 'preview');
583+
584+
try {
585+
var resp = await fetch(API_URL, {
586+
method: 'POST',
587+
headers: { 'Content-Type': 'application/json' },
588+
body: JSON.stringify({ url: repoUrl }),
589+
});
590+
var data = await resp.json();
591+
592+
if (!resp.ok || !data.success) {
593+
showFeedback(data.error || 'Something went wrong. Please try again.', 'error');
594+
submitBtn.classList.remove('loading');
595+
submitBtn.textContent = 'Generate';
596+
return;
597+
}
598+
599+
// Redirect to the skeleton loading page — served by the worker
600+
window.location.href = data.generating_url;
601+
} catch (e) {
602+
showFeedback('Network error. Please try again.', 'error');
603+
submitBtn.classList.remove('loading');
604+
submitBtn.textContent = 'Generate';
605+
}
571606
}
572607
573608
submitBtn.addEventListener('click', submitRequest);
574609
submitInput.addEventListener('keydown', function(e) {
575-
if (e.key === 'Enter') submitRequest();
610+
if (e.key === 'Enter' && submitBtn.classList.contains('active')) submitRequest();
576611
});
577612
578-
// "No results" request link: pre-fill with search query as a guess
613+
// "No results" link: scroll up and focus the submit input
579614
noResultsRequest.addEventListener('click', function() {
580615
var q = searchInput.value.trim();
581616
submitInput.value = q;
@@ -588,3 +623,4 @@ a:focus-visible { outline: 2px solid var(--accent-light); outline-offset: 2px; b
588623
</body>
589624
</html>
590625
`
626+

0 commit comments

Comments
 (0)