1---
2import Base from './Base.astro';
3import '../styles/sub-pages.css';
4import {
5 SKILL_CATEGORIES,
6 CATEGORY_ORDER,
7 CATEGORY_LABELS,
8 COMMAND_RELATIONSHIPS,
9} from '../data/sub-pages-data';
10
11interface Props {
12 title: string;
13 description: string;
14 tagline: string;
15 slug: string;
16 category: string;
17 allCommands: { slug: string; category: string }[];
18}
19
20const { title, description, tagline, slug, category, allCommands } = Astro.props;
21
22const categoryLabel = CATEGORY_LABELS[category] || category;
23const relationships = COMMAND_RELATIONSHIPS[slug] || {};
24
25const sidebarGroups: Record<string, { slug: string }[]> = {};
26for (const cat of CATEGORY_ORDER) {
27 sidebarGroups[cat] = allCommands
28 .filter(c => c.category === cat)
29 .sort((a, b) => a.slug.localeCompare(b.slug));
30}
31---
32
33<Base
34 title={`${title} | Impeccable`}
35 description={description}
36 activeNav="docs"
37 canonicalPath={`/docs/${slug}`}
38 bodyClass="sub-page skills-layout-page"
39>
40 <div class="skills-layout">
41 <aside class="skills-sidebar" aria-label="Commands">
42 <button class="skills-sidebar-toggle" type="button" aria-expanded="false" aria-controls="skills-sidebar-inner">
43 <span class="skills-sidebar-toggle-label">Commands</span>
44 <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>
45 </button>
46 <div class="skills-sidebar-inner" id="skills-sidebar-inner">
47 <p class="skills-sidebar-label">Commands</p>
48 {CATEGORY_ORDER.map(cat => (
49 <div class="skills-sidebar-group">
50 <span class="skills-sidebar-category">{CATEGORY_LABELS[cat]}</span>
51 <ul class="skills-sidebar-list">
52 {sidebarGroups[cat].map(cmd => (
53 <li>
54 <a href={`/docs/${cmd.slug}`} aria-current={cmd.slug === slug ? 'page' : undefined}>
55 <span>{cmd.slug}</span>
56 </a>
57 </li>
58 ))}
59 </ul>
60 </div>
61 ))}
62 </div>
63 </aside>
64
65 <div class="skills-main">
66 <div class="skills-detail">
67 <nav class="skills-breadcrumb" aria-label="Breadcrumb">
68 <a href="/docs">Docs</a>
69 <span aria-hidden="true">/</span>
70 <span>{slug}</span>
71 </nav>
72
73 <header class="sub-page-header">
74 <span class="sub-page-eyebrow">{categoryLabel}</span>
75 <h1 class="sub-page-title">/impeccable {slug}</h1>
76 {tagline && <p class="sub-page-lede">{tagline}</p>}
77 </header>
78
79 <div class="skills-detail-body docs-body prose">
80 <slot />
81 </div>
82
83 {(relationships.leadsTo || relationships.pairs || relationships.combinesWith) && (
84 <footer class="skills-relationships">
85 <h2 class="skills-relationships-title">Related commands</h2>
86 <div class="skills-relationships-list">
87 {relationships.leadsTo?.map(cmd => (
88 <a href={`/docs/${cmd}`} class="skills-relationship-chip" data-relation="leads-to">
89 <span class="skills-relationship-label">leads to</span>
90 <span class="skills-relationship-name">{cmd}</span>
91 </a>
92 ))}
93 {relationships.pairs && (
94 <a href={`/docs/${relationships.pairs}`} class="skills-relationship-chip" data-relation="pairs">
95 <span class="skills-relationship-label">pairs with</span>
96 <span class="skills-relationship-name">{relationships.pairs}</span>
97 </a>
98 )}
99 {relationships.combinesWith?.map(cmd => (
100 <a href={`/docs/${cmd}`} class="skills-relationship-chip" data-relation="combines">
101 <span class="skills-relationship-label">combines with</span>
102 <span class="skills-relationship-name">{cmd}</span>
103 </a>
104 ))}
105 </div>
106 </footer>
107 )}
108 </div>
109 </div>
110 </div>
111
112 <script is:inline>
113 document.addEventListener('click', (e) => {
114 const toggle = e.target.closest('.skills-sidebar-toggle');
115 if (!toggle) return;
116 const expanded = toggle.getAttribute('aria-expanded') === 'true';
117 toggle.setAttribute('aria-expanded', String(!expanded));
118 });
119 </script>
120</Base>