Skip to content

16075 support multiple customized preview styles - #16615

Open
bgyoo970 wants to merge 32 commits into
JabRef:mainfrom
bgyoo970:16075-Support-multiple-customized-preview-styles
Open

16075 support multiple customized preview styles#16615
bgyoo970 wants to merge 32 commits into
JabRef:mainfrom
bgyoo970:16075-Support-multiple-customized-preview-styles

Conversation

@bgyoo970

Copy link
Copy Markdown

Summary

 The available panel was split into the 'CSL' and 'Customized' tabs to display preset or customized preview layouts respectively. Buttons were added to support the addition and deletion of specifically custom styles under the 'customized' tab only, along with a text box to rename customized styles. Changes were made throughout related files to support more than just the one default "PREVIEW" custom layout and persist multiple custom layouts between user sessions.  

Related to issue 16075
Closed Previous PR, due to merge from main

Analogies

 New to the code, progress flows like honey
 Straightforward as chocolate, good first grab
 Helpful resources and references on the money
 Added custom changes through views, buttons, and tabs
 This was more fun as a poem, instead of a paragraph
 Something, something, a rhyme about the moon
 I wish I had more analogies for a good laugh
 but it's getting late, and I should sleep soon
    `jabref-contrib-policy:4.2:reviewed​:ok`

Steps to test

  1. Describe how reviewers can test this fix/feature.
    Ideally, think of how you would guide a beginner user of JabRef to try out your change.

    A. Add Custom Layout: Options → Preferences → Entry preview → Customized Tab → '+' (add button)
    Upon opening the Entry Preview under preferences, navigate to the 'Available' Panel and click on the 'Customized' tab. There you will see a '+' plus button and a '-' minus button. Click on the plus button and you should see a new customized style appear under the 'Customized' tab.

    B. Delete Custom Layout: Options → Preferences → Entry preview → Customized Tab → '-' (minus button)
    Similarly from adding a custom layout, under the 'Customized' tab in the 'Available' Panel, you will see a '-' minus button. This button will be greyed out until you select a custom style under the 'Customized' tab. If none exist, create one using the '+' plus button. Then select the desired custom style to delete. The minus button will be available to click on. Click on the minus button and you will see that the custom style gets removed from the list.

    C. Rename Custom Layout: Options → Preferences → Entry preview → Customized Tab → Under this tab, select custom style to rename → 'Name' text box -> Type name → Hit 'Enter'
    Upon opening the Entry Preview under preferences, navigate to the 'Customized' tab or the 'Selected' Panel and click on a custom style. There, you should see at the bottom right corner of the UI, a text field with the name of your custom style. Rename your style as needed and hit 'Enter' to confirm the change. Note you may only rename custom styles. These custom styles will exist under the 'Selected' Panel or under the 'Customized' Tab. Duplicate names are not allowed.

    D. Persisted Custom Layouts: After making changes to the 'Customized' tab's contents, Save your changes with the button at the bottom of the UI. Then re-navigate to the Entry Preview UI's 'Customized' tab and confirm that the changes have persisted. This will persist all data around the custom layouts around moving, renaming, adding, deleting, and editing layout's text.

  2. Add screenshots (preferred) or videos.

entryeditor-preview-settings-customized-tab untitled

Related issues and pull requests

Closes #16075

AI usage


Claude (Sonnet 5) was used to assist in understanding the code and providing feedback or suggestions to my approaches. I understand the feedback it provided and had it review each step of the CHECKLIST.md. Adjustments were made as needed after reviewing the checklist, while running successful verification steps and unit tests afterwards.

AI CHECKLIST.md walkthrough

Nullability and control flow

  • No == null/!= null in this PR's changes.
  • No Objects.requireNonNull(...).
  • New classes @NullMarked (CustomizedPreviewStyle).
  • Optional consumed via map/flatMap/orElseThrow, never orElse(unusedValue) — PreviewLayout.of now returns Optional end to end, and ClipboardContentGenerator's orElse(...) supplies a genuine fallback value.
  • StringUtil.isBlank(...) used correctly.

Exceptions

  • No bare catch (Exception e).
  • No RuntimeException/IllegalStateException.
  • Logged exceptions passed as the last logger argument.

Style and idioms

  • New BibEntry objects built with withers — not applicable, no BibEntry construction in this change.
  • Modern Java used: List.of(), UUID, SequencedCollection, etc.
  • Regexes use a precompiled Pattern.compile(...) constant — not applicable, no new regexes introduced.
  • Background work uses BackgroundTask, not new Thread() — not applicable, no new background work introduced.
  • No commented-out code, no trivial comments restating the code, no AI-disclosure comments in source.
  • Markdown Javadoc (///) uses Markdown syntax, not JavaDoc inline tags, in new/changed doc comments.

User-facing text

  • All user-facing text localized (Localization.lang in Java, % prefix in FXML).
  • Sentence case (not Title Case); no trailing !; labels do not end with :.
  • Variance expressed with placeholders ("...: %0"), not string concatenation — not applicable, no new strings in this change interpolate variable content.

Security

  • User-controlled data HTML-escaped before being written into any text/html response — not applicable, no HTML response rendering in this change.

Tests

  • Behavior changes in org.jabref.model / org.jabref.logic have added or updated tests.
  • Tests assert object contents (assertEquals), use plain JUnit asserts (not AssertJ), have no @DisplayName, do not catch exceptions, and use @tempdir instead of manual temp directories — no manual temp-directory usage introduced, so the @tempdir clause is not applicable here.

2. Verification commands

Run in this order — cheapest first. Each must pass.

  • ./gradlew :jablib:check (or ./gradlew check for all modules).
  • ./gradlew checkstyleMain checkstyleTest checkstyleJmh.
  • ./gradlew modernizer.
  • ./gradlew --no-configuration-cache :rewriteDryRun reports no changes.
  • ./gradlew javadoc.
  • npx markdownlint-cli2 "docs/**/.md" ".md" — run, since entry-preview.md was added.
  • [/] IntelliJ-format Docker step — not needed; rewriteDryRun reported clean.

3. Documentation

  • CHANGELOG.md entry added.
  • Searched jabref/issues and jabref-koppor/issues for a related issue.
  • Requirement added to docs/requirements/entry-preview.md.
  • [/] Developer documentation under docs/ updated — not applicable, no architecture/dev docs describe this subsystem.

4. Pull request

  • PR body built from .github/PULL_REQUEST_TEMPLATE.md, every section filled.
  • All checklist items kept and marked [x], [ ], or [/].
  • All HTML comments removed from the PR body.
  • PR created (via the GitHub website; equivalent in effect to gh pr create --body-file — no CLI-specific escaping concern applies since the body was entered directly, not piped through a shell command).
  • CHANGELOG.md linked a real issue from the start — no TODO placeholder was used, so there's nothing to swap out post-creation.

Checklist

  • I own the copyright of the code submitted and I license it under the MIT license
  • If AI tools were used, I disclosed them in the "AI usage" section and reviewed, understood, and take full ownership of all AI-generated code
  • I manually tested my changes in running JabRef (always required)
  • I added JUnit tests for changes (if applicable)
  • I added screenshots in the PR description (if change is visible to the user)
  • I added a screenshot in the PR description showing a library with a single entry with me as author and as title the issue number
  • I described the change in CHANGELOG.md in a way that can be understood by the average user (if change is visible to the user)
  • I checked the user documentation for up to dateness and submitted a pull request to our user documentation repository

bgyoo970 and others added 25 commits August 11, 2026 00:23
…ation style, Split 'available' panel into csl and customized tabs. customized styles show in editor preview panel, renaming functionality added, add and delete button added to customized tab to add/delete customized styles, persistence added to preserve customized styles and renamings
Merge changes into forked main branch
… constants to jabref_en.properties, refactored naming for CustomizedPreviewStyle
…d string constants to jabref_en.properties, refactored naming for CustomizedPreviewStyle"

This reverts commit 9d11263.
…last commit, minus changes to abbrv.jabref.org and csl-styles
…comments and null checks. used modern Java for factory instead of constructor. updated string in JabRef_en.properties. updated changelog and created entry-preivew.md with new features.
…ss Localization.lang concat violation, resolved NPE risk on TextBasedPreviewLayout constructor, resolve legacy migration rerun on empty list, updated previewPreferences to use CUID over legacy name, fixed rename test case for null pass, removed dependency on javafx.swing, changing out of main branch, updated doc feature req descriptions
@github-actions github-actions Bot added good first issue An issue intended for project-newcomers. Varies in difficulty. component: entry-preview labels Aug 17, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Support multiple customized entry preview styles

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Split Entry Preview “Available” list into “CSL” and “Customized” tabs.
• Add create/delete/rename UX for custom preview styles (customized tab only).
• Persist multiple custom styles by stable IDs, including legacy migration and tests.
Diagram

graph TD
  A["Preview settings UI"] --> B["PreviewTab"] --> C["PreviewTabViewModel"] --> D["PreviewPreferences"] --> E["JabRefGuiPreferences store"] --> F["PreviewLayout.of(...)" ] --> G["TextBasedPreviewLayout"]
  F["PreviewLayout.of(...)" ] --> H["CSL/BST layouts"]
  C["PreviewTabViewModel"] --> I["CustomizedPreviewStyle (record)"]
  subgraph Legend
    direction LR
    _ui["UI"] ~~~ _vm["ViewModel"] ~~~ _pref[("Preferences")] ~~~ _model["Model/Logic"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Store custom styles as a single JSON blob preference
  • ➕ Atomic read/write avoids partial-series corruption across ID/name/text keys
  • ➕ Simplifies migration logic (one key instead of three numbered series)
  • ➕ Easier to evolve schema (add fields like 'createdAt', 'lastUsed')
  • ➖ Requires JSON parsing/serialization dependency or custom codec
  • ➖ Harder to manually inspect/edit preferences compared to simple key series
2. Persist via existing JabRef preference list serialization utilities
  • ➕ Reuses established patterns for list persistence and reduces custom code
  • ➕ Potentially fewer edge cases around purging trailing keys
  • ➖ May not support multi-field records (id/name/text) without additional encoding
  • ➖ Still needs careful handling of newline escaping and backward compatibility

Recommendation: The chosen approach (stable IDs + explicit migration + Optional-based layout resolution) is directionally correct for rename-safe persistence and multi-style support. If future iterations expand the record schema, consider consolidating the three numbered key-series into a single structured value (e.g., JSON) to make persistence atomic and reduce partial-write edge cases; otherwise the current implementation is acceptable given the added tests and explicit purge/migration safeguards.

Files changed (15) +1440 / -159

Enhancement (6) +573 / -131
JabRefGuiPreferences.javaPersist multiple customized preview styles with legacy migration +100/-26

Persist multiple customized preview styles with legacy migration

• Replaces the single PREVIEW_STYLE string with a list of persisted custom styles (id/name/text) stored as numbered key series. Adds one-time migration from the legacy PREVIEW_STYLE key, newline encoding/decoding, and updates layout-cycle rebuilding to resolve custom layouts by ID via Optional-returning PreviewLayout.of().

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java

PreviewTab.javaSplit Available list into tabs and add custom style add/delete/rename UI +169/-25

Split Available list into tabs and add custom style add/delete/rename UI

• Reworks the JavaFX preferences tab to show 'CSL' vs 'Customized' available styles, adds +/- buttons for custom styles, and adds a name TextField bound to the ViewModel for renaming. Updates drag/drop and move-button behavior to route items between the correct source/destination lists and to refocus tabs based on the last moved layout type.

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java

PreviewTabViewModel.javaIntroduce separate CSL/customized available lists and custom-style CRUD/rename +203/-54

Introduce separate CSL/customized available lists and custom-style CRUD/rename

• Splits the available list into cslListProperty and customizedListProperty, with shared filtering and routing logic based on layout type. Adds creation/deletion of custom styles, rename validation (non-blank, no duplicates across both tabs), improved drag/drop handling, and persistence of all custom styles (chosen + customized) into PreviewPreferences as CustomizedPreviewStyle records.

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java

PreviewPreferences.javaReplace single custom preview layout string with list of customized styles +18/-22

Replace single custom preview layout string with list of customized styles

• Refactors PreviewPreferences to store an ObservableList of CustomizedPreviewStyle records. Updates defaults and selected-layout fallback behavior to derive text-based previews from the first stored custom style when the cycle is empty/invalid.

jabgui/src/main/java/org/jabref/gui/preview/PreviewPreferences.java

CustomizedPreviewStyle.javaIntroduce persisted record for custom preview styles +16/-0

Introduce persisted record for custom preview styles

• Adds a @NullMarked record holding (id, name, text) plus a convenience constructor generating a CUID-based ID to keep renames stable across sessions.

jablib/src/main/java/org/jabref/logic/preview/CustomizedPreviewStyle.java

TextBasedPreviewLayout.javaAdd stable ID and editable name to text-based preview layouts +67/-4

Add stable ID and editable name to text-based preview layouts

• Extends TextBasedPreviewLayout with an id and mutable name, adds constructors/factories for (id,name,text) creation, and updates display-name behavior to reflect user-defined names while preserving sensible fallbacks.

jablib/src/main/java/org/jabref/logic/preview/TextBasedPreviewLayout.java

Bug fix (1) +6 / -4
ClipboardContentGenerator.javaSelect a default custom preview layout from persisted custom styles +6/-4

Select a default custom preview layout from persisted custom styles

• Updates clipboard preview generation to use the first persisted CustomizedPreviewStyle as the fallback template (or a default placeholder when none exist) instead of the legacy single custom preview layout preference.

jabgui/src/main/java/org/jabref/gui/preview/ClipboardContentGenerator.java

Refactor (1) +20 / -24
PreviewLayout.javaResolve preview layouts by identifier via Optional and custom-style IDs +20/-24

Resolve preview layouts by identifier via Optional and custom-style IDs

• Changes PreviewLayout.of(...) to return Optional<PreviewLayout> and resolves text-based custom layouts by stable ID from a provided list of CustomizedPreviewStyle records. Keeps CSL and BST resolution paths while eliminating null returns.

jablib/src/main/java/org/jabref/logic/preview/PreviewLayout.java

Tests (4) +787 / -0
PreviewTabViewModelTest.javaAdd ViewModel tests for multi-custom-style behavior and routing +552/-0

Add ViewModel tests for multi-custom-style behavior and routing

• Adds comprehensive tests covering loading styles into the correct lists, persistence of all custom styles, defaulting behavior when lists are empty, add/remove/rename flows, and drag/drop routing and no-op behavior when dropping into the same list.

jabgui/src/test/java/org/jabref/gui/preferences/preview/PreviewTabViewModelTest.java

PreviewPreferencesTest.javaAdd PreviewPreferences tests for customized-style defaults and selection +92/-0

Add PreviewPreferences tests for customized-style defaults and selection

• Validates that defaults include exactly one default customized style and that getSelectedPreviewLayout falls back to the first customized style when the cycle is empty, while respecting populated cycles.

jabgui/src/test/java/org/jabref/gui/preview/PreviewPreferencesTest.java

PreviewLayoutTest.javaTest PreviewLayout factory resolution for customized styles by ID +65/-0

Test PreviewLayout factory resolution for customized styles by ID

• Adds tests ensuring PreviewLayout.of(...) resolves custom styles by ID (independent of display name) and returns empty Optional for unknown identifiers or empty custom-style lists.

jablib/src/test/java/org/jabref/logic/preview/PreviewLayoutTest.java

TextBasedPreviewLayoutTest.javaTest TextBasedPreviewLayout ID generation and constructor semantics +78/-0

Test TextBasedPreviewLayout ID generation and constructor semantics

• Adds tests confirming generated IDs are non-null/unique when appropriate, that provided IDs are preserved, and that Layout-based construction still yields a non-null ID via accessor behavior.

jablib/src/test/java/org/jabref/logic/preview/TextBasedPreviewLayoutTest.java

Documentation (3) +54 / -0
CHANGELOG.mdDocument multi-custom preview style support in changelog +1/-0

Document multi-custom preview style support in changelog

• Adds a user-facing changelog entry describing the new ability to create/manage/persist customized preview styles and the UI tab split.

CHANGELOG.md

entry-preview.mdAdd requirements doc for Entry Preview tab split and custom-style management +43/-0

Add requirements doc for Entry Preview tab split and custom-style management

• Introduces requirement statements for the CSL/Customized tabs, CRUD + rename behavior, and persistence by stable ID independent of display name.

docs/requirements/entry-preview.md

JabRef_en.propertiesLocalize new Entry Preview UI strings and rename validation messages +10/-0

Localize new Entry Preview UI strings and rename validation messages

• Adds localized strings for CSL/Customized tab titles, the style name field, and validation errors for duplicate/blank renames, plus a template for default custom-style naming.

jablib/src/main/resources/l10n/JabRef_en.properties

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Aug 17, 2026
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. setPreviewLayout(null) called ✓ Resolved 📘 Rule violation ≡ Correctness
Description
refreshPreview() clears state by passing null into setPreviewLayout, violating the
no-null-arguments rule and increasing risk of future NPEs at call sites. Use an explicit clear
method/Optional/property reset instead of a null argument.
Code

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[R209-210]

+        PreviewLayout current = selectedLayoutProperty.getValue();
   setPreviewLayout(null);
-        setPreviewLayout(chosenSelectionModelProperty.getValue().getSelectedItem());
-    }
-
-    private PreviewLayout findLayoutByName(String name) {
-        return availableListProperty.getValue().stream().filter(layout -> layout.getName().equals(name))
-                                    .findAny()
-                                    .orElse(chosenListProperty.getValue().stream().filter(layout -> layout.getName().equals(name))
-                                                              .findAny()
-                                                              .orElse(null));
Evidence
PR Compliance ID 18 forbids passing null as an argument in new/modified code. The changed method
refreshPreview() explicitly calls setPreviewLayout(null).

AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods): AGENTS.md: Do Not Pass Null as an Argument (Except to Same-Named Methods)
jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[208-212]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PreviewTabViewModel.refreshPreview()` calls `setPreviewLayout(null)`, which violates the rule against passing `null` as an argument.
## Issue Context
`setPreviewLayout` already handles `null`, but the compliance requirement forbids introducing/using `null` arguments in new/modified code.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[208-212]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Custom cycle migration broken ✓ Resolved 🐞 Bug ≡ Correctness
Description
Upgraded installations can lose the customized layout entry in the preview cycle because customized
layouts are now resolved only by id, while legacy PREVIEW_CYCLE values use the "PREVIEW" identifier
and the legacy migration generates a new random id. This can drop the customized layout from the
reconstructed cycle and reset the selected preview style after upgrade.
Code

jablib/src/main/java/org/jabref/logic/preview/PreviewLayout.java[R49-52]

+        // Text-based (customized) styles are resolved by stable id, not display name — names are user-editable.
+        return customizedPreviewLayouts.stream()
+                                       .filter(c -> c.id().equals(layoutIdentifier))
+                                       .findFirst()
Evidence
PreviewLayout.of only matches customized layouts by id; existing migrations rewrite stored cycle
values to the "PREVIEW" identifier; and legacy migration currently creates a CustomizedPreviewStyle
via the (name,text) constructor which generates a random id, so "PREVIEW" cannot match any stored
customized-style id.

jablib/src/main/java/org/jabref/logic/preview/PreviewLayout.java[32-55]
jabgui/src/main/java/org/jabref/migrations/PreferencesMigrations.java[313-317]
jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[999-1010]
jablib/src/main/java/org/jabref/logic/preview/CustomizedPreviewStyle.java[10-15]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Customized preview styles are now resolved by stable id (`PreviewLayout.of`), but legacy persisted cycle entries can contain `TextBasedPreviewLayout.NAME` ("PREVIEW"). The current migration stores legacy text as a new `CustomizedPreviewStyle(name,text)` which generates a random id, so the cycle entry "PREVIEW" no longer matches any customized-style id and gets filtered out.
### Issue Context
There is already an existing preferences migration that rewrites stored cycle entries to `TextBasedPreviewLayout.NAME` ("PREVIEW"), so this mismatch will occur on real upgrades.
### Fix Focus Areas
- jablib/src/main/java/org/jabref/logic/preview/PreviewLayout.java[32-55]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[908-930]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[999-1013]
### Suggested fix approach
Implement a backward-compatibility mapping so that legacy cycle identifier `TextBasedPreviewLayout.NAME` resolves to the migrated/default customized style id.
Options (pick one, but be consistent):
1) In `migrateLegacyCustomLayout`, preserve the legacy identifier as the **id** (e.g., `new CustomizedPreviewStyle(TextBasedPreviewLayout.NAME, <displayName>, legacyText)`), so existing cycles referencing "PREVIEW" still resolve.
2) In `JabRefGuiPreferences.getPreviewLayouts` (or `PreviewLayout.of`), special-case `layoutIdentifier.equals(TextBasedPreviewLayout.NAME)` to resolve to the first customized style (or the migrated legacy style).
3) Additionally (or alternatively), migrate stored `PREVIEW_CYCLE` entries from "PREVIEW" to the newly created customized-style id during migration.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Non-/// doc comments added ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
New multi-line documentation-style comments use // instead of required Markdown Javadoc ///,
reducing consistency with the mandated documentation format. Convert these comment blocks to ///
Markdown Javadoc style.
Code

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[R636-639]

+    // Commits an edit made in the style-name field to the currently selected TextBasedPreviewLayout.
+    // No-ops for non-customized (CSL/BST) selections. Reverts the field on blank/duplicate input.
+    // [impl->req~entry-preview.rename-custom-style~1]
+    public void renameSelectedStyle(@NonNull String newName) {
Evidence
PR Compliance ID 10 requires multi-line documentation comments to use ///. The PR introduces
multi-line, doc-style comment blocks using // above renameSelectedStyle and the
CustomizedPreviewStyle record.

AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments: AGENTS.md: Use Markdown Javadoc Comments (///) for Multi-line Documentation Comments
jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[636-668]
jablib/src/main/java/org/jabref/logic/preview/CustomizedPreviewStyle.java[6-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Multi-line documentation comments were introduced using `//` instead of the required Markdown Javadoc `///` format.
## Issue Context
The codebase standardizes multi-line documentation comments on `///` to keep generated docs and inline documentation consistent.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[636-639]
- jablib/src/main/java/org/jabref/logic/preview/CustomizedPreviewStyle.java[6-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Requirement IDs improperly formatted ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new OpenFastTrace requirements use ## headings and wrap req~...~1 identifiers in backticks,
deviating from the required ### + plain identifier-line format. This may break requirement
parsing/trace tooling.
Code

docs/requirements/entry-preview.md[R6-8]

+## Entry Preview shows the Available Panel split into CSL and Customized tabs
+`req~entry-preview.tabs~1`
+
Evidence
PR Compliance ID 34 specifies ### headings and an immediate plain req~...~1 identifier line. The
added requirements file uses ## headings and backticked identifiers.

AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format: AGENTS.md: Requirements Tracing: Add OpenFastTrace Requirement Entries for New Features/Significant Fixes Using Correct Format
docs/requirements/entry-preview.md[6-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`docs/requirements/entry-preview.md` does not follow the required OpenFastTrace format: headings are `##` (not `###`) and requirement identifiers are wrapped in backticks instead of being a plain identifier line.
## Issue Context
Automated requirement tooling typically expects `###` headings followed immediately by a raw `req~...~1` line.
## Fix Focus Areas
- docs/requirements/entry-preview.md[6-8]
- docs/requirements/entry-preview.md[15-17]
- docs/requirements/entry-preview.md[27-29]
- docs/requirements/entry-preview.md[36-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Test catches exceptions in @BeforeAll ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
PreviewTabViewModelTest.initToolkit() catches IllegalStateException, violating the test
convention to avoid catching exceptions in tests. This can hide failures and make tests less
transparent.
Code

jabgui/src/test/java/org/jabref/gui/preferences/preview/PreviewTabViewModelTest.java[R84-88]

+        try {
+            Platform.startup(latch::countDown);
+        } catch (IllegalStateException alreadyStarted) {
+            latch.countDown();
+        }
Evidence
PR Compliance ID 27 states tests should avoid catching exceptions. The new test class explicitly
catches IllegalStateException during JavaFX toolkit initialization.

AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions: AGENTS.md: JUnit/Test Conventions: Naming, Assertions, TempDir, and Avoid Catching Exceptions
jabgui/src/test/java/org/jabref/gui/preferences/preview/PreviewTabViewModelTest.java[81-92]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new test utility method catches `IllegalStateException`, which violates JabRef test conventions.
## Issue Context
Catching exceptions in tests can mask underlying problems; where a special-case is needed (JavaFX toolkit already started), prefer a helper pattern that does not require catching (or centralize it in shared test infrastructure).
## Fix Focus Areas
- jabgui/src/test/java/org/jabref/gui/preferences/preview/PreviewTabViewModelTest.java[81-92]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View review recommended (4)
6. Customized-style migration lacks tests 📎 Requirement gap ☼ Reliability
Description
The PR adds a legacy-to-new preference migration (migrateLegacyCustomLayout) but does not
add/update tests to validate this migration path. This risks legacy users losing/misreading
customized preview layouts after upgrade.
Code

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[R958-960]

+        if (!hasKey(PREVIEW_STYLE_CUSTOMIZED_ID + "0") && !getBoolean(PREVIEW_STYLE_CUSTOMIZED_MIGRATED, false)) {
+            return migrateLegacyCustomLayout(defaults);
+        }
Evidence
PR Compliance ID 4 requires preference migration and automated tests validating the migration. The
PR introduces migrateLegacyCustomLayout and a new PREVIEW_STYLE_CUSTOMIZED_MIGRATED guard, but
existing migration tests only cover updating the legacy PREVIEW_STYLE value, not migrating it into
the new multi-style structure.

Preferences storage supports multiple customized preview styles with migration and tests
jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[953-1013]
jabgui/src/test/java/org/jabref/migrations/GuiPreferencesMigrationsTest.java[75-139]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Migration from legacy `PREVIEW_STYLE` to the new `PREVIEW_STYLE_CUSTOMIZED_*` series is implemented but not covered by automated tests.
## Issue Context
Compliance requires migration + tests to prevent regressions for existing installations.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[953-1013]
- jabgui/src/test/java/org/jabref/migrations/GuiPreferencesMigrationsTest.java[75-139]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. CHANGELOG issue link malformed ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The added CHANGELOG entry links to .../issues/16075# with a trailing fragment marker, which is
non-standard and may be treated as an invalid/unfinalized link. Update it to a clean, finalized
issue link.
Code

CHANGELOG.md[14]

+- We added support for users to create, manage, and persist customized preview styles. UI updated to split "Available" panel into two tabs. [#16075](https://github.com/JabRef/jabref/issues/16075#)
Evidence
PR Compliance IDs 31 and 41 require CHANGELOG entries to use valid, finalized issue/PR links. The
new entry ends its issue URL with a trailing # fragment marker.

AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules: AGENTS.md: CHANGELOG.md Entries: Only for User-Visible Changes and Must Follow Formatting/Linking Rules
CHANGELOG.md[14-14]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new CHANGELOG entry uses an issue URL ending with `#`, which is a malformed/non-final link style.
## Issue Context
Changelog rules require finalized, valid issue/PR links.
## Fix Focus Areas
- CHANGELOG.md[14-14]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Rename misses chosen duplicates ✓ Resolved 🐞 Bug ≡ Correctness
Description
PreviewTabViewModel.renameSelectedStyle checks duplicates only in cslListProperty and
customizedListProperty, so it allows renaming a custom style to the same name as another custom
style currently in chosenListProperty. This breaks uniqueness and makes chosen styles ambiguous.
Code

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[R646-652]

+                // check for duplicates in both lists.
+                boolean isDupInCustomizedListProperty = customizedListProperty.stream()
+                                                                              .filter(existing -> existing != layout)
+                                                                              .anyMatch(existing -> existing.getDisplayName().equalsIgnoreCase(trimmed));
+                boolean isDupInCslListProperty = cslListProperty.stream()
+                                                                .filter(existing -> existing != layout)
+                                                                .anyMatch(existing -> existing.getDisplayName().equalsIgnoreCase(trimmed));
Evidence
Customized styles can reside in chosenListProperty (Selected) or customizedListProperty (Available →
Customized). renameSelectedStyle only checks customizedListProperty and cslListProperty, so a
conflicting name in chosenListProperty is ignored, despite chosen styles being persisted too.

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[143-152]
jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[231-238]
jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[639-662]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`renameSelectedStyle` enforces name uniqueness only against `cslListProperty` and `customizedListProperty`. However, customized styles move between `customizedListProperty` (Available → Customized tab) and `chosenListProperty` (Selected), so another customized style can exist only in `chosenListProperty` and will be missed by the duplicate check.
### Issue Context
Customized preview styles are persisted/stored from both `customizedListProperty` and the `TextBasedPreviewLayout` subset of `chosenListProperty`, so duplicates can be persisted too.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[143-152]
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[231-238]
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[639-662]
### Suggested fix approach
When renaming a `TextBasedPreviewLayout`, check for duplicates across *all* relevant lists:
- `customizedListProperty`
- `chosenListProperty` (filter `TextBasedPreviewLayout`)
- (optionally) CSL/BST lists if you want global uniqueness across all preview layouts
Exclude the layout being renamed from the comparison.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Deleted styles reappear 🐞 Bug ☼ Reliability
Description
When the customized-style series is empty, getCustomizedPreviewStyle returns the defaults instead of
an empty list, so deleting the last customized style cannot persist across restart. This contradicts
the UI’s ability to remove customized styles and can surprise users when styles reappear after
restart.
Code

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[977]

+        return result.isEmpty() ? defaults : result;
Evidence
The preferences writer deletes all series keys for an empty list, but the reader treats an empty
reconstruction as “no stored value” and falls back to defaults, so an intentional empty state cannot
persist.

jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[953-978]
jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[980-985]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`storeCustomizedPreviewStyle` purges the numbered preference series when the customized-style list becomes empty. On load, `getCustomizedPreviewStyle` reconstructs an empty `result` and then returns `defaults`, so the empty state does not round-trip.
### Issue Context
Other parts of the code appear to assume a default exists (e.g., fallbacks in preview-cycle construction), so you need to decide whether “zero customized styles” is allowed. The current behavior allows deletion in the UI but undoes it on next load.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[953-978]
- jabgui/src/main/java/org/jabref/gui/preferences/JabRefGuiPreferences.java[980-997]
### Suggested fix approach
Pick one consistent model and implement it end-to-end:
1) **Allow empty customized list**: persist an explicit marker for emptiness (or store a size key) and have `getCustomizedPreviewStyle` return an empty list when that marker is present.
2) **Require at least one customized style**: prevent deleting the final remaining customized style in the UI (disable/remove action) and/or automatically recreate a default immediately (and persist it) rather than restoring defaults only on next load.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

10. Blank rename leaves field ✓ Resolved 🐞 Bug ☼ Reliability
Description
Submitting a blank name shows a warning but does not restore styleNameProperty, leaving the UI
TextField blank while the underlying layout name remains unchanged. This desynchronizes the
displayed name from the selected layout state.
Code

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[R664-667]

+                dialogService.showWarningDialogAndWait(
+                        Localization.lang("Error"),
+                        Localization.lang("A blank space cannot be used to rename your style."));
+            }
Evidence
The TextField text is driven by styleNameProperty; on blank rename, renameSelectedStyle does not
update styleNameProperty, so the UI can remain at the user-entered blank value even though the model
name was not changed.

jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java[200-205]
jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[639-668]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The style name TextField is fed from `styleNameProperty` (subscribe → setText). On blank input, `renameSelectedStyle` only shows a warning and does not reset `styleNameProperty` back to the current layout name, so the field can remain blank even though rename was rejected.
### Issue Context
The TextField is not bidirectionally bound; user typing does not update `styleNameProperty` until `renameSelectedStyle` does.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTab.java[200-205]
- jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java[639-668]
### Suggested fix approach
In the blank-name branch, set `styleNameProperty` back to `layout.getDisplayName()` (or the last valid value) after showing the warning so the UI reverts to the actual name.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java Outdated
Comment thread jabgui/src/main/java/org/jabref/gui/preferences/preview/PreviewTabViewModel.java Outdated
Comment thread docs/requirements/entry-preview.md Outdated
Comment thread CHANGELOG.md Outdated
Comment thread jablib/src/main/java/org/jabref/logic/preview/PreviewLayout.java
… fix comments, fix and add unit tests, migration tests added, update migration, fix rename bugs
@github-actions

Copy link
Copy Markdown
Contributor

Your pull request conflicts with the target branch.

Please merge upstream/main with your code. For a step-by-step guide to resolve merge conflicts, see https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line.

@github-actions github-actions Bot added status: no-bot-comments and removed status: changes-required Pull requests that are not yet complete labels Aug 19, 2026
@calixtus

calixtus commented Aug 21, 2026

Copy link
Copy Markdown
Member

Hi, just a very quick review of ui without looking into code:
The two new buttons are a bit misplaced, as there are already the buttons with chevrons for moving items from available to selected and back in the next column. too much information, ui looks ripped apart.

The "add new" button should go below the list it belongs to. Maybe also with a text-input for the new name of the new item. See custom entry types. This can maybe combined with "Add BST" as a menu button to add bst or custom or select from different templates.

The remove and edit buttons should go to the right inside of every line for the removable and editable items. Should only show up when hovering the mouse.

Indicator for customized is also too prominent. Think instead of a filename. Common indicator of a dirty flag is an asterisk.

@github-actions github-actions Bot added status: changes-required Pull requests that are not yet complete and removed status: no-bot-comments labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: entry-preview good first issue An issue intended for project-newcomers. Varies in difficulty. status: changes-required Pull requests that are not yet complete

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support multiple customized preview styles

2 participants