Skip to content

Commit 9b98300

Browse files
committed
Redesign action editor + fix Save-button hover fade; release v0.8.1
Fix: the --primary CTA was missing from the hover exclusion :where() list, so a translucent-white overlay overrode its accent fill on hover (specificity 0,3,0 beat the base 0,1,0). Added it to the exclusion, then consolidated the byte-identical --primary into --accent so the drift cannot recur. Redesign "Assign action" modal: wider dialog (580->880), clickable token chips inserting at the caret with live in-field token highlighting (backdrop overlay), auto-growing text field (field-sizing) without the resize grip, HelpTip "?" primitive on throttle/trigger/conditions/name/ signal, and removal of the left accent bars on notices/linked-hint. Bump to 0.8.1 across the 5 manifest/lockfiles + CHANGELOG.
1 parent 0de7552 commit 9b98300

18 files changed

Lines changed: 375 additions & 59 deletions

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.8.1] — 2026-07-14
11+
12+
### Fixed
13+
- The Save button in the action editor no longer washes out on hover — the accent call-to-action keeps its green fill and brightens, instead of fading to a translucent white. (Root cause: the `--primary` button variant was missing from the hover exclusion list, so a translucent-white overlay overrode its accent fill.)
14+
15+
### Changed
16+
- Reworked the "Assign action" editor: a noticeably wider dialog; clickable token chips (`{date}`, `{clipboard}`, `{cursor}`) that insert at the caret, with live in-field highlighting of tokens; "?" help tooltips on throttle, trigger mode, execution conditions, name and signal; an auto-growing text field without the old resize grip; and removal of the left accent bars on notices.
17+
- Consolidated the duplicate `--primary` button style into `--accent` (they were byte-identical), so the hover-fade class of bug cannot recur.
18+
1019
## [0.8.0] — 2026-07-06
1120

1221
### Added

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "sidearm",
33
"private": true,
4-
"version": "0.8.0",
4+
"version": "0.8.1",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sidearm"
3-
version = "0.8.0"
3+
version = "0.8.1"
44
description = "Desktop workflow studio for multi-button mice"
55
authors = ["User"]
66
edition = "2024"

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"productName": "Sidearm",
4-
"version": "0.8.0",
4+
"version": "0.8.1",
55
"identifier": "com.sidearm.desktop",
66
"build": {
77
"beforeDevCommand": "npm run dev",

src/App.css

Lines changed: 188 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -987,10 +987,10 @@ select::checkmark {
987987
.notice .action-button { margin-top: 10px; }
988988

989989
.notice--subtle { background: var(--c-card-bg); border-color: var(--c-card-border); }
990-
/* Blocking states get a solid left accent so they never read as neutral info
991-
(UI-review V5). */
992-
.notice--warning { background: color-mix(in srgb, var(--c-warning) 12%, transparent); border-color: color-mix(in srgb, var(--c-warning) 26%, transparent); border-left: 3px solid var(--c-warning); }
993-
.notice--error { background: rgba(167, 56, 56, 0.14); border-color: rgba(196, 73, 73, 0.28); border-left: 3px solid var(--c-danger); }
990+
/* Blocking states read as distinct via a tinted fill + coloured border on all
991+
sides (no left-only accent bar). */
992+
.notice--warning { background: color-mix(in srgb, var(--c-warning) 13%, transparent); border-color: color-mix(in srgb, var(--c-warning) 38%, transparent); }
993+
.notice--error { background: rgba(167, 56, 56, 0.14); border-color: rgba(196, 73, 73, 0.42); }
994994
.notice--ok { background: rgba(83, 145, 74, 0.14); border-color: color-mix(in srgb, var(--c-ok) 28%, transparent); }
995995
.notice--info { background: rgba(67, 120, 176, 0.14); border-color: color-mix(in srgb, var(--c-info) 28%, transparent); }
996996

@@ -1174,8 +1174,8 @@ input[type="radio"] {
11741174
}
11751175

11761176
/* ── Action Button ── */
1177-
/* Buttons are QUIET by default — accent is reserved for the one primary action
1178-
per screen (--accent / --primary) and for pressed toggle state (--active).
1177+
/* Buttons are QUIET by default — accent (--accent) is reserved for the one
1178+
primary action per screen and for pressed toggle state (--active).
11791179
The old inverted polarity (bright base, quiet opt-in) made every Browse /
11801180
Export / toggle read as a CTA. */
11811181
.action-button {
@@ -1209,6 +1209,11 @@ input[type="radio"] {
12091209
background: rgba(255, 255, 255, 0.12);
12101210
}
12111211

1212+
/* Filled accent CTA gets a slightly stronger brighten on hover than the base. */
1213+
.action-button--accent:hover:not(:disabled) {
1214+
filter: brightness(1.1);
1215+
}
1216+
12121217
/* Toggle "on" state — accent-tinted so pressed/unpressed is unmistakable. */
12131218
.action-button--active {
12141219
background: var(--c-accent-dim);
@@ -2933,8 +2938,8 @@ code {
29332938
background: var(--c-bg);
29342939
border: 1px solid var(--c-border);
29352940
border-radius: var(--radius-modal);
2936-
width: min(580px, 90vw);
2937-
max-height: 80vh;
2941+
width: min(880px, 94vw);
2942+
max-height: 84vh;
29382943
display: flex;
29392944
flex-direction: column;
29402945
box-shadow: var(--shadow-modal);
@@ -3163,17 +3168,6 @@ code {
31633168
gap: 8px;
31643169
}
31653170

3166-
.action-button--primary {
3167-
background: var(--c-accent);
3168-
border-color: transparent;
3169-
color: var(--c-text-on-accent);
3170-
font-weight: 700;
3171-
}
3172-
3173-
.action-button--primary:hover:not(:disabled) {
3174-
filter: brightness(1.1);
3175-
}
3176-
31773171
/* ── Confirm Modal ── */
31783172
.confirm-modal {
31793173
background: var(--c-bg);
@@ -4176,14 +4170,186 @@ code {
41764170
/* "Linked to library snippet «…»" hint shown when a button references a snippet */
41774171
.snippet-linked-hint {
41784172
margin: 0;
4179-
padding: 8px 10px;
4180-
border-left: 3px solid var(--c-accent);
4173+
padding: 8px 12px;
4174+
border: 1px solid color-mix(in srgb, var(--c-accent) 32%, transparent);
41814175
border-radius: var(--radius-sm);
4182-
background: color-mix(in srgb, var(--c-accent) 8%, transparent);
4176+
background: color-mix(in srgb, var(--c-accent) 10%, transparent);
41834177
font-size: var(--fs-caption);
41844178
color: var(--c-text);
41854179
}
41864180

4181+
/* ── Help tip: "?" badge + hover/focus explanation bubble ───────────────────
4182+
Reused beside field labels (throttle, trigger mode, conditions, …). Pure
4183+
CSS reveal — no native `title` delay, no inline styles (CSP). ── */
4184+
.help-tip {
4185+
position: relative;
4186+
display: inline-flex;
4187+
align-items: center;
4188+
justify-content: center;
4189+
width: 15px;
4190+
height: 15px;
4191+
margin-left: 6px;
4192+
border-radius: 50%;
4193+
background: var(--c-card-border);
4194+
color: var(--c-text);
4195+
font-size: 10px;
4196+
font-weight: 700;
4197+
line-height: 1;
4198+
cursor: help;
4199+
vertical-align: middle;
4200+
user-select: none;
4201+
transition: background 120ms ease, color 120ms ease;
4202+
}
4203+
.help-tip:hover,
4204+
.help-tip:focus-visible {
4205+
background: var(--c-accent);
4206+
color: var(--c-text-on-accent);
4207+
outline: none;
4208+
}
4209+
.help-tip__badge {
4210+
pointer-events: none;
4211+
}
4212+
.help-tip__bubble {
4213+
position: absolute;
4214+
bottom: calc(100% + 8px);
4215+
left: 50%;
4216+
transform: translateX(-50%) translateY(4px);
4217+
z-index: 10;
4218+
width: max-content;
4219+
max-width: 260px;
4220+
padding: 8px 10px;
4221+
border-radius: var(--radius-sm);
4222+
background: var(--c-surface);
4223+
border: 1px solid var(--c-border);
4224+
box-shadow: var(--shadow-popover);
4225+
color: var(--c-text);
4226+
font-size: var(--fs-caption);
4227+
font-weight: 400;
4228+
line-height: 1.45;
4229+
text-align: left;
4230+
white-space: normal;
4231+
opacity: 0;
4232+
visibility: hidden;
4233+
pointer-events: none;
4234+
transition: opacity 120ms ease, transform 120ms ease;
4235+
}
4236+
.help-tip:hover .help-tip__bubble,
4237+
.help-tip:focus-visible .help-tip__bubble {
4238+
opacity: 1;
4239+
visibility: visible;
4240+
transform: translateX(-50%) translateY(0);
4241+
}
4242+
4243+
/* ── Token toolbar: quick-insert chips for {date}/{clipboard}/{cursor} ── */
4244+
.token-toolbar {
4245+
display: flex;
4246+
flex-wrap: wrap;
4247+
align-items: center;
4248+
gap: 8px;
4249+
}
4250+
.token-toolbar__label {
4251+
display: inline-flex;
4252+
align-items: center;
4253+
font-size: var(--fs-caption);
4254+
font-weight: 600;
4255+
color: var(--c-text-muted);
4256+
}
4257+
.token-chip {
4258+
padding: 4px 10px;
4259+
border: 1px solid color-mix(in srgb, var(--c-accent) 40%, transparent);
4260+
border-radius: 999px;
4261+
background: var(--c-accent-dim);
4262+
color: var(--c-accent);
4263+
font-family: var(--font-mono);
4264+
font-size: var(--fs-caption);
4265+
font-weight: 600;
4266+
cursor: pointer;
4267+
transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
4268+
}
4269+
.token-chip:hover {
4270+
background: color-mix(in srgb, var(--c-accent) 26%, transparent);
4271+
border-color: var(--c-accent);
4272+
transform: translateY(-1px);
4273+
}
4274+
.token-chip:active {
4275+
transform: translateY(0);
4276+
}
4277+
4278+
/* ── Highlighting textarea: a transparent <textarea> over a backdrop that
4279+
renders the same text with the tokens <mark>ed. Both layers MUST share
4280+
identical text metrics (font/size/line-height/padding/wrapping) or the
4281+
highlight drifts off the glyphs. ── */
4282+
.token-textarea {
4283+
position: relative;
4284+
border: 1px solid var(--c-input-border);
4285+
border-radius: var(--radius-sm);
4286+
background: var(--c-input-bg);
4287+
box-shadow: var(--shadow-inset);
4288+
overflow: hidden;
4289+
transition: border-color 150ms ease, box-shadow 150ms ease;
4290+
}
4291+
.token-textarea:hover {
4292+
border-color: var(--c-border-hover);
4293+
}
4294+
.token-textarea:focus-within {
4295+
border-color: var(--c-accent);
4296+
box-shadow: var(--shadow-inset), 0 0 0 3px var(--c-accent-dim);
4297+
}
4298+
.token-textarea__backdrop,
4299+
.token-textarea .token-textarea__input {
4300+
margin: 0;
4301+
padding: 10px 12px;
4302+
border: 0;
4303+
font-family: var(--font-mono);
4304+
font-size: var(--fs-body-sm);
4305+
line-height: 1.5;
4306+
letter-spacing: normal;
4307+
white-space: pre-wrap;
4308+
overflow-wrap: break-word;
4309+
word-break: break-word;
4310+
tab-size: 4;
4311+
}
4312+
.token-textarea__backdrop {
4313+
position: absolute;
4314+
inset: 0;
4315+
overflow: hidden;
4316+
color: transparent;
4317+
pointer-events: none;
4318+
user-select: none;
4319+
}
4320+
.token-textarea__mark {
4321+
/* Only the background/outline shows; the glyphs come from the input above. */
4322+
background: color-mix(in srgb, var(--c-accent) 22%, transparent);
4323+
color: transparent;
4324+
border-radius: 3px;
4325+
box-shadow: 0 0 0 1px color-mix(in srgb, var(--c-accent) 45%, transparent);
4326+
}
4327+
.token-textarea .token-textarea__input {
4328+
position: relative;
4329+
display: block;
4330+
width: 100%;
4331+
min-height: 148px;
4332+
max-height: 340px;
4333+
background: transparent;
4334+
box-shadow: none;
4335+
color: var(--c-text-bright);
4336+
caret-color: var(--c-accent);
4337+
resize: none;
4338+
/* Auto-grow to the content (Chromium/WebView2 123+); the min/max-height keep
4339+
it a comfortable box and clamp runaway growth. Falls back gracefully to a
4340+
fixed min-height box on older runtimes. */
4341+
field-sizing: content;
4342+
overflow: auto;
4343+
}
4344+
.token-textarea .token-textarea__input:focus {
4345+
outline: none;
4346+
box-shadow: none;
4347+
border-color: transparent;
4348+
}
4349+
.token-textarea .token-textarea__input::placeholder {
4350+
color: var(--c-text-muted);
4351+
}
4352+
41874353
/* Snippet library tab: master-detail. Roster (own scroll) left, sticky editor
41884354
right — selecting a row updates only the editor, so the list never jumps. */
41894355
.snippet-library {

src/components/ActionPickerModal.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
upsertEncoderMapping,
1313
} from "../lib/config-editing";
1414
import { MenuItemsEditor } from "./MenuItemsEditor";
15-
import { CloseButton, ModalFooter, ModalShell, Notice } from "./shared";
15+
import { CloseButton, HelpTip, ModalFooter, ModalShell, Notice } from "./shared";
1616
import {
1717
autoName,
1818
buildAction,
@@ -424,7 +424,10 @@ export function ActionPickerModal({
424424
) : null}
425425

426426
<label className="field mt-12">
427-
<span className="field__label">{t("picker.nameLabel")}</span>
427+
<span className="field__label">
428+
{t("picker.nameLabel")}
429+
<HelpTip text={t("picker.nameHelp")} />
430+
</span>
428431
<input
429432
type="text"
430433
value={nameDraft}
@@ -447,7 +450,10 @@ export function ActionPickerModal({
447450
/>
448451

449452
<label className="field mt-12">
450-
<span className="field__label">{t("picker.throttleLabel")}</span>
453+
<span className="field__label">
454+
{t("picker.throttleLabel")}
455+
<HelpTip text={t("picker.throttleHelp")} />
456+
</span>
451457
<input
452458
type="number"
453459
min={0}
@@ -462,7 +468,6 @@ export function ActionPickerModal({
462468
setThrottleDraft(clamped);
463469
}}
464470
/>
465-
<span className="panel__muted">{t("picker.throttleHelp")}</span>
466471
</label>
467472
</div>
468473
</div>
@@ -490,7 +495,7 @@ export function ActionPickerModal({
490495
</button>
491496
<button
492497
type="button"
493-
className="action-button action-button--primary"
498+
className="action-button action-button--accent"
494499
onClick={handleSave}
495500
disabled={isSaveDisabled(effectiveCategory, drafts)}
496501
>

src/components/ConfirmModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export function ConfirmModal({
107107
<button
108108
ref={confirmButtonRef}
109109
type="button"
110-
className={`action-button ${danger ? "action-button--danger" : "action-button--primary"}`}
110+
className={`action-button ${danger ? "action-button--danger" : "action-button--accent"}`}
111111
onClick={() => {
112112
void handleConfirm("primary");
113113
}}

src/components/ErrorModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export function ErrorModal({ error, onDismiss, onAction }: ErrorModalProps) {
102102
disabled={busyAction !== null}
103103
className={
104104
action.primary
105-
? "action-button action-button--primary"
105+
? "action-button action-button--accent"
106106
: action.danger
107107
? "action-button action-button--danger"
108108
: "action-button action-button--ghost"

0 commit comments

Comments
 (0)