Files
pbakaus_impeccable/site/layouts/Doc.astro
T
Paul BakausandCursor 7648af0095 Apply neo-kinpaku design system and improve live picker UX
Restyle the live picker to match the site kinpaku kit, persist pick mode
in localStorage, fix DESIGN.md color swatches in the parser, and land the
neo-kinpaku site refresh with new tokens, assets, palette script, and
detector rules.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 10:24:25 -07:00

164 lines
6.0 KiB
Plaintext

---
import Base from './Base.astro';
import '../styles/sub-pages.css';
import '../styles/docs-kinpaku.css';
import {
SKILL_CATEGORIES,
CATEGORY_ORDER,
CATEGORY_LABELS,
COMMAND_RELATIONSHIPS,
} from '../data/sub-pages-data';
import { getCommandDemo } from '../scripts/demos/commands/index.js';
interface Props {
title: string;
description: string;
tagline: string;
slug: string;
category: string;
allCommands: { slug: string; category: string }[];
}
const { title, description, tagline, slug, category, allCommands } = Astro.props;
// Decide hero layout at build time: when a command has a registered demo
// (in site/scripts/demos/commands/<id>.js), the hero is a two-column grid
// with the demo on the left and h1/lede on the right — same editorial
// before/after side-by-side that the old site used. When no demo exists,
// the hero stays single-column.
const hasDemo = !!getCommandDemo(slug);
const relationships = COMMAND_RELATIONSHIPS[slug] || {};
const sidebarGroups: Record<string, { slug: string }[]> = {};
for (const cat of CATEGORY_ORDER) {
sidebarGroups[cat] = allCommands
.filter(c => c.category === cat)
.sort((a, b) => a.slug.localeCompare(b.slug));
}
---
<Base
title={`${title} | Impeccable`}
description={description}
activeNav="docs"
canonicalPath={`/docs/${slug}`}
bodyClass="sub-page skills-layout-page docs-kinpaku"
>
<div class="skills-layout">
<aside class="skills-sidebar" aria-label="Commands">
<button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="skills-sidebar-inner">
<span class="skills-sidebar-toggle-label">Commands</span>
<svg class="skills-sidebar-toggle-chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
</button>
<div class="skills-sidebar-inner" id="skills-sidebar-inner">
<p class="skills-sidebar-label">Commands</p>
{CATEGORY_ORDER.map(cat => (
<div class="skills-sidebar-group">
<span class="skills-sidebar-category">{CATEGORY_LABELS[cat]}</span>
<ul class="skills-sidebar-list">
{sidebarGroups[cat].map(cmd => (
<li>
<a href={`/docs/${cmd.slug}`} aria-current={cmd.slug === slug ? 'page' : undefined}>
<span>{cmd.slug}</span>
</a>
</li>
))}
</ul>
</div>
))}
</div>
</aside>
<div class="skills-main">
<div class="skills-detail">
{hasDemo ? (
/* Two-column editorial hero: demo on the left, h1 + lede on
the right. The demo content is populated client-side by the
script below from site/scripts/demos/commands/<id>.js, same
source the homepage uses. */
<div class="docs-hero docs-hero--with-demo">
<div class="docs-command-demo" data-command={slug}></div>
<header class="sub-page-header">
<h1 class="sub-page-title">/impeccable <em>{slug}</em></h1>
{tagline && <p class="sub-page-lede">{tagline}</p>}
</header>
</div>
) : (
<header class="sub-page-header">
<h1 class="sub-page-title">/impeccable <em>{slug}</em></h1>
{tagline && <p class="sub-page-lede">{tagline}</p>}
</header>
)}
<div class="skills-detail-body docs-body prose">
<slot />
</div>
{(relationships.leadsTo || relationships.pairs || relationships.combinesWith) && (
<footer class="skills-relationships">
<h2 class="skills-relationships-title">Related commands</h2>
<div class="skills-relationships-list">
{relationships.leadsTo?.map(cmd => (
<a href={`/docs/${cmd}`} class="skills-relationship-chip" data-relation="leads-to">
<span class="skills-relationship-label">leads to</span>
<span class="skills-relationship-name">{cmd}</span>
</a>
))}
{relationships.pairs && (
<a href={`/docs/${relationships.pairs}`} class="skills-relationship-chip" data-relation="pairs">
<span class="skills-relationship-label">pairs with</span>
<span class="skills-relationship-name">{relationships.pairs}</span>
</a>
)}
{relationships.combinesWith?.map(cmd => (
<a href={`/docs/${cmd}`} class="skills-relationship-chip" data-relation="combines">
<span class="skills-relationship-label">combines with</span>
<span class="skills-relationship-name">{cmd}</span>
</a>
))}
</div>
</footer>
)}
</div>
</div>
</div>
<script is:inline>
document.addEventListener('click', (e) => {
const toggle = e.target.closest('.skills-sidebar-toggle');
if (!toggle) return;
const expanded = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', String(!expanded));
});
</script>
<script>
import { getCommandDemo } from '../scripts/demos/commands/index.js';
import { renderCommandDemo, initCommandDemo } from '../scripts/demo-renderer.js';
import { initSplitCompare } from '../scripts/effects/split-compare.js';
const slot = document.querySelector('.docs-command-demo');
if (slot) {
const command = slot.dataset.command;
const demo = getCommandDemo(command);
if (demo) {
slot.innerHTML = renderCommandDemo(command);
const split = slot.querySelector('.demo-split-comparison');
if (split) {
initSplitCompare(split, {
defaultPosition: 50,
skewAngle: 0,
minPosition: 10,
maxPosition: 90,
});
}
initCommandDemo(command, slot);
} else {
// No demo registered for this command — leave the layout flush.
slot.remove();
}
}
</script>
</Base>