Use slider toggles for boolean controls
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
- Follow the Codex macOS dark UI: near-black backgrounds, subtly raised surfaces, quiet borders, rounded corners, and restrained contrast.
|
- Follow the Codex macOS dark UI: near-black backgrounds, subtly raised surfaces, quiet borders, rounded corners, and restrained contrast.
|
||||||
- Present overviews as one bordered panel with divided rows, a clear primary label, muted supporting text, and compact trailing actions.
|
- Present overviews as one bordered panel with divided rows, a clear primary label, muted supporting text, and compact trailing actions.
|
||||||
- Reuse the shared surface and action-button styles in `src/app/view.rs`; keep button shape, padding, typography, hover, and disabled states consistent across windows. Destructive actions may differ by color only, while navigation controls may remain flat.
|
- Reuse the shared surface and action-button styles in `src/app/view.rs`; keep button shape, padding, typography, hover, and disabled states consistent across windows. Destructive actions may differ by color only, while navigation controls may remain flat.
|
||||||
|
- Render boolean controls as macOS-style slider toggles through the shared `toggle` helper, not checkboxes.
|
||||||
- Prefer generous spacing and clear hierarchy over decoration; avoid one-off colors, card stacks, oversized controls, and screen-specific button styling.
|
- Prefer generous spacing and clear hierarchy over decoration; avoid one-off colors, card stacks, oversized controls, and screen-specific button styling.
|
||||||
|
|
||||||
## Commit gates
|
## Commit gates
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ use crate::model::{
|
|||||||
use crate::settings::{GIB, REASONING_MODES};
|
use crate::settings::{GIB, REASONING_MODES};
|
||||||
use iced::theme::{Palette, palette};
|
use iced::theme::{Palette, palette};
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
Button, Space, Svg, Tooltip, button, checkbox, column, container, image, markdown, mouse_area,
|
Button, Space, Svg, Tooltip, button, column, container, image, markdown, mouse_area, opaque,
|
||||||
opaque, pick_list, progress_bar, row, rule, scrollable, slider, stack, svg, text, text_editor,
|
pick_list, progress_bar, row, rule, scrollable, slider, stack, svg, text, text_editor,
|
||||||
text_input, tooltip,
|
text_input, toggler, tooltip,
|
||||||
};
|
};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme, window};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme, window};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@@ -984,6 +984,10 @@ fn danger_button<'a>(content: impl Into<Element<'a, Message>>) -> Button<'a, Mes
|
|||||||
button(content).padding([8, 14]).style(danger_button_style)
|
button(content).padding([8, 14]).style(danger_button_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle<'a>(checked: bool) -> iced::widget::Toggler<'a, Message> {
|
||||||
|
toggler(checked).size(18).spacing(8)
|
||||||
|
}
|
||||||
|
|
||||||
fn action_button_style(_: &Theme, status: button::Status) -> button::Style {
|
fn action_button_style(_: &Theme, status: button::Status) -> button::Style {
|
||||||
let (background, text_color) = match status {
|
let (background, text_color) = match status {
|
||||||
button::Status::Active | button::Status::Pressed => {
|
button::Status::Active | button::Status::Pressed => {
|
||||||
|
|||||||
@@ -495,10 +495,10 @@ impl App {
|
|||||||
&surface.data,
|
&surface.data,
|
||||||
context,
|
context,
|
||||||
));
|
));
|
||||||
let checkbox = checkbox(checked).label(label);
|
let control = toggle(checked).label(label);
|
||||||
if let Some(path) = local_path(binding_path(binding), context_path.as_deref()) {
|
if let Some(path) = local_path(binding_path(binding), context_path.as_deref()) {
|
||||||
let surface_id = surface.id.clone();
|
let surface_id = surface.id.clone();
|
||||||
checkbox
|
control
|
||||||
.on_toggle(move |value| {
|
.on_toggle(move |value| {
|
||||||
Message::A2uiDataChanged(
|
Message::A2uiDataChanged(
|
||||||
surface_id.clone(),
|
surface_id.clone(),
|
||||||
@@ -508,7 +508,7 @@ impl App {
|
|||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
checkbox.into()
|
control.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"Slider" => {
|
"Slider" => {
|
||||||
@@ -617,7 +617,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
chips = chips.push(choice);
|
chips = chips.push(choice);
|
||||||
} else if multiple {
|
} else if multiple {
|
||||||
let mut choice = checkbox(is_selected).label(label);
|
let mut choice = toggle(is_selected).label(label);
|
||||||
if let Some(path) = path.clone() {
|
if let Some(path) = path.clone() {
|
||||||
let surface_id = surface.id.clone();
|
let surface_id = surface.id.clone();
|
||||||
let option_value = option_value.clone();
|
let option_value = option_value.clone();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ impl App {
|
|||||||
file_list = file_list.push(rule::horizontal(1)).push(
|
file_list = file_list.push(rule::horizontal(1)).push(
|
||||||
container(
|
container(
|
||||||
row![
|
row![
|
||||||
checkbox(selected).on_toggle({
|
toggle(selected).on_toggle({
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
move |_| Message::ToggleGitFile(path.clone())
|
move |_| Message::ToggleGitFile(path.clone())
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ impl App {
|
|||||||
.supports_dspark()
|
.supports_dspark()
|
||||||
.then_some(Message::PreferenceLegacyMtpChanged);
|
.then_some(Message::PreferenceLegacyMtpChanged);
|
||||||
let legacy_mtp = hint(
|
let legacy_mtp = hint(
|
||||||
checkbox(self.preference_draft.legacy_mtp_enabled)
|
toggle(self.preference_draft.legacy_mtp_enabled)
|
||||||
.label("Enable legacy MTP for this model")
|
.label("Enable legacy MTP for this model")
|
||||||
.on_toggle_maybe(legacy_mtp_toggle),
|
.on_toggle_maybe(legacy_mtp_toggle),
|
||||||
"Uses the managed one-stage MTP support GGUF. The target model verifies every drafted token; it is mutually exclusive with DSpark.",
|
"Uses the managed one-stage MTP support GGUF. The target model verifies every drafted token; it is mutually exclusive with DSpark.",
|
||||||
@@ -20,7 +20,7 @@ impl App {
|
|||||||
.supports_dspark()
|
.supports_dspark()
|
||||||
.then_some(Message::PreferenceDsparkChanged);
|
.then_some(Message::PreferenceDsparkChanged);
|
||||||
let dspark = hint(
|
let dspark = hint(
|
||||||
checkbox(self.preference_draft.dspark_enabled)
|
toggle(self.preference_draft.dspark_enabled)
|
||||||
.label("Enable DSpark for this model")
|
.label("Enable DSpark for this model")
|
||||||
.on_toggle_maybe(dspark_toggle),
|
.on_toggle_maybe(dspark_toggle),
|
||||||
"Speculative decoding with the managed DSpark draft artifact: a small model proposes tokens that the main model verifies in one pass. Usually a large speedup; the target model may also stream routed experts from SSD.",
|
"Speculative decoding with the managed DSpark draft artifact: a small model proposes tokens that the main model verifies in one pass. Usually a large speedup; the target model may also stream routed experts from SSD.",
|
||||||
@@ -121,7 +121,7 @@ impl App {
|
|||||||
.align_y(Alignment::Center),
|
.align_y(Alignment::Center),
|
||||||
text("Enter a whole number from 1 to 1440.").size(12),
|
text("Enter a whole number from 1 to 1440.").size(12),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.a2ui_enabled)
|
toggle(self.preference_draft.a2ui_enabled)
|
||||||
.label("Enable interactive A2UI chat surfaces")
|
.label("Enable interactive A2UI chat surfaces")
|
||||||
.on_toggle(Message::PreferenceA2uiChanged),
|
.on_toggle(Message::PreferenceA2uiChanged),
|
||||||
"Lets the local model build validated native charts, tables, forms and other interactive chat surfaces. Turning it off removes the A2UI catalog from the system prompt.",
|
"Lets the local model build validated native charts, tables, forms and other interactive chat surfaces. Turning it off removes the A2UI catalog from the system prompt.",
|
||||||
@@ -134,7 +134,7 @@ impl App {
|
|||||||
"LOCAL ENDPOINT",
|
"LOCAL ENDPOINT",
|
||||||
column![
|
column![
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.endpoint_enabled)
|
toggle(self.preference_draft.endpoint_enabled)
|
||||||
.label("Enable OpenAI-compatible endpoint")
|
.label("Enable OpenAI-compatible endpoint")
|
||||||
.on_toggle(Message::PreferenceEndpointEnabledChanged),
|
.on_toggle(Message::PreferenceEndpointEnabledChanged),
|
||||||
"Serves the loaded model over an OpenAI-style HTTP API, so editors, scripts and agents on this machine can use it. Turned off, only this window can generate.",
|
"Serves the loaded model over an OpenAI-style HTTP API, so editors, scripts and agents on this machine can use it. Turned off, only this window can generate.",
|
||||||
@@ -146,7 +146,7 @@ impl App {
|
|||||||
.on_input(Message::PreferenceEndpointPortChanged),
|
.on_input(Message::PreferenceEndpointPortChanged),
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.endpoint_cors)
|
toggle(self.preference_draft.endpoint_cors)
|
||||||
.label("Allow browser clients (CORS)")
|
.label("Allow browser clients (CORS)")
|
||||||
.on_toggle(Message::PreferenceEndpointCorsChanged),
|
.on_toggle(Message::PreferenceEndpointCorsChanged),
|
||||||
"Answers with permissive CORS headers so JavaScript running in a web page may call the endpoint. Leave it off when only native tools connect.",
|
"Answers with permissive CORS headers so JavaScript running in a web page may call the endpoint. Leave it off when only native tools connect.",
|
||||||
@@ -161,7 +161,7 @@ impl App {
|
|||||||
"DEV BRAIN",
|
"DEV BRAIN",
|
||||||
column![
|
column![
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.dev_brain_enabled)
|
toggle(self.preference_draft.dev_brain_enabled)
|
||||||
.label("Enable project-backed LLM wiki")
|
.label("Enable project-backed LLM wiki")
|
||||||
.on_toggle(Message::PreferenceDevBrainEnabledChanged),
|
.on_toggle(Message::PreferenceDevBrainEnabledChanged),
|
||||||
"Lets the agent use its normal file tools on managed pages in a dedicated Obsidian vault, with indexed search and validation. Disabled means no Dev Brain access or prompt instructions.",
|
"Lets the agent use its normal file tools on managed pages in a dedicated Obsidian vault, with indexed search and validation. Disabled means no Dev Brain access or prompt instructions.",
|
||||||
@@ -228,13 +228,13 @@ impl App {
|
|||||||
.spacing(12)
|
.spacing(12)
|
||||||
.align_y(Alignment::Center),
|
.align_y(Alignment::Center),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.git_indent_heuristic)
|
toggle(self.preference_draft.git_indent_heuristic)
|
||||||
.label("Use indentation heuristic")
|
.label("Use indentation heuristic")
|
||||||
.on_toggle(Message::PreferenceGitIndentHeuristicChanged),
|
.on_toggle(Message::PreferenceGitIndentHeuristicChanged),
|
||||||
"Shift ambiguous hunk boundaries toward indentation changes, which usually makes source-code diffs easier to read.",
|
"Shift ambiguous hunk boundaries toward indentation changes, which usually makes source-code diffs easier to read.",
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.git_ignore_blank_lines)
|
toggle(self.preference_draft.git_ignore_blank_lines)
|
||||||
.label("Ignore blank-line changes")
|
.label("Ignore blank-line changes")
|
||||||
.on_toggle(Message::PreferenceGitIgnoreBlankLinesChanged),
|
.on_toggle(Message::PreferenceGitIgnoreBlankLinesChanged),
|
||||||
"Hide hunks whose changed lines are all blank.",
|
"Hide hunks whose changed lines are all blank.",
|
||||||
@@ -348,13 +348,13 @@ impl App {
|
|||||||
prefill,
|
prefill,
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.quality)
|
toggle(self.preference_draft.quality)
|
||||||
.label("Prefer exact quality kernels")
|
.label("Prefer exact quality kernels")
|
||||||
.on_toggle(Message::PreferenceQualityChanged),
|
.on_toggle(Message::PreferenceQualityChanged),
|
||||||
"Runs the exact Metal kernels instead of the fast approximations. Slightly slower, and it removes the small numeric differences those approximations introduce.",
|
"Runs the exact Metal kernels instead of the fast approximations. Slightly slower, and it removes the small numeric differences those approximations introduce.",
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.warm_weights)
|
toggle(self.preference_draft.warm_weights)
|
||||||
.label("Warm mapped weights at load time")
|
.label("Warm mapped weights at load time")
|
||||||
.on_toggle(Message::PreferenceWarmWeightsChanged),
|
.on_toggle(Message::PreferenceWarmWeightsChanged),
|
||||||
"Reads every mapped weight page once at load, so the first reply is not interrupted by page faults from disk. Loading takes longer and memory pressure rises immediately.",
|
"Reads every mapped weight page once at load, so the first reply is not interrupted by page faults from disk. Loading takes longer and memory pressure rises immediately.",
|
||||||
@@ -402,13 +402,13 @@ impl App {
|
|||||||
.on_input(Message::PreferenceMtpMarginChanged),
|
.on_input(Message::PreferenceMtpMarginChanged),
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.glm_mtp)
|
toggle(self.preference_draft.glm_mtp)
|
||||||
.label("Enable integrated GLM MTP")
|
.label("Enable integrated GLM MTP")
|
||||||
.on_toggle_maybe(glm_mtp_toggle),
|
.on_toggle_maybe(glm_mtp_toggle),
|
||||||
"Uses the prediction head built into GLM 5.2 for speculative decoding, so no separate draft model is loaded. Available for GLM 5.2 only.",
|
"Uses the prediction head built into GLM 5.2 for speculative decoding, so no separate draft model is loaded. Available for GLM 5.2 only.",
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.glm_mtp_timing)
|
toggle(self.preference_draft.glm_mtp_timing)
|
||||||
.label("Log GLM MTP timing counters")
|
.label("Log GLM MTP timing counters")
|
||||||
.on_toggle_maybe(glm_mtp_timing_toggle),
|
.on_toggle_maybe(glm_mtp_timing_toggle),
|
||||||
"Records per-stage timings of the speculative path to the log, to show where the acceleration actually goes. A diagnostic aid that costs a little throughput.",
|
"Records per-stage timings of the speculative path to the log, to show where the acceleration actually goes. A diagnostic aid that costs a little throughput.",
|
||||||
@@ -421,7 +421,7 @@ impl App {
|
|||||||
dspark_confidence,
|
dspark_confidence,
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.dspark_strict)
|
toggle(self.preference_draft.dspark_strict)
|
||||||
.label("DSpark target-only decode")
|
.label("DSpark target-only decode")
|
||||||
.on_toggle_maybe(dspark_strict_toggle),
|
.on_toggle_maybe(dspark_strict_toggle),
|
||||||
"Lets the draft model only propose, never decide: every token is sampled by the full model. Gives up some of the speedup in exchange for output identical to non-speculative decoding.",
|
"Lets the draft model only propose, never decide: every token is sampled by the full model. Gives up some of the speedup in exchange for output identical to non-speculative decoding.",
|
||||||
@@ -457,13 +457,13 @@ impl App {
|
|||||||
Space::new().height(6),
|
Space::new().height(6),
|
||||||
text("SSD STREAMING").size(11).color(muted_text()),
|
text("SSD STREAMING").size(11).color(muted_text()),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.ssd_streaming)
|
toggle(self.preference_draft.ssd_streaming)
|
||||||
.label("Enable SSD-backed model streaming")
|
.label("Enable SSD-backed model streaming")
|
||||||
.on_toggle(Message::PreferenceSsdChanged),
|
.on_toggle(Message::PreferenceSsdChanged),
|
||||||
"Leaves the routed expert weights on disk and pages them in as they are needed, so a model larger than this machine's memory still runs. Every cache miss waits for the SSD; speculative support weights remain resident while target experts stream.",
|
"Leaves the routed expert weights on disk and pages them in as they are needed, so a model larger than this machine's memory still runs. Every cache miss waits for the SSD; speculative support weights remain resident while target experts stream.",
|
||||||
),
|
),
|
||||||
hint(
|
hint(
|
||||||
checkbox(self.preference_draft.ssd_streaming_cold)
|
toggle(self.preference_draft.ssd_streaming_cold)
|
||||||
.label("Skip automatic expert preload")
|
.label("Skip automatic expert preload")
|
||||||
.on_toggle(Message::PreferenceSsdColdChanged),
|
.on_toggle(Message::PreferenceSsdColdChanged),
|
||||||
"Starts with an empty expert cache instead of reading the likely experts up front. The model is ready sooner and uses less memory, at the price of slow first replies.",
|
"Starts with an empty expert cache instead of reading the likely experts up front. The model is ready sooner and uses less memory, at the price of slow first replies.",
|
||||||
|
|||||||
Reference in New Issue
Block a user