Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
274c813
Rework openreference PR
subhramit Nov 20, 2025
1895625
Remove some redundant preference parameters
subhramit Nov 20, 2025
c1925f2
Run formatter
subhramit Nov 20, 2025
434428d
Openrewrite
subhramit Nov 20, 2025
f93ffd5
Remove hilarious copy paste error
subhramit Nov 20, 2025
8a73bce
Almost fix tests
subhramit Nov 20, 2025
5f224ee
Refactor tests
subhramit Nov 20, 2025
24900b7
Fix tests
subhramit Nov 20, 2025
478acea
Use `create`
subhramit Nov 20, 2025
ebd6660
Illegal arg
subhramit Nov 20, 2025
7b0f76a
Illegal arg
subhramit Nov 20, 2025
2b0f3d0
Add back omitted tests
subhramit Nov 20, 2025
00294d6
Add back remove latex braces test
subhramit Nov 20, 2025
e407071
Maintain test parity
subhramit Nov 20, 2025
bf9cd1d
Reformat code
subhramit Nov 20, 2025
77f53cc
Comments
subhramit Nov 20, 2025
5d2c159
Merge branch 'main' into open-reference
subhramit Nov 20, 2025
4632e7e
Remove more redundant parameters
subhramit Nov 20, 2025
96d92e9
Merge remote-tracking branch 'origin/open-reference' into open-reference
subhramit Nov 20, 2025
8964400
boy scout woohoo - remove redundant get calls
subhramit Nov 20, 2025
83d35cb
underscores
subhramit Nov 20, 2025
79c7717
boy scout - remove redundant constructors from `WebSearchTabViewModel`
subhramit Nov 20, 2025
b820113
more boy scout
subhramit Nov 20, 2025
85416a4
Merge branch 'main' into open-reference
subhramit Nov 21, 2025
d0ae921
Merge branch 'main' into open-reference
subhramit Nov 21, 2025
06c61df
Swap order of search engines
subhramit Nov 21, 2025
8e3e763
Swap constants
subhramit Nov 21, 2025
d6d5add
l10n
subhramit Nov 21, 2025
6144478
Use dialogservice.notify, link comment
subhramit Nov 21, 2025
712e5eb
Move isValid to url utils
subhramit Nov 21, 2025
afaaa3b
Use JSpecify `@Nullable`
subhramit Nov 21, 2025
c4c9b10
Defensive checks, add tests
subhramit Nov 21, 2025
2d7204c
URL checking optimizations
subhramit Nov 21, 2025
f77a720
Javadoc
subhramit Nov 21, 2025
e47b3ed
fix checkValue
subhramit Nov 21, 2025
83207e3
Reformat URLUtilTest
subhramit Nov 21, 2025
d4d843c
Stricter online link check in LinkedFile
subhramit Nov 21, 2025
2af144c
Merge branch 'main' into open-reference
subhramit Nov 21, 2025
4d3484e
Revert "Stricter online link check in LinkedFile"
subhramit Nov 21, 2025
0bda89c
feat(external-search): add Semantic Scholar search
InAnYan Nov 23, 2025
ef502f1
chore: fix submodules
InAnYan Nov 23, 2025
8e6316c
try: remove submodule
InAnYan Nov 23, 2025
c21889b
fix: submodules
InAnYan Nov 23, 2025
68237f2
fix(search): add proper search for Semantic Scholar when author is pr…
InAnYan Nov 23, 2025
da26f2a
Merge remote-tracking branch 'origin/main' into open-reference
koppor Nov 23, 2025
c05d993
First the grouping element, then the sub menu elements
koppor Nov 23, 2025
ef1aa53
Add CHANGELOG.md entry for the update at UrlChecker
koppor Nov 23, 2025
64a636e
Remove static imports, syserr artifacts and run formatter
subhramit Nov 23, 2025
e8718ac
Merge branch 'main' into open-reference
subhramit Nov 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- We added "Close library" to the File menu. [#14381](https://github.com/JabRef/jabref/issues/14381)
- We added a "Regenerate" button for the AI chat allowing the user to make the language model reformulate its response to the previous prompt. [#12191](https://github.com/JabRef/jabref/issues/12191)
- We added support for transliteration of fields to English and automatic transliteration of generated citation key. [#11377](https://github.com/JabRef/jabref/issues/11377)
- We added support for "Search Google Scholar" and "Search Semantic Scholar" to quickly search for a selected entry's title in Google Scholar or Semantic Scholar directly from the main table's context menu [#12268](https://github.com/JabRef/jabref/issues/12268)
- We introduced a new "Search Engine URL Template" setting in Preferences to allow users to customize their search engine URL templates [#12268](https://github.com/JabRef/jabref/issues/12268)

### Changed

- We replaced the standard ComboBox with a SearchableComboBox and added a free text field in custom Entry Types [#14082](https://github.com/JabRef/jabref/issues/14082)
- We separated the "Clean up entries" dialog into three tabs for clarity [#13819](https://github.com/JabRef/jabref/issues/13819)
- `JabKit`: `--porcelain` does not output any logs to the console anymore. [#14244](https://github.com/JabRef/jabref/pull/14244)
- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>L</kbd> now opens the terminal in the active library directory. [#14130](https://github.com/JabRef/jabref/issues/14130)
- The URL integrity check now checks the complete URL syntax. [#14370](https://github.com/JabRef/jabref/pull/14370)
- We changed fixed-value ComboBoxes to SearchableComboBox for better usability. [#14083](https://github.com/JabRef/jabref/issues/14083)

### Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ public enum StandardActions implements Action {
EXTRACT_FILE_REFERENCES_ONLINE(Localization.lang("Extract references from file (online)"), IconTheme.JabRefIcons.FILE_STAR),
EXTRACT_FILE_REFERENCES_OFFLINE(Localization.lang("Extract references from file (offline)"), IconTheme.JabRefIcons.FILE_STAR),
OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI),
SEARCH(Localization.lang("Search...")),
SEARCH_GOOGLE_SCHOLAR(Localization.lang("Search Google Scholar")),
SEARCH_SEMANTIC_SCHOLAR(Localization.lang("Search Semantic Scholar")),
SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")),
MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get bibliographic data from %0", "DOI/ISBN/..."), KeyBinding.MERGE_WITH_FETCHED_ENTRY),
BATCH_MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get bibliographic data from %0 (fully automated)", "DOI/ISBN/...")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ public static ContextMenu create(BibEntryTableViewModel entry,
extractFileReferencesOffline,

factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager, preferences)),
factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferences)),

createSearchSubMenu(factory, dialogService, stateManager, preferences),

new SeparatorMenuItem(),

Expand Down Expand Up @@ -237,4 +238,17 @@ private static Menu createSendSubMenu(ActionFactory factory,

return sendMenu;
}

private static Menu createSearchSubMenu(ActionFactory factory,
DialogService dialogService,
StateManager stateManager,
GuiPreferences preferences) {
Menu searchMenu = factory.createMenu(StandardActions.SEARCH);
searchMenu.getItems().addAll(
factory.createMenuItem(StandardActions.SEARCH_GOOGLE_SCHOLAR, new SearchGoogleScholarAction(dialogService, stateManager, preferences)),
factory.createMenuItem(StandardActions.SEARCH_SEMANTIC_SCHOLAR, new SearchSemanticScholarAction(dialogService, stateManager, preferences)),
factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager, preferences))
);
return searchMenu;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.jabref.gui.maintable;

import java.io.IOException;
import java.util.List;

import javafx.beans.binding.BooleanExpression;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.desktop.os.NativeDesktop;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.ExternalLinkCreator;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.jabref.gui.actions.ActionHelper.isFieldSetForSelectedEntry;
import static org.jabref.gui.actions.ActionHelper.needsEntriesSelected;

public class SearchGoogleScholarAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchGoogleScholarAction.class);

private final DialogService dialogService;
private final StateManager stateManager;
private final GuiPreferences preferences;
private final ExternalLinkCreator externalLinkCreator;

public SearchGoogleScholarAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences) {
this.dialogService = dialogService;
this.stateManager = stateManager;
this.preferences = preferences;

this.externalLinkCreator = new ExternalLinkCreator(preferences.getImporterPreferences());

BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager);
this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet));
}

@Override
public void execute() {
stateManager.getActiveDatabase().ifPresent(databaseContext -> {
final List<BibEntry> bibEntries = stateManager.getSelectedEntries();
externalLinkCreator.getGoogleScholarSearchURL(bibEntries.getFirst()).ifPresent(url -> {
try {
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
} catch (IOException ex) {
LOGGER.warn("Could not open Google Scholar", ex);
dialogService.notify(Localization.lang("Unable to open Google Scholar.") + " " + ex.getMessage());
}
});
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.jabref.gui.maintable;

import java.io.IOException;
import java.util.List;

import javafx.beans.binding.BooleanExpression;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.ActionHelper;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.desktop.os.NativeDesktop;
import org.jabref.gui.preferences.GuiPreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.ExternalLinkCreator;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SearchSemanticScholarAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchSemanticScholarAction.class);

private final DialogService dialogService;
private final StateManager stateManager;
private final GuiPreferences preferences;
private final ExternalLinkCreator externalLinkCreator;

public SearchSemanticScholarAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences) {
this.dialogService = dialogService;
this.stateManager = stateManager;
this.preferences = preferences;

this.externalLinkCreator = new ExternalLinkCreator(preferences.getImporterPreferences());

BooleanExpression fieldIsSet = ActionHelper.isFieldSetForSelectedEntry(StandardField.TITLE, stateManager);
this.executable.bind(ActionHelper.needsEntriesSelected(1, stateManager).and(fieldIsSet));
}

@Override
public void execute() {
stateManager.getActiveDatabase().ifPresent(databaseContext -> {
final List<BibEntry> bibEntries = stateManager.getSelectedEntries();
externalLinkCreator.getSemanticScholarSearchURL(bibEntries.getFirst()).ifPresent(url -> {
try {
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
} catch (IOException ex) {
LOGGER.warn("Could not open Semantic Scholar", ex);
dialogService.notify(Localization.lang("Unable to open Semantic Scholar.") + " " + ex.getMessage());
}
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.jabref.gui.actions.ActionHelper.isFieldSetForSelectedEntry;
import static org.jabref.gui.actions.ActionHelper.needsEntriesSelected;

public class SearchShortScienceAction extends SimpleCommand {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchShortScienceAction.class);

private final DialogService dialogService;
private final StateManager stateManager;
private final GuiPreferences preferences;
private final ExternalLinkCreator externalLinkCreator;

public SearchShortScienceAction(DialogService dialogService, StateManager stateManager, GuiPreferences preferences) {
this.dialogService = dialogService;
this.stateManager = stateManager;
this.preferences = preferences;
this.externalLinkCreator = new ExternalLinkCreator(preferences.getImporterPreferences());

BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager);
this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet));
Expand All @@ -41,11 +48,12 @@ public void execute() {
dialogService.notify(Localization.lang("This operation requires exactly one item to be selected."));
return;
}
ExternalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
externalLinkCreator.getShortScienceSearchURL(bibEntries.getFirst()).ifPresent(url -> {
try {
NativeDesktop.openExternalViewer(databaseContext, preferences, url, StandardField.URL, dialogService, bibEntries.getFirst());
} catch (IOException ex) {
dialogService.showErrorDialogAndWait(Localization.lang("Unable to open ShortScience."), ex);
LOGGER.warn("Could not open ShortScience", ex);
dialogService.notify(Localization.lang("Unable to open ShortScience.") + " " + ex.getMessage());
}
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.jabref.gui.preferences.websearch;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class SearchEngineItem {
private final StringProperty name;
private final StringProperty urlTemplate;

public SearchEngineItem(String name, String urlTemplate) {
this.name = new SimpleStringProperty(name);
this.urlTemplate = new SimpleStringProperty(urlTemplate);
}

public StringProperty nameProperty() {
return name;
}

public StringProperty urlTemplateProperty() {
return urlTemplate;
}

public String getName() {
return name.get();
}

public String getUrlTemplate() {
return urlTemplate.get();
}

public void setUrlTemplate(String urlTemplate) {
this.urlTemplate.set(urlTemplate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
Expand Down Expand Up @@ -45,6 +48,10 @@ public class WebSearchTab extends AbstractPreferenceTabView<WebSearchTabViewMode
@FXML private CheckBox grobidEnabled;
@FXML private TextField grobidURL;

@FXML private TableView<SearchEngineItem> searchEngineTable;
@FXML private TableColumn<SearchEngineItem, String> searchEngineName;
@FXML private TableColumn<SearchEngineItem, String> searchEngineUrlTemplate;

@FXML private VBox fetchersContainer;

private final ReadOnlyBooleanProperty refAiEnabled;
Expand Down Expand Up @@ -77,6 +84,16 @@ public String getTabName() {
public void initialize() {
this.viewModel = new WebSearchTabViewModel(preferences, refAiEnabled, taskExecutor);

searchEngineName.setCellValueFactory(param -> param.getValue().nameProperty());
searchEngineName.setCellFactory(TextFieldTableCell.forTableColumn());
searchEngineName.setEditable(false);

searchEngineUrlTemplate.setCellValueFactory(param -> param.getValue().urlTemplateProperty());
searchEngineUrlTemplate.setCellFactory(TextFieldTableCell.forTableColumn());
searchEngineUrlTemplate.setEditable(true);

searchEngineTable.setItems(viewModel.getSearchEngines());

enableWebSearch.selectedProperty().bindBidirectional(viewModel.enableWebSearchProperty());
warnAboutDuplicatesOnImport.selectedProperty().bindBidirectional(viewModel.warnAboutDuplicatesOnImportProperty());
downloadLinkedOnlineFiles.selectedProperty().bindBidirectional(viewModel.shouldDownloadLinkedOnlineFiles());
Expand Down
Loading
Loading