feat(noctalia): add knowledge lookup panel

Provide a portable, cache-ignored Obsidian vault lookup plugin and bind it to Super+N.
This commit is contained in:
2026-08-13 15:47:57 +02:00
parent ba75f3c7f7
commit fd50b7deb9
12 changed files with 1776 additions and 2 deletions
@@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Safely move or delete entries inside the knowledge vault."""
from __future__ import annotations
import json
import shutil
import sys
from pathlib import Path
VAULT: Path
def fail(message: str) -> int:
print(message, file=sys.stderr)
return 1
def vault_entry(relative: str) -> Path | None:
if not relative or relative.startswith("/") or "\0" in relative:
return None
candidate = VAULT / relative
parent = candidate.parent.resolve()
if parent != VAULT and VAULT not in parent.parents:
return None
return parent / candidate.name
def move_entry(source_relative: str, destination_relative: str) -> int:
source = vault_entry(source_relative)
destination_directory = (VAULT / destination_relative).resolve()
if source is None or not (source.exists() or source.is_symlink()):
return fail("selected entry no longer exists")
if destination_directory != VAULT and VAULT not in destination_directory.parents:
return fail("destination escaped the vault")
if not destination_directory.is_dir():
return fail("destination directory no longer exists")
if source.is_dir() and (
destination_directory == source or source in destination_directory.parents
):
return fail("cannot move a directory into itself")
destination = destination_directory / source.name
if destination.exists() or destination.is_symlink():
return fail(f"already exists: {destination.name}")
try:
source.rename(destination)
except OSError as error:
return fail(f"cannot move {source.name}: {error}")
json.dump(
{
"action": "move",
"relative": destination.relative_to(VAULT).as_posix(),
"kind": "directory" if destination.is_dir() else "note",
},
sys.stdout,
)
return 0
def delete_entry(source_relative: str) -> int:
source = vault_entry(source_relative)
if source is None or not (source.exists() or source.is_symlink()):
return fail("selected entry no longer exists")
try:
if source.is_symlink():
source.unlink()
elif source.is_dir():
shutil.rmtree(source)
else:
source.unlink()
except OSError as error:
return fail(f"cannot delete {source.name}: {error}")
json.dump({"action": "delete", "relative": source_relative}, sys.stdout)
return 0
def main() -> int:
global VAULT
if len(sys.argv) < 4:
return fail(
"usage: manage-entry.py VAULT ACTION SOURCE [DESTINATION_DIRECTORY]"
)
VAULT = Path(sys.argv[1]).expanduser().resolve()
if not VAULT.is_dir():
return fail(f"vault does not exist: {VAULT}")
action, source = sys.argv[2:4]
if action == "move" and len(sys.argv) == 5:
return move_entry(source, sys.argv[4])
if action == "delete" and len(sys.argv) == 4:
return delete_entry(source)
return fail("invalid file operation")
if __name__ == "__main__":
raise SystemExit(main())