8388dd63fe
Prettier reformats JSX into one expression per line; splitting
"{file.prefix}{file.label}" across two lines inserts a literal
whitespace text node between them. Snapshot diff against
.agents/snapshots/skills.txt showed the rendered HTML emitted
"├── SKILL.md" (two spaces) where vanilla showed one. Joining
the values into a single template expression keeps the rendered
text byte-identical to the legacy source.
307 lines
9.3 KiB
TypeScript
307 lines
9.3 KiB
TypeScript
---
|
|
// SkillPackageExplorer — the four-file skill-package picker. The interactive
|
|
// version of the `data-package-file` buttons that the vanilla skills page
|
|
// shipped, rewritten for the Astro chapter surface. The four package entries
|
|
// mirror skills/app.js so the migration preserves content 1:1; the buttons
|
|
// carry `data-skill-file` and the click handler swaps the preview panel
|
|
// contents client-side.
|
|
//
|
|
// Page is `client:visible` rather than `client:load`: the picker sits below
|
|
// the hero and grid; deferring until it scrolls into view keeps initial JS
|
|
// to zero for the above-the-fold content. The page ships zero JS for the
|
|
// hero/grid/footer parts — only the picker island hydrates.
|
|
|
|
interface PackageFile {
|
|
id: 'skill' | 'references' | 'scripts' | 'assets';
|
|
prefix: '├── ' | '└── ';
|
|
label: string;
|
|
/** Small caption under the file label, mirrors the vanilla source. */
|
|
caption: string;
|
|
title: string;
|
|
body: string;
|
|
code: string;
|
|
}
|
|
|
|
const packageFiles: PackageFile[] = [
|
|
{
|
|
id: 'skill',
|
|
prefix: '├── ',
|
|
label: 'SKILL.md',
|
|
caption: 'trigger + workflow',
|
|
title: 'The operating contract',
|
|
body: 'The one file that should always be loaded. Define the exact trigger, the ordered workflow, safety limits, and the evidence the agent returns.',
|
|
code: '---\nname: review-ui\ndescription: Review a changed UI for focus, reflow, and motion.\n---\n\n1. Inspect the changed interaction.\n2. Run the UI checks.\n3. Return findings with evidence.',
|
|
},
|
|
{
|
|
id: 'references',
|
|
prefix: '├── ',
|
|
label: 'references/',
|
|
caption: 'conditional facts',
|
|
title: 'Facts, only when needed',
|
|
body: 'Keep conditional detail out of the main instruction. A dialog pattern, framework caveat, or accessibility checklist belongs here when it is not needed for every review.',
|
|
code: 'references/\n└── accessibility.md\n ├── keyboard interaction patterns\n └── focus and reflow checklist',
|
|
},
|
|
{
|
|
id: 'scripts',
|
|
prefix: '├── ',
|
|
label: 'scripts/',
|
|
caption: 'deterministic checks',
|
|
title: 'Mechanics that should not depend on memory',
|
|
body: 'Turn deterministic checks into runnable tools. The agent still judges the result, but it should not have to recreate a viewport test or filename rule by hand.',
|
|
code: 'scripts/\n└── check-reflow.mjs\n └── checks 320px, 1280px, and 4K widths',
|
|
},
|
|
{
|
|
id: 'assets',
|
|
prefix: '└── ',
|
|
label: 'assets/',
|
|
caption: 'templates + examples',
|
|
title: 'Starting material, not hidden instructions',
|
|
body: 'Use assets for templates and examples a person or agent can copy. Keep them clearly named so package readers can choose the right starting point.',
|
|
code: 'assets/\n├── review-report.md\n└── focus-test-fixture.html',
|
|
},
|
|
];
|
|
---
|
|
|
|
<div class="package-workbench" data-package-workbench>
|
|
<div class="package-tree" role="tablist" aria-label="Files in the review-ui skill package">
|
|
<p>REVIEW-UI / SKILL PACKAGE</p>
|
|
{
|
|
packageFiles.map((file, index) => (
|
|
<button
|
|
class:list={[{ active: index === 0 }]}
|
|
data-skill-file={file.id}
|
|
role="tab"
|
|
aria-selected={String(index === 0)}
|
|
>
|
|
<code>{`${file.prefix}${file.label}`}</code>
|
|
<small>{file.caption}</small>
|
|
</button>
|
|
))
|
|
}
|
|
</div>
|
|
<article class="package-preview" id="package-preview" aria-live="polite"></article>
|
|
</div>
|
|
|
|
<script type="application/json" data-skill-files set:html={JSON.stringify(packageFiles)} />
|
|
|
|
<script is:inline>
|
|
(function () {
|
|
const preview = document.querySelector('#package-preview');
|
|
const buttons = document.querySelectorAll('[data-skill-file]');
|
|
const dataNode = document.querySelector('[data-skill-files]');
|
|
if (!preview || !dataNode) return;
|
|
const files = JSON.parse(dataNode.textContent || '[]');
|
|
|
|
function renderPackage(id) {
|
|
const item = files.find((entry) => entry.id === id);
|
|
if (!item) return;
|
|
preview.classList.remove('is-swapping');
|
|
void preview.offsetWidth;
|
|
preview.classList.add('is-swapping');
|
|
preview.innerHTML =
|
|
'<span>SELECTED / ' +
|
|
item.label +
|
|
'</span>' +
|
|
'<h3>' +
|
|
item.title +
|
|
'</h3>' +
|
|
'<p>' +
|
|
item.body +
|
|
'</p>' +
|
|
'<pre><code>' +
|
|
item.code +
|
|
'</code></pre>';
|
|
buttons.forEach(function (button) {
|
|
const active = button.dataset.skillFile === id;
|
|
button.classList.toggle('active', active);
|
|
button.setAttribute('aria-selected', String(active));
|
|
});
|
|
}
|
|
|
|
buttons.forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
renderPackage(button.dataset.skillFile);
|
|
});
|
|
});
|
|
|
|
renderPackage('skill');
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
.package-workbench {
|
|
display: grid;
|
|
grid-template-columns: minmax(190px, 0.85fr) minmax(0, 1.3fr);
|
|
min-width: 0;
|
|
background: var(--ink);
|
|
border: 1px solid var(--ink);
|
|
box-shadow: 10px 10px 0 color-mix(in srgb, var(--gold) 55%, transparent);
|
|
}
|
|
|
|
.package-tree {
|
|
padding: 22px 16px;
|
|
/* token-gap: legacy 1px solid #426070 over --ink; no token matches; owner design-system-keeper */
|
|
border-right: 1px solid #426070;
|
|
min-width: 0;
|
|
}
|
|
|
|
.package-tree > p,
|
|
.package-preview > span {
|
|
margin: 0 0 14px;
|
|
color: var(--gold);
|
|
/* token-gap: source uses 11px monospace; no --step-* covers 11px on this surface; design-system-keeper */
|
|
font:
|
|
700 11px / 1.35 ui-monospace,
|
|
monospace;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.package-tree button {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
align-items: center;
|
|
gap: 8px;
|
|
width: 100%;
|
|
padding: 12px 8px;
|
|
border: 0;
|
|
border-left: 2px solid transparent;
|
|
background: transparent;
|
|
/* token-gap: legacy #d6e1e4 over --ink tree buttons; no token matches; design-system-keeper */
|
|
color: #d6e1e4;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
transition:
|
|
background 0.2s ease,
|
|
border-color 0.2s ease,
|
|
transform 0.2s ease;
|
|
}
|
|
|
|
.package-tree button:hover,
|
|
.package-tree button:focus-visible,
|
|
.package-tree button.active {
|
|
border-left-color: var(--gold);
|
|
/* token-gap: legacy #1f3a4b active/hover on --ink tree; no token matches; design-system-keeper */
|
|
background: #1f3a4b;
|
|
outline: 0;
|
|
}
|
|
|
|
.package-tree button:hover {
|
|
transform: translateX(3px);
|
|
}
|
|
|
|
.package-tree code {
|
|
min-width: 0;
|
|
overflow-wrap: anywhere;
|
|
font:
|
|
700 13px / 1.4 ui-monospace,
|
|
monospace;
|
|
}
|
|
|
|
.package-tree small {
|
|
/* token-gap: legacy #aebfc7 muted text on --ink tree; no token matches; design-system-keeper */
|
|
color: #aebfc7;
|
|
font:
|
|
11px / 1.25 Arial,
|
|
sans-serif;
|
|
text-align: right;
|
|
}
|
|
|
|
.package-preview {
|
|
min-width: 0;
|
|
padding: 26px;
|
|
/* token-gap: legacy #173245 preview surface (between --ink and --blue); no token matches; design-system-keeper */
|
|
background: #173245;
|
|
color: var(--paper);
|
|
}
|
|
|
|
.package-preview h3 {
|
|
margin: 0 0 8px;
|
|
font-size: clamp(24px, 3vw, 38px);
|
|
line-height: 1.02;
|
|
letter-spacing: -0.045em;
|
|
}
|
|
|
|
.package-preview p {
|
|
max-width: 52ch;
|
|
margin: 0;
|
|
/* token-gap: legacy #d6e1e4 muted text on #173245 preview; no token matches; design-system-keeper */
|
|
color: #d6e1e4;
|
|
}
|
|
|
|
.package-preview pre {
|
|
max-width: 100%;
|
|
margin: 20px 0 0;
|
|
padding: 15px;
|
|
overflow: auto;
|
|
/* token-gap: legacy #466274 rule on preview pre border; no token matches; design-system-keeper */
|
|
border: 1px solid #466274;
|
|
/* token-gap: legacy #102837 fill on preview pre background; no token matches; design-system-keeper */
|
|
background: #102837;
|
|
/* token-gap: legacy #d6e1e4 code text on #102837; no token matches; design-system-keeper */
|
|
color: #d6e1e4;
|
|
font:
|
|
12px / 1.55 ui-monospace,
|
|
monospace;
|
|
}
|
|
|
|
.package-preview.is-swapping {
|
|
animation: package-preview-in 0.34s ease both;
|
|
}
|
|
|
|
@keyframes package-preview-in {
|
|
from {
|
|
opacity: 0.25;
|
|
transform: translateY(7px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 800px) {
|
|
.package-workbench {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.package-tree {
|
|
border-right: 0;
|
|
/* token-gap: legacy #426070 mobile layout rule over --ink; no token matches; design-system-keeper */
|
|
border-bottom: 1px solid #426070;
|
|
}
|
|
.package-preview {
|
|
padding: 22px;
|
|
}
|
|
}
|
|
|
|
/* token-gap: legacy 520px breakpoint from skills/styles.css (mobile phone), not in named set 560/800/1100/1600/2200; design-system-keeper */
|
|
@media (max-width: 520px) {
|
|
.package-tree {
|
|
padding: 18px 10px;
|
|
}
|
|
.package-tree button {
|
|
padding: 12px 6px;
|
|
}
|
|
.package-tree small {
|
|
display: none;
|
|
}
|
|
.package-preview {
|
|
padding: 18px;
|
|
}
|
|
.package-preview pre {
|
|
/* token-gap: legacy 11px mobile font-size from skills/styles.css; no --step-* covers 11px; design-system-keeper */
|
|
font-size: 11px;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
scroll-behavior: auto !important;
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|
|
</style>
|