Files
org-notes-quartz/flake.nix
Ignacio Ballesteros 79e93c167e quartz build
2026-02-14 18:00:32 +01:00

335 lines
11 KiB
Nix

{
description = "org-to-quartz: Convert org notes to Quartz-compatible markdown";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
python = pkgs.python311;
pythonPackages = python.pkgs;
org-to-quartz = pythonPackages.buildPythonApplication {
pname = "org-to-quartz";
version = "0.1.0";
format = "pyproject";
src = ./.;
nativeBuildInputs = [
pythonPackages.setuptools
pythonPackages.wheel
];
propagatedBuildInputs = [
pythonPackages.pybtex
pythonPackages.requests
pythonPackages.pyyaml
pkgs.pandoc
];
# Make pandoc available at runtime
makeWrapperArgs = [
"--prefix" "PATH" ":" "${pkgs.pandoc}/bin"
];
meta = {
description = "Convert org notes to Quartz-compatible markdown";
mainProgram = "org-to-quartz";
};
};
# Quartz package - builds the Quartz static site generator
quartz = pkgs.buildNpmPackage {
pname = "quartz";
version = "4.5.2";
src = pkgs.fetchFromGitHub {
owner = "jackyzha0";
repo = "quartz";
rev = "v4.5.2";
hash = "sha256-A6ePeNmcsbtKVnm7hVFOyjyc7gRYvXuG0XXQ6fvTLEw=";
};
npmDepsHash = "sha256-xxK9qy04m1olekOJIyYJHfdkYFzpjsgcfyFPuKsHpKE=";
# Quartz doesn't have a build step in the traditional sense
# It's a CLI tool that builds sites at runtime
dontNpmBuild = true;
# Install the quartz CLI
installPhase = ''
runHook preInstall
mkdir -p $out/lib/quartz
cp -r . $out/lib/quartz
mkdir -p $out/bin
cat > $out/bin/quartz <<'WRAPPER'
#!/usr/bin/env bash
# Quartz CLI wrapper - runs quartz from its library directory
# or from current directory if it contains a quartz config
QUARTZ_LIB="$out/lib/quartz"
if [[ -f "./quartz.config.ts" ]]; then
# Run from current directory (user's project)
exec ${pkgs.nodejs}/bin/node "$QUARTZ_LIB/quartz/bootstrap-cli.mjs" "$@"
else
# Run from quartz lib directory
cd "$QUARTZ_LIB"
exec ${pkgs.nodejs}/bin/node quartz/bootstrap-cli.mjs "$@"
fi
WRAPPER
# Replace $out with actual path
substituteInPlace $out/bin/quartz --replace-quiet '$out' "$out"
chmod +x $out/bin/quartz
runHook postInstall
'';
meta = {
description = "A fast, batteries-included static-site generator for digital gardens";
homepage = "https://quartz.jzhao.xyz";
mainProgram = "quartz";
};
};
# Default quartz config that works in Nix sandbox (no network fetches)
defaultQuartzConfig = pkgs.writeText "quartz.config.ts" ''
import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
const config: QuartzConfig = {
configuration: {
pageTitle: "Quartz Notes",
pageTitleSuffix: "",
enableSPA: true,
enablePopovers: true,
analytics: null,
locale: "en-US",
baseUrl: "localhost",
ignorePatterns: ["private", "templates", ".obsidian"],
defaultDateType: "modified",
theme: {
fontOrigin: "local",
cdnCaching: false,
typography: {
header: "sans-serif",
body: "sans-serif",
code: "monospace",
},
colors: {
lightMode: {
light: "#faf8f8",
lightgray: "#e5e5e5",
gray: "#b8b8b8",
darkgray: "#4e4e4e",
dark: "#2b2b2b",
secondary: "#284b63",
tertiary: "#84a59d",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#fff23688",
},
darkMode: {
light: "#161618",
lightgray: "#393639",
gray: "#646464",
darkgray: "#d4d4d4",
dark: "#ebebec",
secondary: "#7b97aa",
tertiary: "#84a59d",
highlight: "rgba(143, 159, 169, 0.15)",
textHighlight: "#b3aa0288",
},
},
},
},
plugins: {
transformers: [
Plugin.FrontMatter(),
Plugin.CreatedModifiedDate({
priority: ["frontmatter", "git", "filesystem"],
}),
Plugin.SyntaxHighlighting({
theme: {
light: "github-light",
dark: "github-dark",
},
keepBackground: false,
}),
Plugin.OxHugoFlavouredMarkdown(),
Plugin.GitHubFlavoredMarkdown(),
Plugin.TableOfContents(),
Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
Plugin.Description(),
Plugin.Latex({ renderEngine: "katex" }),
],
filters: [Plugin.RemoveDrafts()],
emitters: [
Plugin.AliasRedirects(),
Plugin.ComponentResources(),
Plugin.ContentPage(),
Plugin.FolderPage(),
Plugin.TagPage(),
Plugin.ContentIndex({
enableSiteMap: true,
enableRSS: true,
}),
Plugin.Assets(),
Plugin.Static(),
Plugin.Favicon(),
Plugin.NotFoundPage(),
// CustomOgImages disabled - requires network access for fonts
],
},
}
export default config
'';
# Function to build Quartz pages from a content directory
# Usage: buildQuartzPages { contentDir = ./my-org-notes; }
# contentDir: directory containing org files or markdown files
# quartzConfig: optional path to quartz.config.ts (uses sandbox-friendly default if not provided)
# quartzLayout: optional path to quartz.layout.ts
buildQuartzPages = {
contentDir,
quartzConfig ? null,
quartzLayout ? null,
name ? "quartz-pages"
}:
pkgs.stdenv.mkDerivation {
inherit name;
# Don't use src, we copy everything in buildPhase
dontUnpack = true;
nativeBuildInputs = [
pkgs.nodejs
org-to-quartz
];
buildPhase = ''
runHook preBuild
# Set up a writable quartz directory
cp -r ${quartz}/lib/quartz/* .
chmod -R u+w .
# Remove default content
rm -rf content
mkdir -p content
# Convert org files to markdown, or copy markdown directly
if ls ${contentDir}/*.org >/dev/null 2>&1; then
echo "Converting org notes from ${contentDir}..."
org-to-quartz ${contentDir} content
else
echo "Copying content from ${contentDir}..."
cp -r ${contentDir}/* content/
fi
# Apply quartz config (use sandbox-friendly default if not provided)
${if quartzConfig != null then ''
echo "Using custom quartz.config.ts..."
cp ${quartzConfig} quartz.config.ts
'' else ''
echo "Using default sandbox-friendly quartz.config.ts..."
cp ${defaultQuartzConfig} quartz.config.ts
''}
${pkgs.lib.optionalString (quartzLayout != null) ''
echo "Using custom quartz.layout.ts..."
cp ${quartzLayout} quartz.layout.ts
''}
# Build the static site
export HOME=$(mktemp -d)
echo "Building Quartz site..."
node quartz/bootstrap-cli.mjs build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mv public $out
runHook postInstall
'';
};
# Script to serve quartz with converted notes (for development)
quartz-serve = pkgs.writeShellScriptBin "quartz-serve" ''
set -e
NOTES_DIR="''${1:-.}"
PORT="''${2:-8080}"
WORK_DIR=$(mktemp -d)
echo "Setting up Quartz..."
cp -r ${quartz}/lib/quartz/* "$WORK_DIR/"
chmod -R u+w "$WORK_DIR"
echo "Converting org notes from $NOTES_DIR..."
rm -rf "$WORK_DIR/content"
mkdir -p "$WORK_DIR/content"
${org-to-quartz}/bin/org-to-quartz "$NOTES_DIR" "$WORK_DIR/content" -v
cd "$WORK_DIR"
echo ""
echo "Starting Quartz on http://localhost:$PORT"
${pkgs.nodejs}/bin/node quartz/bootstrap-cli.mjs build --serve --port "$PORT"
'';
# Example: build pages from example-notes directory
example-pages = buildQuartzPages {
contentDir = ./example-notes;
name = "example-quartz-pages";
};
in {
packages = {
default = org-to-quartz;
org-to-quartz = org-to-quartz;
quartz = quartz;
example-pages = example-pages;
};
# Export the buildQuartzPages function for use in other flakes
lib = {
inherit buildQuartzPages;
};
devShells.default = pkgs.mkShell {
buildInputs = [
python
pythonPackages.pybtex
pythonPackages.requests
pythonPackages.pyyaml
pythonPackages.pytest
pkgs.pandoc
pkgs.nodejs
];
};
apps = {
default = {
type = "app";
program = "${org-to-quartz}/bin/org-to-quartz";
};
serve = {
type = "app";
program = "${quartz-serve}/bin/quartz-serve";
};
};
}
);
}