Files
pbakaus_impeccable/server/lib/api-handlers.js
T
Paul Bakaus 1bdb3ea17a Redesign website with interactive demos and improved layout
- Add interactive before/after demos for all skills (UX Writing, Spatial Design, Motion Design, Typography, Interaction Design, Color & Contrast, Responsive Design)
- Implement tabbed demo navigation for skills with multiple examples
- Refactor app.js into modular JS files (skills.js, commands.js, data.js, skill-demos.js, command-demos.js, demo-toggles.js)
- Add new CSS modules (skill-demos.css, workflow.css, problem-section.css, solution-section.css)
- Redesign commands section with sidebar nav matching skills layout
- Replace large download buttons with compact icon buttons
- Fix gray-on-pink color clashing throughout UI
- Remove frontend-design skill (Anthropic's work, not ours)
- Format skill names properly (e.g., 'UX Writing' not 'ux-writing')
- Remove redundant 'Combines Well With' from skills
- Add 'The Problem' and 'The Solution' sections to homepage
- Various CSS fixes for buttons, backgrounds, and spacing
2025-12-06 12:33:00 -08:00

147 lines
4.3 KiB
JavaScript

import { readdir } from "fs/promises";
import { basename, join } from "path";
// Read all skills from source directory
export async function getSkills() {
const sourceDir = join(process.cwd(), "source");
const skillsDir = join(sourceDir, "skills");
const files = await readdir(skillsDir);
const skills = [];
for (const file of files) {
if (file.endsWith(".md")) {
const content = await Bun.file(join(skillsDir, file)).text();
const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---/);
if (frontmatterMatch) {
const frontmatter = frontmatterMatch[1];
const nameMatch = frontmatter.match(/name:\s*(.+)/);
const descMatch = frontmatter.match(/description:\s*(.+)/);
skills.push({
id: file.replace(".md", ""),
name: nameMatch?.[1]?.trim() || file.replace(".md", ""),
description: descMatch?.[1]?.trim() || "No description available",
});
}
}
}
return skills;
}
// Read all commands from source directory
export async function getCommands() {
const sourceDir = join(process.cwd(), "source");
const commandsDir = join(sourceDir, "commands");
const files = await readdir(commandsDir);
const commands = [];
for (const file of files) {
if (file.endsWith(".md")) {
const content = await Bun.file(join(commandsDir, file)).text();
const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---/);
if (frontmatterMatch) {
const frontmatter = frontmatterMatch[1];
const nameMatch = frontmatter.match(/name:\s*(.+)/);
const descMatch = frontmatter.match(/description:\s*(.+)/);
commands.push({
id: file.replace(".md", ""),
name: nameMatch?.[1]?.trim() || file.replace(".md", ""),
description: descMatch?.[1]?.trim() || "No description available",
});
}
}
}
return commands;
}
// Get the appropriate file path for a provider
export function getFilePath(type, provider, id) {
const distDir = join(process.cwd(), "dist");
if (type === "skill") {
if (provider === "cursor") {
return join(distDir, "cursor", ".cursor", "rules", `${id}.md`);
} else if (provider === "claude-code") {
return join(distDir, "claude-code", ".claude", "skills", id, "SKILL.md");
} else if (provider === "gemini") {
return join(distDir, "gemini", `GEMINI.${id}.md`);
} else if (provider === "codex") {
return join(distDir, "codex", `AGENTS.${id}.md`);
}
} else if (type === "command") {
if (provider === "cursor") {
return join(distDir, "cursor", ".cursor", "commands", `${id}.md`);
} else if (provider === "claude-code") {
return join(distDir, "claude-code", ".claude", "commands", `${id}.md`);
} else if (provider === "gemini") {
return join(distDir, "gemini", ".gemini", "commands", `${id}.toml`);
} else if (provider === "codex") {
return join(distDir, "codex", ".codex", "prompts", `${id}.md`);
}
}
return null;
}
// Handle individual file download
export async function handleFileDownload(type, provider, id) {
if (type !== "skill" && type !== "command") {
return new Response("Invalid type", { status: 400 });
}
const filePath = getFilePath(type, provider, id);
if (!filePath) {
return new Response("Invalid provider", { status: 400 });
}
try {
const file = Bun.file(filePath);
const exists = await file.exists();
if (!exists) {
return new Response("File not found", { status: 404 });
}
const fileName = basename(filePath);
return new Response(file, {
headers: {
"Content-Type": "application/octet-stream",
"Content-Disposition": `attachment; filename="${fileName}"`,
},
});
} catch (error) {
console.error("Error downloading file:", error);
return new Response("Error downloading file", { status: 500 });
}
}
// Handle bundle download
export async function handleBundleDownload(provider) {
const distDir = join(process.cwd(), "dist");
const zipPath = join(distDir, `${provider}.zip`);
try {
const file = Bun.file(zipPath);
const exists = await file.exists();
if (!exists) {
return new Response("Bundle not found", { status: 404 });
}
return new Response(file, {
headers: {
"Content-Type": "application/zip",
"Content-Disposition": `attachment; filename="impeccable-style-${provider}.zip"`,
},
});
} catch (error) {
console.error("Error downloading bundle:", error);
return new Response("Error downloading bundle", { status: 500 });
}
}