@slidef/cli
Version:
CLI tool for converting PDF slides to web-viewable format
180 lines ⢠8.29 kB
JavaScript
import * as fs from "fs/promises";
import * as path from "path";
import { fileURLToPath } from "url";
import chalk from "chalk";
import ora from "ora";
import { getAllSlides, saveSlideIndex } from "../utils/file.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
* Generate CSS for theme customization
*/
function generateThemeStyles(theme) {
const styles = [":root {"];
if (theme.primaryColor) {
styles.push(` --primary-color: ${theme.primaryColor};`);
}
if (theme.backgroundColor) {
styles.push(` --bg-primary: ${theme.backgroundColor};`);
}
if (theme.textColor) {
styles.push(` --text-primary: ${theme.textColor};`);
}
if (theme.progressColor) {
styles.push(` --progress-fill: ${theme.progressColor};`);
}
if (theme.fontFamily) {
styles.push(` --font-family: ${theme.fontFamily};`);
}
styles.push("}");
if (theme.fontFamily) {
styles.push(`body { font-family: ${theme.fontFamily}; }`);
}
return styles.join("\n");
}
/**
* Apply baseUrl to all asset paths in HTML
*/
function applyBaseUrl(html, baseUrl) {
// Ensure baseUrl ends with / if it's not just "/"
const base = baseUrl === "/" ? "" : baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
// Replace asset paths
return html
.replace(/href="\/favicon\.svg"/g, `href="${base}/favicon.svg"`)
.replace(/href="\/css\//g, `href="${base}/css/`)
.replace(/src="\/js\//g, `src="${base}/js/`)
.replace(/src="\/slides\//g, `src="${base}/slides/`);
}
export async function publishCommand(options) {
const spinner = ora("Publishing slides...").start();
try {
const cwd = process.cwd();
// Load config
let config = {
baseUrl: "/",
publishDir: "public",
slidesDir: "slides",
};
try {
const configPath = path.join(cwd, "slidef.config.json");
const configData = await fs.readFile(configPath, "utf-8");
config = { ...config, ...JSON.parse(configData) };
}
catch {
// Config doesn't exist, use defaults
}
const slidesDir = path.resolve(options.slides || config.slidesDir || "slides");
const outputDir = path.resolve(options.output || config.publishDir || "public");
// Ensure output directory exists
await fs.mkdir(outputDir, { recursive: true });
spinner.text = "Scanning slides directory...";
// Get all slides
const slides = await getAllSlides(slidesDir);
if (slides.length === 0) {
spinner.warn(chalk.yellow("No slides found to publish"));
console.log(chalk.gray(`\nRun ${chalk.cyan("slidef convert <pdf-file>")} to add slides first`));
return;
}
spinner.text = "Generating slide index...";
// Save slide index
await saveSlideIndex(outputDir, slides);
spinner.text = "Copying viewer files...";
// Copy viewer files from templates directory
const templatesDir = path.join(__dirname, "../templates");
// Copy and customize HTML files
let indexHtml = await fs.readFile(path.join(templatesDir, "index.html"), "utf-8");
let viewerHtml = await fs.readFile(path.join(templatesDir, "viewer.html"), "utf-8");
// Apply config customization
indexHtml = indexHtml
.replace("<title>Slidef - Slide Presentations</title>", `<title>${config.title}</title>`)
.replace('<h1 class="page-title">š Slide Presentations</h1>', `<h1 class="page-title">š ${config.title}</h1>`)
.replace('<p class="page-subtitle">View and manage your slide decks</p>', `<p class="page-subtitle">${config.subtitle}</p>`);
// Apply theme customization
if (config.theme) {
const themeStyles = generateThemeStyles(config.theme);
const styleTag = `<style>${themeStyles}</style></head>`;
indexHtml = indexHtml.replace("</head>", styleTag);
viewerHtml = viewerHtml.replace("</head>", styleTag);
}
// Apply baseUrl to asset paths
const baseUrl = config.baseUrl || "/";
// Inject BASE_URL into HTML for JavaScript to use
const baseUrlScript = `<script>window.BASE_URL = "${baseUrl}";</script></head>`;
indexHtml = indexHtml.replace("</head>", baseUrlScript);
viewerHtml = viewerHtml.replace("</head>", baseUrlScript);
indexHtml = applyBaseUrl(indexHtml, baseUrl);
viewerHtml = applyBaseUrl(viewerHtml, baseUrl);
await fs.writeFile(path.join(outputDir, "index.html"), indexHtml, "utf-8");
await fs.writeFile(path.join(outputDir, "viewer.html"), viewerHtml, "utf-8");
// Copy favicon
await fs.copyFile(path.join(templatesDir, "favicon.svg"), path.join(outputDir, "favicon.svg"));
// Copy CSS directory
const cssOutputDir = path.join(outputDir, "css");
await fs.mkdir(cssOutputDir, { recursive: true });
await copyDirectory(path.join(templatesDir, "css"), cssOutputDir);
// Copy JS directory
const jsOutputDir = path.join(outputDir, "js");
await fs.mkdir(jsOutputDir, { recursive: true });
await copyDirectory(path.join(templatesDir, "js"), jsOutputDir);
spinner.text = "Copying slides...";
// Ensure slides directory exists in output
const outputSlidesDir = path.join(outputDir, "slides");
await fs.mkdir(outputSlidesDir, { recursive: true });
// Copy slides directory if they're not already in output
if (path.resolve(slidesDir) !== path.resolve(outputSlidesDir)) {
// Copy all slide directories
const entries = await fs.readdir(slidesDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
const srcDir = path.join(slidesDir, entry.name);
const destDir = path.join(outputSlidesDir, entry.name);
// Copy directory recursively
await copyDirectory(srcDir, destDir);
}
}
}
spinner.text = "Creating slide routes...";
// Create directory for each slide with index.html (for clean URLs without .html)
for (const slide of slides) {
const slideRouteDir = path.join(outputDir, slide.name);
await fs.mkdir(slideRouteDir, { recursive: true });
const slideHtmlPath = path.join(slideRouteDir, "index.html");
await fs.writeFile(slideHtmlPath, viewerHtml, "utf-8");
}
spinner.succeed(chalk.green(`Successfully published ${chalk.cyan(slides.length)} slide(s)`));
console.log(chalk.gray("\nGenerated files:"));
console.log(chalk.gray(` ${outputDir}/`));
console.log(chalk.gray(` āāā index.html`));
console.log(chalk.gray(` āāā viewer.html`));
console.log(chalk.gray(` āāā css/`));
console.log(chalk.gray(` āāā js/`));
console.log(chalk.gray(` āāā slides-index.json`));
console.log(chalk.gray(` āāā slides/`));
console.log(chalk.green("\n⨠Your slides are ready!"));
console.log(chalk.gray(`Open ${chalk.cyan(path.join(outputDir, "index.html"))} in a browser to view`));
console.log(chalk.gray("Or deploy to GitHub Pages for online access"));
}
catch (error) {
spinner.fail(chalk.red("Publish failed"));
console.error(chalk.red("\nError:"), error instanceof Error ? error.message : error);
process.exit(1);
}
}
/**
* Recursively copy directory
*/
async function copyDirectory(src, dest) {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirectory(srcPath, destPath);
}
else {
await fs.copyFile(srcPath, destPath);
}
}
}
//# sourceMappingURL=publish.js.map