Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.vscode/
|
||||||
87
README.md
Normal file
87
README.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Java Browser
|
||||||
|
|
||||||
|
Полнофункциональный веб-браузер на Java с JavaFX.
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- 🎨 Красивая стартовая страница с градиентным дизайном
|
||||||
|
- 🔍 Выбор поисковой системы (Google, Yandex, DuckDuckGo, Bing)
|
||||||
|
- ⭐ Система закладок с сохранением
|
||||||
|
- 📜 История посещений (до 100 записей)
|
||||||
|
- 📑 Множественные вкладки
|
||||||
|
- ⚡ Оптимизированная загрузка страниц
|
||||||
|
- 💾 Автосохранение всех данных
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- Java 17 или выше
|
||||||
|
- Maven 3.6+
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
### Через Maven (рекомендуется):
|
||||||
|
```bash
|
||||||
|
mvn clean javafx:run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Через JAR:
|
||||||
|
```bash
|
||||||
|
mvn clean package
|
||||||
|
java -jar target/java-browser-1.0-SNAPSHOT.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## Управление
|
||||||
|
|
||||||
|
### Навигация
|
||||||
|
- **◀ ▶** — Навигация назад/вперёд
|
||||||
|
- **⟳** — Обновить страницу
|
||||||
|
- **🏠** — Вернуться на стартовую страницу
|
||||||
|
- **★** — Добавить текущую страницу в закладки
|
||||||
|
- **☰** — Открыть/закрыть панель с закладками и историей
|
||||||
|
- **+** — Создать новую вкладку
|
||||||
|
|
||||||
|
### Управление окном
|
||||||
|
- **□** — Развернуть/восстановить окно на весь экран
|
||||||
|
- Окно можно свободно изменять в размере, перетаскивая края
|
||||||
|
- Стандартные кнопки Windows для сворачивания и закрытия работают как обычно
|
||||||
|
|
||||||
|
### Закрытие браузера
|
||||||
|
При закрытии окна появится диалог с тремя вариантами:
|
||||||
|
- **Свернуть** — минимизировать окно в панель задач
|
||||||
|
- **Выйти** — полностью закрыть браузер
|
||||||
|
- **Отмена** — продолжить работу
|
||||||
|
|
||||||
|
## Оптимизации
|
||||||
|
|
||||||
|
- Кэширование WebView для быстрой загрузки
|
||||||
|
- Асинхронная обработка навигации
|
||||||
|
- Оптимизированный User-Agent
|
||||||
|
- Фильтрация служебных URL в истории
|
||||||
|
- Ограничение истории до 100 записей
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── main/
|
||||||
|
│ ├── java/com/browser/
|
||||||
|
│ │ ├── BrowserApp.java # Главный класс приложения
|
||||||
|
│ │ ├── BrowserController.java # Контроллер UI и логики
|
||||||
|
│ │ ├── DataManager.java # Управление данными
|
||||||
|
│ │ └── Launcher.java # Launcher для JAR
|
||||||
|
│ └── resources/
|
||||||
|
│ └── styles/
|
||||||
|
│ └── browser.css # Стили интерфейса
|
||||||
|
└── pom.xml # Maven конфигурация
|
||||||
|
```
|
||||||
|
|
||||||
|
## Хранение данных
|
||||||
|
|
||||||
|
Все данные сохраняются в:
|
||||||
|
- Windows: `C:\Users\[username]\.javabrowser\`
|
||||||
|
- Linux/Mac: `~/.javabrowser/`
|
||||||
|
|
||||||
|
Файлы:
|
||||||
|
- `bookmarks.json` — закладки
|
||||||
|
- `history.json` — история
|
||||||
|
- `settings.json` — настройки (поисковая система)
|
||||||
44
dependency-reduced-pom.xml
Normal file
44
dependency-reduced-pom.xml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.browser</groupId>
|
||||||
|
<artifactId>java-browser</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>0.0.8</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.browser.BrowserApp</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer>
|
||||||
|
<mainClass>com.browser.Launcher</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<javafx.version>21</javafx.version>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
</properties>
|
||||||
|
</project>
|
||||||
69
pom.xml
Normal file
69
pom.xml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.browser</groupId>
|
||||||
|
<artifactId>java-browser</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<javafx.version>21</javafx.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-controls</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-web</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>0.0.8</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>com.browser.BrowserApp</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.1</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>com.browser.Launcher</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
26
src/main/java/com/browser/BrowserApp.java
Normal file
26
src/main/java/com/browser/BrowserApp.java
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package com.browser;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class BrowserApp extends Application {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) {
|
||||||
|
BrowserController controller = new BrowserController(primaryStage);
|
||||||
|
Scene scene = new Scene(controller.getRoot(), 1280, 800);
|
||||||
|
scene.getStylesheets().add(getClass().getResource("/styles/browser.css").toExternalForm());
|
||||||
|
|
||||||
|
primaryStage.setTitle("NeveTime Browser");
|
||||||
|
primaryStage.setScene(scene);
|
||||||
|
primaryStage.setResizable(true);
|
||||||
|
primaryStage.setMinWidth(600);
|
||||||
|
primaryStage.setMinHeight(600);
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
403
src/main/java/com/browser/BrowserController.java
Normal file
403
src/main/java/com/browser/BrowserController.java
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
package com.browser;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.concurrent.Worker;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.web.WebEngine;
|
||||||
|
import javafx.scene.web.WebView;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
|
||||||
|
public class BrowserController {
|
||||||
|
private BorderPane root;
|
||||||
|
private TabPane tabPane;
|
||||||
|
private TextField urlField;
|
||||||
|
private ComboBox<String> searchEngineCombo;
|
||||||
|
private VBox sidePanel;
|
||||||
|
private ListView<String> bookmarksList;
|
||||||
|
private ListView<String> historyList;
|
||||||
|
private DataManager dataManager;
|
||||||
|
private ObservableList<String> bookmarks;
|
||||||
|
private ObservableList<String> history;
|
||||||
|
private Stage stage;
|
||||||
|
|
||||||
|
private static final String[] SEARCH_ENGINES = {"Google", "DuckDuckGo"};
|
||||||
|
|
||||||
|
public BrowserController(Stage stage) {
|
||||||
|
this.stage = stage;
|
||||||
|
dataManager = new DataManager();
|
||||||
|
bookmarks = FXCollections.observableArrayList(dataManager.loadBookmarks());
|
||||||
|
history = FXCollections.observableArrayList(dataManager.loadHistory());
|
||||||
|
initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initUI() {
|
||||||
|
root = new BorderPane();
|
||||||
|
root.getStyleClass().add("browser-root");
|
||||||
|
root.setTop(createToolbar());
|
||||||
|
|
||||||
|
tabPane = new TabPane();
|
||||||
|
tabPane.getStyleClass().add("browser-tabs");
|
||||||
|
|
||||||
|
sidePanel = createSidePanel();
|
||||||
|
sidePanel.setVisible(false);
|
||||||
|
sidePanel.setManaged(false);
|
||||||
|
|
||||||
|
HBox center = new HBox(tabPane, sidePanel);
|
||||||
|
HBox.setHgrow(tabPane, Priority.ALWAYS);
|
||||||
|
root.setCenter(center);
|
||||||
|
|
||||||
|
createNewTab();
|
||||||
|
}
|
||||||
|
|
||||||
|
private HBox createToolbar() {
|
||||||
|
HBox toolbar = new HBox(8);
|
||||||
|
toolbar.setPadding(new Insets(10));
|
||||||
|
toolbar.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
toolbar.getStyleClass().add("toolbar");
|
||||||
|
|
||||||
|
Button backBtn = createNavButton("◀", "Назад");
|
||||||
|
backBtn.setOnAction(e -> goBack());
|
||||||
|
|
||||||
|
Button forwardBtn = createNavButton("▶", "Вперёд");
|
||||||
|
forwardBtn.setOnAction(e -> goForward());
|
||||||
|
|
||||||
|
Button refreshBtn = createNavButton("⟳", "Обновить");
|
||||||
|
refreshBtn.setOnAction(e -> refresh());
|
||||||
|
|
||||||
|
Button homeBtn = createNavButton("🏠", "Домой");
|
||||||
|
homeBtn.setOnAction(e -> showStartPage());
|
||||||
|
|
||||||
|
// Левый спейсер для центрирования поиска
|
||||||
|
Region leftSpacer = new Region();
|
||||||
|
HBox.setHgrow(leftSpacer, Priority.ALWAYS);
|
||||||
|
|
||||||
|
urlField = new TextField();
|
||||||
|
urlField.setPromptText("Введите URL или поисковый запрос...");
|
||||||
|
urlField.getStyleClass().add("url-field");
|
||||||
|
urlField.setPrefWidth(600);
|
||||||
|
urlField.setMaxWidth(600);
|
||||||
|
urlField.setOnAction(e -> navigate(urlField.getText()));
|
||||||
|
|
||||||
|
searchEngineCombo = new ComboBox<>();
|
||||||
|
searchEngineCombo.getItems().addAll(SEARCH_ENGINES);
|
||||||
|
searchEngineCombo.setValue(dataManager.loadSearchEngine());
|
||||||
|
searchEngineCombo.getStyleClass().add("search-combo");
|
||||||
|
searchEngineCombo.setOnAction(e -> dataManager.saveSearchEngine(searchEngineCombo.getValue()));
|
||||||
|
|
||||||
|
// Правый спейсер для центрирования поиска
|
||||||
|
Region rightSpacer = new Region();
|
||||||
|
HBox.setHgrow(rightSpacer, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Button addBookmarkBtn = createNavButton("★", "Добавить закладку");
|
||||||
|
addBookmarkBtn.setOnAction(e -> addBookmark());
|
||||||
|
|
||||||
|
Button panelBtn = createNavButton("☰", "Панель");
|
||||||
|
panelBtn.setOnAction(e -> toggleSidePanel());
|
||||||
|
|
||||||
|
Button newTabBtn = createNavButton("+", "Новая вкладка");
|
||||||
|
newTabBtn.setOnAction(e -> createNewTab());
|
||||||
|
|
||||||
|
Button maximizeBtn = createNavButton("□", "Развернуть/Восстановить");
|
||||||
|
maximizeBtn.setOnAction(e -> stage.setMaximized(!stage.isMaximized()));
|
||||||
|
|
||||||
|
toolbar.getChildren().addAll(backBtn, forwardBtn, refreshBtn, homeBtn,
|
||||||
|
leftSpacer, urlField, searchEngineCombo, rightSpacer,
|
||||||
|
addBookmarkBtn, panelBtn, newTabBtn, maximizeBtn);
|
||||||
|
|
||||||
|
return toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Button createNavButton(String text, String tooltip) {
|
||||||
|
Button btn = new Button(text);
|
||||||
|
btn.getStyleClass().add("nav-button");
|
||||||
|
btn.setTooltip(new Tooltip(tooltip));
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VBox createSidePanel() {
|
||||||
|
VBox panel = new VBox(10);
|
||||||
|
panel.setPadding(new Insets(10));
|
||||||
|
panel.setPrefWidth(280);
|
||||||
|
panel.getStyleClass().add("side-panel");
|
||||||
|
|
||||||
|
Label bookmarksLabel = new Label("📑 Закладки");
|
||||||
|
bookmarksLabel.getStyleClass().add("panel-label");
|
||||||
|
bookmarksList = new ListView<>(bookmarks);
|
||||||
|
bookmarksList.setPrefHeight(200);
|
||||||
|
bookmarksList.getStyleClass().add("panel-list");
|
||||||
|
bookmarksList.setOnMouseClicked(e -> {
|
||||||
|
if (e.getClickCount() == 2) {
|
||||||
|
String selected = bookmarksList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selected != null) navigate(selected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button deleteBookmarkBtn = new Button("Удалить закладку");
|
||||||
|
deleteBookmarkBtn.getStyleClass().add("panel-button");
|
||||||
|
deleteBookmarkBtn.setOnAction(e -> {
|
||||||
|
String selected = bookmarksList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selected != null) {
|
||||||
|
bookmarks.remove(selected);
|
||||||
|
dataManager.saveBookmarks(bookmarks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Label historyLabel = new Label("📜 История");
|
||||||
|
historyLabel.getStyleClass().add("panel-label");
|
||||||
|
historyList = new ListView<>(history);
|
||||||
|
historyList.setPrefHeight(250);
|
||||||
|
historyList.getStyleClass().add("panel-list");
|
||||||
|
historyList.setOnMouseClicked(e -> {
|
||||||
|
if (e.getClickCount() == 2) {
|
||||||
|
String selected = historyList.getSelectionModel().getSelectedItem();
|
||||||
|
if (selected != null) {
|
||||||
|
String url = selected.split(" - ")[0];
|
||||||
|
navigate(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button clearHistoryBtn = new Button("Очистить историю");
|
||||||
|
clearHistoryBtn.getStyleClass().add("panel-button");
|
||||||
|
clearHistoryBtn.setOnAction(e -> {
|
||||||
|
history.clear();
|
||||||
|
dataManager.saveHistory(history);
|
||||||
|
});
|
||||||
|
|
||||||
|
panel.getChildren().addAll(bookmarksLabel, bookmarksList, deleteBookmarkBtn,
|
||||||
|
new Separator(), historyLabel, historyList, clearHistoryBtn);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createNewTab() {
|
||||||
|
Tab tab = new Tab("Новая вкладка");
|
||||||
|
tab.setClosable(tabPane.getTabs().size() > 0);
|
||||||
|
|
||||||
|
WebView webView = new WebView();
|
||||||
|
webView.setCache(true);
|
||||||
|
webView.setContextMenuEnabled(true);
|
||||||
|
|
||||||
|
WebEngine engine = webView.getEngine();
|
||||||
|
engine.setJavaScriptEnabled(true);
|
||||||
|
engine.setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36");
|
||||||
|
|
||||||
|
engine.titleProperty().addListener((obs, oldTitle, newTitle) -> {
|
||||||
|
if (newTitle != null && !newTitle.isEmpty()) {
|
||||||
|
tab.setText(newTitle.length() > 20 ? newTitle.substring(0, 20) + "..." : newTitle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
engine.locationProperty().addListener((obs, oldLoc, newLoc) -> {
|
||||||
|
if (newLoc != null && tabPane.getSelectionModel().getSelectedItem() == tab) {
|
||||||
|
// Перехват поиска со стартовой страницы
|
||||||
|
if (newLoc.startsWith("search:")) {
|
||||||
|
try {
|
||||||
|
String query = URLDecoder.decode(newLoc.substring(7), "UTF-8");
|
||||||
|
Platform.runLater(() -> navigate(query));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else if (!newLoc.startsWith("data:")) {
|
||||||
|
urlField.setText(newLoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Индикатор загрузки
|
||||||
|
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
|
||||||
|
if (newState == Worker.State.RUNNING) {
|
||||||
|
tab.setText("Загрузка...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tab.setContent(webView);
|
||||||
|
tab.setUserData(webView);
|
||||||
|
tabPane.getTabs().add(tab);
|
||||||
|
tabPane.getSelectionModel().select(tab);
|
||||||
|
|
||||||
|
showStartPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showStartPage() {
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
webView.getEngine().loadContent(getStartPageHTML());
|
||||||
|
urlField.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStartPageHTML() {
|
||||||
|
String engine = searchEngineCombo.getValue();
|
||||||
|
return "<!DOCTYPE html><html><head><meta charset='UTF-8'><style>" +
|
||||||
|
"* { margin: 0; padding: 0; box-sizing: border-box; }" +
|
||||||
|
"body { font-family: 'Segoe UI', Arial, sans-serif; " +
|
||||||
|
"background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 100%); " +
|
||||||
|
"min-height: 100vh; display: flex; flex-direction: column; " +
|
||||||
|
"align-items: center; justify-content: center; color: white; }" +
|
||||||
|
".container { text-align: center; padding: 40px; }" +
|
||||||
|
".logo { font-size: 3.2em; font-weight: 700; margin-bottom: 8px; color: #8b5cf6; }" +
|
||||||
|
".subtitle { color: #888; margin-bottom: 40px; font-size: 1.1em; }" +
|
||||||
|
".engine-badge { display: inline-block; padding: 6px 14px; " +
|
||||||
|
"background: #252542; border: 1px solid #3a3a5a; border-radius: 16px; " +
|
||||||
|
"color: #aaa; font-size: 0.9em; margin-bottom: 35px; }" +
|
||||||
|
".search-box { display: flex; max-width: 550px; margin: 0 auto 50px; " +
|
||||||
|
"background: #1e1e32; border-radius: 12px; border: 1px solid #3a3a5a; }" +
|
||||||
|
".search-box:focus-within { border-color: #8b5cf6; }" +
|
||||||
|
".search-input { flex: 1; padding: 16px 22px; font-size: 16px; border: none; " +
|
||||||
|
"background: transparent; color: white; outline: none; }" +
|
||||||
|
".search-input::placeholder { color: #666; }" +
|
||||||
|
".search-btn { padding: 16px 28px; font-size: 14px; font-weight: 600; border: none; " +
|
||||||
|
"border-radius: 0 10px 10px 0; background: #8b5cf6; color: white; cursor: pointer; }" +
|
||||||
|
".search-btn:hover { background: #7c3aed; }" +
|
||||||
|
".quick-links { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }" +
|
||||||
|
".quick-link { display: flex; align-items: center; gap: 10px; padding: 14px 24px; background: #252542; border-radius: 10px; " +
|
||||||
|
"color: #ddd; text-decoration: none; font-size: 14px; border: 1px solid #3a3a5a; }" +
|
||||||
|
".quick-link:hover { background: #8b5cf6; border-color: #8b5cf6; color: white; }" +
|
||||||
|
".quick-link:hover svg { fill: white; }" +
|
||||||
|
".quick-link svg { width: 18px; height: 18px; fill: #aaa; }" +
|
||||||
|
".time { position: fixed; top: 30px; right: 40px; font-size: 2.5em; " +
|
||||||
|
"color: rgba(255,255,255,0.15); font-weight: 300; }" +
|
||||||
|
"</style></head><body>" +
|
||||||
|
"<div class='time' id='clock'></div>" +
|
||||||
|
"<div class='container'>" +
|
||||||
|
"<h1 class='logo'>NeveTime Browser</h1>" +
|
||||||
|
"<p class='subtitle'>Быстрый и современный браузер</p>" +
|
||||||
|
"<div class='engine-badge'>Поиск в " + engine + "</div>" +
|
||||||
|
"<form class='search-box' onsubmit='search(event)'>" +
|
||||||
|
"<input type='text' class='search-input' id='searchInput' placeholder='Поиск в интернете...' autofocus>" +
|
||||||
|
"<button type='submit' class='search-btn'>Найти</button></form>" +
|
||||||
|
"<div class='quick-links'>" +
|
||||||
|
"<a href='https://github.com' class='quick-link'><svg viewBox='0 0 24 24'><path d='M12 0C5.37 0 0 5.37 0 12c0 5.31 3.44 9.8 8.21 11.39.6.11.82-.26.82-.57v-2.23c-3.34.73-4.04-1.61-4.04-1.61-.55-1.39-1.33-1.76-1.33-1.76-1.09-.74.08-.73.08-.73 1.2.08 1.84 1.24 1.84 1.24 1.07 1.83 2.8 1.3 3.49 1 .11-.78.42-1.3.76-1.6-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.12-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 016 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.24 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22v3.29c0 .31.22.68.83.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z'/></svg>GitHub</a>" +
|
||||||
|
"<a href='https://youtube.com' class='quick-link'><svg viewBox='0 0 24 24'><path d='M23.5 6.2a3 3 0 00-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.5a3 3 0 00-2.1 2.1C0 8.1 0 12 0 12s0 3.9.5 5.8a3 3 0 002.1 2.1c1.9.5 9.4.5 9.4.5s7.5 0 9.4-.5a3 3 0 002.1-2.1c.5-1.9.5-5.8.5-5.8s0-3.9-.5-5.8zM9.5 15.6V8.4l6.3 3.6-6.3 3.6z'/></svg>YouTube</a>" +
|
||||||
|
"<a href='https://wikipedia.org' class='quick-link'><svg viewBox='0 0 24 24'><path d='M12.1 13.1c-.9 1.9-2.2 4.5-2.9 5.7-.6 1.1-1.1.9-1.5 0-1.4-3.3-4.3-9.1-5.7-12.4-.3-.6-.4-1-.6-1.1-.2-.2-.6-.2-1.1-.3-.2 0-.3-.1-.3-.2v-.5h5.4v.5c0 .1-.1.2-.2.2l-.6.1c-.5 0-.7.2-.7.4 0 .1.1.3.2.6l4.8 10.5 2.4-4.8-.5-1.1-1.7-3.3c-.1-.2-.3-.6-.4-.9-.7-1.4-.7-1.5-1.4-1.6-.2 0-.3-.1-.3-.2v-.5h4.3v.5c0 .1-.1.2-.2.2l-.3.1c-.8.1-.7.4-.1 1.4l1.6 3.3 1.8-3.5c.3-.6.2-.8.1-.9-.1-.1-.3-.2-.7-.2h-.3c-.1 0-.2-.1-.2-.2v-.5h4.4v.5c0 .1-.1.2-.2.2-.7 0-1.3.1-1.7.7l-.4.6-2.5 4.9 3.3 6.6c.6-1.2 2.5-4.9 3.2-6.4.3-.6.3-.9.3-1.1 0-.5-.5-.5-1.1-.6-.2 0-.2-.1-.2-.2v-.5h4v.5c0 .1-.1.2-.2.2-1.5.1-1.6.5-2.4 2.2l-3.7 7.4c-.3.5-.7.5-1 0l-3.5-6.9z'/></svg>Wikipedia</a>" +
|
||||||
|
"<a href='https://stackoverflow.com' class='quick-link'><svg viewBox='0 0 24 24'><path d='M15 22v-4h3v6H3v-6h3v4h9zm.5-10.2l.9 2.1-9.7 4.1-.9-2.1 9.7-4.1zm2.5-3.5l-1.4 1.6-8.2-6.9 1.4-1.6 8.2 6.9zM21 0l-1.7 1.3-6.4 8.6 1.7 1.3L21 0zM6 18v-2h9v2H6z'/></svg>Stack Overflow</a>" +
|
||||||
|
"</div></div>" +
|
||||||
|
"<script>" +
|
||||||
|
"function updateClock() { var now = new Date(); " +
|
||||||
|
"document.getElementById('clock').textContent = now.toLocaleTimeString('ru-RU', {hour: '2-digit', minute: '2-digit'}); }" +
|
||||||
|
"updateClock(); setInterval(updateClock, 1000);" +
|
||||||
|
"function search(e) { e.preventDefault(); var query = document.getElementById('searchInput').value; " +
|
||||||
|
"if (query) window.location.href = 'search:' + encodeURIComponent(query); }" +
|
||||||
|
"</script></body></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigate(String input) {
|
||||||
|
if (input == null || input.trim().isEmpty()) return;
|
||||||
|
input = input.trim();
|
||||||
|
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView == null) return;
|
||||||
|
|
||||||
|
String url;
|
||||||
|
if (input.startsWith("search:")) {
|
||||||
|
try {
|
||||||
|
String query = URLDecoder.decode(input.substring(7), "UTF-8");
|
||||||
|
url = getSearchUrl(query);
|
||||||
|
} catch (Exception e) {
|
||||||
|
url = getSearchUrl(input.substring(7));
|
||||||
|
}
|
||||||
|
} else if (input.matches("^https?://.*")) {
|
||||||
|
url = input;
|
||||||
|
} else if (input.contains(".") && !input.contains(" ")) {
|
||||||
|
url = "https://" + input;
|
||||||
|
} else {
|
||||||
|
url = getSearchUrl(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String finalUrl = url;
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
webView.getEngine().load(finalUrl);
|
||||||
|
addToHistory(finalUrl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSearchUrl(String query) {
|
||||||
|
String engine = searchEngineCombo.getValue();
|
||||||
|
try {
|
||||||
|
query = java.net.URLEncoder.encode(query, "UTF-8");
|
||||||
|
} catch (Exception e) {}
|
||||||
|
return switch (engine) {
|
||||||
|
case "Yandex" -> "https://yandex.ru/search/?text=" + query;
|
||||||
|
case "DuckDuckGo" -> "https://duckduckgo.com/?q=" + query;
|
||||||
|
case "Bing" -> "https://www.bing.com/search?q=" + query;
|
||||||
|
default -> "https://www.google.com/search?q=" + query;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToHistory(String url) {
|
||||||
|
if (url == null || url.isEmpty() || url.startsWith("data:") || url.startsWith("about:")) return;
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm dd.MM"));
|
||||||
|
String entry = url + " - " + timestamp;
|
||||||
|
history.add(0, entry);
|
||||||
|
if (history.size() > 100) history.remove(history.size() - 1);
|
||||||
|
dataManager.saveHistory(history);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBookmark() {
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
String url = webView.getEngine().getLocation();
|
||||||
|
if (url != null && !url.isEmpty() && !url.startsWith("data:") && !bookmarks.contains(url)) {
|
||||||
|
bookmarks.add(url);
|
||||||
|
dataManager.saveBookmarks(bookmarks);
|
||||||
|
showAlert("Закладка добавлена", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showAlert(String title, String message) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.INFORMATION);
|
||||||
|
alert.setTitle(title);
|
||||||
|
alert.setHeaderText(null);
|
||||||
|
alert.setContentText(message);
|
||||||
|
alert.showAndWait();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void goBack() {
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView != null && webView.getEngine().getHistory().getCurrentIndex() > 0) {
|
||||||
|
webView.getEngine().getHistory().go(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void goForward() {
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
var hist = webView.getEngine().getHistory();
|
||||||
|
if (hist.getCurrentIndex() < hist.getEntries().size() - 1) {
|
||||||
|
hist.go(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh() {
|
||||||
|
WebView webView = getCurrentWebView();
|
||||||
|
if (webView != null) webView.getEngine().reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void toggleSidePanel() {
|
||||||
|
sidePanel.setVisible(!sidePanel.isVisible());
|
||||||
|
sidePanel.setManaged(sidePanel.isVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebView getCurrentWebView() {
|
||||||
|
Tab tab = tabPane.getSelectionModel().getSelectedItem();
|
||||||
|
return tab != null ? (WebView) tab.getUserData() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BorderPane getRoot() { return root; }
|
||||||
|
}
|
||||||
82
src/main/java/com/browser/DataManager.java
Normal file
82
src/main/java/com/browser/DataManager.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package com.browser;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DataManager {
|
||||||
|
private static final String DATA_DIR = System.getProperty("user.home") + "/.javabrowser";
|
||||||
|
private static final String BOOKMARKS_FILE = DATA_DIR + "/bookmarks.json";
|
||||||
|
private static final String HISTORY_FILE = DATA_DIR + "/history.json";
|
||||||
|
private static final String SETTINGS_FILE = DATA_DIR + "/settings.json";
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
public DataManager() {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(Paths.get(DATA_DIR));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> loadBookmarks() {
|
||||||
|
return loadList(BOOKMARKS_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveBookmarks(List<String> bookmarks) {
|
||||||
|
saveList(BOOKMARKS_FILE, bookmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> loadHistory() {
|
||||||
|
return loadList(HISTORY_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveHistory(List<String> history) {
|
||||||
|
saveList(HISTORY_FILE, history);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String loadSearchEngine() {
|
||||||
|
try {
|
||||||
|
String content = Files.readString(Paths.get(SETTINGS_FILE));
|
||||||
|
Settings settings = gson.fromJson(content, Settings.class);
|
||||||
|
return settings != null && settings.searchEngine != null ? settings.searchEngine : "Google";
|
||||||
|
} catch (IOException e) {
|
||||||
|
return "Google";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSearchEngine(String engine) {
|
||||||
|
try {
|
||||||
|
Settings settings = new Settings();
|
||||||
|
settings.searchEngine = engine;
|
||||||
|
Files.writeString(Paths.get(SETTINGS_FILE), gson.toJson(settings));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> loadList(String file) {
|
||||||
|
try {
|
||||||
|
String content = Files.readString(Paths.get(file));
|
||||||
|
List<String> list = gson.fromJson(content, new TypeToken<List<String>>(){}.getType());
|
||||||
|
return list != null ? new ArrayList<>(list) : new ArrayList<>();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveList(String file, List<String> list) {
|
||||||
|
try {
|
||||||
|
Files.writeString(Paths.get(file), gson.toJson(list));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Settings {
|
||||||
|
String searchEngine;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/main/java/com/browser/Launcher.java
Normal file
8
src/main/java/com/browser/Launcher.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package com.browser;
|
||||||
|
|
||||||
|
// Launcher class needed for JAR execution with JavaFX
|
||||||
|
public class Launcher {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BrowserApp.main(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
184
src/main/resources/styles/browser.css
Normal file
184
src/main/resources/styles/browser.css
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/* Lightweight Modern Theme */
|
||||||
|
.browser-root {
|
||||||
|
-fx-background-color: #0f0f1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-padding: 10px 16px;
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
-fx-border-width: 0 0 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-text-fill: #e0e0e0;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-padding: 8px 14px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-border-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:pressed {
|
||||||
|
-fx-background-color: #7c3aed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-field {
|
||||||
|
-fx-background-color: #1e1e32;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-prompt-text-fill: #666;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-padding: 10px 20px;
|
||||||
|
-fx-background-radius: 12px;
|
||||||
|
-fx-border-radius: 12px;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-field:focused {
|
||||||
|
-fx-border-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-padding: 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .list-cell {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-view {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-cell {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-padding: 10px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-cell:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs {
|
||||||
|
-fx-background-color: #0f0f1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab-header-area {
|
||||||
|
-fx-background-color: #12121f;
|
||||||
|
-fx-padding: 6px 6px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab-header-background {
|
||||||
|
-fx-background-color: #12121f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-background-radius: 8px 8px 0 0;
|
||||||
|
-fx-padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab:selected {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab .tab-label {
|
||||||
|
-fx-text-fill: #aaa;
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab:selected .tab-label {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-panel {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-label {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 15px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list {
|
||||||
|
-fx-background-color: #1e1e32;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: #ccc;
|
||||||
|
-fx-padding: 10px 12px;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell:hover {
|
||||||
|
-fx-background-color: #2a2a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell:selected {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-padding: 10px 16px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:hover {
|
||||||
|
-fx-background-color: #7c3aed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator .line {
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .thumb {
|
||||||
|
-fx-background-color: #3a3a5a;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .thumb:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .increment-button, .scroll-bar .decrement-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-padding: 8px 12px;
|
||||||
|
-fx-background-radius: 6px;
|
||||||
|
}
|
||||||
BIN
target/classes/com/browser/BrowserApp.class
Normal file
BIN
target/classes/com/browser/BrowserApp.class
Normal file
Binary file not shown.
BIN
target/classes/com/browser/BrowserController.class
Normal file
BIN
target/classes/com/browser/BrowserController.class
Normal file
Binary file not shown.
BIN
target/classes/com/browser/DataManager$1.class
Normal file
BIN
target/classes/com/browser/DataManager$1.class
Normal file
Binary file not shown.
BIN
target/classes/com/browser/DataManager$Settings.class
Normal file
BIN
target/classes/com/browser/DataManager$Settings.class
Normal file
Binary file not shown.
BIN
target/classes/com/browser/DataManager.class
Normal file
BIN
target/classes/com/browser/DataManager.class
Normal file
Binary file not shown.
BIN
target/classes/com/browser/Launcher.class
Normal file
BIN
target/classes/com/browser/Launcher.class
Normal file
Binary file not shown.
184
target/classes/styles/browser.css
Normal file
184
target/classes/styles/browser.css
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
/* Lightweight Modern Theme */
|
||||||
|
.browser-root {
|
||||||
|
-fx-background-color: #0f0f1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-padding: 10px 16px;
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
-fx-border-width: 0 0 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-text-fill: #e0e0e0;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-padding: 8px 14px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-border-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:pressed {
|
||||||
|
-fx-background-color: #7c3aed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-field {
|
||||||
|
-fx-background-color: #1e1e32;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-prompt-text-fill: #666;
|
||||||
|
-fx-font-size: 14px;
|
||||||
|
-fx-padding: 10px 20px;
|
||||||
|
-fx-background-radius: 12px;
|
||||||
|
-fx-border-radius: 12px;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url-field:focused {
|
||||||
|
-fx-border-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-border-color: #3a3a5a;
|
||||||
|
-fx-border-radius: 8px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-padding: 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .list-cell {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-view {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-cell {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-padding: 10px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-combo .combo-box-popup .list-cell:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs {
|
||||||
|
-fx-background-color: #0f0f1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab-header-area {
|
||||||
|
-fx-background-color: #12121f;
|
||||||
|
-fx-padding: 6px 6px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab-header-background {
|
||||||
|
-fx-background-color: #12121f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-background-radius: 8px 8px 0 0;
|
||||||
|
-fx-padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab:selected {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab .tab-label {
|
||||||
|
-fx-text-fill: #aaa;
|
||||||
|
-fx-font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.browser-tabs .tab:selected .tab-label {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-panel {
|
||||||
|
-fx-background-color: #1a1a2e;
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-label {
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 15px;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
-fx-padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list {
|
||||||
|
-fx-background-color: #1e1e32;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: #ccc;
|
||||||
|
-fx-padding: 10px 12px;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell:hover {
|
||||||
|
-fx-background-color: #2a2a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-list .list-cell:selected {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-padding: 10px 16px;
|
||||||
|
-fx-background-radius: 8px;
|
||||||
|
-fx-cursor: hand;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-button:hover {
|
||||||
|
-fx-background-color: #7c3aed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator .line {
|
||||||
|
-fx-border-color: #2a2a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .thumb {
|
||||||
|
-fx-background-color: #3a3a5a;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .thumb:hover {
|
||||||
|
-fx-background-color: #8b5cf6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-bar .increment-button, .scroll-bar .decrement-button {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
-fx-background-color: #252542;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 12px;
|
||||||
|
-fx-padding: 8px 12px;
|
||||||
|
-fx-background-radius: 6px;
|
||||||
|
}
|
||||||
BIN
target/java-browser-1.0-SNAPSHOT.jar
Normal file
BIN
target/java-browser-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
3
target/maven-archiver/pom.properties
Normal file
3
target/maven-archiver/pom.properties
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
artifactId=java-browser
|
||||||
|
groupId=com.browser
|
||||||
|
version=1.0-SNAPSHOT
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
com\browser\Launcher.class
|
||||||
|
com\browser\BrowserApp.class
|
||||||
|
com\browser\DataManager$Settings.class
|
||||||
|
com\browser\BrowserController.class
|
||||||
|
com\browser\DataManager$1.class
|
||||||
|
com\browser\DataManager.class
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
d:\Desktop\adadad\src\main\java\com\browser\BrowserApp.java
|
||||||
|
d:\Desktop\adadad\src\main\java\com\browser\BrowserController.java
|
||||||
|
d:\Desktop\adadad\src\main\java\com\browser\DataManager.java
|
||||||
|
d:\Desktop\adadad\src\main\java\com\browser\Launcher.java
|
||||||
BIN
target/original-java-browser-1.0-SNAPSHOT.jar
Normal file
BIN
target/original-java-browser-1.0-SNAPSHOT.jar
Normal file
Binary file not shown.
Reference in New Issue
Block a user