feat: add Alpine visual companion mockups

This commit is contained in:
Drew Ritter
2026-05-08 15:52:38 -07:00
parent 8811b0f2d7
commit 63101959b5
12 changed files with 2014 additions and 14 deletions

View File

@@ -193,6 +193,7 @@
.mock-button { background: var(--accent); color: white; border: none; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.85rem; }
.mock-input { background: var(--bg-primary); border: 1px solid var(--border); border-radius: 6px; padding: 0.5rem; width: 100%; }
</style>
<script defer src="/vendor/alpine.js"></script>
</head>
<body>
<div class="header">
@@ -207,7 +208,7 @@
</div>
<div class="indicator-bar">
<span id="indicator-text">Click an option above, then return to the terminal</span>
<span id="indicator-text">Interact with the mockup, then return to the terminal</span>
</div>
</body>

View File

@@ -51,7 +51,7 @@
const container = target.closest('.options') || target.closest('.cards');
const selected = container ? container.querySelectorAll('.selected') : [];
if (selected.length === 0) {
indicator.textContent = 'Click an option above, then return to the terminal';
indicator.textContent = 'Interact with the mockup, then return to the terminal';
} else if (selected.length === 1) {
const label = selected[0].querySelector('h3, .content h3, .card-body h3')?.textContent?.trim() || selected[0].dataset.choice;
indicator.innerHTML = '<span class="selected-text">' + label + ' selected</span> — return to terminal to continue';

View File

@@ -101,6 +101,26 @@ h1 { color: #333; } p { color: #666; }</style>
const frameTemplate = fs.readFileSync(path.join(__dirname, 'frame-template.html'), 'utf-8');
const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8');
const helperInjection = '<script>\n' + helperScript + '\n</script>';
const ALPINE_VENDOR_PATH = path.join(__dirname, 'vendor', 'alpine.js');
function loadVendorFile(filePath, name) {
try {
return fs.readFileSync(filePath);
} catch (error) {
throw new Error(
`Failed to load vendored ${name} at ${filePath}; ` +
'run the refresh command in skills/brainstorming/scripts/vendor/THIRD_PARTY_NOTICES.md. ' +
error.message
);
}
}
const VENDOR_FILES = new Map([
['/vendor/alpine.js', {
content: loadVendorFile(ALPINE_VENDOR_PATH, 'Alpine'),
contentType: 'application/javascript; charset=utf-8'
}]
]);
// ========== Helper Functions ==========
@@ -124,11 +144,30 @@ function getNewestScreen() {
return files.length > 0 ? files[0].path : null;
}
function parseRequestUrl(req) {
// Vendor routing depends on URL normalization before exact pathname allowlist checks.
return new URL(req.url, 'http://localhost');
}
function serveVendorFile(requestUrl, res) {
const vendorFile = VENDOR_FILES.get(requestUrl.pathname);
if (!vendorFile) {
res.writeHead(404);
res.end('Not found');
return;
}
res.writeHead(200, { 'Content-Type': vendorFile.contentType });
res.end(vendorFile.content);
}
// ========== HTTP Request Handler ==========
function handleRequest(req, res) {
touchActivity();
if (req.method === 'GET' && req.url === '/') {
const requestUrl = parseRequestUrl(req);
if (req.method === 'GET' && requestUrl.pathname === '/') {
const screenFile = getNewestScreen();
let html = screenFile
? (raw => isFullDocument(raw) ? raw : wrapInFrame(raw))(fs.readFileSync(screenFile, 'utf-8'))
@@ -142,8 +181,10 @@ function handleRequest(req, res) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
res.end(html);
} else if (req.method === 'GET' && req.url.startsWith('/files/')) {
const fileName = req.url.slice(7);
} else if (req.method === 'GET' && requestUrl.pathname.startsWith('/vendor/')) {
serveVendorFile(requestUrl, res);
} else if (req.method === 'GET' && requestUrl.pathname.startsWith('/files/')) {
const fileName = requestUrl.pathname.slice(7);
const filePath = path.join(CONTENT_DIR, path.basename(fileName));
if (!fs.existsSync(filePath)) {
res.writeHead(404);

View File

@@ -0,0 +1,48 @@
# Third-Party Notices
## Alpine.js
- Package: `alpinejs`
- Version: `3.15.12`
- Source: `https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz`
- Vendored file: `package/dist/cdn.min.js`
- Local path: `skills/brainstorming/scripts/vendor/alpine.js`
- SHA256: `57b37d7cae9a27d965fdae4adcc844245dfdc407e655aee85dcfff3a08036a3f`
Refresh command:
```bash
cd "$(git rev-parse --show-toplevel)"
tmpdir="$(mktemp -d)"
curl -fsSL https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz -o "$tmpdir/alpinejs-3.15.12.tgz"
tar -xzf "$tmpdir/alpinejs-3.15.12.tgz" -C "$tmpdir" package/dist/cdn.min.js
cp "$tmpdir/package/dist/cdn.min.js" skills/brainstorming/scripts/vendor/alpine.js
shasum -a 256 skills/brainstorming/scripts/vendor/alpine.js
rm -rf "$tmpdir"
```
License:
```text
MIT License
Copyright © 2019-2025 Caleb Porzio and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,12 @@
{
"name": "alpinejs",
"version": "3.15.12",
"license": "MIT",
"sourceUrl": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.12.tgz",
"sourcePackagePath": "package/dist/cdn.min.js",
"localPath": "skills/brainstorming/scripts/vendor/alpine.js",
"npmIntegrity": "sha512-nJvPAQVNPdZZ0NrExJ/kzQco3ijR8LwvCOadQecllESiqT4NyZ/57sN9V2XyvhlBGAbmlKYgeWZvYdKq99ij/Q==",
"sha256": "57b37d7cae9a27d965fdae4adcc844245dfdc407e655aee85dcfff3a08036a3f",
"approvalArtifact": "SUP-215",
"vendoredAt": "2026-05-08"
}