Contributing
Contributions are welcome: bug reports, fixes, new features, extensions, and corrections to this manual. This page describes how to report a problem, how changes are made and what a pull request should contain.
Marginalia is released under the MIT License. By contributing you agree that your contribution is released under the same license.
Reporting bugs and ideas
Open an issue on GitHub. For a bug, include:
- what you did, what happened and what you expected;
- the version (desktop app archive name, or the commit for a Docker server) and how you run it;
- the inference provider type and model, if the problem is about generation (never your API key);
- the relevant part of the log - see Where are the logs?. Logs may contain story text; remove what you don't want to share.
Security problems (access to other users' data, login, cookies) should not be reported in a public issue - contact the maintainer privately through GitHub first.
Planned work
TODO.md is the list of planned work for the first
release, ordered by priority. Items marked 🧩 are good candidates for an extension instead of a change to the core.
Before you start
-
For anything larger than a small fix, open an issue (or comment on an existing one) first and describe what you plan. Some features belong in an extension rather than in the core - see Plugin development.
-
Read Architecture and the page about the area you are changing.
-
Set up the build and make sure
mvn testpasses on a clean checkout - see Building from source.
Workflow
-
Fork the repository and create a branch from
masterwith a short descriptive name (fix-summary-prompt,lorebook-tag-filter). -
Make the change, with tests where the area is testable (see Testing).
-
Run
mvn testinmarginalia/. If you touched a plugin or classes plugins use, build the plugins too. -
Start Marginalia and try the change in the browser - most of the application is UI, which the tests don't cover.
-
Update the manual if users or developers will notice the change (see
Documentation ). -
Push and open a pull request against
master.
CI runs the tests and builds the desktop app on all platforms for every pull request that changes marginalia/
(see Continuous integration). A pull request is merged when CI is green and
the change has been reviewed.
Commits
Commit messages follow the style already in the history: one line, lowercase, starting with a verb in the imperative, class and method names in backticks, related changes separated by a semicolon:
add `tagMultiComboBox` to `LorebookView`; enable tagging functionality and integration with current lorebook
throw `IllegalArgumentException` for invalid lorebook format in `LorebookServiceImpl`
fix `setSummaryPrompt` assignment error; correct `userPrompt` reference in manuscript save logic
Common verbs: add, fix, update, remove, refactor, rename, migrate, handle. Mention the issue number
when the commit fixes one (... (#17)).
Keep commits focused - one fix or one step of a feature per commit, not a mix of a feature, a reformat and an
unrelated fix. Don't commit generated or local files: target/, src/main/frontend/generated/,
src/main/bundles/, flow-build-info.json in src/main/resources and .idea/ are ignored by Git for this reason.
Pull requests
A good pull request:
-
does one thing, and says in the description what changed and why, with the issue it fixes;
-
for a UI change, includes a screenshot or a short description of what to click to see it;
-
adds or updates tests where it can, and passes
mvn test; -
adds a Flyway migration for every entity change (see Database & migrations) - never edit a migration that is already in
master; -
updates the manual;
-
doesn't change formatting of code it doesn't otherwise touch.
Code style
There is no formatter configuration in the repository; match the code around your change. In short:
Java
-
Java 25. Modern language features (records,
switchexpressions, pattern matching, text blocks,varwhere the type is obvious) are welcome. -
4 spaces, no tabs; opening braces on the same line; lines up to about 120 characters.
-
Javadoc on classes and on public methods whose purpose isn't obvious from the name. Write why and the contract (what
nullmeans, which thread, which transaction), not a restatement of the signature. -
Logging through SLF4J (
private static final Logger log = LoggerFactory.getLogger(X.class)), with{}placeholders. Story content, prompts and lore are logged atDEBUGonly; API keys and passwords never. -
Return
nullfor "not found" where the existing service API does (find,findForUser), and keep that consistent within a class.
Naming
-
Entities keep their historical names: a book is
Manuscript, a part isChatMessage, an inference provider isAI. Use the user-facing words (book, part, inference provider) in UI text, the manual and log messages. -
Services are an interface in
domain/serviceand an implementation<Name>Implindomain/service/impl, registered in the Spring XML (see Services). -
UI classes: a tab of the book window is
Manuscript<Name>Part, a dialog<Name>Dialog, a reusable component inui/componentsorui/widgets.
Architecture rules
-
Transactions live on services (
@CommonTx,@CommonTxReadOnly), never on UI classes. Entities held by the UI are detached; reload them through the service before changing them. -
The current user comes from the session-scoped
Userbean; services check ownership (findForUser) - the UI must not be the only thing protecting other users' data. -
UI text is never hard-coded: add a key to
loc/L.javaand the English text toloc/LocalizationEN.java, and useloc.getValue(L.KEY)(see User interface). -
Threads: long work runs off the UI thread, and UI updates go through
ui.access(...)(see Background work and push). -
Extension points: methods that extensions decorate are
@Extendable. Renaming or removing such a method, its parameters or its local variables breaks plugins - check the bundled plugins and mention the change in the pull request (see Keeping hooks working). -
Schema: every entity change comes with a migration; Hibernate only validates the schema.
Tests
Use JUnit Jupiter and AssertJ, the test bases and the fake LLM server; no real network calls. See the checklist for a new test.
Documentation
The manual you are reading is in manual/ (Markdown for Retype); docs/ is the generated site.
-
Change the Markdown in
manual/, then runretype buildand commitdocs/in the same pull request. Never edit files indocs/by hand. -
Preview with
retype start. -
Write for the reader of that part: the User guide talks about what users see and click (UI names in italics, no class names), the developer guide about code (paths relative to the main package, class names in backticks).
-
Screenshots go to
manual/images/. When a page needs a screenshot you can't make, link the path anyway and add a line toTODO.IMAGES.mddescribing what it should show. Diagrams are Mermaid code blocks, not images. -
A screenshot that shows a third-party picture needs a credit on the Credits page (author, source, license, changes), and must be shared under the license of the picture.
-
The class labels on
dev-ui-map-book.pnganddev-ui-map-workspace.pngare drawn bytools/annotate_ui_maps.py(needs Pillow) onto a plain screenshot saved asdev-ui-map-<name>-not-annotated.pngnext to it. After the screen changes, take a new plain screenshot, adjust the coordinates at the top of the script if something moved, and run it. -
Every developer page has
verified(the commit it was last checked against the code) andcovers(the code it describes) in its front matter.python3 tools/check_dev_docs.pylists the pages whose covered code changed since then (-vnames modified files,--checkfails when any page is behind). After you review a page, mark it with--stamp <page.md>in a commit of its own. When a page starts describing other code, extend itscovers. -
Bugs you find while writing documentation are reported as an issue (see
Reporting bugs and ideas ).
Extensions
New features that not every user needs - and anything experimental - are often better as an extension than as a
change to the core. An extension can be developed and released on its own, and doesn't need a pull request at all. If
you want it bundled with Marginalia in marginalia/plugins/, open an issue first. See
Plugin development and the example plugin.