feat: obsidian-based hugo blogging
This commit is contained in:
parent
fc29ac5e0e
commit
1474042b29
3 changed files with 216 additions and 26 deletions
|
|
@ -1,4 +1,5 @@
|
|||
{ lib
|
||||
{ config
|
||||
, lib
|
||||
, pkgs
|
||||
, ...
|
||||
}:
|
||||
|
|
@ -30,11 +31,129 @@ let
|
|||
useMarkdownLinks = false;
|
||||
showUnsupportedFiles = true;
|
||||
};
|
||||
|
||||
website = "${config.home.homeDirectory}/development/bruvduroiu/website";
|
||||
blogsFolder = "${config.home.homeDirectory}/Documents/Keep/4 - Blogs";
|
||||
|
||||
# Single action: mirror the vault's `4 - Blogs` folder into the Hugo repo's content/.
|
||||
# Everything else (build, commit, push, local preview) is handled manually outside
|
||||
# Obsidian. rsync --delete gives true mirror semantics (deletions propagate); cp -a
|
||||
# would leave ghosts of deleted posts behind.
|
||||
blog-sync = pkgs.writeShellApplication {
|
||||
name = "blog-sync";
|
||||
runtimeInputs = with pkgs; [ rsync ];
|
||||
text = ''
|
||||
vault="${blogsFolder}"
|
||||
repo="${website}"
|
||||
|
||||
# Safety: refuse to mirror an unseeded vault — rsync --delete would otherwise
|
||||
# wipe every post in the repo.
|
||||
if [ ! -d "$vault/blog" ] || [ ! -f "$vault/_index.md" ]; then
|
||||
echo "blog-sync: '4 - Blogs' looks unseeded (missing blog/ or _index.md); aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rsync -a --delete --exclude='.obsidian' "$vault/" "$repo/content/"
|
||||
echo "blog-sync: mirrored 4 - Blogs -> $repo/content"
|
||||
'';
|
||||
};
|
||||
|
||||
# One Shell-commands `data.json` command entry. Mirrors the plugin's
|
||||
# newShellCommandConfiguration() exactly — the module writes data.json as a
|
||||
# read-only store symlink, and Shell commands uses the loaded file as-is (it only
|
||||
# merges defaults to backfill `debug`/`settings_version`), so every field a current
|
||||
# install expects must be present or command registration can throw.
|
||||
mkShellCommand = id: alias: command: {
|
||||
inherit id alias;
|
||||
platform_specific_commands.default = command;
|
||||
shells = { };
|
||||
icon = null;
|
||||
confirm_execution = false;
|
||||
ignore_error_codes = [ ];
|
||||
input_contents.stdin = null;
|
||||
output_handlers = {
|
||||
stdout = { handler = "ignore"; convert_ansi_code = true; };
|
||||
stderr = { handler = "notification"; convert_ansi_code = true; };
|
||||
};
|
||||
output_wrappers = { stdout = null; stderr = null; };
|
||||
output_channel_order = "stdout-first";
|
||||
output_handling_mode = "buffered";
|
||||
execution_notification_mode = null;
|
||||
events = { };
|
||||
debounce = null;
|
||||
command_palette_availability = "enabled";
|
||||
preactions = [ ];
|
||||
variable_default_values = { };
|
||||
};
|
||||
|
||||
# Shell commands' built-in shells hardcode /bin/bash, /bin/dash, /bin/zsh in
|
||||
# getBinaryPath() and spawn THAT (the configured shell string only drives matching) —
|
||||
# none of those exist on NixOS, which provides only /bin/sh. So a built-in shell is
|
||||
# unusable here by construction (an unset shell falls back to $SHELL=fish, which the
|
||||
# plugin also doesn't support). Define a custom shell pinned to /bin/sh instead.
|
||||
# Mirrors CustomShellModel.getDefaultConfiguration(); our command is an absolute-path
|
||||
# binary, so a POSIX sh is all that is needed.
|
||||
nixShellId = "nixos-binsh";
|
||||
nixShell = {
|
||||
id = nixShellId;
|
||||
name = "NixOS /bin/sh";
|
||||
description = "POSIX sh via /bin/sh — built-in shells hardcode nonexistent /bin/bash and /bin/dash on NixOS.";
|
||||
binary_path = "/bin/sh";
|
||||
shell_arguments = [ "-c" "{{shell_command_content}}" ];
|
||||
host_platform = "linux";
|
||||
host_platform_configurations = { };
|
||||
shell_platform = null;
|
||||
escaper = "UnixShell";
|
||||
path_translator = null;
|
||||
shell_command_wrapper = null;
|
||||
shell_command_test = null;
|
||||
};
|
||||
|
||||
# Full SC_MainSettings default object (settings_version pinned to the packaged
|
||||
# plugin version, so no migration runs against the read-only file) + our command.
|
||||
shellcommandsSettings = {
|
||||
settings_version = "0.23.0";
|
||||
debug = false;
|
||||
obsidian_command_palette_prefix = "Execute: ";
|
||||
preview_variables_in_command_palette = true;
|
||||
show_autocomplete_menu = true;
|
||||
working_directory = "";
|
||||
default_shells = { linux = nixShellId; };
|
||||
environment_variable_path_augmentations = { };
|
||||
show_installation_warnings = true;
|
||||
error_message_duration = 20;
|
||||
notification_message_duration = 10;
|
||||
execution_notification_mode = "disabled";
|
||||
output_channel_clipboard_also_outputs_to_notification = true;
|
||||
output_channel_notification_decorates_output = true;
|
||||
enable_events = true;
|
||||
approve_modals_by_pressing_enter_key = true;
|
||||
command_palette.re_execute_last_shell_command = {
|
||||
enabled = true;
|
||||
prefix = "Re-execute: ";
|
||||
};
|
||||
max_visible_lines_in_shell_command_fields = false;
|
||||
shell_commands = [
|
||||
(mkShellCommand "blog-sync" "Blog: sync vault -> repo content" "${blog-sync}/bin/blog-sync")
|
||||
];
|
||||
prompts = [ ];
|
||||
builtin_variables = { };
|
||||
custom_variables = [ ];
|
||||
custom_variables_notify_changes_via = {
|
||||
obsidian_uri = true;
|
||||
output_assignment = true;
|
||||
};
|
||||
custom_shells = [ nixShell ];
|
||||
output_wrappers = [ ];
|
||||
};
|
||||
in
|
||||
{
|
||||
# Scripts, templates, and bases are stored here as version-controlled backup.
|
||||
# They live in the vault directly (synced via Syncthing).
|
||||
|
||||
# blog-sync is also exposed on PATH for use outside Obsidian.
|
||||
home.packages = [ blog-sync ];
|
||||
|
||||
programs.obsidian = {
|
||||
enable = true;
|
||||
package = pkgs.obsidian;
|
||||
|
|
@ -45,6 +164,21 @@ in
|
|||
settings = {
|
||||
app = appDefaults // {
|
||||
attachmentFolderPath = "9 - Attachments";
|
||||
# All new notes land in 5 - Main Notes. Blogs start there too but the Blog Post
|
||||
# template immediately tp.file.move()s them into 4 - Blogs/blog/<slug>/.
|
||||
newFileLocation = "folder";
|
||||
newFileFolderPath = "5 - Main Notes";
|
||||
};
|
||||
# Declarative custom hotkeys (.obsidian/hotkeys.json). Shell-commands command
|
||||
# ids are `obsidian-shellcommands:shell-command-<id>`; Templater registers each
|
||||
# enabled template-hotkey as `templater-obsidian:<vault-relative path>`.
|
||||
hotkeys = {
|
||||
"obsidian-shellcommands:shell-command-blog-sync" = [
|
||||
{ modifiers = [ "Mod" "Shift" ]; key = "P"; }
|
||||
];
|
||||
"templater-obsidian:9 - Templates/Blog Post.md" = [
|
||||
{ modifiers = [ "Mod" "Shift" ]; key = "B"; }
|
||||
];
|
||||
};
|
||||
corePlugins = [
|
||||
"backlink"
|
||||
|
|
@ -79,9 +213,11 @@ in
|
|||
command_palette = true;
|
||||
templates_folder = "9 - Templates";
|
||||
user_scripts_folder = "9 - Scripts";
|
||||
# Register the Blog Post template as a hotkeyable command.
|
||||
enabled_templates_hotkeys = [ "9 - Templates/Blog Post.md" ];
|
||||
};
|
||||
}
|
||||
{
|
||||
{
|
||||
pkg = pkgs.callPackage ./plugins/dataview.nix { };
|
||||
settings = {
|
||||
enableDataviewJs = true;
|
||||
|
|
@ -94,7 +230,7 @@ in
|
|||
}
|
||||
{
|
||||
pkg = pkgs.callPackage ./plugins/maps.nix { };
|
||||
settings = {
|
||||
settings = {
|
||||
tileSets = [
|
||||
{
|
||||
id = "protomaps";
|
||||
|
|
@ -105,29 +241,11 @@ in
|
|||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
Blog = {
|
||||
enable = true;
|
||||
target = "development/bruvduroiu/website/content";
|
||||
settings = {
|
||||
corePlugins = [
|
||||
"backlink"
|
||||
"bookmarks"
|
||||
"command-palette"
|
||||
"editor-status"
|
||||
"file-explorer"
|
||||
"global-search"
|
||||
"note-composer"
|
||||
"outgoing-link"
|
||||
"outline"
|
||||
"page-preview"
|
||||
"slash-command"
|
||||
"switcher"
|
||||
"tag-pane"
|
||||
"templates"
|
||||
"word-count"
|
||||
{
|
||||
# Single rsync command (see blog-sync above).
|
||||
pkg = pkgs.callPackage ./plugins/shellcommands.nix { };
|
||||
settings = shellcommandsSettings;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
35
home/programs/office/obsidian/plugins/shellcommands.nix
Normal file
35
home/programs/office/obsidian/plugins/shellcommands.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{ pkgs, ... }:
|
||||
# Unlike templater.nix / dataview.nix (which build from source), Shell commands is
|
||||
# fetched as its prebuilt release artifacts. Obsidian plugins are distributed as just
|
||||
# three files (main.js / manifest.json / styles.css), so fetching the release avoids an
|
||||
# npm/rollup build and the per-release npmDeps-hash churn that comes with it.
|
||||
let
|
||||
version = "0.23.0";
|
||||
base = "https://github.com/Taitava/obsidian-shellcommands/releases/download/${version}";
|
||||
|
||||
mainJs = pkgs.fetchurl {
|
||||
url = "${base}/main.js";
|
||||
hash = "sha256-DF4kepHJbAr18+MzukO+QoVGlWbIGdWbfeEprTPRTYo=";
|
||||
};
|
||||
manifest = pkgs.fetchurl {
|
||||
url = "${base}/manifest.json";
|
||||
hash = "sha256-IK6Mz6iXICfTW0RX0htquGrryOOx3uXro02tGeRqJGI=";
|
||||
};
|
||||
styles = pkgs.fetchurl {
|
||||
url = "${base}/styles.css";
|
||||
hash = "sha256-O9g4DlqlP8RH6mpMFN37lBmPGm0APm5fmUtFnPloTFM=";
|
||||
};
|
||||
in
|
||||
pkgs.runCommandLocal "obsidian.plugins.shellcommands-${version}"
|
||||
{
|
||||
inherit version;
|
||||
# The obsidian HM module reads `pkg.manifestId` (falling back to manifest.json's id)
|
||||
# to name the plugin's directory under .obsidian/plugins/.
|
||||
passthru.manifestId = "obsidian-shellcommands";
|
||||
}
|
||||
''
|
||||
mkdir -p $out
|
||||
cp ${mainJs} $out/main.js
|
||||
cp ${manifest} $out/manifest.json
|
||||
cp ${styles} $out/styles.css
|
||||
''
|
||||
37
home/programs/office/obsidian/templates/blog-post.md
Normal file
37
home/programs/office/obsidian/templates/blog-post.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<%*
|
||||
// Blog Post — Hugo page-bundle scaffold.
|
||||
// Backup of the vault file `9 - Templates/Blog Post.md`. Templates live in the vault
|
||||
// (Syncthing-synced); this copy is version-controlled, like scripts/moedict_fetch.js.
|
||||
const title = await tp.system.prompt("Post title");
|
||||
const slug = title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/(^-|-$)/g, "");
|
||||
// Move into a page bundle: 4 - Blogs/blog/<slug>/index.md (co-located images work).
|
||||
await tp.file.rename("index");
|
||||
await tp.file.move("/4 - Blogs/blog/" + slug + "/index");
|
||||
-%>
|
||||
---
|
||||
title: "<% title %>"
|
||||
date: <% tp.date.now("YYYY-MM-DDTHH:mm:ssZ") %>
|
||||
topics: []
|
||||
draft: true
|
||||
---
|
||||
|
||||
<!--
|
||||
Optional front matter (uncomment as needed):
|
||||
categories: []
|
||||
series: "" # name of the series this post belongs to
|
||||
tools: [] # technologies mentioned (taxonomy)
|
||||
layout: "default" # default | tutorial | longform
|
||||
description: # auto-generated from first paragraph if unset
|
||||
image: # auto-detects cover.* in the page bundle
|
||||
-->
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
## Main Content
|
||||
|
||||
|
||||
## Conclusion
|
||||
Loading…
Add table
Add a link
Reference in a new issue