Replace afterburner.fx with FxmlKit - #733
Conversation
* Migrate CopyToPreferences * Migrate EntryEditorPreferences * Migrate MergePreferences * Migrate AutoCompletePreferences * Migrate CoreGuiPreferences * Migrate WorkspacePreferences * Migrate UnlinkedFilesDialogPreferences * Migrate SidePanePreferences * Migrate ExternalApplicationPreferences * Dedup bindings for inverted keys * Migrate GroupsPreferences * Migrate SpecialFieldsPreferences * Migrate PreviewPreferences * Migrate NameDisplayPreferences * Migrate MainTablePreferences * Migrate ColumnPreferences * Reduce duplication * Migrate NewEntryPreferences * Migrate DonationPreferences * Migrate MrDlibPreferences * Fix missing types in PrefsFilter * Small fixups and comments * CHANGELOG.md * Parse enums safely * Simplify initializer * CHANGELOG.md * Use EnumSet --------- Co-authored-by: Carl Christian Snethlage <calixtus@users.noreply.github.com>
…directory name (JabRef#16163) CitationStyleCatalogGenerator detected whether it was being run from the IDE by comparing the checked-out repository directory name against the literal string "jabref". Forks checked out under a different name (e.g. jabref-koppor) always failed this check, causing the generator to look in the wrong directory, produce an empty citation style catalog, and fail every CSL-dependent test. Detect the styles root by checking whether it actually exists instead of relying on the directory name.
…16164) * Fix jabkit -p/-d flags not propagating across command levels Each jabkit (sub)command mixed in its own `new SharedOptions()` instance, so e.g. `jabkit -p check consistency` set the root command's copy of `porcelain` while `check`/`consistency` read their own, always-false copy. Resolve the mixin through a single shared instance (via a custom picocli IFactory) so the flag has one value across the whole command tree, regardless of where it's placed. Also detect the short forms -p/-d (not just --porcelain/--debug) during early logging setup, which runs before picocli parses arguments. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Extract shared-mixin factory to JabKit.createFactory, add regression test Reuses the same factory in the test harness (was building CommandLine without it, so tests never exercised the fix) and adds a test proving `-p` before the subcommand now reaches a nested subcommand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Update CHANGELOG.md --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Carl Christian Snethlage <50491877+calixtus@users.noreply.github.com>
* Add PLAN.md for entry editor re-implementation (JabRef#12711) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Add ALL_FIELDS entry editor tab showing citation key, required and all set fields First step for JabRef#12711: single scroll-list tab ("Fields") is the default; classic category tabs (required/optional/other/deprecated) and Comments remain available but are off by default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: natural-height scroll-list layout FieldsEditorTab gets two hooks (layoutEditors, stretchContentToTabHeight) so the new tab can scroll with natural row heights instead of stretching editors to the tab height. Multiline editors capped at 4 visible rows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: group fields into sections (identifiers, files & links, comments) Partition logic lives in FieldListSections (plain Java, unit-tested); section headers rendered with separator, Google-Contacts style (JabRef#12711). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: add-field chips, 'Show more' for secondary optional fields, free-form add Unset important-optional fields appear as one-click chips below the list; 'Show more' reveals the secondary-optional ones (JabRef#12711, Google-Contacts style). Arbitrary fields can be added via an editable combo box using FieldFactory.parseField. Still-empty user-added fields stay visible until another entry is opened. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: live refresh when fields are set/unset externally Subscribes to the entry's event bus; rebuilds only when the computed shown-field set differs from the visible one, so typing inside a visible editor never rebuilds or steals focus. A field cleared in a visible editor stays visible until another entry is opened. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Entry editor scroll-list tab: checkstyle fix, CHANGELOG, plan status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: fix collapsed rows, rename tab to 'Main' FieldNameLabel and EditorTextField set an infinite pref height to fill the stretch layout's percent-height rows; in the natural-height scroll list this collapsed every row to an equal tiny share. Reset labels to computed size and normalize text inputs when laying out the list. Tab display name changed from 'Fields' to 'Main' (decision by koppor); the preview stays as an in-tab pane. Verified in a live GUI session: sections, scrolling, add-chips (insert + focus + chip removal), and entry switching all work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: collapsible sections with per-section add-chips Per feedback in JabRef#12711: Identifiers, Files and links, and Comments are always-present collapsible sections - collapsed when empty, expanded when a member field is set; a manual toggle survives rebuilds for the current entry. Each section offers add-chips for its unset member fields (Identifiers collects all identifier fields). The entry type's optional-field chips moved directly below the main fields (semantic grouping); the free-form add row stays at the bottom. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: bibliometrics and meta sections replace default General tab Per feedback in JabRef#12711: the default customized tabs 'General' and 'Abstract' duplicated the Main tab, so new profiles get none (stored user customizations are kept). Their unique fields move into two new collapsible sections: Bibliometrics (citation count, ICORE ranking) and Meta (crossref, groups, owner, timestamps, special fields - fields about the entry rather than the paper). Meta offers add-chips only for the manually edited fields (crossref, groups, owner). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * PLAN.md: record bibliometrics/meta status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * AllFieldsTab: offer special fields as add-chips in the Meta section Priority, read status, ranking, quality, relevance, and printed can now be added via chips like the other meta fields; only the auto-managed timestamp fields stay chip-less. Also records two follow-ups in PLAN.md (highlight chips of missing required fields; free-form box must only offer fields not already available above) and resolves the ABSTRACT placement question (stays in main). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * PLAN.md: special-field chip label polish as step 15 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Remove classic category tabs and entry editor tab customization Decisions from live review (JabRef#12711): the Main tab replaces the classic category tabs entirely, so their code is removed rather than kept as opt-in: RequiredFieldsTab, OptionalFieldsTabBase, ImportantOptional-, DetailOptional-, DeprecatedFields-, OtherFields-, CommentsTab and their BuiltIn constants, factory cases and SHOW_* preference keys. Stored keys from older versions become dead entries; no migration needed. Custom field-set tabs are removed as well (UserDefinedFieldsTab, CustomizedFieldsTab model, customTab* preference series, the two obsolete preference migrations, and the tab-editing UI): the entry editor preferences page now only toggles visibility of built-in tabs. MathSciNet stays. 16 obsolete localization keys dropped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Comply with CHECKLIST.md: JSpecify, Optional idioms, requirements, docs AllFieldsTab/FieldListSections annotated @NullMarked; null-literal checks replaced (Optional-based listener subscription, pattern-match cell rendering, StringUtil.isBlank); isPresent/get blocks replaced with ifPresent; owner sanitizing uses a precompiled Pattern; markdown Javadoc uses [refs] instead of {@link}. OpenRewrite applied (List.getFirst). Requirements added to docs/requirements/entry-editor.md with impl tracing tags; PLAN.md markdownlint-clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Refine entry editor requirements: sections, chips, free-form add, live refresh Split the two coarse requirements into six precise ones (single list, collapsible sections, optional-field chips, per-section chips, free-form field-name box, live refresh) and add the corresponding impl tracing tags in AllFieldsTab. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix IntelliJ formatting: one case label per line in FieldListSections Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fix submodules * Fix CI: robust CSL styles-root detection and OpenRewrite formatting CitationStyleCatalogGenerator used the repository directory name to decide whether it was run from the IDE, which broke for forks checked out under a different name (e.g. jabref-koppor), causing the citation style catalog to be empty and all CSL-dependent tests to fail. Detect the styles root by checking file existence instead. Also apply the OpenRewrite fix (reference equality for enum) in FieldListSections that CI flagged as an uncommitted formatting diff. * Remove PLAN.md --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Carl Christian Snethlage <calixtus@users.noreply.github.com>
Removes the JabRef-maintained afterburner.fx fork: - org.jabref.injection.Injector (jablib): self-contained service locator keeping afterburner's semantics (create-and-cache singletons, jakarta @Inject field resolution, fresh presenter instances) - org.jabref.gui.util.ViewLoader/ViewLoaderResult (jabgui): drop-in fluent FXML loader keeping fx:root, view-as-controller and the FXML/CSS naming conventions - FxmlKit 1.5.1 (jabgui only): global DiAdapter + ResourceBundle wiring so new views can use FxmlView and FXML/CSS hot reload - ResourceLocator SPI implementations and provides clauses removed - ADR-0065 documents the decision and the rejected alternatives Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rebuild import groups of all touched files (mechanical sed had left the swapped imports in the wrong checkstyle group) - Annotate the new classes with @NullMarked, use Optional/JSpecify idioms instead of null checks, Locale.ROOT for name lowercasing - Annotate ViewLoader with @AllowedToUseClassGetResource (FXML and stylesheets have to be located by URL) - Make PLAN.md markdownlint-clean Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Custom controls referenced in FXML files (e.g. FieldFormatterCleanupsPanel) load their own FXML in their constructors via ViewLoader; without the mock, static loading of the referencing FXML files fails. The lookup is reflective because jabgui is runtimeOnly on jablib's test module path (a compile-time requires would create a module cycle). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Display-verified test results (
With this, all four ViewLoader code paths ( |
|
|
||
| ### Changed | ||
|
|
||
| - We replaced the abandoned afterburner.fx framework (used for FXML view loading and dependency injection) with [FxmlKit](https://github.com/dlsc-software-consulting-gmbh/FxmlKit) and a small JabRef-owned service locator. <!-- TODO: add PR link --> |
There was a problem hiding this comment.
No changelog entry for tech details.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
heylogs' forge-ref rule rejects the non-standard "koppor#733" label (GitHub short refs are #N for same-repo or owner/repo#N cross-repo, never "reponame#N") - use #733 instead. Align the ternary continuation in LocalizationParser.getResourceKeysInFxml2Content under the condition expression, matching the project's IntelliJ code style (as enforced by the CI formatting check).
|
🤖 Generated with Claude Code Merging The bulk is ~20 Both sides restructure the same view-construction code, so resolving means re-deciding how each tab gets built — new code rather than a merge resolution. No compile check was run; CI would be the compile check. Generated by Claude Code |
Related issues and pull requests
No matching issue found — searched jabref/issues and jabref-koppor/issues for "afterburner"; the only hit (jabref-koppor#135, Debian packaging) is unrelated.
PR Description
Replaces the JabRef-maintained fork of the abandoned afterburner.fx framework with FxmlKit plus two small JabRef-owned glue classes:
org.jabref.injection.Injector(service locator with afterburner's exact semantics, in jablib) andorg.jabref.gui.util.ViewLoader/ViewLoaderResult(drop-in fluent FXML loader keepingfx:root, view-as-controller, and the naming conventions, in jabgui). This removes the fork's maintenance burden (JavaFX POM pins, build metadata patches, releases), frees jablib of any GUI-framework dependency, and lets newly written views use FxmlKit'sFxmlViewand FXML/CSS hot reload — all 118 existing views only changed imports.docs/decisions/0065-replace-afterburner-fx-with-fxmlkit.mddocuments the decision, including why FxmlKit cannot be adopted wholesale (nofx:root/pre-existing-controller support; itsLiteDiAdapterholds strong references to injected objects, which would leak per-dialog view instances).This is a draft (working state pushed for safekeeping and early review).
PLAN.mdin the repo root contains the full research findings and the phase-by-phase checklist. Next steps:./gradlew :jabgui:run): About dialog (DialogPane path), entry editor (fx:root field editors), preferences (fx:include path), integrity check dialog (view(Class)path) — the implementation machine is headless.TODOin theCHANGELOG.mdentry with the upstream PR link when this is upstreamed.PLAN.mdbefore upstreaming.Analogies: Like honey, this change should be smooth and leave no sticky residue — the fluent
ViewLoaderAPI stays exactly as sweet as before. Like chocolate, it is mostly about what is inside: the wrapper (imports) changed everywhere, but the filling (view behavior) must taste identical. And like the moon, the JabRef-owned glue code only shines by reflection — the light source is the service locator behind it.jabref-contrib-policy:4.2:reviewed:okSteps to test
./gradlew :jabgui:run) — the main frame appearing at all already exercises the new loader for toolbar/menus/entry editor.setAsDialogPanepath.fx:rootcustom-control path.fx:includecontroller creation.ViewLoader.view(Class)+getController()path.AI usage
Claude Code (model claude-fable-5).
AI CHECKLIST.md walkthrough
1. Code self-review
Nullability and control flow
== null/!= nullchecks — JSpecify annotations (@NullMarked,@Nullable,@NonNull) used instead. (One deliberate exception:Field.get(instance) == nullinInjector#injectField— the reflection API has no JSpecify surface, and "only inject unset fields" is the required semantics.)Objects.requireNonNull(...)— nullability expressed via JSpecify annotations.@NullMarked(org.jspecify.annotations.NullMarked). (Injector,ViewLoader,ViewLoaderResult,InjectorDiAdapter.)Optionalconsumed withifPresent/ifPresentOrElse/map/orElseThrow— neverorElse(unusedValue)nor anisPresent()+get()block.StringUtil.isBlank(...)used instead ofs == null || s.isBlank(). (No blank-string checks in the diff.)Exceptions
catch (Exception e)— only specific exceptions are caught. (ReflectiveOperationExceptionandIOException.)throw new RuntimeException(...)/IllegalStateException(...)— these tear down the whole application. (Deliberate exception:ViewLoader/Injectorkeep afterburner's documentedIllegalStateExceptioncontract — drop-in replacement; 118 call sites rely on unchecked fail-fast for broken FXML/wiring, surfaced byFallbackExceptionHandleras an error dialog; no caller catches it.)LOGGER.info("...", e)), not concatenated into the message string. (No exception logging added in this diff.)Style and idioms
BibEntryobjects built with withers (withField, notsetField). (NoBibEntryconstruction in the diff.)List.of()/Map.of()/Set.of(),Path.of(),SequencedCollection/SequencedSet, text blocks. (Diff usesOptional#or,String#formatted, pattern-matchinginstanceof; no collection literals needed.)Pattern.compile(...)constant, notString.matches(...). (No regexes in the diff.)org.jabref.logic.util.BackgroundTask, notnew Thread(). (No background work in the diff.)///) uses Markdown syntax, not JavaDoc inline tags:`code`instead of{@code},[ClassName]instead of{@link}.User-facing text
Localization.langin Java,%prefix in FXML). (No user-facing strings added; exception messages are developer-facing, consistent with existing practice.)!; labels do not end with:. (No user-facing text.)"...: %0"), not string concatenation. (No user-facing text.)Security
text/htmlresponse — including exception/error messages, not just the success body (XSS). (Notext/htmlresponses touched.)Tests
org.jabref.model/org.jabref.logichave added or updated tests. (Neworg.jabref.injection.InjectorTestcovers create-and-cache singletons, registered-instance lookup,@Injectfield resolution for registered and on-demand services, untouched pre-set fields, and fresh presenter instances.)assertEquals), use plain JUnit asserts (not AssertJ), have no@DisplayName, do not catch exceptions (let them propagate so JUnit reports setup/teardown failures directly), and use@TempDirinstead of manual temp directories.2. Verification commands
./gradlew :jablib:check(or./gradlew checkfor all modules). (Passes aftergit submodule update --init— csl-styles/csl-locales/abbrv.jabref.org were missing in this checkout — except three environment-bound suites:LocalizationConsistencyTestuses the TestFXApplicationExtensionand needs an X display;RemoteSetupTest/RemoteCommunicationTestneed free ports. All three fail identically on the pre-migration tree, verified with agit stashbaseline run on this machine.)./gradlew checkstyleMain checkstyleTest checkstyleJmh. (Passes; import groups of all 183 touched files rebuilt to thejava, javax, javafx, org.jabref, *convention.)./gradlew modernizer../gradlew --no-configuration-cache :rewriteDryRunreports no changes (run./gradlew rewriteRunto fix)../gradlew javadoc.npx markdownlint-cli2 "docs/**/*.md" "*.md"(only if Markdown changed). (0 errors.)rewriteRun:docker run -v $(pwd):/github/workspace ghcr.io/leventebajczi/intellij-format:master "*.java" "" ".idea/codeStyles/Project.xml". (Not needed;rewriteRunand checkstyle are clean.)3. Documentation
CHANGELOG.mdentry added if the change is visible to the user (end-user wording, no extra blank lines). UseTODOas the issue/PR reference placeholder when no issue is known and the PR is not yet created — never a fake number.TODO(nocloses/fixesfor merely-similar issues). (No confident match: jabref-koppor#135 is about Debian packaging.)docs/requirements/<area>.mdif the change is a new feature or significant bug fix (skip for refactors, minor fixes, and internal changes). (Internal refactoring.)docs/updated if behavior or architecture changed. (docs/code-howtos/index.mddependency-injection section and new ADRdocs/decisions/0065-replace-afterburner-fx-with-fxmlkit.md.)4. Pull request
.github/PULL_REQUEST_TEMPLATE.md, every section filled.[x],[ ], or[/].gh pr create --body-file <file>(not--body).CHANGELOG.mdused aTODOplaceholder, it was replaced with the real PR-number link after PR creation, then committed and pushed.Additional verification beyond the checklist:
JabGuiArchitectureTest,JabSrvArchitectureTest, and the newInjectorTestare green. Remaining:jabgui:testfailures are environmental (23 TestFX suites need an X display; machine is headless) or pre-existing (KeyBindingViewModelTest,JournalAbbreviationRepositoryTestfail identically on the pre-migration tree — verified with agit stashbaseline run).Checklist
CHANGELOG.mdin a way that can be understood by the average user (if change is visible to the user)🤖 Generated with Claude Code