feat: browser list mvp
This commit is contained in:
+13
-16
@@ -38,8 +38,6 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
|||||||
в `logos/` уже лежат отобранные PNG (chrome, firefox, opera, yandex, brave)
|
в `logos/` уже лежат отобранные PNG (chrome, firefox, opera, yandex, brave)
|
||||||
- [x] `.gitignore`: `tools/out/`, `*.log`, `coldstart.txt`
|
- [x] `.gitignore`: `tools/out/`, `*.log`, `coldstart.txt`
|
||||||
|
|
||||||
Коммит: `chore: repo hygiene`
|
|
||||||
|
|
||||||
## Этап B — утилиты в `tools/`
|
## Этап B — утилиты в `tools/`
|
||||||
|
|
||||||
Все скрипты — PowerShell, без сторонних зависимостей.
|
Все скрипты — PowerShell, без сторонних зависимостей.
|
||||||
@@ -55,8 +53,6 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
|||||||
`Stop-Process`. Решает проблему «GUI-процесс блокирует терминал агента»
|
`Stop-Process`. Решает проблему «GUI-процесс блокирует терминал агента»
|
||||||
- [ ] Опционально: `slint-lsp` / VS Code extension — ей для редактора, агент не зависит
|
- [ ] Опционально: `slint-lsp` / VS Code extension — ей для редактора, агент не зависит
|
||||||
|
|
||||||
Коммит: `feat(tools): visual dev loop for ai agents`
|
|
||||||
|
|
||||||
Проверка цикла: `render-refs` → `run-and-shot` → оба PNG существуют, агент их прочитал.
|
Проверка цикла: `render-refs` → `run-and-shot` → оба PNG существуют, агент их прочитал.
|
||||||
|
|
||||||
## Этап C — возвращение к no-frame окну (цель дизайна)
|
## Этап C — возвращение к no-frame окну (цель дизайна)
|
||||||
@@ -78,19 +74,22 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
|||||||
|
|
||||||
Требует ручной проверки: перетаскивание мышью, Esc, центрирование на активном мониторе.
|
Требует ручной проверки: перетаскивание мышью, Esc, центрирование на активном мониторе.
|
||||||
|
|
||||||
Коммит: `feat(ui): frameless transparent window with drag`
|
|
||||||
|
|
||||||
## Этап D — список браузеров (MVP-ядро)
|
## Этап D — список браузеров (MVP-ядро)
|
||||||
|
|
||||||
- [ ] Раскомментировать/восстановить ListView + BrowserItem в main.slint (было закомментировано)
|
- [x] ListView + BrowserItem восстановлены в main.slint (список растягивается, карточки 84px)
|
||||||
- [ ] Логотипы: PNG из `logos/` вместо emoji (поле icon → путь к png)
|
- [x] Логотипы: PNG из `logos/`; `BrowserConfig.icon` теперь `image` — грузится в Rust
|
||||||
- [ ] Мок-данные 4 браузеров в VecModel (уже были в старом src/main.rs)
|
через `Image::load_from_path` (@image-url не умеет рантайм-пути; serde-derive
|
||||||
- [ ] Выбор кликом + подсветка selected; hover-стили
|
из types.slint убран — вернём в этапе E строкой в TOML)
|
||||||
- [ ] Кнопка «Открыть» → callback → Rust: `std::process::Command(path).args(flags).arg(url)`
|
- [x] Мок-данные 4 браузеров; selected-index = первый is_default
|
||||||
- [ ] URL из argv: `std::env::args().nth(1)`
|
- [x] Выбор кликом + ring-подсветка + hover; галочка — SVG (`icons/check.svg`),
|
||||||
- [ ] Закрытие окна после запуска
|
т.к. глифа U+2713 в шрифтах нет; добавлен токен `Theme.surface-item`
|
||||||
|
- [x] Чекбоксы «Запомнить выбор…»/«Всегда спрашивать» — кастомный CheckRow по макету
|
||||||
|
- [x] «Открыть» → `Command::new(path).args(flags).arg(url)`; при успехе окно скрывается
|
||||||
|
(loop завершается), при ошибке — сообщение в stderr
|
||||||
|
- [x] URL из argv (`std::env::args().nth(1)`)
|
||||||
|
|
||||||
Коммит: `feat: browser list mvp`
|
Примечание: мок-пути (firefox.exe, chrome.exe, zen.exe) вне PATH — запуск вернёт
|
||||||
|
ошибку в stderr до этапа E (автодетект/конфиг). Клик-выбор проверяется визуально.
|
||||||
|
|
||||||
## Этап E — конфиг и настройки
|
## Этап E — конфиг и настройки
|
||||||
|
|
||||||
@@ -100,8 +99,6 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
|||||||
- [ ] Автодетект установленных браузеров (реестр Windows) как fallback при отсутствии конфига
|
- [ ] Автодетект установленных браузеров (реестр Windows) как fallback при отсутствии конфига
|
||||||
- [ ] Чтение флагов из .lnk ярлыков (крейс `lnk`) — по желанию
|
- [ ] Чтение флагов из .lnk ярлыков (крейс `lnk`) — по желанию
|
||||||
|
|
||||||
Коммит: `feat: config + settings page`
|
|
||||||
|
|
||||||
## Этап F — релиз
|
## Этап F — релиз
|
||||||
|
|
||||||
- [ ] `[profile.release]`: lto, codegen-units=1, strip
|
- [ ] `[profile.release]`: lto, codegen-units=1, strip
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<polyline points="20 6 9 17 4 12" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 200 B |
+61
-19
@@ -1,43 +1,55 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use i_slint_backend_winit::WinitWindowAccessor;
|
use i_slint_backend_winit::WinitWindowAccessor;
|
||||||
|
use slint::Model;
|
||||||
|
|
||||||
use crate::winit::center_window;
|
use crate::winit::center_window;
|
||||||
mod winit;
|
mod winit;
|
||||||
|
|
||||||
slint::include_modules!();
|
slint::include_modules!();
|
||||||
|
|
||||||
|
fn load_icon(rel: &str) -> slint::Image {
|
||||||
|
slint::Image::load_from_path(std::path::Path::new(rel)).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
fn init() -> State {
|
fn init() -> State {
|
||||||
let browser_model = Rc::new(slint::VecModel::<BrowserConfig>::from(vec![
|
let browsers = vec![
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
icon: "🦊".into(),
|
icon: load_icon("logos/firefox_48x48.png"),
|
||||||
name: "Work browser".into(),
|
name: "Work browser".into(),
|
||||||
path: "firefox.exe".into(),
|
path: "firefox.exe".into(),
|
||||||
flags: "--profile work".into(),
|
flags: "--profile work".into(),
|
||||||
is_default: true,
|
is_default: true,
|
||||||
},
|
},
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
icon: "🔥".into(),
|
icon: load_icon("logos/chrome_48x48.png"),
|
||||||
name: "Ku6epXBOCTuK".into(),
|
name: "Ku6epXBOCTuK".into(),
|
||||||
path: "chrome.exe".into(),
|
path: "chrome.exe".into(),
|
||||||
flags: "--profile personal".into(),
|
flags: "--profile personal".into(),
|
||||||
is_default: false,
|
is_default: false,
|
||||||
},
|
},
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
icon: "🌐".into(),
|
icon: load_icon("logos/opera_48x48.png"),
|
||||||
name: "Chrome".into(),
|
name: "Chrome".into(),
|
||||||
path: "chrome.exe".into(),
|
path: "chrome.exe".into(),
|
||||||
flags: "".into(),
|
flags: "".into(),
|
||||||
is_default: false,
|
is_default: false,
|
||||||
},
|
},
|
||||||
BrowserConfig {
|
BrowserConfig {
|
||||||
icon: "🧘".into(),
|
icon: load_icon("icons/globe.svg"),
|
||||||
name: "Zen Browser".into(),
|
name: "Zen Browser".into(),
|
||||||
path: "zen.exe".into(),
|
path: "zen.exe".into(),
|
||||||
flags: "".into(),
|
flags: "".into(),
|
||||||
is_default: false,
|
is_default: false,
|
||||||
},
|
},
|
||||||
]));
|
];
|
||||||
|
|
||||||
|
let default_index = browsers
|
||||||
|
.iter()
|
||||||
|
.position(|b| b.is_default)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let browser_model = Rc::new(slint::VecModel::<BrowserConfig>::from(browsers));
|
||||||
|
|
||||||
let main_window = MainWindow::new().unwrap();
|
let main_window = MainWindow::new().unwrap();
|
||||||
|
|
||||||
@@ -45,30 +57,60 @@ fn init() -> State {
|
|||||||
main_window.global::<Theme>().set_is_dark(false);
|
main_window.global::<Theme>().set_is_dark(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
main_window.set_current_url("ku6epxboctuk.github.io".into());
|
let url = std::env::args()
|
||||||
|
.nth(1)
|
||||||
|
.unwrap_or_else(|| "ku6epxboctuk.github.io".into());
|
||||||
|
main_window.set_current_url(url.into());
|
||||||
|
main_window.set_selected_index(default_index as i32);
|
||||||
main_window.set_remember_choice(true);
|
main_window.set_remember_choice(true);
|
||||||
main_window.set_always_ask(true);
|
main_window.set_always_ask(true);
|
||||||
|
|
||||||
main_window.on_browser_selected({
|
main_window.on_browser_selected({
|
||||||
|
let main_window = main_window.clone_strong();
|
||||||
|
let browser_model = browser_model.clone();
|
||||||
move |browser: BrowserConfig| {
|
move |browser: BrowserConfig| {
|
||||||
println!("Selected browser: {:?} {:?}", browser.path, browser.flags);
|
let index = (0..browser_model.row_count())
|
||||||
|
.find(|&i| {
|
||||||
|
browser_model
|
||||||
|
.row_data(i)
|
||||||
|
.is_some_and(|b| b.name == browser.name)
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
main_window.set_selected_index(index as i32);
|
||||||
|
println!(
|
||||||
|
"Selected [{}]: {:?} {:?}",
|
||||||
|
index, browser.path, browser.flags
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
main_window.on_toggle_remember(move |checked| {
|
main_window.on_open_clicked({
|
||||||
println!("Remember choice: {}", checked);
|
let main_window = main_window.clone_strong();
|
||||||
});
|
let browser_model = browser_model.clone();
|
||||||
|
move || {
|
||||||
|
let index = main_window.get_selected_index().max(0) as usize;
|
||||||
|
let Some(browser) = browser_model.row_data(index) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let url = main_window.get_current_url().to_string();
|
||||||
|
|
||||||
main_window.on_toggle_always_ask(move |checked| {
|
println!(
|
||||||
println!("Always ask: {}", checked);
|
"Launching [{}]: {:?} {:?} {}",
|
||||||
});
|
index, browser.path, browser.flags, url
|
||||||
|
);
|
||||||
|
|
||||||
main_window.on_settings_clicked(move || {
|
let mut cmd = std::process::Command::new(browser.path.to_string());
|
||||||
println!("Settings clicked");
|
if !browser.flags.is_empty() {
|
||||||
});
|
cmd.args(browser.flags.split_whitespace());
|
||||||
|
}
|
||||||
|
|
||||||
main_window.on_open_clicked(move || {
|
match cmd.arg(url).spawn() {
|
||||||
println!("Open clicked");
|
Ok(_) => {
|
||||||
|
let _ = main_window.window().hide();
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("launch failed: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
main_window.on_request_drag({
|
main_window.on_request_drag({
|
||||||
|
|||||||
+82
-68
@@ -1,98 +1,112 @@
|
|||||||
import {
|
import { Theme } from "./theme.slint";
|
||||||
HorizontalBox,
|
|
||||||
VerticalBox,
|
|
||||||
} from "std-widgets.slint";
|
|
||||||
import { BrowserConfig } from "./types.slint";
|
|
||||||
|
|
||||||
export component BrowserItem {
|
export component BrowserItem inherits Rectangle {
|
||||||
in property <BrowserConfig> browser;
|
in property <image> browser-icon;
|
||||||
|
in property <string> browser-name;
|
||||||
|
in property <bool> is-default;
|
||||||
in property <bool> selected;
|
in property <bool> selected;
|
||||||
|
callback clicked;
|
||||||
|
|
||||||
height: 88px;
|
height: 84px;
|
||||||
width: 100%;
|
background: transparent;
|
||||||
|
|
||||||
Rectangle {
|
panel := Rectangle {
|
||||||
width: 100%;
|
y: 4px;
|
||||||
height: 100%;
|
height: 76px;
|
||||||
border-radius: 16px;
|
border-radius: 12px;
|
||||||
background: selected ? #1e293b : #0f172a;
|
background: touch.has-hover ? Theme.surface-hover
|
||||||
|
: root.selected ? Theme.surface-elevated
|
||||||
|
: Theme.surface-item;
|
||||||
|
border-width: root.selected ? 2px : 1px;
|
||||||
|
border-color: root.selected ? #3b82f680 : transparent;
|
||||||
|
|
||||||
HorizontalBox {
|
touch := TouchArea {
|
||||||
padding: 16px;
|
mouse-cursor: pointer;
|
||||||
|
clicked => {
|
||||||
|
root.clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalLayout {
|
||||||
|
padding: 14px;
|
||||||
spacing: 16px;
|
spacing: 16px;
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
vertical-stretch: 0;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
background: #1e293b;
|
background: Theme.surface-elevated;
|
||||||
|
|
||||||
Text {
|
Image {
|
||||||
width: 100%;
|
x: (parent.width - self.width) / 2;
|
||||||
height: 100%;
|
y: (parent.height - self.height) / 2;
|
||||||
horizontal-alignment: center;
|
width: 30px;
|
||||||
vertical-alignment: center;
|
height: 30px;
|
||||||
text: browser.icon;
|
source: root.browser-icon;
|
||||||
font-size: 24px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticalBox {
|
VerticalLayout {
|
||||||
alignment: center;
|
alignment: center;
|
||||||
spacing: 4px;
|
|
||||||
|
|
||||||
Text {
|
HorizontalLayout {
|
||||||
text: browser.name;
|
spacing: 8px;
|
||||||
color: #f8fafc;
|
alignment: start;
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (browser.is-default): Rectangle {
|
|
||||||
height: 22px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #3b82f6;
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: "По умолчанию";
|
text: root.browser-name;
|
||||||
color: #60a5fa;
|
color: Theme.text-main;
|
||||||
font-size: 12px;
|
font-size: 16px;
|
||||||
}
|
font-weight: 500;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VerticalBox {
|
|
||||||
alignment: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
|
|
||||||
if (selected): Rectangle {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 12px;
|
|
||||||
background: #3b82f6;
|
|
||||||
|
|
||||||
Text {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
horizontal-alignment: center;
|
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
text: "✓";
|
}
|
||||||
color: #ffffff;
|
|
||||||
font-size: 14px;
|
if root.is-default: Rectangle {
|
||||||
|
height: 22px;
|
||||||
|
border-radius: 11px;
|
||||||
|
background: #3b82f620;
|
||||||
|
|
||||||
|
HorizontalLayout {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "По умолчанию";
|
||||||
|
color: #60a5fa;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-alignment: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!selected): Text {
|
Rectangle {
|
||||||
|
width: 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: root.selected ? #3b82f6 : Theme.surface-elevated;
|
||||||
|
|
||||||
|
Image {
|
||||||
|
x: (parent.width - self.width) / 2;
|
||||||
|
y: (parent.height - self.height) / 2;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
source: @image-url("../icons/check.svg");
|
||||||
|
colorize: #ffffff;
|
||||||
|
visible: root.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
horizontal-alignment: center;
|
horizontal-alignment: center;
|
||||||
vertical-alignment: center;
|
vertical-alignment: center;
|
||||||
text: ">";
|
text: "›";
|
||||||
color: #64748b;
|
color: Theme.text-muted;
|
||||||
font-size: 18px;
|
font-size: 14px;
|
||||||
|
visible: !root.selected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-28
@@ -7,6 +7,53 @@ import { Theme } from "theme.slint";
|
|||||||
|
|
||||||
export { Theme }
|
export { Theme }
|
||||||
|
|
||||||
|
component CheckRow inherits Rectangle {
|
||||||
|
in property <string> label;
|
||||||
|
in-out property <bool> checked;
|
||||||
|
callback toggled();
|
||||||
|
|
||||||
|
height: 28px;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
touch := TouchArea {
|
||||||
|
mouse-cursor: pointer;
|
||||||
|
clicked => {
|
||||||
|
root.checked = !root.checked;
|
||||||
|
root.toggled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalLayout {
|
||||||
|
spacing: 12px;
|
||||||
|
padding-left: 4px;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 20px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: root.checked ? #3b82f6 : transparent;
|
||||||
|
border-width: 2px;
|
||||||
|
border-color: root.checked ? #3b82f6 : Theme.border-main;
|
||||||
|
|
||||||
|
Image {
|
||||||
|
x: (parent.width - self.width) / 2;
|
||||||
|
y: (parent.height - self.height) / 2;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
source: @image-url("../icons/check.svg");
|
||||||
|
colorize: #ffffff;
|
||||||
|
visible: root.checked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: root.label;
|
||||||
|
color: Theme.text-muted;
|
||||||
|
font-size: 14px;
|
||||||
|
vertical-alignment: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export component MainWindow inherits Window {
|
export component MainWindow inherits Window {
|
||||||
title: "bropicker";
|
title: "bropicker";
|
||||||
in property <[BrowserConfig]> browser-model: [];
|
in property <[BrowserConfig]> browser-model: [];
|
||||||
@@ -65,37 +112,44 @@ export component MainWindow inherits Window {
|
|||||||
request-drag => root.request-drag();
|
request-drag => root.request-drag();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListView {
|
ListView {
|
||||||
// max-height: 400px;
|
vertical-stretch: 1;
|
||||||
|
|
||||||
// for browser[i] in root.browser-model: BrowserItem {
|
for browser[i] in root.browser-model: BrowserItem {
|
||||||
// browser: browser;
|
browser-icon: browser.icon;
|
||||||
// selected: i == root.selected-index;
|
browser-name: browser.name;
|
||||||
// }
|
is-default: browser.is-default;
|
||||||
// }
|
selected: i == root.selected-index;
|
||||||
|
clicked => {
|
||||||
// spacing: 12px;
|
root.browser-selected(browser);
|
||||||
// padding-left: 4px;
|
}
|
||||||
|
}
|
||||||
// CheckBox {
|
}
|
||||||
// text: "Запомнить выбор для " + root.current-url;
|
|
||||||
// checked <=> root.remember-choice;
|
|
||||||
// toggled => {
|
|
||||||
// root.toggle-remember(root.remember-choice);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// CheckBox {
|
|
||||||
// text: "Всегда спрашивать";
|
|
||||||
// checked <=> root.always-ask;
|
|
||||||
// toggled => {
|
|
||||||
// root.toggle-always-ask(root.always-ask);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
vertical-stretch: 1;
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalLayout {
|
||||||
|
spacing: 8px;
|
||||||
|
padding-left: 4px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
|
||||||
|
CheckRow {
|
||||||
|
label: "Запомнить выбор для " + root.current-url;
|
||||||
|
checked <=> root.remember-choice;
|
||||||
|
toggled => {
|
||||||
|
root.toggle-remember(root.remember-choice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckRow {
|
||||||
|
label: "Всегда спрашивать";
|
||||||
|
checked <=> root.always-ask;
|
||||||
|
toggled => {
|
||||||
|
root.toggle-always-ask(root.always-ask);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Footer {
|
Footer {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export global Theme {
|
|||||||
|
|
||||||
out property <brush> surface-main: is-dark ? #0f172a : #ffffff;
|
out property <brush> surface-main: is-dark ? #0f172a : #ffffff;
|
||||||
// out property <brush> surface-control: is-dark ? #1e293b4d : #ffffff;
|
// out property <brush> surface-control: is-dark ? #1e293b4d : #ffffff;
|
||||||
|
out property <brush> surface-item: is-dark ? #1e293b4d : #f8fafc;
|
||||||
out property <brush> surface-elevated: is-dark ? #1e293b80 : #f8fafc;
|
out property <brush> surface-elevated: is-dark ? #1e293b80 : #f8fafc;
|
||||||
out property <brush> surface-hover: is-dark ? #1e293b : #ffffff;
|
out property <brush> surface-hover: is-dark ? #1e293b : #ffffff;
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
@rust-attr(derive(serde::Serialize, serde::Deserialize))
|
|
||||||
export struct BrowserConfig {
|
export struct BrowserConfig {
|
||||||
icon: string,
|
icon: image,
|
||||||
name: string,
|
name: string,
|
||||||
path: string,
|
path: string,
|
||||||
flags: string,
|
flags: string,
|
||||||
is-default: bool,
|
is-default: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user