Skip to content

Feature unlinked files wizard 12709#15051

Merged
Siedlerchr merged 32 commits into
JabRef:mainfrom
LoayTarek5:feature-unlinked-files-wizard-12709
Feb 23, 2026
Merged

Feature unlinked files wizard 12709#15051
Siedlerchr merged 32 commits into
JabRef:mainfrom
LoayTarek5:feature-unlinked-files-wizard-12709

Conversation

@LoayTarek5

Copy link
Copy Markdown
Collaborator

Closes #12709

change "Search for unlinked local files" flow from a single dialog into a multi-step wizard (search configuration, file selection, import results). main word doin in org.jabref.gui.externalfiles and is opened from the main menu and main table; it reuses UnlinkedFilesDialogViewModel and still saves the user’s choices (extension, date range, sort) in UnlinkedFilesDialogPreferences. The aim is to make the workflow clearer and easier to follow while keeping the same behavior and preference persistence.

Steps to test

1- open library or select it.
2- in a toolbar click on lookup Lookup, then choose Search for unlinked local files or press "Shift+F7".
3- first page will open(Configure Search, that will contain some filter fields(Diretory, File type, Last edited, Sort by ), then click Next.
4- next page will open(Select files to import), choose at least one files, then click next.
5- final page(Import result) will contain all imported files with the status, files's dir, and message, then click Finish or Cancel.

image image image

Mandatory checks

  • I own the copyright of the code submitted and I license it under the MIT license
  • 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 described the change in CHANGELOG.md in a way that is understandable for the average user (if change is visible to the user)
  • [/] I checked the user documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request updating file(s) in https://github.com/JabRef/user-documentation/tree/main/en.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Convert unlinked files dialog to multi-step wizard workflow

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Convert unlinked files search from single dialog to multi-step wizard
• Add three wizard pages: search configuration, file selection, import results
• Implement file tree selection with checkboxes and expand/collapse controls
• Display import results in table with status, file path, and messages
Diagram
flowchart LR
  A["FindUnlinkedFilesAction"] -->|instantiate| B["UnlinkedFilesWizard"]
  B -->|show| C["SearchConfigurationPage"]
  C -->|next| D["FileSelectionPage"]
  D -->|next| E["ImportResultsPage"]
  E -->|finish| F["Save Preferences"]
  C -->|uses| G["UnlinkedFilesDialogViewModel"]
  D -->|uses| G
  E -->|uses| G
Loading

Grey Divider

File Changes

1. jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java ✨ Enhancement +250/-0

Add search configuration wizard page

• New wizard page for configuring search parameters (directory, file type, date range, sort order)
• Implements directory browsing with file picker button
• Uses ComboBox controls for file type, date range, and sort order filters
• Loads and persists user preferences via UnlinkedFilesDialogPreferences
• Validates directory path input with visual feedback

jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java


2. jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java ✨ Enhancement +231/-0

Add file selection wizard page with tree view

• New wizard page for selecting unlinked files from search results
• Implements CheckTreeView with recursive tree structure for file hierarchy
• Provides select/unselect all and expand/collapse all buttons
• Shows progress indicator during file search task
• Disables next button until at least one file is selected

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java


3. jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java ✨ Enhancement +174/-0

Add import results display wizard page

• New wizard page displaying import results in a table format
• Shows status icon, file path, and import message for each result
• Displays summary with count of successful and failed imports
• Includes export results button for saving import report
• Shows progress indicator during import task execution

jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java


View more (4)
4. jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java ✨ Enhancement +107/-0

Add wizard controller for unlinked files workflow

• New class orchestrating the three-page wizard workflow
• Initializes wizard with SearchConfigurationPage, FileSelectionPage, and ImportResultsPage
• Manages UnlinkedFilesDialogViewModel lifecycle and dependency injection
• Saves user preferences on wizard completion
• Cancels running tasks on wizard cancellation

jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java


5. jabgui/src/main/java/org/jabref/gui/externalfiles/FindUnlinkedFilesAction.java ✨ Enhancement +4/-1

Refactor action to use wizard instead of dialog

• Refactor to use new UnlinkedFilesWizard instead of UnlinkedFilesDialogView
• Uses Injector to instantiate UnlinkedFilesWizard with dependencies
• Calls wizard.show() to display multi-step workflow

jabgui/src/main/java/org/jabref/gui/externalfiles/FindUnlinkedFilesAction.java


6. jabgui/src/test/java/org/jabref/gui/externalfiles/FindUnlinkedFilesActionTest.java 🧪 Tests +10/-0

Add test mocks for wizard dependencies

• Add mock objects for GuiPreferences, TaskExecutor, UndoManager, and FileUpdateMonitor
• Enhance test setup to support new wizard dependencies

jabgui/src/test/java/org/jabref/gui/externalfiles/FindUnlinkedFilesActionTest.java


7. CHANGELOG.md 📝 Documentation +1/-1

Document unlinked files dialog enhancement

• Document conversion of unlinked files dialog to wizard interface
• Reference issue #12709 in changelog entry

CHANGELOG.md


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (5) 📎 Requirement gaps (1)

Grey Divider


Action required

1. Wizard missing library step📎 Requirement gap ✓ Correctness
Description
• The new UnlinkedFilesWizard starts directly with configuration/selection/results pages and does
not provide any wizard step for selecting the library. • This violates the requirement that the
wizard has distinct steps including an explicit library selection step. • Users without the intended
library context cannot be guided through choosing the correct library within the assistant flow.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[R64-91]

+    private void initializeWizard() {
+        this.bibDatabaseContext = stateManager.getActiveDatabase()
+                                              .orElseThrow(() -> new NullPointerException("No active library"));
+
+        viewModel = new UnlinkedFilesDialogViewModel(
+                dialogService,
+                undoManager,
+                fileUpdateMonitor,
+                preferences,
+                stateManager,
+                taskExecutor);
+
+        page1 = new SearchConfigurationPage(viewModel, bibDatabaseContext, preferences);
+        page2 = new FileSelectionPage(viewModel);
+        page3 = new ImportResultsPage(viewModel);
+
+        page1.setPrefSize(650, 550);
+        page2.setPrefSize(650, 550);
+        page3.setPrefSize(650, 550);
+
+        page1.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+        page2.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+        page3.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+
+        wizard = new Wizard();
+        wizard.setTitle(Localization.lang("Search for unlinked local files"));
+        wizard.setFlow(new Wizard.LinearFlow(page1, page2, page3));
+    }
Evidence
Compliance ID 7 requires the assistant to include a distinct library selection step. The wizard flow
is defined with only three pages (configuration, file selection, import results) and the library is
taken implicitly from the active database rather than being selected as a step.

Convert 'Unlinked files' dialog into a wizard/assistant
jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-91]

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 unlinked-files assistant is required to include a distinct library selection step, but the wizard currently assumes the active database and defines a linear flow with only three pages.
## Issue Context
Compliance requires steps covering library selection, directory selection, extension selection, searching, and selecting results to import.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-91]

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


2. Labels end with colon📘 Rule violation ✓ Correctness
Description
• The wizard configuration page creates label texts by appending ":" to localized strings,
resulting in label texts that end with a colon. • This violates the GUI text convention to avoid
:-suffixed labels and also violates localization guidance by constructing user-facing strings via
concatenation.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[R95-160]

+        ///Directory selection
+        Label dirLabel = new Label(Localization.lang("Directory") + ":");
+        grid.add(dirLabel, 0, row);
+
+        HBox directoryBox = new HBox(5);
+        directoryBox.setAlignment(Pos.CENTER_LEFT);
+        directoryBox.setMaxWidth(Double.MAX_VALUE);
+        HBox.setHgrow(directoryBox, Priority.ALWAYS);
+
+        directoryPathField = new TextField();
+        directoryPathField.textProperty().bindBidirectional(viewModel.directoryPathProperty());
+        directoryPathField.setMaxWidth(Double.MAX_VALUE);
+        HBox.setHgrow(directoryPathField, Priority.ALWAYS);
+
+        browseButton = new Button();
+        browseButton.setGraphic(IconTheme.JabRefIcons.OPEN.getGraphicNode());
+        browseButton.setOnAction(e -> viewModel.browseFileDirectory());
+        browseButton.setMinWidth(30);
+        browseButton.setPrefWidth(30);
+
+        directoryBox.getChildren().addAll(directoryPathField, browseButton);
+        GridPane.setHgrow(directoryBox, Priority.ALWAYS);
+        grid.add(directoryBox, 1, row);
+
+        row++;
+
+        /// File type filter
+        Label fileTypeLabel = new Label(Localization.lang("File type") + ":");
+        grid.add(fileTypeLabel, 0, row);
+
+        fileTypeCombo = new ComboBox<>();
+        fileTypeCombo.setItems(viewModel.getFileFilters());
+        fileTypeCombo.valueProperty().bindBidirectional(viewModel.selectedExtensionProperty());
+
+        new ViewModelListCellFactory<FileExtensionViewModel>()
+                .withText(FileExtensionViewModel::getDescription)
+                .withIcon(this::getIconForExtension)
+                .install(fileTypeCombo);
+
+        fileTypeCombo.setMaxWidth(Double.MAX_VALUE);
+        GridPane.setHgrow(fileTypeCombo, Priority.ALWAYS);
+        grid.add(fileTypeCombo, 1, row);
+
+        row++;
+
+        ///  Date filter
+        Label dateLabel = new Label(Localization.lang("Last edited") + ":");
+        grid.add(dateLabel, 0, row);
+
+        fileDateCombo = new ComboBox<>();
+        fileDateCombo.setItems(viewModel.getDateFilters());
+        fileDateCombo.valueProperty().bindBidirectional(viewModel.selectedDateProperty());
+
+        new ViewModelListCellFactory<DateRange>()
+                .withText(DateRange::getDateRange)
+                .install(fileDateCombo);
+
+        fileDateCombo.setMaxWidth(Double.MAX_VALUE);
+        GridPane.setHgrow(fileDateCombo, Priority.ALWAYS);
+        grid.add(fileDateCombo, 1, row);
+
+        row++;
+
+        /// Sort order
+        Label sortLabel = new Label(Localization.lang("Sort by") + ":");
+        grid.add(sortLabel, 0, row);
Evidence
Compliance ID 20 forbids UI label texts ending with :. Compliance ID 21 requires localized strings
to avoid concatenation for user-facing text. The code appends a colon to multiple localized labels
(Directory/File type/Last edited/Sort by).

AGENTS.md
AGENTS.md
jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[95-160]

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

## Issue description
UI labels are built as `Localization.lang(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;...&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;) + &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;`, which produces `:`-suffixed labels and builds localized UI text via concatenation.
## Issue Context
Project conventions require label texts not to end with `:` and user-facing strings should not be assembled via concatenation.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[95-160]

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


3. Wizard throws NullPointerException📘 Rule violation ⛯ Reliability
Description
initializeWizard() throws a NullPointerException when there is no active library
(stateManager.getActiveDatabase().orElseThrow(...)). • This violates the exception-handling
convention to avoid unchecked exceptions and also fails to handle the edge case gracefully for a
user-invoked action. • The result is a potential application crash (or unhandled error) instead of a
clear, recoverable message to the user.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[R64-67]

+    private void initializeWizard() {
+        this.bibDatabaseContext = stateManager.getActiveDatabase()
+                                              .orElseThrow(() -> new NullPointerException("No active library"));
+
Evidence
Compliance ID 19 forbids throwing unchecked exceptions like NullPointerException. The code
explicitly throws an NPE when no active library is present, rather than handling the condition with
a user-friendly flow.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-67]

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 wizard initialization throws `NullPointerException` when no active library is available.
## Issue Context
This is a user-triggered GUI action; it should fail gracefully with a clear, localized message and avoid unchecked exceptions.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-67]

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


View more (2)
4. Progress UI not restored🐞 Bug ✓ Correctness
Description
• FileSelectionPage replaces the page center with the results content when a tree root arrives, but
it never switches the center back to the progress pane when the tree root is cleared for a new
search. • Because the progress pane’s visibility is only bound (and not kept in the layout center),
re-searching can show an empty/stale list with no progress feedback. • This is triggered by
SearchConfigurationPage resetting treeRootProperty to Optional.empty() on entry and by
startSearch() doing the same while running.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R129-143]

+        EasyBind.subscribe(unlinkedFilesList.rootProperty(), root -> {
+            if (root != null) {
+                ((CheckBoxTreeItem<FileNodeViewModel>) root).setSelected(true);
+                root.setExpanded(true);
+
+                EasyBind.bindContent(viewModel.checkedFileListProperty(),
+                        unlinkedFilesList.getCheckModel().getCheckedItems());
+
+                updateFileCount(root);
+
+                ((BorderPane) getContent()).setCenter(contentPane);
+            } else {
+                EasyBind.bindContent(viewModel.checkedFileListProperty(),
+                        FXCollections.observableArrayList());
+            }
Evidence
FileSelectionPage sets the BorderPane center to contentPane when root != null, but in the `root
== null branch it only rebinds the checked list and never restores progressPane` as the center.
Meanwhile, the wizard flow explicitly clears the tree root when re-entering configuration, and the
search task clears it again while running—so root == null is a normal state during searches.

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[114-143]
jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[236-243]
jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-136]

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

## Issue description
`FileSelectionPage` switches the UI to `contentPane` when results arrive, but never switches back to `progressPane` when `treeRootProperty` is reset to `Optional.empty()` for a new search. This causes the progress indicator to be hidden (it’s no longer in the center of the BorderPane), leaving users without feedback.
### Issue Context
`SearchConfigurationPage.onEnteringPage` resets `treeRootProperty` to empty, and `UnlinkedFilesDialogViewModel.startSearch()` also resets it while running.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[129-144]
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[190-196]

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


5. Brittle success counting🐞 Bug ✓ Correctness
Description
• ImportResultsPage counts “successful” imports by checking whether the (localized) message text
contains the substring "success". • Import messages are created via Localization.lang(...) and are
not guaranteed to be English or to contain the exact substring "success" (even in English they
commonly use “successfully”). • This will misreport the summary counts and can mislead users about
the actual import outcome.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[R145-150]

+    private void updateSummary() {
+        int totalCount = viewModel.resultTableItems().size();
+        long successCount = viewModel.resultTableItems().stream()
+                                     .filter(item -> item.message().get().toLowerCase().contains("success"))
+                                     .count();
+        long failCount = totalCount - successCount;
Evidence
The summary logic relies on parsing a localized message string for an English substring. However,
ImportHandler generates these messages using Localization, and the success/failure signal already
exists at result construction time (boolean success passed into ImportFilesResultItemViewModel).
Therefore the current approach is unreliable and will break for translations and message phrasing
changes.

jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[145-157]
jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[180-194]
jabgui/src/main/java/org/jabref/gui/externalfiles/ImportFilesResultItemViewModel.java[20-28]

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

## Issue description
`ImportResultsPage.updateSummary()` determines success by checking if the localized message contains the English substring `&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;success&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;`, which will be wrong for translations and even some English strings.
### Issue Context
`ImportFilesResultItemViewModel` already receives a `success` boolean in its constructor, but it is not stored/exposed.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[145-150]
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportFilesResultItemViewModel.java[14-40]

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



Remediation recommended

6. Configure Search title-cased📘 Rule violation ✓ Correctness
Description
• The wizard page header uses Localization.lang("Configure Search"), which is title case. • This
violates the UI wording convention to use sentence case for user-facing text.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[R58-60]

+        setHeaderText(Localization.lang("Configure Search"));
+        setupUI(bibDatabaseContext, preferences);
+        setupValidation();
Evidence
Compliance ID 21 requires sentence case for UI strings. The header text is set to a title-cased
phrase.

AGENTS.md
jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[58-60]

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 wizard header text uses title case, but UI wording conventions require sentence case.
## Issue Context
This is user-facing UI text and should follow JabRef wording conventions.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[58-60]

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


7. Trivial FIX comments added📘 Rule violation ✓ Correctness
Description
• New code contains patch-style comments like // --- FIX 1 / // --- FIX 2 and other trivial
section comments. • This violates the commenting standard to avoid trivial comments and keep the
codebase free of noisy, non-rationale commentary.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R39-67]

+    // --- FIX 1: Define the missing property ---
+    private final BooleanProperty invalidProperty = new SimpleBooleanProperty(false);
+
+    private CheckTreeView<FileNodeViewModel> unlinkedFilesList;
+    private ProgressIndicator progressIndicator;
+    private Label progressLabel;
+    private Label fileCountLabel;
+    private VBox progressPane;
+    private VBox contentPane;
+
+    private Button selectAllButton;
+    private Button unselectAllButton;
+    private Button expandAllButton;
+    private Button collapseAllButton;
+    private boolean nextButtonBound = false;
+
+    public FileSelectionPage(UnlinkedFilesDialogViewModel viewModel) {
+        this.viewModel = viewModel;
+
+        setHeaderText(Localization.lang("Select files to import"));
+        setGraphic(null);
+        setupUI();
+        setupBindings();
+    }
+
+    // --- FIX 2: Expose the property getter ---
+    public BooleanProperty invalidProperty() {
+        return invalidProperty;
+    }
Evidence
Compliance ID 14 requires removing trivial comments and avoiding noise that restates what the code
already shows. The added comments are patch notes rather than durable rationale/documentation.

AGENTS.md
jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[39-67]

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 wizard page code includes trivial/patch-note comments (e.g., `FIX 1`, `FIX 2`) that add noise without explaining lasting rationale.
## Issue Context
Project commenting standards require removing trivial comments and keeping comments focused on the &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;why&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[39-67]

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


8. Changelog text unclear📘 Rule violation ✓ Correctness
Description
• The changelog entry "We changed unlinked files dialog to a proper one." is grammatically awkward
and does not clearly describe the user-visible change. • This violates the requirement to keep
changelog/user-facing written text free of typographical/grammatical issues.
Code

CHANGELOG.md[39]

+- We changed unlinked files dialog to a proper one. [#12709](https://github.com/JabRef/jabref/issues/12709#issuecomment-3831169893)
Evidence
Compliance ID 24 requires written text (including changelogs) to be free of
typographical/grammatical errors and malformed phrasing. The added sentence is unclear and
unprofessional in wording.

CHANGELOG.md[39-39]
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 changelog entry is unclear and grammatically awkward.
## Issue Context
Changelog entries are user-facing and should clearly describe the change.
## Fix Focus Areas
- CHANGELOG.md[39-39]

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


View more (1)
9. Concurrent searches possible 🐞 Bug ⛯ Reliability
Description
• FileSelectionPage auto-starts viewModel.startSearch() on every page entry whenever
treeRootProperty is empty, without checking whether a search task is already running. •
startSearch() overwrites the findUnlinkedFilesTask field and starts a new task without
cancelling the previous one, so multiple crawlers can run concurrently and race updates to shared
state (taskActiveProperty, treeRootProperty, progress properties). • This can lead to
inconsistent UI state and wasted resources if the user re-enters the page while the previous search
is still active.
Code

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R191-195]

+    public void onEnteringPage(Wizard wizard) {
+        // Start search if not already done
+        if (viewModel.treeRootProperty().get().isEmpty()) {
+            viewModel.startSearch();
+        }
Evidence
The page entry hook starts a search solely based on treeRootProperty().get().isEmpty(). In the
view model, startSearch() always instantiates a new UnlinkedFilesCrawler and executes it; there
is no guard and no cancellation of an already-running search. Since cancelTasks() only cancels the
currently referenced task, any overwritten task reference would not be cancelable anymore via this
method.

jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[191-195]
jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-144]
jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[221-228]

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

## Issue description
`FileSelectionPage.onEnteringPage` can call `startSearch()` multiple times while a previous search is still active, because it only checks `treeRootProperty().get().isEmpty()`.
### Issue Context
`UnlinkedFilesDialogViewModel.startSearch()` always creates and executes a new `UnlinkedFilesCrawler` and does not cancel/guard against an already-running `findUnlinkedFilesTask`.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[191-195]
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-144]
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[221-228]

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


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java Outdated
Comment thread jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java Outdated
@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Feb 7, 2026
@github-actions github-actions Bot removed the status: changes-required Pull requests that are not yet complete label Feb 7, 2026
@testlens-app

testlens-app Bot commented Feb 7, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 20f53bf
▶️ Tests: 11194 executed
🟡 Checks: 46/49 completed


Learn more about TestLens at testlens.app.

@LoayTarek5

Copy link
Copy Markdown
Collaborator Author

I did my best to implement this feature, in the past days i have continues reading javafx, ControlsFX(Wizard), make mini program to get solid base and get myself fimiliar with codebase also,
For sure, maybe the code needs refactoring or not best, so i'm open to any changes, or any guidance

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Feb 7, 2026
@koppor

koppor commented Feb 7, 2026

Copy link
Copy Markdown
Member

I did my best to implement this feature, in the past days i have continues reading javafx, ControlsFX(Wizard), make mini program to get solid base and get myself fimiliar with codebase also, For sure, maybe the code needs refactoring or not best, so i'm open to any changes, or any guidance

Good style is to have the unit tests passing before commenting. This reduces load on our side and frees time for more proper reviewing. Thank you for your understanding.

@LoayTarek5

Copy link
Copy Markdown
Collaborator Author

I did my best to implement this feature, in the past days i have continues reading javafx, ControlsFX(Wizard), make mini program to get solid base and get myself fimiliar with codebase also, For sure, maybe the code needs refactoring or not best, so i'm open to any changes, or any guidance

Good style is to have the unit tests passing before commenting. This reduces load on our side and frees time for more proper reviewing. Thank you for your understanding.

Ok, I will check any failure test and try to fix now.

@github-actions github-actions Bot removed the status: changes-required Pull requests that are not yet complete label Feb 7, 2026
@LoayTarek5

LoayTarek5 commented Feb 9, 2026

Copy link
Copy Markdown
Collaborator Author

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (5) 📎 Requirement gaps (1)

Grey Divider Action required
  1. Wizard missing library step 📎 Requirement gap ✓ Correctness

Description

• The new UnlinkedFilesWizard starts directly with configuration/selection/results pages and does
not provide any wizard step for selecting the library.
• This violates the requirement that the wizard has distinct steps including an explicit library
selection step.
• Users without the intended library context cannot be guided through choosing the correct library
within the assistant flow.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[R64-91]](https://github.com/JabRef/jabref/pull/15051/files#diff-d7f54ac9cae01ec1210ed542456f24cca2c109c2724f8b88be38089a0c0f36d2R64-R91)

+    private void initializeWizard() {
+        this.bibDatabaseContext = stateManager.getActiveDatabase()
+                                              .orElseThrow(() -> new NullPointerException("No active library"));
+
+        viewModel = new UnlinkedFilesDialogViewModel(
+                dialogService,
+                undoManager,
+                fileUpdateMonitor,
+                preferences,
+                stateManager,
+                taskExecutor);
+
+        page1 = new SearchConfigurationPage(viewModel, bibDatabaseContext, preferences);
+        page2 = new FileSelectionPage(viewModel);
+        page3 = new ImportResultsPage(viewModel);
+
+        page1.setPrefSize(650, 550);
+        page2.setPrefSize(650, 550);
+        page3.setPrefSize(650, 550);
+
+        page1.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+        page2.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+        page3.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+
+        wizard = new Wizard();
+        wizard.setTitle(Localization.lang("Search for unlinked local files"));
+        wizard.setFlow(new Wizard.LinearFlow(page1, page2, page3));
+    }

Evidence

Compliance ID 7 requires the assistant to include a distinct library selection step. The wizard flow
is defined with only three pages (configuration, file selection, import results) and the library is
taken implicitly from the active database rather than being selected as a step.

Convert 'Unlinked files' dialog into a wizard/assistant
[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-91]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java/#L64-L91)

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 unlinked-files assistant is required to include a distinct library selection step, but the wizard currently assumes the active database and defines a linear flow with only three pages.
## Issue Context
Compliance requires steps covering library selection, directory selection, extension selection, searching, and selecting results to import.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-91]

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

  1. Labels end with colon 📘 Rule violation ✓ Correctness

Description

• The wizard configuration page creates label texts by appending ":" to localized strings,
resulting in label texts that end with a colon.
• This violates the GUI text convention to avoid :-suffixed labels and also violates localization
guidance by constructing user-facing strings via concatenation.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[R95-160]](https://github.com/JabRef/jabref/pull/15051/files#diff-4eec7993aed1da156565e492576b1ce91cf13f51fc39372d19ca77176d669c46R95-R160)

+        ///Directory selection
+        Label dirLabel = new Label(Localization.lang("Directory") + ":");
+        grid.add(dirLabel, 0, row);
+
+        HBox directoryBox = new HBox(5);
+        directoryBox.setAlignment(Pos.CENTER_LEFT);
+        directoryBox.setMaxWidth(Double.MAX_VALUE);
+        HBox.setHgrow(directoryBox, Priority.ALWAYS);
+
+        directoryPathField = new TextField();
+        directoryPathField.textProperty().bindBidirectional(viewModel.directoryPathProperty());
+        directoryPathField.setMaxWidth(Double.MAX_VALUE);
+        HBox.setHgrow(directoryPathField, Priority.ALWAYS);
+
+        browseButton = new Button();
+        browseButton.setGraphic(IconTheme.JabRefIcons.OPEN.getGraphicNode());
+        browseButton.setOnAction(e -> viewModel.browseFileDirectory());
+        browseButton.setMinWidth(30);
+        browseButton.setPrefWidth(30);
+
+        directoryBox.getChildren().addAll(directoryPathField, browseButton);
+        GridPane.setHgrow(directoryBox, Priority.ALWAYS);
+        grid.add(directoryBox, 1, row);
+
+        row++;
+
+        /// File type filter
+        Label fileTypeLabel = new Label(Localization.lang("File type") + ":");
+        grid.add(fileTypeLabel, 0, row);
+
+        fileTypeCombo = new ComboBox<>();
+        fileTypeCombo.setItems(viewModel.getFileFilters());
+        fileTypeCombo.valueProperty().bindBidirectional(viewModel.selectedExtensionProperty());
+
+        new ViewModelListCellFactory<FileExtensionViewModel>()
+                .withText(FileExtensionViewModel::getDescription)
+                .withIcon(this::getIconForExtension)
+                .install(fileTypeCombo);
+
+        fileTypeCombo.setMaxWidth(Double.MAX_VALUE);
+        GridPane.setHgrow(fileTypeCombo, Priority.ALWAYS);
+        grid.add(fileTypeCombo, 1, row);
+
+        row++;
+
+        ///  Date filter
+        Label dateLabel = new Label(Localization.lang("Last edited") + ":");
+        grid.add(dateLabel, 0, row);
+
+        fileDateCombo = new ComboBox<>();
+        fileDateCombo.setItems(viewModel.getDateFilters());
+        fileDateCombo.valueProperty().bindBidirectional(viewModel.selectedDateProperty());
+
+        new ViewModelListCellFactory<DateRange>()
+                .withText(DateRange::getDateRange)
+                .install(fileDateCombo);
+
+        fileDateCombo.setMaxWidth(Double.MAX_VALUE);
+        GridPane.setHgrow(fileDateCombo, Priority.ALWAYS);
+        grid.add(fileDateCombo, 1, row);
+
+        row++;
+
+        /// Sort order
+        Label sortLabel = new Label(Localization.lang("Sort by") + ":");
+        grid.add(sortLabel, 0, row);

Evidence

Compliance ID 20 forbids UI label texts ending with :. Compliance ID 21 requires localized strings
to avoid concatenation for user-facing text. The code appends a colon to multiple localized labels
(Directory/File type/Last edited/Sort by).

AGENTS.md
AGENTS.md
[jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[95-160]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java/#L95-L160)

Agent prompt

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

## Issue description
UI labels are built as `Localization.lang(&amp;amp;amp;amp;amp;amp;amp;amp;quot;...&amp;amp;amp;amp;amp;amp;amp;amp;quot;) + &amp;amp;amp;amp;amp;amp;amp;amp;quot;:&amp;amp;amp;amp;amp;amp;amp;amp;quot;`, which produces `:`-suffixed labels and builds localized UI text via concatenation.
## Issue Context
Project conventions require label texts not to end with `:` and user-facing strings should not be assembled via concatenation.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[95-160]

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

  1. Wizard throws NullPointerException 📘 Rule violation ⛯ Reliability

Description

• initializeWizard() throws a NullPointerException when there is no active library
(stateManager.getActiveDatabase().orElseThrow(...)).
• This violates the exception-handling convention to avoid unchecked exceptions and also fails to
handle the edge case gracefully for a user-invoked action.
• The result is a potential application crash (or unhandled error) instead of a clear, recoverable
message to the user.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[R64-67]](https://github.com/JabRef/jabref/pull/15051/files#diff-d7f54ac9cae01ec1210ed542456f24cca2c109c2724f8b88be38089a0c0f36d2R64-R67)

+    private void initializeWizard() {
+        this.bibDatabaseContext = stateManager.getActiveDatabase()
+                                              .orElseThrow(() -> new NullPointerException("No active library"));
+

Evidence

Compliance ID 19 forbids throwing unchecked exceptions like NullPointerException. The code
explicitly throws an NPE when no active library is present, rather than handling the condition with
a user-friendly flow.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-67]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java/#L64-L67)

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 wizard initialization throws `NullPointerException` when no active library is available.
## Issue Context
This is a user-triggered GUI action; it should fail gracefully with a clear, localized message and avoid unchecked exceptions.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesWizard.java[64-67]

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

View more (2)

  1. Progress UI not restored 🐞 Bug ✓ Correctness

Description

• FileSelectionPage replaces the page center with the results content when a tree root arrives, but
it never switches the center back to the progress pane when the tree root is cleared for a new
search.
• Because the progress pane’s visibility is only bound (and not kept in the layout center),
re-searching can show an empty/stale list with no progress feedback.
• This is triggered by SearchConfigurationPage resetting treeRootProperty to Optional.empty() on
entry and by startSearch() doing the same while running.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R129-143]](https://github.com/JabRef/jabref/pull/15051/files#diff-198a81a823fa1e8855a228a55bd5d745de2978705ae7182aa0575ea299b9ca60R129-R143)

+        EasyBind.subscribe(unlinkedFilesList.rootProperty(), root -> {
+            if (root != null) {
+                ((CheckBoxTreeItem<FileNodeViewModel>) root).setSelected(true);
+                root.setExpanded(true);
+
+                EasyBind.bindContent(viewModel.checkedFileListProperty(),
+                        unlinkedFilesList.getCheckModel().getCheckedItems());
+
+                updateFileCount(root);
+
+                ((BorderPane) getContent()).setCenter(contentPane);
+            } else {
+                EasyBind.bindContent(viewModel.checkedFileListProperty(),
+                        FXCollections.observableArrayList());
+            }

Evidence

FileSelectionPage sets the BorderPane center to contentPane when root != null, but in the root == null branch it only rebinds the checked list and never restores progressPane as the center.
Meanwhile, the wizard flow explicitly clears the tree root when re-entering configuration, and the
search task clears it again while running—so root == null is a normal state during searches.

[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[114-143]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java/#L114-L143)
[jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[236-243]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java/#L236-L243)
[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-136]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java/#L121-L136)

Agent prompt

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

## Issue description
`FileSelectionPage` switches the UI to `contentPane` when results arrive, but never switches back to `progressPane` when `treeRootProperty` is reset to `Optional.empty()` for a new search. This causes the progress indicator to be hidden (it’s no longer in the center of the BorderPane), leaving users without feedback.
### Issue Context
`SearchConfigurationPage.onEnteringPage` resets `treeRootProperty` to empty, and `UnlinkedFilesDialogViewModel.startSearch()` also resets it while running.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[129-144]
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[190-196]

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

  1. Brittle success counting 🐞 Bug ✓ Correctness

Description

• ImportResultsPage counts “successful” imports by checking whether the (localized) message text
contains the substring "success".
• Import messages are created via Localization.lang(...) and are not guaranteed to be English or
to contain the exact substring "success" (even in English they commonly use “successfully”).
• This will misreport the summary counts and can mislead users about the actual import outcome.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[R145-150]](https://github.com/JabRef/jabref/pull/15051/files#diff-e903a245239d6d0f25422d3a4fc09b552631333986b7cbe493b31e2fbaa71b93R145-R150)

+    private void updateSummary() {
+        int totalCount = viewModel.resultTableItems().size();
+        long successCount = viewModel.resultTableItems().stream()
+                                     .filter(item -> item.message().get().toLowerCase().contains("success"))
+                                     .count();
+        long failCount = totalCount - successCount;

Evidence

The summary logic relies on parsing a localized message string for an English substring. However,
ImportHandler generates these messages using Localization, and the success/failure signal already
exists at result construction time (boolean success passed into ImportFilesResultItemViewModel).
Therefore the current approach is unreliable and will break for translations and message phrasing
changes.

[jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[145-157]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java/#L145-L157)
[jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java[180-194]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java/#L180-L194)
[jabgui/src/main/java/org/jabref/gui/externalfiles/ImportFilesResultItemViewModel.java[20-28]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/ImportFilesResultItemViewModel.java/#L20-L28)

Agent prompt

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

## Issue description
`ImportResultsPage.updateSummary()` determines success by checking if the localized message contains the English substring `&amp;amp;amp;amp;amp;amp;amp;amp;quot;success&amp;amp;amp;amp;amp;amp;amp;amp;quot;`, which will be wrong for translations and even some English strings.
### Issue Context
`ImportFilesResultItemViewModel` already receives a `success` boolean in its constructor, but it is not stored/exposed.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportResultsPage.java[145-150]
- jabgui/src/main/java/org/jabref/gui/externalfiles/ImportFilesResultItemViewModel.java[14-40]

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

Remediation recommended
  1. Configure Search title-cased 📘 Rule violation ✓ Correctness

Description

• The wizard page header uses Localization.lang("Configure Search"), which is title case.
• This violates the UI wording convention to use sentence case for user-facing text.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[R58-60]](https://github.com/JabRef/jabref/pull/15051/files#diff-4eec7993aed1da156565e492576b1ce91cf13f51fc39372d19ca77176d669c46R58-R60)

+        setHeaderText(Localization.lang("Configure Search"));
+        setupUI(bibDatabaseContext, preferences);
+        setupValidation();

Evidence

Compliance ID 21 requires sentence case for UI strings. The header text is set to a title-cased
phrase.

AGENTS.md
[jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[58-60]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java/#L58-L60)

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 wizard header text uses title case, but UI wording conventions require sentence case.
## Issue Context
This is user-facing UI text and should follow JabRef wording conventions.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/SearchConfigurationPage.java[58-60]

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

✅ 7. Trivial FIX comments added 📘 Rule violation ✓ Correctness

Description

• New code contains patch-style comments like // --- FIX 1 / // --- FIX 2 and other trivial
section comments.
• This violates the commenting standard to avoid trivial comments and keep the codebase free of
noisy, non-rationale commentary.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R39-67]](https://github.com/JabRef/jabref/pull/15051/files#diff-198a81a823fa1e8855a228a55bd5d745de2978705ae7182aa0575ea299b9ca60R39-R67)

+    // --- FIX 1: Define the missing property ---
+    private final BooleanProperty invalidProperty = new SimpleBooleanProperty(false);
+
+    private CheckTreeView<FileNodeViewModel> unlinkedFilesList;
+    private ProgressIndicator progressIndicator;
+    private Label progressLabel;
+    private Label fileCountLabel;
+    private VBox progressPane;
+    private VBox contentPane;
+
+    private Button selectAllButton;
+    private Button unselectAllButton;
+    private Button expandAllButton;
+    private Button collapseAllButton;
+    private boolean nextButtonBound = false;
+
+    public FileSelectionPage(UnlinkedFilesDialogViewModel viewModel) {
+        this.viewModel = viewModel;
+
+        setHeaderText(Localization.lang("Select files to import"));
+        setGraphic(null);
+        setupUI();
+        setupBindings();
+    }
+
+    // --- FIX 2: Expose the property getter ---
+    public BooleanProperty invalidProperty() {
+        return invalidProperty;
+    }

Evidence

Compliance ID 14 requires removing trivial comments and avoiding noise that restates what the code
already shows. The added comments are patch notes rather than durable rationale/documentation.

AGENTS.md
[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[39-67]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java/#L39-L67)

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 wizard page code includes trivial/patch-note comments (e.g., `FIX 1`, `FIX 2`) that add noise without explaining lasting rationale.
## Issue Context
Project commenting standards require removing trivial comments and keeping comments focused on the &amp;amp;amp;amp;amp;amp;amp;amp;quot;why&amp;amp;amp;amp;amp;amp;amp;amp;quot;.
## Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[39-67]

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

  1. Changelog text unclear 📘 Rule violation ✓ Correctness

Description

• The changelog entry "We changed unlinked files dialog to a proper one." is grammatically awkward
and does not clearly describe the user-visible change.
• This violates the requirement to keep changelog/user-facing written text free of
typographical/grammatical issues.

Code

[CHANGELOG.md[39]](https://github.com/JabRef/jabref/pull/15051/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4edR39-R39)

+- We changed unlinked files dialog to a proper one. [#12709](https://github.com/JabRef/jabref/issues/12709#issuecomment-3831169893)

Evidence

Compliance ID 24 requires written text (including changelogs) to be free of
typographical/grammatical errors and malformed phrasing. The added sentence is unclear and
unprofessional in wording.

[CHANGELOG.md[39-39]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/CHANGELOG.md/#L39-L39)
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 changelog entry is unclear and grammatically awkward.
## Issue Context
Changelog entries are user-facing and should clearly describe the change.
## Fix Focus Areas
- CHANGELOG.md[39-39]

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

View more (1)

  1. Concurrent searches possible 🐞 Bug ⛯ Reliability

Description

• FileSelectionPage auto-starts viewModel.startSearch() on every page entry whenever
treeRootProperty is empty, without checking whether a search task is already running.
• startSearch() overwrites the findUnlinkedFilesTask field and starts a new task without
cancelling the previous one, so multiple crawlers can run concurrently and race updates to shared
state (taskActiveProperty, treeRootProperty, progress properties).
• This can lead to inconsistent UI state and wasted resources if the user re-enters the page while
the previous search is still active.

Code

[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[R191-195]](https://github.com/JabRef/jabref/pull/15051/files#diff-198a81a823fa1e8855a228a55bd5d745de2978705ae7182aa0575ea299b9ca60R191-R195)

+    public void onEnteringPage(Wizard wizard) {
+        // Start search if not already done
+        if (viewModel.treeRootProperty().get().isEmpty()) {
+            viewModel.startSearch();
+        }

Evidence

The page entry hook starts a search solely based on treeRootProperty().get().isEmpty(). In the
view model, startSearch() always instantiates a new UnlinkedFilesCrawler and executes it; there
is no guard and no cancellation of an already-running search. Since cancelTasks() only cancels the
currently referenced task, any overwritten task reference would not be cancelable anymore via this
method.

[jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[191-195]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java/#L191-L195)
[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-144]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java/#L121-L144)
[jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[221-228]](https://github.com/JabRef/jabref/blob/7451541821b82faf1534688ab3ea974ed468eeff/jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java/#L221-L228)

Agent prompt

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

## Issue description
`FileSelectionPage.onEnteringPage` can call `startSearch()` multiple times while a previous search is still active, because it only checks `treeRootProperty().get().isEmpty()`.
### Issue Context
`UnlinkedFilesDialogViewModel.startSearch()` always creates and executes a new `UnlinkedFilesCrawler` and does not cancel/guard against an already-running `findUnlinkedFilesTask`.
### Fix Focus Areas
- jabgui/src/main/java/org/jabref/gui/externalfiles/FileSelectionPage.java[191-195]
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[121-144]
- jabgui/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java[221-228]

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

Grey Divider

ⓘ The new review experience is currently in Beta. Learn more
Grey Divider

Qodo Logo

I am resolving these Code Review

@koppor

koppor commented Feb 9, 2026

Copy link
Copy Markdown
Member

I am resolving these Code Review

Only the ones which make sense. This is an AI talking to you...

The library selected is the once the user starts with. Thus, it is OK.

@github-actions github-actions Bot added the status: changes-required Pull requests that are not yet complete label Feb 9, 2026
@github-actions github-actions Bot removed the status: changes-required Pull requests that are not yet complete label Feb 9, 2026
@koppor koppor added status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers and removed status: no-bot-comments labels Feb 9, 2026
…les-wizard-12709

* upstream/main: (106 commits)
  Merge common gating parts into composite action (JabRef#15197)
  Support protected institutional authors in PersonNamesChecker (JabRef#15175)
  adapt wix (JabRef#14969)
  Improve CI (JabRef#15189)
  Revert "Reduce complexity in dependencies setup (JabRef#15169)" (JabRef#15191)
  Fix compilation
  Fix heylogs test
  Fix icon on Linux (JabRef#15188)
  chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.5 (JabRef#15178)
  New Crowdin updates (JabRef#15173)
  Reduce complexity in dependencies setup (JabRef#15169)
  Start new development cycle
  snapcraft
  snapcraft
  use snapctl
  update metadata fiels
  try with  mesa candidate
  fix snapcraft and skmanrc to use correct version
  Release v6.0-alpha.5
  chore(sbom): update CycloneDX SBOM files (JabRef#15172)
  ...
@Siedlerchr Siedlerchr enabled auto-merge February 23, 2026 20:15
@Siedlerchr Siedlerchr disabled auto-merge February 23, 2026 20:25
@Siedlerchr Siedlerchr merged commit d5de07a into JabRef:main Feb 23, 2026
48 of 49 checks passed
RakockiW pushed a commit to RakockiW/jabref that referenced this pull request Mar 1, 2026
* Update CHANGELOG.md to include changes to the unlinked files dialog (JabRef#12709)

* Add FileSelectionPage for managing unlinked files in the import dialog

* Refactor FindUnlinkedFilesAction to use UnlinkedFilesWizard for displaying unlinked files

* Add ImportResultsPage for displaying import results in the unlinked files dialog

* Add SearchConfigurationPage for configuring search settings in the unlinked files dialog

* Add UnlinkedFilesWizard class to manage the unlinked files dialog workflow, integrating search configuration, file selection, and import results pages.

* Enhance FindUnlinkedFilesActionTest by adding mocks for GuiPreferences, TaskExecutor, UndoManager, and FileUpdateMonitor.

* Update CHANGELOG with recent changes and fixes

* Update CHANGELOG with recent changes and fixes

* Refactor FileSelectionPage to remove commented out code.

* Add new localization entries for search configuration and import results in JabRef_en.properties

* remove trailing whitespace

* reformat my code

* Refactor by removing sizeToScene() to reduce heavy layout calculations.

* Update UnlinkedFilesWizard to adjust window size and add the JabRef icon to the wizard window.

* Update CHANGELOG.md to reflect the replacement of the unlinked files dialog to be more descriptive.

* Enhance FileSelectionPage to display a progress pane during file search initiation.

* Add success property and method to ImportFilesResultItemViewModel for better result handling

* Refactor updateSummary method in ImportResultsPage to use isSuccess method for counting successful imports

* Fix label formatting in SearchConfigurationPage by removing unnecessary colons.

* Refactor UnlinkedFilesWizard by checking for an active database before proceeding, enhancing error handling for user feedback.

* Fix localization keys for unlinked files wizard

* move cahngelog

---------

Co-authored-by: Siedlerchr <siedlerkiller@gmail.com>
priyanshu16095 pushed a commit to priyanshu16095/jabref that referenced this pull request Mar 3, 2026
* Update CHANGELOG.md to include changes to the unlinked files dialog (JabRef#12709)

* Add FileSelectionPage for managing unlinked files in the import dialog

* Refactor FindUnlinkedFilesAction to use UnlinkedFilesWizard for displaying unlinked files

* Add ImportResultsPage for displaying import results in the unlinked files dialog

* Add SearchConfigurationPage for configuring search settings in the unlinked files dialog

* Add UnlinkedFilesWizard class to manage the unlinked files dialog workflow, integrating search configuration, file selection, and import results pages.

* Enhance FindUnlinkedFilesActionTest by adding mocks for GuiPreferences, TaskExecutor, UndoManager, and FileUpdateMonitor.

* Update CHANGELOG with recent changes and fixes

* Update CHANGELOG with recent changes and fixes

* Refactor FileSelectionPage to remove commented out code.

* Add new localization entries for search configuration and import results in JabRef_en.properties

* remove trailing whitespace

* reformat my code

* Refactor by removing sizeToScene() to reduce heavy layout calculations.

* Update UnlinkedFilesWizard to adjust window size and add the JabRef icon to the wizard window.

* Update CHANGELOG.md to reflect the replacement of the unlinked files dialog to be more descriptive.

* Enhance FileSelectionPage to display a progress pane during file search initiation.

* Add success property and method to ImportFilesResultItemViewModel for better result handling

* Refactor updateSummary method in ImportResultsPage to use isSuccess method for counting successful imports

* Fix label formatting in SearchConfigurationPage by removing unnecessary colons.

* Refactor UnlinkedFilesWizard by checking for an active database before proceeding, enhancing error handling for user feedback.

* Fix localization keys for unlinked files wizard

* move cahngelog

---------

Co-authored-by: Siedlerchr <siedlerkiller@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

good third issue status: no-bot-comments status: ready-for-review Pull Requests that are ready to be reviewed by the maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make "unlinked files" dialog a wizard

3 participants