d12frosted
vulpea-ui
Emacs Lisp

A widget-based sidebar for Emacs that displays contextual information (stats, outline, backlinks, links) for vulpea notes

Last updated Jul 8, 2026
29
Stars
2
Forks
5
Issues
+1
Stars/day
Attention Score
34
Language breakdown
No language data available.
β–Έ Files click to expand
README

#+title: vulpea-ui #+author: Boris Buliga

Visual tools for [[https://github.com/d12frosted/vulpea][vulpea]] notes: a per-note sidebar of widgets, plus standalone views like the schema dashboard.

#+begin_html

MELPA CI version

Sponsor

Screenshot

#+end_html

  • Features
  • Two ways to see your notes: a per-note sidebar of widgets, and standalone views
  • Configurable per-frame sidebar (position, size, auto-hide)
  • Built-in widgets: outline, backlinks, unlinked mentions, forward links, outgoing mentions, stats, schema health
  • Schema dashboard: a collection-wide view of how every note measures up to its schema
  • Collection view: a sortable table over a filtered set of notes, with dired-style marks and bulk actions (tags, metadata, delete, your own function)
  • Built on [[https://github.com/d12frosted/vui.el][vui]] components, with an easy API for your own widgets
  • Installation
** Dependencies
  • Emacs 29.1+
  • [[https://github.com/d12frosted/vulpea][vulpea]] 2.5.0+
  • [[https://github.com/d12frosted/vui.el][vui]] 1.3.0+
  • [[https://github.com/BurntSushi/ripgrep][ripgrep]] - optional, used by the [[*Unlinked mentions widget][unlinked mentions widget]]
** Using package.el (MELPA)

#+begin_src emacs-lisp (package-install 'vulpea-ui) #+end_src

** Using straight.el

#+begin_src emacs-lisp (straight-use-package 'vulpea-ui) #+end_src

** Using elpaca

#+begin_src emacs-lisp (elpaca vulpea-ui) #+end_src

  • Usage
#+begin_src emacs-lisp (require 'vulpea-ui)

;; Open sidebar (vulpea-ui-sidebar-open)

;; Or toggle with a keybinding (global-set-key (kbd "C-c v s") #'vulpea-ui-sidebar-toggle) #+end_src

** Automatic sidebar

To automatically open the sidebar when entering org-mode buffers:

#+begin_src emacs-lisp (add-hook 'org-mode-hook #'vulpea-ui-sidebar-open) #+end_src

  • Configuration
** Sidebar position and size

#+begin_src emacs-lisp ;; Position: 'right (default), 'left, 'top, 'bottom (setq vulpea-ui-sidebar-position 'right)

;; Size of the sidebar. A float is a fraction of the frame (0.0-1.0), ;; an integer is a column/line count. See the docstring for the full ;; set of options. (setq vulpea-ui-sidebar-size 0.33) #+end_src

** Outline widget

#+begin_src emacs-lisp ;; Maximum heading depth (nil = unlimited) (setq vulpea-ui-outline-max-depth 3) #+end_src

** Schema health widget

Flags how the notes in the current file measure up to the vulpea schema(s) that apply to them - and that means every note in the file, the file-level note and each heading-level note, not just whichever one the sidebar is anchored on. A file can hold several notes (a heading with its own =:ID:=, tags and metadata is a note in its own right), so an =:execution:= heading's missing field is no longer hidden behind a schema-less =:journal:= file.

With a single schema-bearing note it reads as one status: nothing unless a schema applies, a green =healthy= line when it conforms, or its violations otherwise. With several it breaks the file down per note - the file-level note first, then headings in document order - each violated note under its title (click it to jump there), with a trailing count for any notes that are clean. Each violation has a =fix= button that prompts for a corrected value and writes it back into that note (a heading's field lands in the heading, not the top of the file), and the field name jumps to it. Files whose IDs live only on headings work too. Needs a vulpea with the schema validation engine (the =fix= button needs =vulpea-schema-fix-violation=).

#+begin_src emacs-lisp ;; Glyphs - portable text by default; swap in nerd-font / all-the-icons ;; glyphs if you prefer (setq vulpea-ui-schema-health-ok-glyph "βœ“") (setq vulpea-ui-schema-health-issue-glyph "βœ—") (setq vulpea-ui-schema-health-bullet "●") #+end_src

The faces =vulpea-ui-schema-health-ok-face= / =-error-face= / =-warning-face= / =-field-face= / =-message-face= / =-action-face= / =-note-face= inherit =success= / =error= / =warning= / =bold= / =shadow= / =link= / =bold=, so they follow your theme.

** Backlinks widget

#+begin_src emacs-lisp ;; Show/hide content previews (setq vulpea-ui-backlinks-show-preview t)

;; Characters before/after link in prose previews (setq vulpea-ui-backlinks-prose-chars-before 30) (setq vulpea-ui-backlinks-prose-chars-after 50)

;; Filter which notes appear in backlinks ;; (function receiving vulpea-note, return non-nil to include) (setq vulpea-ui-backlinks-note-filter (lambda (note) (not (member "archive" (vulpea-note-tags note)))))

;; Filter by context type ;; t = all types, or a list of: meta, header, table, list, quote, code, footnote, prose (setq vulpea-ui-backlinks-context-types t)

;; Sorting: nil (no sorting), 'title-asc, 'title-desc, or custom function (setq vulpea-ui-backlinks-sort 'title-asc) #+end_src

** Link types

By default only =id:= links count as note-to-note links in the backlinks and links widgets, link statistics, and the collection backlink-count column. If other link types carry your note graph - for example =denote:= links whose identifiers double as note IDs - add them:

#+begin_src emacs-lisp (setq vulpea-ui-link-types '("id" "denote")) #+end_src

The destination of a link of a listed type must be a note ID. Counting collection backlinks over more than one type requires a vulpea version where =vulpea-db-query-backlink-counts= accepts a list of types.

** Behaviour options

#+begin_src emacs-lisp ;; Auto-hide sidebar when switching to non-vulpea buffers ;; When nil, sidebar remains visible with stale content (setq vulpea-ui-sidebar-auto-hide t)

;; Start widgets collapsed (setq vulpea-ui-default-widget-collapsed nil)

;; Auto-refresh sidebar on save and idle (enabled by default) (setq vulpea-ui-auto-refresh t)

;; Idle delay before auto-refresh (in seconds) (setq vulpea-ui-auto-refresh-delay 1.5) #+end_src

** Performance

For large org files or many backlinks, enable fast parsing:

#+begin_src emacs-lisp ;; Skip org-mode hooks during parsing (faster but may cause issues ;; if your setup depends on mode hooks for org-element parsing) (setq vulpea-ui-fast-parse t) #+end_src

  • Commands
| Command | Description | |------------------------------+---------------------------| | =vulpea-ui-sidebar-open= | Open the sidebar | | =vulpea-ui-sidebar-close= | Close the sidebar | | =vulpea-ui-sidebar-toggle= | Toggle sidebar visibility | | =vulpea-ui-sidebar-refresh= | Force refresh content | | =vulpea-ui-schema-dashboard= | Open the schema dashboard | | =vulpea-ui-collection= | Open a collection view |
  • Sidebar keybindings
| Key | Action | |---------+---------------------------------| | =q= | Close sidebar | | =g= | Refresh content | | =TAB= | Navigate to next widget | | =S-TAB= | Navigate to previous widget | | =RET= | Activate widget at point |

=TAB=, =S-TAB=, and =RET= are inherited from =vui-mode=. =q= and =g= are sidebar-specific.

For quick navigation to any widget, consider [[https://github.com/d12frosted/ace-link-vui][ace-link-vui]].

  • Schema dashboard
#+begin_html

Screenshot

#+end_html

The schema dashboard is vulpea-ui's first standalone view: a dedicated buffer rather than a sidebar widget. Where the schema health widget answers "is /this/ note healthy?", the dashboard answers it for the whole collection. =M-x vulpea-ui-schema-dashboard= opens a =vulpea schema= buffer listing every registered schema with how many notes it covers and how many are invalid, sorted so the schemas needing attention come first and unused ones sink to the bottom. =:include= relationships are annotated both ways (=includes= / =included by=).

Schemas with violations start expanded; their invalid notes start collapsed. Expand a note to its individual violations, each showing the field, a terse reason, and a =fix= button. Fixing pops the note open at that field, prompts for a corrected value, then re-indexes and refreshes so the note drops off once it is healthy, so you always see what you are fixing and can fix or skip each one on its own. The field name jumps to the note without fixing. =g= rescans, =q= buries the buffer. The scan is one query plus an in-memory pass, so it stays fast on a large collection. Needs a vulpea with =vulpea-schema-collection-health= (the =fix= button additionally needs =vulpea-schema-fix-violation=).

  • Collection view
The collection view is a table over a filtered set of notes - think a lightweight Airtable for your collection. It is built on =tabulated-list-mode=, so you get a sticky header, click-to-sort columns and familiar navigation for free.

=M-x vulpea-ui-collection= completes over your saved views; free input is parsed as a query, so =wine -beer #red? level:0 country:France= works straight from the prompt (bare words require a tag, =-tag= excludes one, =#tag?= means any-of, =level:=, =dir:=, =title:=, =body:= (file content), =todo:= and =KEY:VALUE= / =KEY:*= condition on the rest). The syntax is exactly what the mode line prints as the filter summary, so the description you see is a query you can type. Empty input opens the whole collection. Press =?= inside the view for a menu of everything it can do; the mode line shows how many notes matched out of the whole collection, how many are marked, the active filter, and averages for numeric meta columns (=:142/4231 {rating avg 8.4}=).

Filters are refined without leaving the buffer (=/= prefix) and columns are added and removed on the fly (=c= prefix), so a typical session is: open everything, narrow by a tag or two, add the meta columns you care about, mark, act, and save the result as a named view with =w=.

Views are plists. Save them in =vulpea-ui-collection-views= (or press =w= in a collection buffer to save the current one):

#+begin_src emacs-lisp (setq vulpea-ui-collection-views '(("wines" . (:filter (:tags-all ("wine") :level 0) :columns (title (meta "country") (meta "producer") tags modified) :sort ("Modified" . t))))) #+end_src

The =:filter= plist supports =:tags-all=, =:tags-any=, =:tags-none=, =:level= (0 for file-level notes), =:directory= (absolute prefix or a relative path component), =:title= (regexp), =:meta= (alist of =(KEY . VALUE)= conditions, =(KEY . t)= for mere presence) and =:predicate= (any function on the note). The cheapest condition is pushed down to the database, the rest is applied in memory.

Columns: =title=, =context= (file title and outline path of a heading note - tells apart a dozen rows all called "Tasks"), =tags=, =(meta KEY)=, =todo=, =priority=, =scheduled=, =deadline=, =created=, =modified=, =links= (outgoing link count), =backlinks= (incoming link count, one grouped query for the whole table), =aliases= and =file=. Any column takes =:name= and =:width= overrides, e.g. =(title :width 60)=. Widths fit the content by default (the defaults act as caps); a column you resize by hand keeps its width and is pinned into the view when you save it.

A view without =:columns= gets adaptive defaults: columns are derived from the notes being shown, so empty ones never appear - title always, context when heading-level notes are present, tags and todo when some note carries them, plus the backlink count (sort by it to surface your hub notes). Ad-hoc views come sorted by title. Set =vulpea-ui-collection-default-columns= to an explicit list to opt out.

Keybindings:

| Key | Action | |-------+---------------------------------------------------| | =RET= | Visit the note | | =o= | Visit the note in another window | | =C-o= | Preview the note without leaving the table | | =SPC= | Show the row's full values | | =m= | Mark the note at point (or the region's rows) | | =u= | Unmark the note at point (or the region's rows) | | =U= | Unmark all | | =t= | Invert marks | | =% m= | Mark notes whose title matches a regexp | | =% t= | Mark notes carrying a tag | | =+= | Add a tag to the selection | | =-= | Remove a tag from the selection | | =e= | Edit the field of the column at point | | =E= | Remove a metadata field from the selection | | =T= | Set a todo state on the selection | | =Z= | Undo the last batch edit (one level) | | =G= | Group rows by a column (Emacs 30+) | | === | Narrow to the value of the cell at point | | =D= | Delete the selected notes (with confirmation) | | =x= | Apply a function to the selection | | =y= | Copy the selection as an org list of links | | =Y= | Export the selection to an org buffer | | =d= | Open the selection's files in dired, pre-marked | | =/ t= | Require a tag | | =/ T= | Exclude a tag | | =/ s= | Narrow titles live (updates as you type) | | =/ g= | Narrow by file content (ripgrep when available) | | =/ d= | Scope to a directory | | =/ m= | Condition on a metadata field | | =/ l= | Filter by level | | =/ k= | Remove a single filter condition | | =/ /= | Clear the filter | | =c a= | Add a column | | =c k= | Remove a column | | =w= | Save the current view | | =g= | Re-query and refresh | | =S= | Sort by column at point (=tabulated-list-mode=) | | =?= | Menu with all of the above |

=e= is context-aware: on the tags column it rewrites the tags of the note at point (completion, prefilled); on a meta column it reads a value for that key - prefilled from the note at point - and sets it on the selection; anywhere else it prompts for the key. =T= sets a todo state on the selection's heading-level notes (empty input clears it). =Z= undoes the last batch edit precisely: adding a tag only strips it from notes that gained it, meta edits restore each note's previous values, and todo edits restore the old states.

=== is the drill-down gesture: it adds a filter condition from the cell at point - a tag from the tags column, the value of a meta cell, a todo state, or the note's directory from the context and file columns. See "France", press ===, see the wines. Hovering a truncated cell shows its full value, and =SPC= prints the whole row as =column: value= lines.

Actions apply to the marked notes, or to the note at point when nothing is marked (=y= and =Y= fall back to the whole view instead). Marks are keyed by note id, so they survive sorting and re-querying. Edits touching more than one note ask first and list the notes they are about to change. Bulk tag and metadata edits go through vulpea's batch operations (=vulpea-tags-batch-add= and friends), so they need a vulpea recent enough to provide them. Deleting removes the files of file-level notes; heading-level notes are skipped.

=d= hands the selection off to dired with every file pre-marked, so batch rename, moving between directories, wdired and shell commands come from dired instead of being reimplemented here.

A view's =:filter= can also carry =:source=, a function returning the candidate notes, with the rest of the filter applied on top. vulpea already ships the interesting queries, so janitor dashboards are one saved view away:

#+begin_src emacs-lisp (add-to-list 'vulpea-ui-collection-views '("orphans" . (:filter (:source vulpea-db-query-orphan-notes)))) #+end_src

Collection buffers are bookmarkable: =C-x r m= records the view and jumping to the bookmark re-opens it. =org-store-link= works too, and the =vulpea-collection:= link type turns any note into a dashboard - =[[vulpea-collection:wine level:0][my cellar]]= opens the live view when followed (the path is a saved view name or a query). Saved views are managed with =vulpea-ui-collection-rename-view= and =vulpea-ui-collection-delete-view= (also in the =?= menu).

Like the sidebar, the view refreshes itself when vulpea's background extraction lands changes in the database (vulpea 2.6+), coalescing bulk syncs into a single refresh. Only visible buffers re-query immediately; hidden ones wait until they are shown again.

Saved views also show up as a =Collections= widget in the sidebar (one click from any note to a view), and any view can be embedded in a note as an org dynamic block:

#+begin_src org ,#+BEGIN: vulpea-collection :query "wine rating:*" :columns (title (meta "rating")) ,#+END: #+end_src

=C-c C-x x= (=org-dynamic-block-insert=) offers the block; titles render as =id:= links, and =:view "name"= reuses a saved view.

  • Default widgets
** Stats widget

#+begin_html

Screenshot

#+end_html

Shows character count, word count, and link count for the current note.

** Schema health widget

Validates every note in the current file against its vulpea schema(s), covering both the file-level note and each heading-level note (a heading with its own =:ID:=, tags and metadata is a note in its own right).

A single schema-bearing note reads as one status - a green =healthy= line when it conforms, or its violations otherwise, each a severity-coloured bullet (red for structural problems, amber for value problems), the field and a terse reason:

#+begin_html

File-level validation

#+end_html

When the file holds several notes it breaks them down per note - the file-level note first, then headings in document order - so a violation on a heading is never hidden behind a schema-less =:journal:= file:

#+begin_html

Heading-level validation

#+end_html

Every violation has a =fix= button that prompts for a value and writes it back into that note (a heading's field lands in the heading, not the top of the file), and the field name jumps to it. Files whose IDs live only on headings work too. Shown only when some note in the file bears a schema.

** Outline widget

#+begin_html

Screenshot

#+end_html

Displays the heading structure of the note. Click headings to navigate.

** Backlinks widget

#+begin_html

Screenshot

#+end_html

Shows notes that link to the current note, grouped by file with:

  • Heading path context (where in the document the link appears)
  • Links targeting a heading-level ID of the current file are included
too, annotated with a muted =β†’ Heading= suffix naming the targeted heading; links to the file-level ID look as before
  • Content preview with context type indicators:
- =⊒= meta property - =Β§= header - =β–€= table - =Β·= list item - =>= quote - =Ξ»= code - =†= footnote - (no indicator) prose

** Unlinked mentions widget

Shows notes that mention the current note's title or one of its aliases as plain text, without an =id:= link pointing back - the notes you may want to link. Mentions are grouped by the mentioning note; click a context line to jump to it.

Unlike the other widgets, this one loads asynchronously: scanning the whole collection for a note's title is expensive, so the search is delegated to [[https://github.com/BurntSushi/ripgrep][ripgrep]] via =vulpea-note-unlinked-mentions-async= and the widget shows a =Searching…= state until results arrive. Results are cached until you switch notes or refresh the sidebar (=g= / =vulpea-ui-sidebar-refresh=); an idle soft-refresh reuses the cache.

If ripgrep is not on =exec-path= the widget reports it instead of failing. To disable the widget entirely:

#+begin_src emacs-lisp (vulpea-ui-unregister-widget 'unlinked-mentions) #+end_src

To hide specific notes from the results without changing the search, set =vulpea-ui-unlinked-mentions-note-filter= to a predicate on the mentioning note, e.g. to drop notes carrying an =index= tag:

#+begin_src emacs-lisp (setq vulpea-ui-unlinked-mentions-note-filter (lambda (note) (not (member "index" (vulpea-note-tags note))))) #+end_src

To instead exclude notes from the search itself (collection-wide, affecting both mention widgets), customise =vulpea-mentions-note-filter=.

** Links widget

#+begin_html

Screenshot

#+end_html

Shows notes that the current note links to.

** Outgoing mentions widget

The mirror of the [[*Unlinked mentions widget][unlinked mentions widget]]: instead of notes that mention this one without linking back, it shows existing notes that this note mentions as plain text without an =id:= link to them - the notes you may want to link out to. Suggestions are grouped by the note you could link to; click a context line to jump to the mention in the current note, or use the =link= button before it to convert that occurrence into an =id:= link in place. Each group also has a =link all= button that links every occurrence for that note at once. Linking re-checks the live buffer before editing, so a suggestion that has gone stale (the surrounding text changed, or you already linked it) is simply skipped. It keeps your place in the sidebar and does not re-scan, so the suggestion stays listed (and point stays put) until you refresh with =g= or save - which makes it easy to link several in a row. Which notes count as candidates is controlled by =vulpea-mentions-note-filter= (file-level notes by default).

Like the unlinked mentions widget it loads asynchronously via ripgrep (=vulpea-buffer-unlinked-mentions-async=), scanning the note's live buffer so unsaved edits are included. It shows a =Searching…= state until results arrive and caches them until you switch notes or refresh the sidebar (=g= / =vulpea-ui-sidebar-refresh=).

If ripgrep is not on =exec-path= the widget reports it instead of failing. To disable the widget entirely:

#+begin_src emacs-lisp (vulpea-ui-unregister-widget 'outgoing-mentions) #+end_src

To hide specific candidate notes from the results without changing the search, set =vulpea-ui-outgoing-mentions-note-filter= to a predicate on the candidate note, e.g. to drop notes carrying an =index= tag:

#+begin_src emacs-lisp (setq vulpea-ui-outgoing-mentions-note-filter (lambda (note) (not (member "index" (vulpea-note-tags note))))) #+end_src

  • Widget Registration
Widgets are registered with =vulpea-ui-register-widget=, which allows filtering by predicate and ordering.

** Built-in widgets

vulpea-ui registers these widgets by default:

| Widget | Order | Component | |---------------------+-------+--------------------------------------| | =stats= | 100 | =vulpea-ui-widget-stats= | | =outline= | 200 | =vulpea-ui-widget-outline= | | =backlinks= | 300 | =vulpea-ui-widget-backlinks= | | =unlinked-mentions= | 350 | =vulpea-ui-widget-unlinked-mentions= | | =links= | 400 | =vulpea-ui-widget-links= | | =outgoing-mentions= | 450 | =vulpea-ui-widget-outgoing-mentions= |

** Registering widgets

#+begin_src emacs-lisp (vulpea-ui-register-widget 'my-widget :component 'my-custom-widget-component :predicate #'my-note-predicate ; optional: only show when this returns non-nil :order 150) ; optional: default 100 #+end_src

** Modifying widget properties

=vulpea-ui-widget-set= updates a single property on an already-registered widget. This works for any widget, including the built-in ones, so you can customise them without redefining anything.

#+begin_src emacs-lisp ;; Change a widget's order (vulpea-ui-widget-set 'stats :order 50)

;; Attach a predicate to a built-in widget (vulpea-ui-widget-set 'outline :predicate #'my/show-outline-p)

;; Remove a widget (vulpea-ui-unregister-widget 'links) #+end_src

** Toggling a widget per note

Built-in widgets have no predicate by default, so they are shown for every note. You can install one with =vulpea-ui-widget-set= to decide per note whether the widget shows up. A common recipe is a global default variable combined with an org property that overrides it on individual notes.

The example below hides the =outline= widget by default and shows it only on notes that set =:OUTLINE: t=. Flip =my/vulpea-ui-always-show-outline= to =t= to invert the default, in which case =:OUTLINE: nil= hides the widget on specific notes.

#+begin_src emacs-lisp (defvar my/vulpea-ui-always-show-outline nil "When non-nil, show the outline widget unless a note opts out.")

(defun my/vulpea-ui-show-outline-p (note) "Return non-nil if the outline widget should be shown for NOTE. An `OUTLINE' property on the note overrides the default variable." (if-let* ((props (vulpea-note-properties note)) (entry (assoc "OUTLINE" props))) (not (equal (cdr entry) "nil")) my/vulpea-ui-always-show-outline))

(vulpea-ui-widget-set 'outline :predicate #'my/vulpea-ui-show-outline-p) #+end_src

The same pattern works for any widget (=stats=, =backlinks=, =links=, or your own).

** How it works

  • Widgets are filtered by their =:predicate= (if any) against the current note
  • Remaining widgets are sorted by =:order= (ascending)
  • Each widget's =:component= is rendered
This allows packages like =vulpea-journal= to register context-specific widgets that only appear when viewing certain notes.
  • Custom widgets
Create custom widgets using vui's =vui-defcomponent= macro:

#+begin_src emacs-lisp (vui-defcomponent my-custom-widget () "My custom widget." :render (let ((note (use-vulpea-ui-note))) (vui-component 'vulpea-ui-widget :title "My Widget" :count 42 :children (lambda () (vui-text "Custom content here"))))) #+end_src

Register the widget:

#+begin_src emacs-lisp (vulpea-ui-register-widget 'my-widget :component 'my-custom-widget :order 250) ; after outline, before backlinks #+end_src

For context-specific widgets (e.g., only for notes with a certain tag):

#+begin_src emacs-lisp (defun my-project-note-p (note) "Return non-nil if NOTE is a project note." (member "project" (vulpea-note-tags note)))

(vulpea-ui-register-widget 'project-tasks :component 'my-project-tasks-widget :predicate #'my-project-note-p :order 150) #+end_src

  • Utility Functions
** vulpea-ui-clean-org-markup

Cleans org-mode markup from text for display purposes:

#+begin_src emacs-lisp (vulpea-ui-clean-org-markup text) #+end_src

Transformations:

  • Links: =[[url][title]]= β†’ =title=, =[[url]]= β†’ =url= (bare =[[id:...]]= links are removed)
  • Drawers: =:PROPERTIES:...:END:= blocks are removed
  • Metadata: =#+TITLE:=, =#+FILETAGS:=, etc. lines are removed
  • Whitespace: multiple spaces/tabs collapsed to single space
Useful for rendering previews in custom widgets.

  • Faces
| Face | Description | |--------------------------------------+-------------------------| | =vulpea-ui-widget-header-face= | Widget header text | | =vulpea-ui-widget-count-face= | Widget count numbers | | =vulpea-ui-outline-heading-face= | Outline headings | | =vulpea-ui-stats-face= | Statistics text | | =vulpea-ui-backlink-preview-face= | Backlink preview text | | =vulpea-ui-backlink-heading-face= | Backlink heading path | | =vulpea-ui-backlink-meta-key-face= | Meta block keys | | =vulpea-ui-backlink-meta-value-face= | Meta block values | | =vulpea-ui-backlink-context-face= | Context type indicators | | =vulpea-ui-mention-context-face= | Mention context line | | =vulpea-ui-mention-action-face= | Link action buttons |
  • Related Projects
  • [[https://github.com/d12frosted/vulpea-journal][vulpea-journal]] - Daily journaling with calendar, navigation, and "on this day" widgets
  • [[https://github.com/d12frosted/ace-link-vui][ace-link-vui]] - Ace-link style navigation for VUI buffers
  • License
Copyright (C) 2024-2026 Boris Buliga

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

  • Support
If you enjoy this project, you can support its development via [[https://github.com/sponsors/d12frosted][GitHub Sponsors]] or [[https://www.patreon.com/d12frosted][Patreon]].
πŸ”— More in this category

Β© 2026 GitRepoTrend Β· d12frosted/vulpea-ui Β· Updated daily from GitHub