quartz build
This commit is contained in:
60
README.md
Normal file
60
README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# org-to-quartz
|
||||
|
||||
Convert org-mode notes to a Quartz static site.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Build pages from your notes
|
||||
|
||||
```bash
|
||||
nix build .#example-pages
|
||||
# Output in ./result/
|
||||
```
|
||||
|
||||
### Serve locally (development)
|
||||
|
||||
```bash
|
||||
nix run .#serve ./path/to/your/notes
|
||||
# Opens http://localhost:8080
|
||||
```
|
||||
|
||||
### Convert org to markdown only
|
||||
|
||||
```bash
|
||||
nix run . -- ./input-notes ./output-dir
|
||||
```
|
||||
|
||||
## Use in your own flake
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs.org-notes-quartz.url = "github:your-user/org-notes-quartz";
|
||||
|
||||
outputs = { self, nixpkgs, org-notes-quartz, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
buildQuartzPages = org-notes-quartz.lib.${system}.buildQuartzPages;
|
||||
in {
|
||||
packages.${system}.site = buildQuartzPages {
|
||||
contentDir = ./notes;
|
||||
# quartzConfig = ./quartz.config.ts; # optional
|
||||
# quartzLayout = ./quartz.layout.ts; # optional
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Available packages
|
||||
|
||||
| Package | Description |
|
||||
|-----------------------------|---------------------------------------------|
|
||||
| `default` / `org-to-quartz` | CLI to convert org files to Quartz markdown |
|
||||
| `quartz` | Quartz v4.5.2 static site generator |
|
||||
| `example-pages` | Example build from `test-notes/` |
|
||||
|
||||
## Available apps
|
||||
|
||||
| App | Description |
|
||||
|-----------|-------------------------------|
|
||||
| `default` | Run org-to-quartz converter |
|
||||
| `serve` | Serve notes with live preview |
|
||||
38
example-notes/concepts/atomic-notes.org
Normal file
38
example-notes/concepts/atomic-notes.org
Normal file
@@ -0,0 +1,38 @@
|
||||
#+title: Atomic Notes
|
||||
#+date: 2024-01-11
|
||||
#+filetags: :concept:writing:
|
||||
|
||||
An atomic note contains exactly one idea, fully expressed.
|
||||
|
||||
* Characteristics
|
||||
|
||||
- Self-contained
|
||||
- Single focus
|
||||
- Linkable
|
||||
- Reusable
|
||||
|
||||
* Benefits
|
||||
|
||||
When notes are atomic:
|
||||
|
||||
- Easy to link from multiple contexts
|
||||
- Simple to reorganize
|
||||
- Clear to understand
|
||||
- Quick to write
|
||||
|
||||
* Examples
|
||||
|
||||
Good atomic note titles:
|
||||
- "Spaced repetition improves long-term retention"
|
||||
- "Links create unexpected connections"
|
||||
- "Writing clarifies thinking"
|
||||
|
||||
Bad titles:
|
||||
- "Notes about learning"
|
||||
- "Stuff I read"
|
||||
|
||||
* Connection to Other Concepts
|
||||
|
||||
Atomic notes are the foundation of [[file:zettelkasten.org][Zettelkasten]] and enable effective [[file:linking.org][Linking]].
|
||||
|
||||
This principle comes from software engineering - see cite:martin2008 on the Single Responsibility Principle.
|
||||
26
example-notes/concepts/knowledge-management.org
Normal file
26
example-notes/concepts/knowledge-management.org
Normal file
@@ -0,0 +1,26 @@
|
||||
#+title: Knowledge Management
|
||||
#+date: 2024-01-10
|
||||
#+filetags: :concept:learning:
|
||||
|
||||
Knowledge management is the process of creating, sharing, using, and managing information.
|
||||
|
||||
* Core Principles
|
||||
|
||||
1. *Capture* - Record ideas as they come
|
||||
2. *Organize* - Structure for retrieval
|
||||
3. *Connect* - Link related concepts
|
||||
4. *Review* - Revisit and refine
|
||||
|
||||
* Related Concepts
|
||||
|
||||
- [[file:zettelkasten.org][Zettelkasten]] - A specific method for knowledge management
|
||||
- [[file:atomic-notes.org][Atomic Notes]] - The building blocks
|
||||
- [[file:linking.org][Linking]] - How connections create value
|
||||
|
||||
* Tools
|
||||
|
||||
See [[file:../projects/website.org][Website Project]] for how to publish your knowledge base.
|
||||
|
||||
* References
|
||||
|
||||
The field draws from library science cite:weinberger2007 and cognitive psychology cite:ahrens2017.
|
||||
43
example-notes/concepts/linking.org
Normal file
43
example-notes/concepts/linking.org
Normal file
@@ -0,0 +1,43 @@
|
||||
#+title: Linking
|
||||
#+date: 2024-01-13
|
||||
#+filetags: :concept:structure:
|
||||
|
||||
Links between notes create a network of knowledge.
|
||||
|
||||
* Types of Links
|
||||
|
||||
** Hierarchical
|
||||
Parent-child relationships (folders, outlines)
|
||||
|
||||
** Associative
|
||||
Related concepts ([[file:zettelkasten.org][Zettelkasten]] style)
|
||||
|
||||
** Sequential
|
||||
Reading order, workflows
|
||||
|
||||
* Why Links Matter
|
||||
|
||||
#+begin_src text
|
||||
[Note A] --- related to --- [Note B]
|
||||
| |
|
||||
+--- supports --- [Note C] -+
|
||||
#+end_src
|
||||
|
||||
Links reveal:
|
||||
- Hidden connections
|
||||
- Knowledge gaps
|
||||
- Entry points
|
||||
|
||||
* Backlinks
|
||||
|
||||
When [[file:atomic-notes.org][Atomic Notes]] links here, this page knows about it. Backlinks show:
|
||||
|
||||
- Who references this concept
|
||||
- How the idea is used
|
||||
- Related contexts
|
||||
|
||||
* Tools Supporting Links
|
||||
|
||||
See [[file:../projects/website.org][Website Project]] for publishing linked notes.
|
||||
|
||||
The web itself is built on links cite:bernerslee1999.
|
||||
41
example-notes/concepts/spaced-repetition.org
Normal file
41
example-notes/concepts/spaced-repetition.org
Normal file
@@ -0,0 +1,41 @@
|
||||
#+title: Spaced Repetition
|
||||
#+date: 2024-01-14
|
||||
#+filetags: :concept:learning:memory:
|
||||
|
||||
Spaced repetition is a learning technique that involves reviewing material at increasing intervals.
|
||||
|
||||
* The Forgetting Curve
|
||||
|
||||
Without review, we forget:
|
||||
- 50% within an hour
|
||||
- 70% within 24 hours
|
||||
- 90% within a week
|
||||
|
||||
* How Spacing Helps
|
||||
|
||||
Review schedule example:
|
||||
|
||||
| Review | Interval |
|
||||
|--------|----------|
|
||||
| 1st | 1 day |
|
||||
| 2nd | 3 days |
|
||||
| 3rd | 1 week |
|
||||
| 4th | 2 weeks |
|
||||
| 5th | 1 month |
|
||||
|
||||
* Software
|
||||
|
||||
- Anki
|
||||
- Org-drill (Emacs)
|
||||
- RemNote
|
||||
|
||||
* Connection to Note-Taking
|
||||
|
||||
[[file:zettelkasten.org][Zettelkasten]] provides natural spaced repetition through:
|
||||
- Random encounters while linking
|
||||
- Review during searches
|
||||
- Connections triggering recall
|
||||
|
||||
* Research
|
||||
|
||||
Based on memory research cite:ebbinghaus1885 and modern applications cite:pimsleur1967.
|
||||
38
example-notes/concepts/zettelkasten.org
Normal file
38
example-notes/concepts/zettelkasten.org
Normal file
@@ -0,0 +1,38 @@
|
||||
#+title: Zettelkasten
|
||||
#+date: 2024-01-12
|
||||
#+filetags: :concept:methodology:productivity:
|
||||
|
||||
The Zettelkasten (German for "slip box") is a note-taking method developed by sociologist Niklas Luhmann.
|
||||
|
||||
* Key Features
|
||||
|
||||
- Each note contains one idea ([[file:atomic-notes.org][Atomic Notes]])
|
||||
- Notes link to each other extensively ([[file:linking.org][Linking]])
|
||||
- No strict hierarchy - emergence through connections
|
||||
|
||||
* How It Works
|
||||
|
||||
#+begin_quote
|
||||
"One cannot think without writing." - Niklas Luhmann
|
||||
#+end_quote
|
||||
|
||||
1. Write fleeting notes
|
||||
2. Convert to permanent notes
|
||||
3. Add to the slip box with links
|
||||
4. Review connections
|
||||
|
||||
* Implementation
|
||||
|
||||
| Analog | Digital |
|
||||
|--------|---------|
|
||||
| Index cards | Org-roam |
|
||||
| Physical box | Obsidian |
|
||||
| Manual links | Wiki links |
|
||||
|
||||
* Why It Works
|
||||
|
||||
The method leverages [[file:spaced-repetition.org][Spaced Repetition]] through organic review and builds on [[file:knowledge-management.org][Knowledge Management]] principles.
|
||||
|
||||
* Further Reading
|
||||
|
||||
See cite:ahrens2017 for the definitive guide.
|
||||
25
example-notes/daily/2024-01-20.org
Normal file
25
example-notes/daily/2024-01-20.org
Normal file
@@ -0,0 +1,25 @@
|
||||
#+title: 2024-01-20
|
||||
#+date: 2024-01-20
|
||||
#+filetags: :daily:journal:
|
||||
|
||||
* Tasks
|
||||
|
||||
- [X] Review [[file:../concepts/zettelkasten.org][Zettelkasten]] notes
|
||||
- [X] Update [[file:../projects/website.org][Website Project]] status
|
||||
- [ ] Read more of cite:ahrens2017
|
||||
|
||||
* Notes
|
||||
|
||||
Realized that [[file:../concepts/atomic-notes.org][Atomic Notes]] principle applies beyond note-taking:
|
||||
- Commit messages
|
||||
- Documentation sections
|
||||
- Email paragraphs
|
||||
|
||||
* Ideas
|
||||
|
||||
Could [[file:../concepts/spaced-repetition.org][Spaced Repetition]] work for code review? Flag old code for periodic review.
|
||||
|
||||
* References
|
||||
|
||||
- Discussed [[file:../concepts/knowledge-management.org][Knowledge Management]] with colleague
|
||||
- Found new source on [[file:../concepts/linking.org][Linking]] theory
|
||||
17
example-notes/index.org
Normal file
17
example-notes/index.org
Normal file
@@ -0,0 +1,17 @@
|
||||
#+title: Welcome
|
||||
#+date: 2024-01-15
|
||||
#+filetags: :home:
|
||||
|
||||
This is a digital garden built from org-mode notes.
|
||||
|
||||
* Getting Started
|
||||
|
||||
Explore the different sections:
|
||||
|
||||
- [[file:concepts/knowledge-management.org][Knowledge Management]] - Core concepts
|
||||
- [[file:projects/website.org][Website Project]] - An example project
|
||||
- [[file:references/index.org][References]] - Books and papers
|
||||
|
||||
* Recent Updates
|
||||
|
||||
Check out [[file:concepts/zettelkasten.org][Zettelkasten]] for note-taking methodology.
|
||||
34
example-notes/projects/learning-system.org
Normal file
34
example-notes/projects/learning-system.org
Normal file
@@ -0,0 +1,34 @@
|
||||
#+title: Learning System
|
||||
#+date: 2024-01-18
|
||||
#+filetags: :project:planning:
|
||||
|
||||
A personal system for continuous learning.
|
||||
|
||||
* Components
|
||||
|
||||
1. *Capture* - Quick notes, highlights, ideas
|
||||
2. *Process* - Convert to [[file:../concepts/atomic-notes.org][Atomic Notes]]
|
||||
3. *Connect* - Add [[file:../concepts/linking.org][Links]] to existing notes
|
||||
4. *Review* - [[file:../concepts/spaced-repetition.org][Spaced Repetition]] for retention
|
||||
|
||||
* Daily Practice
|
||||
|
||||
| Time | Activity |
|
||||
|------|----------|
|
||||
| Morning | Review queue |
|
||||
| Anytime | Capture ideas |
|
||||
| Evening | Process inbox |
|
||||
|
||||
* Tools
|
||||
|
||||
- Org-mode for capture
|
||||
- Org-roam for [[file:../concepts/zettelkasten.org][Zettelkasten]]
|
||||
- Org-drill for review
|
||||
|
||||
* Inspiration
|
||||
|
||||
Based on [[file:../concepts/knowledge-management.org][Knowledge Management]] research and cite:forte2022 Building a Second Brain methodology.
|
||||
|
||||
* Status
|
||||
|
||||
Currently implementing the capture phase. See [[file:website.org][Website Project]] for publishing component.
|
||||
40
example-notes/projects/website.org
Normal file
40
example-notes/projects/website.org
Normal file
@@ -0,0 +1,40 @@
|
||||
#+title: Website Project
|
||||
#+date: 2024-01-20
|
||||
#+filetags: :project:active:
|
||||
|
||||
Building a digital garden from org-mode notes.
|
||||
|
||||
* Overview
|
||||
|
||||
Transform [[file:../concepts/knowledge-management.org][Knowledge Management]] practices into a public website.
|
||||
|
||||
* Goals
|
||||
|
||||
- [X] Convert org files to markdown
|
||||
- [X] Preserve [[file:../concepts/linking.org][Linking]] between notes
|
||||
- [ ] Add search functionality
|
||||
- [ ] Deploy to hosting
|
||||
|
||||
* Technical Stack
|
||||
|
||||
| Component | Tool |
|
||||
|-----------|------|
|
||||
| Source | Org-mode |
|
||||
| Converter | org-to-quartz |
|
||||
| Generator | Quartz |
|
||||
| Hosting | TBD |
|
||||
|
||||
* Architecture
|
||||
|
||||
#+begin_src text
|
||||
org files --> org-to-quartz --> markdown --> Quartz --> HTML
|
||||
#+end_src
|
||||
|
||||
* Related
|
||||
|
||||
- [[file:../concepts/zettelkasten.org][Zettelkasten]] - The methodology behind the content
|
||||
- [[file:../references/index.org][References]] - Sources cited throughout
|
||||
|
||||
* Notes
|
||||
|
||||
Using [[file:../concepts/atomic-notes.org][Atomic Notes]] makes each page focused and linkable.
|
||||
32
example-notes/references/ahrens2017.org
Normal file
32
example-notes/references/ahrens2017.org
Normal file
@@ -0,0 +1,32 @@
|
||||
#+title: How to Take Smart Notes
|
||||
#+date: 2024-01-05
|
||||
#+filetags: :reference:book:
|
||||
|
||||
* Citation
|
||||
|
||||
Ahrens, S. (2017). /How to Take Smart Notes/. CreateSpace.
|
||||
|
||||
* Summary
|
||||
|
||||
A guide to the [[file:../concepts/zettelkasten.org][Zettelkasten]] method based on Niklas Luhmann's practice.
|
||||
|
||||
* Key Ideas
|
||||
|
||||
1. Writing is thinking
|
||||
2. One idea per note ([[file:../concepts/atomic-notes.org][Atomic Notes]])
|
||||
3. Links over folders ([[file:../concepts/linking.org][Linking]])
|
||||
4. Bottom-up organization
|
||||
|
||||
* Quotes
|
||||
|
||||
#+begin_quote
|
||||
"The slip-box is designed to present you with ideas you have already forgotten."
|
||||
#+end_quote
|
||||
|
||||
#+begin_quote
|
||||
"Every intellectual endeavour starts from an already existing preconception."
|
||||
#+end_quote
|
||||
|
||||
* Application
|
||||
|
||||
This book directly influenced the [[file:../projects/learning-system.org][Learning System]] project.
|
||||
31
example-notes/references/index.org
Normal file
31
example-notes/references/index.org
Normal file
@@ -0,0 +1,31 @@
|
||||
#+title: References
|
||||
#+date: 2024-01-08
|
||||
#+filetags: :reference:index:
|
||||
|
||||
Collection of books, papers, and resources.
|
||||
|
||||
* Books
|
||||
|
||||
** Knowledge Management
|
||||
- cite:ahrens2017 - How to Take Smart Notes
|
||||
- cite:forte2022 - Building a Second Brain
|
||||
|
||||
** Software & Writing
|
||||
- cite:martin2008 - Clean Code
|
||||
- cite:weinberger2007 - Everything Is Miscellaneous
|
||||
|
||||
* Papers
|
||||
|
||||
- cite:ebbinghaus1885 - Memory: A Contribution to Experimental Psychology
|
||||
- cite:pimsleur1967 - A Memory Schedule
|
||||
|
||||
* Web Resources
|
||||
|
||||
- cite:bernerslee1999 - Weaving the Web
|
||||
|
||||
* How References Are Used
|
||||
|
||||
References connect to concepts:
|
||||
- [[file:../concepts/zettelkasten.org][Zettelkasten]] cites cite:ahrens2017
|
||||
- [[file:../concepts/spaced-repetition.org][Spaced Repetition]] cites cite:ebbinghaus1885
|
||||
- [[file:../concepts/linking.org][Linking]] cites cite:bernerslee1999
|
||||
254
flake.nix
254
flake.nix
@@ -44,35 +44,267 @@
|
||||
};
|
||||
};
|
||||
|
||||
# Script to serve quartz with converted notes
|
||||
# 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 "Cloning Quartz..."
|
||||
${pkgs.git}/bin/git clone --depth 1 https://github.com/jackyzha0/quartz.git "$WORK_DIR/quartz" 2>/dev/null
|
||||
|
||||
echo "Installing dependencies..."
|
||||
cd "$WORK_DIR/quartz"
|
||||
${pkgs.nodejs}/bin/npm install --silent
|
||||
echo "Setting up Quartz..."
|
||||
cp -r ${quartz}/lib/quartz/* "$WORK_DIR/"
|
||||
chmod -R u+w "$WORK_DIR"
|
||||
|
||||
echo "Converting org notes from $NOTES_DIR..."
|
||||
${org-to-quartz}/bin/org-to-quartz "$NOTES_DIR" "$WORK_DIR/quartz/content" -v
|
||||
rm -rf "$WORK_DIR/content"
|
||||
mkdir -p "$WORK_DIR/content"
|
||||
${org-to-quartz}/bin/org-to-quartz "$NOTES_DIR" "$WORK_DIR/content" -v
|
||||
|
||||
# Enable OxHugo plugin
|
||||
${pkgs.gnused}/bin/sed -i 's/Plugin.GitHubFlavoredMarkdown()/Plugin.OxHugoFlavouredMarkdown(),\n Plugin.GitHubFlavoredMarkdown()/' quartz.config.ts
|
||||
cd "$WORK_DIR"
|
||||
|
||||
echo ""
|
||||
echo "Starting Quartz on http://localhost:$PORT"
|
||||
${pkgs.nodejs}/bin/npx quartz build --serve --port "$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 {
|
||||
|
||||
@@ -11,18 +11,26 @@ from .citations import CitationResolver
|
||||
|
||||
|
||||
def find_org_files(input_dir: Path) -> list[Path]:
|
||||
"""Find all .org files in directory (non-recursive)."""
|
||||
return list(input_dir.glob("*.org"))
|
||||
"""Find all .org files in directory (recursive)."""
|
||||
return list(input_dir.rglob("*.org"))
|
||||
|
||||
|
||||
def convert_file(
|
||||
org_path: Path,
|
||||
input_dir: Path,
|
||||
output_dir: Path,
|
||||
citation_resolver: CitationResolver | None,
|
||||
verbose: bool = False,
|
||||
) -> Path | None:
|
||||
"""Convert a single org file to Quartz markdown.
|
||||
|
||||
Args:
|
||||
org_path: Path to the org file
|
||||
input_dir: Root input directory (for computing relative paths)
|
||||
output_dir: Root output directory
|
||||
citation_resolver: Optional citation resolver
|
||||
verbose: Enable verbose output
|
||||
|
||||
Returns:
|
||||
Path to created note directory, or None on error
|
||||
"""
|
||||
@@ -33,8 +41,21 @@ def convert_file(
|
||||
if verbose:
|
||||
print(f" Parsed: {doc.title or org_path.stem}")
|
||||
|
||||
# Compute relative path from input dir to preserve directory structure
|
||||
relative_path = org_path.relative_to(input_dir)
|
||||
|
||||
# Special case: root index.org becomes content/index.md directly
|
||||
if relative_path.stem == "index" and relative_path.parent == Path("."):
|
||||
note_dir = output_dir
|
||||
output_path = output_dir / "index.md"
|
||||
else:
|
||||
# Replace .org with directory containing index.md
|
||||
# e.g., concepts/zettelkasten.org -> concepts/zettelkasten/index.md
|
||||
note_relative_dir = relative_path.parent / relative_path.stem
|
||||
note_dir = output_dir / note_relative_dir
|
||||
output_path = note_dir / "index.md"
|
||||
|
||||
# Create output directory for this note
|
||||
note_dir = output_dir / doc.slug
|
||||
note_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy images first (before conversion, to get path mapping)
|
||||
@@ -50,7 +71,6 @@ def convert_file(
|
||||
md_content = update_image_paths(md_content, image_mapping)
|
||||
|
||||
# Write output
|
||||
output_path = note_dir / "index.md"
|
||||
output_path.write_text(md_content, encoding="utf-8")
|
||||
|
||||
return note_dir
|
||||
@@ -123,7 +143,7 @@ def main() -> int:
|
||||
|
||||
for org_path in org_files:
|
||||
print(f"Converting: {org_path.name}")
|
||||
result = convert_file(org_path, args.output_dir, citation_resolver, args.verbose)
|
||||
result = convert_file(org_path, args.input_dir, args.output_dir, citation_resolver, args.verbose)
|
||||
if result:
|
||||
success_count += 1
|
||||
if args.verbose:
|
||||
|
||||
@@ -57,7 +57,8 @@ def process_wikilinks(content: str) -> str:
|
||||
Pandoc converts org links to markdown links:
|
||||
- [[roam:Title]] -> [roam:Title](roam:Title) -> [[Title]]
|
||||
- [[id:uuid][Desc]] -> [Desc](id:uuid) -> [[Desc]]
|
||||
- [[file:x.org][Desc]] -> [Desc](file:x.org) -> [[Desc]]
|
||||
- [[file:x.org][Desc]] -> [Desc](x) (relative link)
|
||||
- [[file:../dir/x.org][Desc]] -> [Desc](../dir/x) (relative link)
|
||||
"""
|
||||
# [roam:Title](roam:Title) -> [[Title]]
|
||||
content = re.sub(r"\[roam:([^\]]+)\]\(roam:[^)]+\)", r"[[\1]]", content)
|
||||
@@ -65,8 +66,21 @@ def process_wikilinks(content: str) -> str:
|
||||
# [Description](id:uuid) -> [[Description]]
|
||||
content = re.sub(r"\[([^\]]+)\]\(id:[a-f0-9-]+\)", r"[[\1]]", content)
|
||||
|
||||
# [Description](file:something.org) -> [[Description]]
|
||||
content = re.sub(r"\[([^\]]+)\]\(file:[^)]+\.org\)", r"[[\1]]", content)
|
||||
# [Description](file:path/to/something.org) -> [Description](/path/to/something)
|
||||
# Convert org file links to proper Quartz paths
|
||||
def convert_file_link(match):
|
||||
description = match.group(1)
|
||||
path = match.group(2)
|
||||
# Remove file: prefix and .org suffix, convert to Quartz path
|
||||
# ../concepts/foo.org -> ../concepts/foo
|
||||
clean_path = re.sub(r"\.org$", "", path)
|
||||
return f"[{description}]({clean_path})"
|
||||
|
||||
content = re.sub(r"\[([^\]]+)\]\(file:([^)]+\.org)\)", convert_file_link, content)
|
||||
|
||||
# Also handle relative paths without file: prefix (pandoc sometimes drops it)
|
||||
# [Description](../something.org) -> [Description](../something)
|
||||
content = re.sub(r"\[([^\]]+)\]\(([^):]+\.org)\)", convert_file_link, content)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
Reference in New Issue
Block a user