From 9d8ff55e22f7275b885c9dd275031bfa6ede7bf0 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:12:26 +0800 Subject: [PATCH 1/6] docs(site): start the How it works rail unlit, in step with its first step --- site/components/landing/How.tsx | 12 +++++++----- site/e2e/smoke.spec.ts | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/site/components/landing/How.tsx b/site/components/landing/How.tsx index b27f441..e745a33 100644 --- a/site/components/landing/How.tsx +++ b/site/components/landing/How.tsx @@ -113,7 +113,10 @@ export function How({ demo, receipt, repair }: { demo: LaidOutDiagram; receipt: }; layout(); setPinned(true); - setStep(0); + const draw = (progress: number) => { + if (fill.current) fill.current.style.transform = `scaleX(${progress})`; + setStep(Math.min(3, Math.floor(progress * 4))); + }; // CSS sticky does the pin; ScrollTrigger only reads how far through the runway the page is. const st = ScrollTrigger.create({ trigger: p, @@ -121,11 +124,10 @@ export function How({ demo, receipt, repair }: { demo: LaidOutDiagram; receipt: end: () => `+=${runway.current}`, invalidateOnRefresh: true, onRefreshInit: layout, - onUpdate: ({ progress }) => { - if (fill.current) fill.current.style.transform = `scaleX(${progress})`; - setStep(Math.min(3, Math.floor(progress * 4))); - }, + onUpdate: ({ progress }) => draw(progress), }); + // onUpdate waits for a scroll; until then the rail would keep its resting (full) fill. + draw(st.progress); return () => { st.kill(); p.style.height = ''; diff --git a/site/e2e/smoke.spec.ts b/site/e2e/smoke.spec.ts index f2105a9..2cf66a6 100644 --- a/site/e2e/smoke.spec.ts +++ b/site/e2e/smoke.spec.ts @@ -68,6 +68,8 @@ test('with motion allowed the walkthrough pins and the scroll picks its step', a await page.goto('/'); const pin = page.locator('.how-pin'); await expect(pin).toHaveAttribute('data-live', 'true'); + // Before any scroll the rail is unlit, as step 1 is. + await expect.poll(() => page.locator('.hfill').evaluate((el) => getComputedStyle(el).transform)).toBe('matrix(0, 0, 0, 1, 0, 0)'); const at = (f: number) => page.evaluate((f) => { const p = document.querySelector('.how-pin')!; From 543287519da02cf8fac8180cf3cbcbbb19d5e641 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:12:26 +0800 Subject: [PATCH 2/6] =?UTF-8?q?test(site):=20wait=20for=20the=20docs=20to?= =?UTF-8?q?=20hydrate=20before=20pressing=20=E2=8C=98K=20in=20the=20search?= =?UTF-8?q?=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/e2e/smoke.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/site/e2e/smoke.spec.ts b/site/e2e/smoke.spec.ts index 2cf66a6..a7d4f6a 100644 --- a/site/e2e/smoke.spec.ts +++ b/site/e2e/smoke.spec.ts @@ -193,9 +193,12 @@ test('the old kind pages redirect into the docs', async ({ page }) => { test('the docs search opens with ⌘K, finds a field and goes to it', async ({ page }) => { await page.goto('/docs'); - await page.keyboard.press('ControlOrMeta+k'); const box = page.getByRole('combobox', { name: 'Search the docs' }); - await expect(box).toBeFocused(); + // The shortcut is bound once the page hydrates; a press before that lands nowhere. + await expect(async () => { + await page.keyboard.press('ControlOrMeta+k'); + await expect(box).toBeFocused({ timeout: 500 }); + }).toPass(); await box.fill('statsNote'); await expect(page.getByRole('option').first()).toContainText('statsNote'); await page.keyboard.press('Enter'); From 52317dfadef0b4388ec61e4f4f80c10597db2785 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:35:18 +0800 Subject: [PATCH 3/6] docs(readme): add npm, CI and licence badges under the header --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 7a887cb..570f455 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,12 @@

+

+ npm + CI + MIT licence +

+

Website · Docs · Examples

From 2409553d9319994a9cc064a6376b266276afbbc2 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:41:15 +0800 Subject: [PATCH 4/6] docs(site): pin canonical, Open Graph and sitemap URLs to stackmap.omsimos.com --- site/lib/seo.test.ts | 12 ++++-------- site/lib/seo.ts | 11 +++++------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/site/lib/seo.test.ts b/site/lib/seo.test.ts index c1c7213..2a4d23e 100644 --- a/site/lib/seo.test.ts +++ b/site/lib/seo.test.ts @@ -20,20 +20,16 @@ function pageRoutes(dir = join(site, 'app')): string[] { } describe('siteUrl', () => { - it('uses the production domain Vercel reports', () => { - expect(siteUrl({ VERCEL_PROJECT_PRODUCTION_URL: 'stackmap.dev' }).href).toBe('https://stackmap.dev/'); - }); - - it('falls back to the project domain outside Vercel', () => { - expect(siteUrl({}).href).toBe('https://stackmap-site-hyameros-projects.vercel.app/'); + it('is the custom domain, never a vercel.app one', () => { + expect(siteUrl().href).toBe('https://stackmap.omsimos.com/'); }); }); describe('robots', () => { it('lets crawlers in on production and points them at the sitemap', () => { - const r = robotsFor({ VERCEL_ENV: 'production', VERCEL_PROJECT_PRODUCTION_URL: 'stackmap.dev' }); + const r = robotsFor({ VERCEL_ENV: 'production', VERCEL_PROJECT_PRODUCTION_URL: 'stackmap-site.vercel.app' }); expect(r.rules).toEqual({ userAgent: '*', allow: '/' }); - expect(r.sitemap).toBe('https://stackmap.dev/sitemap.xml'); + expect(r.sitemap).toBe('https://stackmap.omsimos.com/sitemap.xml'); }); it('keeps previews and local builds out of the index', () => { diff --git a/site/lib/seo.ts b/site/lib/seo.ts index cc7f7c5..602f662 100644 --- a/site/lib/seo.ts +++ b/site/lib/seo.ts @@ -21,15 +21,14 @@ export const OPEN_GRAPH = { /** Every static page; the sitemap test fails when a page.tsx is added without its route here. */ export const ROUTES = ['/', '/docs', '/docs/viewer', '/docs/cli', '/docs/schema', '/docs/brands', '/examples'] as const; -// Vercel sets the production domain on every deployment, previews included, so canonical and OG URLs -// never point at a preview. It follows a custom domain once one is attached. -export function siteUrl(env: Env = process.env): URL { - return new URL(`https://${env.VERCEL_PROJECT_PRODUCTION_URL ?? 'stackmap-site-hyameros-projects.vercel.app'}`); -} +// Pinned rather than read from VERCEL_PROJECT_PRODUCTION_URL: Vercel reports the project's vercel.app domain there +// even with the custom domain attached, and canonical, OG and sitemap URLs must all name the one real address. +export const SITE_URL = 'https://stackmap.omsimos.com'; +export const siteUrl = (): URL => new URL(SITE_URL); export function robotsFor(env: Env = process.env): MetadataRoute.Robots { if (env.VERCEL_ENV !== 'production') return { rules: { userAgent: '*', disallow: '/' } }; - return { rules: { userAgent: '*', allow: '/' }, sitemap: new URL('/sitemap.xml', siteUrl(env)).href }; + return { rules: { userAgent: '*', allow: '/' }, sitemap: new URL('/sitemap.xml', siteUrl()).href }; } export function sitemapFor(base: URL = siteUrl()): MetadataRoute.Sitemap { From 41bcfb17ba9ac39dcf730bc9267f47112ac6549b Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:41:15 +0800 Subject: [PATCH 5/6] docs(readme): link the website at stackmap.omsimos.com --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a887cb..79a46c0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- Website · Docs · Examples + Website · Docs · Examples

Ask your coding agent to diagram a codebase, a system, a process or a request. It writes a small typed JSON, stackmap validates it with repair hints the agent acts on, lays it out, and delivers one self-contained HTML viewer. No server, no account, nothing to install to open it. @@ -83,7 +83,7 @@ The agent writes `.stackmap//diagram.json`, validates and repairs it, and - **Edges** can be `async` or a `return`, with a `tone` for the main path, security crossings and failure paths. - **No coordinates:** stackmap lays everything out, with [ELK](https://eclipse.dev/elk/) for architecture and dataflow and its own layout for lanes and sequences. -Full reference: [skill/references/schema.md](skill/references/schema.md), or the [schema reference](https://stackmap-site.vercel.app/docs/schema) on the website. Modelling guidance: [authoring contract](skill/references/authoring-contract.md). +Full reference: [skill/references/schema.md](skill/references/schema.md), or the [schema reference](https://stackmap.omsimos.com/docs/schema) on the website. Modelling guidance: [authoring contract](skill/references/authoring-contract.md). ## The CLI From e1aadd531a3e800166796c94d80192d6ac6af803 Mon Sep 17 00:00:00 2001 From: Joseph Dale Banares Date: Fri, 2 Oct 2026 17:41:15 +0800 Subject: [PATCH 6/6] chore(cli): point the npm homepage at stackmap.omsimos.com --- packages/cli/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 91fce8a..d41ff12 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -13,7 +13,7 @@ "html", "dataflow" ], - "homepage": "https://github.com/hyamero/stackmap#readme", + "homepage": "https://stackmap.omsimos.com", "repository": { "type": "git", "url": "git+https://github.com/hyamero/stackmap.git",