Commit Graph

6858 Commits

Author SHA1 Message Date
Jordan Ritter 018be75633 fix(showcase/scripts): loadDocsLinks parse failures surface via allErrors
A malformed docs-links.json was only console.warn'd and treated as
empty — build ran green while the override file silently rotted.
Accept an errors accumulator and push parse failures into it so
main()'s non-zero exit path fires. Missing file and stale-shape
tolerance are unchanged (both are legitimate states).
2026-04-20 17:49:58 -07:00
Jordan Ritter 3326b3c1e1 fix(showcase/scripts): probeOg adds timeout + error logging
Without a timeout on the inner fetch, a hung upstream would stall the
entire docs-probe run indefinitely (Node's fetch has no default
timeout). Add a 10s AbortController. The bare catch also left 'error'
states opaque — log URL + error kind so operators can tell an
abort from DNS from TLS.
2026-04-20 17:49:58 -07:00
Jordan Ritter 5068852a29 fix(showcase/scripts): probeHealth logs error details
The bare catch returned 'unknown' with no trace, making it impossible
to distinguish DNS failures from aborts from TLS errors in the unknown
bucket. Log the URL plus a kind:code pair so spikes of 'unknown' are
actually diagnosable.
2026-04-20 17:49:58 -07:00
Jordan Ritter ef9b856ed6 fix(showcase/scripts): generate-status env-gates mock e2e/smoke/qa + warning banner
mockE2E/mockSmoke/mockQA were called unconditionally from main(), so
production dashboards shipped seeded test data in columns that aren't
wired to real CI yet. Gate each behind GENERATE_STATUS_MOCK_*
env vars (default off → emit null / 'unknown'). Print a prominent
banner to stderr naming which columns are mock whenever any gate is
on, so operators can't discover stub data by surprise.
2026-04-20 17:49:57 -07:00
Jordan Ritter 22b9050608 fix(showcase/shell): next.config throws on corrupt registry in production; logs in dev
A bare catch swallowed JSON.parse failures, silently returning [] and
making every /<slug> framework redirect disappear. Throw in production
(registry is a required build-time artifact) and console.warn in dev
(so a transient mid-write doesn't kill the dev loop). Also throw if
the file is missing in production for the same reason.
2026-04-20 17:49:57 -07:00
github-actions[bot] 61c3833110 style: auto-fix formatting 2026-04-21 00:42:20 +00:00
Jordan Ritter 3a3b3e37b4 refactor(showcase/scripts): starter-consistency uses agent-URL factory instead of .source munging
The cross-starter consistency test had two AGENT_URL matchers:

    function re8000() {
      return new RegExp(AGENT_URL_LOCALHOST_8000_RE.source, flags);
    }

    function re8123() {
      return new RegExp(
        AGENT_URL_LOCALHOST_8000_RE.source.replace(/8000\\b/, "8123\\b"),
        flags,
      );
    }

The 8123 variant does regex-source string munging — brittle: the
replacement target `8000\\b` has to stay in lockstep with the
shared regex's literal source, and a future port format change would
snap in a subtle way (the test could keep passing while matching
subtly wrong content, or start failing for reasons unrelated to the
starter it's guarding).

Replace the `.source.replace` hack with an exported factory
`makeAgentUrlLocalhostPortRE(port)` in `generate-starters.ts` and
have both `re8000`/`re8123` use it. The existing
`AGENT_URL_LOCALHOST_8000_RE` constant is preserved (now delegating to
the factory) so any external import stays happy.

Bonus: the factory validates port bounds so a typo can't silently
produce a pattern that never matches.
2026-04-20 17:40:22 -07:00
Jordan Ritter 571519b6ab fix(showcase/scripts): bundle-demo-content collates README regions; skips binary files
Two orthogonal bugs in the demo-content bundler:

1) README regions were silently dropped. `collectDemoFiles` pulls the
   demo-dir root README out into the `readme` field rather than
   appending to `files`, but it STILL wrote any `@region[...]`
   slices it found inside the README into `perFileRegions` under the
   README's bundled path. The region-collation loop at write time
   iterated `fileOrder = files.map(f => f.filename)`, which by
   construction never contains the README path — so those regions
   went nowhere. Walk any `perFileRegions` keys not in fileOrder
   after the main pass, so README regions still land in the public
   map. Preserves the stable file-order rule (README contributions
   come after source files, alphabetical tiebreak).

2) Binary-like files were being passed through
   `fs.readFileSync(abs, "utf-8")` and then stringified into the
   bundle. A PNG or font file typically produces a mess of
   replacement characters in `demo-content.json` — waste at best,
   visible corruption at worst when the Code tab ever tried to
   render them. Extend `SKIP_EXTENSIONS` with the usual binary
   suspects (images, fonts, archives, media, PDFs) so the walker
   drops them at scan time.
2026-04-20 17:40:11 -07:00
Jordan Ritter 91d847be06 fix(showcase/scripts): probe-docs reads shell_docs_path with legacy fallback
The canonical schema in `scripts/generate-registry.ts` + per-package
`docs-links.json` calls the shell path `shell_docs_path`. probe-docs
was still reading ONLY the legacy `shell_docs_url` key, so any
future cleanup of `shared/feature-registry.json` to the canonical
key would silently turn every shell-docs status into 'missing'.

Prefer `shell_docs_path`, fall back to `shell_docs_url` so older
registry snapshots still contribute. Emit a one-shot dev-mode warning
when we see only the legacy key, so a lingering registry stays visible
without spamming CI logs.
2026-04-20 17:39:58 -07:00
Jordan Ritter 1c1a26b3cb fix(ci): showcase_deploy notify surfaces jq errors; drop top-level 'local'
Two small but load-bearing hygiene fixes in the notify job's shell:

1) The matrix-legs collection pipeline used `jq -r '...' 2>/dev/null`,
   swallowing any jq parse or schema errors before the surrounding
   pipeline substitution could see them. If the gh-api payload shape
   ever shifts (schema rev, partial response, encoding glitch), we
   now want the error in the job log so an operator knows WHY notify
   fell back to softer wording — the existing `-z "$pairs"` guard
   still handles the empty-output path without relying on suppression.

2) The duplicated inline `truncate_csv` helper was declaring
   `local ws=...` on its OWN line AFTER `local budget=...` on the
   function's first line. Redeclaring `local` inside the same
   function body is legal but prints 'local: not in a function'
   warnings on some bash versions (3.x, noexec edge cases) and is
   outright forbidden by `set -eu` hardening profiles. Collapse `ws`
   into the single `local` declaration at the top and use a plain
   assignment where it's computed.
2026-04-20 17:39:49 -07:00
Jordan Ritter a6b7941cd0 fix(ci): showcase_deploy classifier — skipped no longer misclassified as failure
The BUILD outcome classifier read:

    elif [ "$BUILD" = "failure" ] || [ -n "$BUILD" ]; then

After the earlier `success` + `skipped && detect=success` branches
had claimed the obvious green cases, any non-empty BUILD string fell
through the `-n` test and was tagged FAILURE — including values like
"skipped" that reach this branch when detect-changes did NOT emit
`success` (e.g. cancelled pre-stage races or a partial matrix skip).
That means the Slack bot would occasionally cry red at runs that never
failed.

Simplify to an exact string match on "failure"; the final `else`
branch already handles the empty/unknown case by emitting OUTCOME=skip,
which is the correct disposition for indeterminate state.
2026-04-20 17:39:23 -07:00
Jordan Ritter b4b3a80515 ci(workflows): bump actions/checkout to v4 in check-binaries
The rest of the showcase workflows already pin `actions/checkout@v4`.
This one lagged on v3, which is now long-deprecated (Node 16 runner
EOL). Bringing it in line with the fleet avoids a future forced bump
when GitHub retires v3 runners entirely.
2026-04-20 17:39:08 -07:00
Jordan Ritter 28e74e2a9f chore(ci): purge test-integration-tmp pollution from workflows
Remove every trace of the defunct test-integration-tmp service from the
showcase deploy workflow:

- drop the `test-integration-tmp` entry from the workflow_dispatch
  inputs.service dropdown,
- drop the detect-changes `test_integration_tmp` output + paths-filter
  entry, and
- delete the entire build-test_integration_tmp job (including its
  placeholder RAILWAY_SERVICE_ID literal).

Matches the jpr5/test_integration_tmp_leak cleanup — the test harness
leak has been patched at its source (create-integration.test.ts), but
these workflow vestiges were never part of that fix and only survived
because nothing read them after the regression guard landed.
2026-04-20 17:39:02 -07:00
github-actions[bot] 1e5dfb4b4b style: auto-fix formatting 2026-04-21 00:28:09 +00:00
Jordan Ritter 6379cbbda0 Merge jpr5/ftg-shell-ops-v2: Fix Cycle 1+2 (smart rigor) 2026-04-20 17:26:32 -07:00
Jordan Ritter 06309d9dbb fix(showcase/shell-docs): next.config build detection uses NEXT_PHASE
Replace the argv-sniffing 'process.argv.includes("build")' check with
process.env.NEXT_PHASE === "phase-production-build", the Next.js
canonical signal for a production build. The argv approach is fragile:
it breaks under wrappers, programmatic invocation, or any tool that
invokes next via a different argv shape.
2026-04-20 17:23:51 -07:00
Jordan Ritter e637a17dcc fix(showcase/shell-docs): Dockerfile fails fast when COMMIT_SHA is default unknown
COMMIT_SHA defaulted to 'unknown', so a caller that forgot to pass
--build-arg COMMIT_SHA would silently ship an image whose footer,
health checks, and error reports all report 'unknown'. Add a guard in
the builder stage that refuses to proceed when COMMIT_SHA is unset or
still the 'unknown' default.
2026-04-20 17:23:46 -07:00
Jordan Ritter c756b6404e fix(showcase/shell-docs): scripts stage uses npm ci when lockfile present
showcase/scripts/ now ships a package-lock.json (committed here for the
first time), so the Docker builder can use 'npm ci' for deterministic
installs instead of 'npm install'. Update the comment to match actual
state and copy the lockfile into the scripts stage.
2026-04-20 17:23:36 -07:00
Jordan Ritter 91122247a8 fix(showcase/shell-docs): Dockerfile prunes dev deps in runner
The runner stage was copying the entire builder node_modules tree,
shipping tailwindcss, postcss, tsx, typescript, and every @types/*
package into the production image. Add 'npm prune --omit=dev' after
the node_modules copy (and also copy package-lock.json so prune runs
deterministically) to strip dev-only packages from the runtime image.
2026-04-20 17:23:19 -07:00
Jordan Ritter 9a978efec6 refactor(showcase/shell-docs): reuse docs-render stripLeadingImports instead of fence-aware duplicate
The ag-ui and reference pages each carried their own near-identical
stripImportsFenceAware copy with a TODO(dedup) flag. The canonical
implementation already exists in docs-render.tsx as stripLeadingImports
and has better semantics — it only strips imports that appear in the
top-of-file header region (before the first non-import content line),
rather than any import anywhere outside a fence. That distinction
matters for doc bodies that legitimately discuss imports in prose
outside code fences.

Export stripLeadingImports from docs-render and point both pages at it;
remove the local duplicates.
2026-04-20 17:17:37 -07:00
Jordan Ritter 39027610bf fix(showcase/shell-docs): Card renders icon, className, children instead of dropping them
Card's type accepted icon, className, and children but the component
body never read them — silent prop drop. Render icon above the title,
merge className onto the wrapper, and render children below the
description so MDX authors get the behavior the type signature
promises.
2026-04-20 17:16:40 -07:00
Jordan Ritter 72ca49d4b2 fix(showcase/shell-docs): layout reserved-slug collision logs in production too 2026-04-20 17:16:11 -07:00
Jordan Ritter 0bab312efe fix(showcase/shell-docs): middleware cookie secure only in prod; surface posthog http errors 2026-04-20 17:16:02 -07:00
Jordan Ritter 1964255193 fix(showcase/shell-docs): single Callout implementation; remove mdx-components duplicate
Two Callout components coexisted: a restricted variant in
mdx-components.tsx (info | warn | error only) and the broader-surface
variant in docs-callout.tsx (info | tip | warn | warning | error |
danger | note). reference/[...slug]/page.tsx imported from
mdx-components, silently limiting authors to three types. Collapse to
a single implementation by re-exporting docs-callout's Callout from
mdx-components so existing import paths keep working.
2026-04-20 17:15:55 -07:00
Jordan Ritter 5cdcda816f fix(showcase/shell-docs): MDX Link renders next/link for client-side nav
The registry's <Link> shim rendered a plain <a>, forcing a full page
reload on every internal MDX link. next/link was already imported in
this file; now the shim routes through it when href is present, falling
back to a bare <a> only when no href is provided.
2026-04-20 17:15:49 -07:00
Jordan Ritter 2f665fa533 fix(showcase/shell-docs): ag-ui page strips leading H1 to prevent duplicate title render
The page wrapper renders the extracted title inside its own <h1>.
When no frontmatter title is present the title comes from the MDX
body's first H1 — which MDXRemote then also renders, producing two
stacked h1s at the top of every ag-ui doc. Strip the first leading
`# …` line (after any blank lines) from the MDX content before
rendering so the wrapper provides the single h1 and the body flows
from the first paragraph.
2026-04-20 17:15:46 -07:00
Jordan Ritter b6734accb2 fix(showcase/shell-docs): middleware matcher excludes static assets + handles Sec-Purpose prefetch 2026-04-20 17:15:32 -07:00
Jordan Ritter c06c18de68 refactor(showcase/shell-docs): consumers import FRAMEWORK_CATEGORY_ORDER from docs-render
Both sidebar-framework-selector.tsx and the catch-all docs page.tsx
kept local copies of FRAMEWORK_CATEGORY_ORDER that mirrored the
canonical one exported from docs-render. Drift between the copies
would have shown up as divergent category ordering across the UI.
Remove the local duplicates and import the single source of truth.
2026-04-20 17:15:16 -07:00
Jordan Ritter a6dc0a720f fix(showcase/shell-docs): convertTablesInJSX skips nested same-tag containers
The non-greedy regex matches through the first same-family close tag,
so nested containers like <Card>outer <Card>inner</Card> rest</Card>
closed at the inner </Card> and left the remaining 'rest</Card>' as
literal text. Capture the opening tag name and bail when the inner
body contains another occurrence of it — renders correctly through
MDX's own JSX handling instead of producing broken markup.
2026-04-20 17:14:50 -07:00
Jordan Ritter 473ba4d2bf fix(showcase/shell-docs): CopyButton clears pending reset timer on unmount 2026-04-20 17:13:17 -07:00
Jordan Ritter 25d8f6fb60 fix(showcase/shell-docs): guard ag-ui + reference filesystem reads against path traversal
The ag-ui and reference catch-all routes accept arbitrary URL segments
and feed them directly to path.join(CONTENT_DIR, slugPath) + fs.readFileSync.
A crafted request like /ag-ui/..%2F..%2Fsecrets decodes to a slugPath
that path.join happily resolves outside CONTENT_DIR, leaking arbitrary
files on disk.

Route every user-controlled filesystem access through the existing
safe-fs helpers (resolveWithinDir / safeReadFileSync) so escapes
return a safe miss and fall through to notFound().
2026-04-20 17:13:14 -07:00
Jordan Ritter 621b00b972 fix(showcase/shell-docs): search-modal guards Enter against IME composition; supports mailto/tel schemes 2026-04-20 17:13:13 -07:00
Jordan Ritter 6edd8836ad fix(showcase/shell-docs): docs-render CRLF-safe frontmatter + fence marker stored fully
Accept \r?\n in extractFrontmatter so Windows-authored MDX files
don't silently skip frontmatter extraction. In stripLeadingImports,
store the full fence marker (``` or ~~~) rather than its first
character so a stray single backtick inside a fenced block doesn't
prematurely close it.
2026-04-20 17:13:09 -07:00
Jordan Ritter cfc47fb896 fix(showcase/shell-docs): loadDoc uses gray-matter; guarded IO in docs-render readers
Replace the hand-rolled frontmatter regex in loadDoc with gray-matter
(already a dep) so quoted values, folded YAML, multiline descriptions,
and malformed frontmatter no longer fall through silently. Wrap every
fs read in readTitle, loadDoc, inlineSnippets, and buildNavTreeFromFilesystem
in try/catch — a single unreadable file / permission error used to
crash the entire page render.
2026-04-20 17:12:50 -07:00
Jordan Ritter 98c4139e2d chore(showcase/shell-dojo): add .gitignore and lockfile for parity with sibling shell packages 2026-04-20 16:57:29 -07:00
Jordan Ritter 7b5fb016ef refactor(showcase/shell-docs): hoist table helpers to satisfy oxlint
Move parseTableRow, isTableRow, isTableSeparator to module scope
so they aren't recreated on every convertTablesInJSX call. Fixes
eslint-plugin-unicorn(consistent-function-scoping) warnings. No
behavior change.
2026-04-20 16:55:35 -07:00
Jordan Ritter ac81e10deb chore(showcase/shell-docs): audit SNIPPET_MAP aliases; export FRAMEWORK_CATEGORY_ORDER
- Document every SNIPPET_MAP alias inline (AgUI/AGUI,
  FrontendTools/FrontEndToolsImpl) so future maintainers can see why
  the duplicates exist and don't collapse them blindly. Confirmed
  FrontEndToolsImpl is still referenced from live MDX under
  integrations/langgraph/*.
- Verified every SNIPPET_MAP target exists on disk with matching
  casing (including migrate-to-1.10.X.mdx and migrate-to-1.8.2.mdx).
- Export FRAMEWORK_CATEGORY_ORDER (+ FrameworkCategory type) from
  docs-render as the single source of truth. Consumer files
  (sidebar-framework-selector.tsx, [[...slug]]/page.tsx) each define
  the same constant independently today; a follow-up agent can
  retire those duplicates once they pick up the import.
2026-04-20 16:53:34 -07:00
Jordan Ritter 9500518203 fix(showcase/shell-docs): error boundaries log full error + generic copy + reportable id
- Log the full Error instance (not just .message/.digest) so the stack
  trace actually reaches the server log / browser devtools.
- Include the pathname (via usePathname) in the log message so reports
  can be tied back to the page that crashed.
- Replace misleading migration-specific copy ("This page will be fixed
  shortly. Some ... components are still being migrated.") with a
  generic user-facing message — the error boundary catches errors from
  many causes, not just migration gaps.
- Show error.digest explicitly as a reportable "Error ID" so users can
  include it when reporting the issue; drop the fallback to
  error.message because it can leak internal details in a user-facing
  surface.
2026-04-20 16:53:32 -07:00
Jordan Ritter a21efbdcc6 refactor(showcase/shell-docs): reference pages share loadItems helper + recursive static params + guarded reads
- Extract duplicated loadItems/getAllItems into @/lib/reference-items so the
  /reference index page and the /reference/[...slug] page read the same
  tree the same way (same subdirs, same walker, same gray-matter path,
  same caching).
- Walker is now recursive: subfolder files like components/inputs/textarea.mdx
  are indexed and statically generated. Previously only top-level .mdx
  files under components/ and hooks/ were picked up.
- Wrap gray-matter and fs reads in try/catch per file: a single malformed
  frontmatter block no longer crashes the whole static-generation pass —
  we log the offending path and skip that file.
- Fence-aware import stripper in the slug page so code samples containing
  import ... lines inside fences are not corrupted. (Duplicated inline
  here and in ag-ui page with a TODO(dedup) marker pointing at a future
  shared helper in @/lib/docs-render.)
- loadReferenceItems memoizes in production (module-scope cache keyed by
  subdir). Dev bypasses the cache so edits show up without a server
  restart.
2026-04-20 16:53:03 -07:00
Jordan Ritter 6bd81286bf perf(showcase/shell-docs): memoize readTitle and readMeta
buildNavTree walks the entire content tree on every page render and
calls readTitle / readMeta O(pages) times per request. Each call
previously reopened and re-parsed the file from disk.

- Process-scoped Map caches keyed by resolved absolute path.
- Null results are cached too — a missing or malformed file still
  short-circuits on the second visit.
- Memory footprint is trivial: titles are short strings, meta is a
  small JSON object.
- Stale-during-process semantics are acceptable for Next.js build
  and server runtime lifetimes; authors re-run the process to pick
  up MDX/meta.json edits (same as before in production builds).
2026-04-20 16:52:01 -07:00
Jordan Ritter cdb35b17ba fix(showcase/shell-docs): ag-ui page reuses docsComponents + gray-matter + guarded fs reads
- Replace local MDX components map with spread of shared docsComponents
  from @/lib/mdx-registry, so AG-UI pages get the full component set
  (Tabs, FrameworkTabs, Snippet, Steps from @/components/docs-steps,
  etc.) instead of silently rendering raw JSX when MDX uses anything
  outside the tiny local subset.
- Use the shared InlineDemo from mdx-registry, whose Open full demo
  link uses an absolute NEXT_PUBLIC_SHELL_URL/integrations/... URL.
  The prior local copy used a relative /integrations/... URL that 404d
  on the docs host (no /integrations route on docs.showcase.copilotkit.ai).
- Parse frontmatter with gray-matter instead of a whole-file title:
  regex that could match any title: line buried inside an MDX body.
- Wrap fs.readFileSync in try/catch with meaningful error logs naming
  the offending path; fall back gracefully instead of crashing render.
- Fence-aware leading-import stripper so MDX code samples that show
  import ... lines are not corrupted (duplicated here; TODO(dedup) in
  comment to hoist into @/lib/docs-render next time another route needs
  it).
2026-04-20 16:51:46 -07:00
Jordan Ritter 50a6488363 refactor(showcase/shell-docs): broaden JSX table containers; accept outer-pipe-optional GFM; log readMeta parse errors
- JSX_CONTAINER_TAGS now covers Callout, Card, Cards, Step, Steps,
  Tabs alongside the original Accordion/Tab. Previously a Markdown
  table inside e.g. <Callout> silently failed to promote to HTML
  and rendered as raw pipes.
- convertMarkdownTableToHtml's parseRow and the row/separator
  detection in convertTablesInJSX now accept GFM tables written
  WITHOUT outer leading/trailing pipes (valid GFM — previously
  rejected because we sliced (1, -1) unconditionally).
- readMeta now logs the offending path + parse error when JSON.parse
  fails, replacing the silent catch. Malformed meta.json no longer
  collapses to 'no nav ordering' with zero diagnostic signal.
2026-04-20 16:51:30 -07:00
Jordan Ritter dd72890693 fix(showcase/shell-docs): framework-selector handles /docs/* clear + router.replace + strict strip
- Clear selection button was hidden on /docs/* because it branched on
  framework (URL-derived, always null there). A user landing on /docs
  with a stale storedFramework from a previous session had no way to
  clear the preference from the selector UI. Show the button whenever
  framework OR storedFramework is set; skip the navigation when we're
  not on a framework-scoped route (it's a pure preference change).
- router.push -> router.replace on framework change. Picking a backend
  is a pivot on the same logical page, not forward navigation. Using
  push clutters the back stack with every framework the user clicked
  through, making the browser Back button useless.
- Type-guard e.target with instanceof Node before passing to contains,
  rather than hard-casting. EventTarget can be non-DOM (e.g. events
  against window); the cast silently degrades to contains(null) which
  returns false in most engines but is undefined behaviour in spec.
- Document that stripFrameworkPrefix intentionally only inspects
  parts[0] and does NOT recurse — /<fw>/<fw>/x must keep the inner
  <fw>/x as the feature tail.
2026-04-20 16:51:02 -07:00
Jordan Ritter 0e29c78a0d refactor(showcase/shell-docs): dedupe FRAMEWORK_CATEGORY_ORDER + stable sort by slug
- Rename INTEGRATION_CATEGORY_IDS to FRAMEWORK_CATEGORY_ORDER to match
  the identically-defined constant in /[[...slug]]/page.tsx (the two
  will be consolidated into @/lib/registry in a follow-up owned by the
  registry refactor; left a TODO pointing at the canonical home).
- sort_order tiebreak: Array#sort is not guaranteed stable for ties
  across engines. When multiple integrations default to 999 the
  rendered order shuffles between V8 revisions. Add an explicit
  alphabetical-by-slug tiebreak so the rendered panel is
  deterministic.
2026-04-20 16:49:36 -07:00
Jordan Ritter 4940ffb9ef chore(showcase/shell-docs): sidebar-link drop dead fallbackHref path
fallbackHref was assigned to href and then immediately overwritten in
every branch of the scope switch — the value was never read. Since
useFramework() returns the URL-derived framework identically during
SSR and post-hydration, the resolved href matches what fallbackHref
was pre-computing server-side, so the 'client takes over on mount'
comment was describing a no-op.

- Remove the dead assignment; resolve href directly from framework.
- Keep fallbackHref as an optional prop (ignored) to avoid churning
  call sites that still pass it.
2026-04-20 16:49:28 -07:00
Jordan Ritter 7713d6b531 fix(showcase/shell-docs): sidebar-nav scrolls only on route change; try/catch + spec-value behavior
The effect had no dependency array, so it re-ran on every render and
hijacked the user's own sidebar scroll whenever any unrelated re-render
fired (state/context flips, parent rerenders). Key the effect on
pathname so scroll-to-active only runs on route change.

Also:
- behavior: 'instant' is a Chromium non-standard extension; other
  engines silently ignore it. Use 'auto' which is the spec-compliant
  'no smooth animation' value.
- Wrap scrollIntoView in try/catch — it can throw on detached nodes
  and in obscure iframe/security contexts; a sidebar scroll
  restoration never justifies taking down the route.
2026-04-20 16:49:21 -07:00
Jordan Ritter 484556e41d Revert "chore(showcase/shell-docs): mdx-registry stubs log discarded props in dev"
This reverts commit 0f46be9c37.
2026-04-20 16:49:11 -07:00
Jordan Ritter c54c37c3e9 fix(showcase/shell-docs): fence-aware import stripping + cycle protection + missing-snippet logging
- Replace the global /^import\s+.+$/gm pass with a fence-aware walker
  (stripLeadingImports). The old regex silently mangled docs code
  samples like 'import os' inside Python fences; the new pass only
  drops top-of-file MDX import lines and leaves fence bodies alone.
- Add Set<string> cycle tracking through inlineSnippets' recursion so
  a self- or mutually-referencing snippet can't loop until the stack
  overflows. On a repeat, emit a warning and inline a comment marker
  so the author sees something diagnosable.
- Log warn when a <Component /> reference can't be mapped to a
  snippet file (either missing from SNIPPET_MAP or missing on disk);
  previously the reference silently rendered nothing.
2026-04-20 16:48:38 -07:00
Jordan Ritter 3338f0f82f fix(showcase/shell-docs): search trigger avoids hydration mismatch + respects input focus
- isMac now starts as null and is only resolved in useEffect, so SSR output matches the first client render; reserve horizontal space on the shortcut pill with a non-breaking-space placeholder (+ suppressHydrationWarning) so ⌘K/Ctrl+K swap-in doesn't reflow the button
- Cmd/Ctrl+K no longer hijacks the browser shortcut when focus is inside an unrelated <input>, <textarea>, <select>, or contenteditable; still toggles when focus is within the search modal itself (data-search-modal boundary)
- Trigger buttons use setOpen((prev) => !prev) so Cmd+K-on-top-of-existing-modal toggles correctly and the hint reflects the true action
2026-04-20 16:48:11 -07:00
Jordan Ritter 3cd0aa53e4 fix(showcase/shell-docs): search modal types, load error, cleanup, dedupe, split loading states
- Replace useState<any> for registryData with typed Registry | null
- Surface registry-load failures: .catch logs + inline "Search index failed to load" banner
- Track the setTimeout focus id and clear it in effect cleanup
- Let static search-index matches render immediately; show a '[loading...]' hint until registry.json resolves; show 'no results' only after loading completes
- Use a ref for selectedIndex so Enter never reads a stale value after reset-on-input
- Dedupe results by type+href before slicing to 12 (stable key becomes type-href)
- Route navigation through a shared helper that detects external URLs (http(s)://, //) and uses window.location.assign for them, router.push otherwise
- Warn once in dev when an integration has no description so it gets fixed upstream
2026-04-20 16:48:02 -07:00