diff --git a/README.md b/README.md
index 7a887cb..2f44a56 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,13 @@
- 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 +89,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
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",
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..a7d4f6a 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')!;
@@ -191,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');
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 {