fix(readmymind): style the modal footer, point the empty-result copy at the steer note

The footer buttons shipped with class="btn btn-secondary/primary", but no
.btn or .btn-secondary rule exists in this codebase, so all four rendered
as unstyled UA buttons. Moved them to the btn-toolbar convention every
other modal footer uses, with a scoped flex-row footer rule (btn-toolbar
is display:flex, block-level) mirroring the runSummaryModal footer.

Send's accent needs a (0,4,0) re-assert: the skin block's bare
.btn-toolbar rule is (0,2,1) under html:not([data-skin="og"]) and beats
.btn-toolbar.btn-primary (0,2,0), the same specificity trap CLAUDE.md
documents for mobile.css. Scoped to this modal; the repo-wide greying of
btn-primary on non-OG skins is pre-existing and left as a design call.

The empty-result copy now points at the steer note sitting right below
it ("Add a steer note and Rethink to try again"), zh-CN updated.

Verified with the steer E2E (still green) plus desktop, phone (390px),
and error-phase screenshots; static guards extended to pin the footer
convention and the accent re-assert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-10 11:15:47 +02:00
parent 831af88579
commit 29efd0e970
6 changed files with 57 additions and 12 deletions
+1 -1
View File
@@ -124,7 +124,7 @@ Agent use cases this unlocks: a lead session records intentions as the user stat
1. **Intent store + capture + intent endpoints + skill docs.** Immediately useful to agents even before any UI exists.
2. **Context assembler + predictor + predict endpoint + desktop button/modal.** The feature as pitched. The assembler ships with all collectors it can serve from day one (transcript, intent, git, run-summary, siblings); the approvals collector activates when PR #245 lands.
3. **Phone accessory key, rethink steering, alternates row.** Part 1 (shipped): the alternates row (tappable, swap into the field without losing edits; Rethink rejects the whole shown set), the phone 🧠 keyboard-accessory key (both bar templates, `rmm-enabled` marker class on the bar), and a phone-sized modal (small dialog, not full-screen). Part 2 (shipped): rethink steering, the free-text steer note under the suggestions, sent as `steer`, visible whenever Rethink is live (ready and empty-result phases), cleared on each open.
3. **Phone accessory key, rethink steering, alternates row.** Part 1 (shipped): the alternates row (tappable, swap into the field without losing edits; Rethink rejects the whole shown set), the phone 🧠 keyboard-accessory key (both bar templates, `rmm-enabled` marker class on the bar), and a phone-sized modal (small dialog, not full-screen). Part 2 (shipped): rethink steering, the free-text steer note under the suggestions, sent as `steer`, visible whenever Rethink is live (ready and empty-result phases), cleared on each open; the empty-result copy points at the note, and the footer buttons moved to the styled `btn-toolbar` convention (the bare `btn btn-*` classes they shipped with match no CSS in this codebase and rendered as unstyled UA buttons).
4. Explicitly later: proactive predict-on-idle (ghost suggestion chip), auto-compaction of `recentPrompts` into `goals` via a cheap model, codex/gemini capture, cross-case "global" intent.
## Open questions
+2 -1
View File
@@ -255,7 +255,8 @@
'Read My Mind: predict your next prompt': '读心术:预测您的下一条提示',
'Predict my next prompt': '预测我的下一条提示',
'Reading your mind…': '正在读取您的想法…',
'No suggestion this time. Rethink to try again.': '这次没有建议。点击「重想」再试一次。',
'No suggestion this time. Add a steer note and Rethink to try again.':
'这次没有建议。可添加引导备注后点击「重想」再试一次。',
Rethink: '重想',
Insert: '插入',
"Put the text on the session's composer without submitting it": '将文本放入会话输入框但不提交',
+8 -5
View File
@@ -2894,7 +2894,7 @@
<div class="readmymind-why" id="readMyMindWhy" data-i18n-skip></div>
<div class="readmymind-alternates" id="readMyMindAlternates" data-i18n-skip style="display:none"></div>
</div>
<div class="readmymind-error" style="display:none">No suggestion this time. Rethink to try again.</div>
<div class="readmymind-error" style="display:none">No suggestion this time. Add a steer note and Rethink to try again.</div>
<!-- Rethink steer note: the user's own words about what they actually
meant, sent as `steer` with the next Rethink. Shown whenever
Rethink is live (ready AND empty-result phases), hidden while a
@@ -2903,11 +2903,14 @@
<input type="text" id="readMyMindSteer" class="readmymind-steer-input" maxlength="2000" placeholder="Steer the rethink, e.g. 'no, I meant the mobile bug'" aria-label="Steer note for Rethink" onkeydown="if(event.key==='Enter')app.rethinkReadMyMind()">
</div>
</div>
<!-- btn-toolbar, not bare "btn btn-*": no .btn/.btn-secondary rule exists
in this codebase, so the bare classes render as unstyled UA buttons.
btn-toolbar is the convention every other modal footer uses. -->
<div class="modal-footer">
<button class="btn btn-secondary" onclick="app.closeReadMyMind()">Dismiss</button>
<button class="btn btn-secondary" id="readMyMindRethink" onclick="app.rethinkReadMyMind()">Rethink</button>
<button class="btn btn-secondary" onclick="app.sendReadMyMind(false)" title="Put the text on the session's composer without submitting it">Insert</button>
<button class="btn btn-primary" onclick="app.sendReadMyMind(true)">Send</button>
<button class="btn-toolbar" onclick="app.closeReadMyMind()">Dismiss</button>
<button class="btn-toolbar" id="readMyMindRethink" onclick="app.rethinkReadMyMind()">Rethink</button>
<button class="btn-toolbar" onclick="app.sendReadMyMind(false)" title="Put the text on the session's composer without submitting it">Insert</button>
<button class="btn-toolbar btn-primary" onclick="app.sendReadMyMind(true)">Send</button>
</div>
</div>
</div>
+4 -5
View File
@@ -1336,15 +1336,14 @@ html.mobile-init .file-browser-panel {
width: calc(100% - 2rem);
}
/* Four footer buttons on a narrow phone: let them wrap instead of clipping,
and give buttons + alternate rows finger-sized targets. */
and give buttons + alternate rows finger-sized targets. The flex row
itself comes from the base rule in styles.css. */
.readmymind-modal .modal-footer {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 0.5rem;
}
.readmymind-modal .modal-footer .btn {
.readmymind-modal .modal-footer .btn-toolbar {
flex: 1 1 auto;
justify-content: center;
min-height: 38px;
}
.readmymind-alt {
+26
View File
@@ -10927,6 +10927,32 @@ kbd {
.readmymind-steer-input::placeholder {
color: var(--text-dim);
}
/* Footer row: the buttons are btn-toolbar (display: flex, block-level), so
without this rule the four of them stack vertically. Mirrors the
runSummaryModal footer; the ≤430px block in mobile.css adds wrapping. */
.readmymind-modal .modal-footer {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
padding: 0.75rem 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
/* Send keeps the primary accent: the skin block's bare .btn-toolbar rule is
(0,2,1) under html:not([data-skin="og"]) and outranks the base
.btn-toolbar.btn-primary (0,2,0) — the specificity trap CLAUDE.md documents
for mobile.css — so the accent is re-asserted here at (0,4,0). Scoped to
this modal on purpose; un-greying every btn-primary on the new skins is a
design call, not this feature's. */
.readmymind-modal .modal-footer .btn-toolbar.btn-primary {
background: var(--accent);
border-color: var(--accent);
color: #fff;
}
.readmymind-modal .modal-footer .btn-toolbar.btn-primary:hover {
background: var(--accent-hover);
border-color: var(--accent-hover);
color: #fff;
}
/* Keyboard-accessory 🧠 key: the phone surface for the same opt-in setting
(the header 🧠 button stays phone-hidden in mobile.css). The key ships in
+16
View File
@@ -89,4 +89,20 @@ describe('read my mind phone key + alternates (static guards)', () => {
// A fresh open resets the note along with the rethink memory.
expect(ui).toMatch(/steer\.value = ''/);
});
it('styles the footer with btn-toolbar (bare "btn btn-*" matches no CSS in this codebase)', () => {
const modal = html.slice(html.indexOf('id="readMyMindModal"'), html.indexOf('id="approvalsDrawer"'));
// The unstyled classes the footer originally shipped with must not return.
expect(modal).not.toMatch(/class="btn /);
expect(modal.match(/class="btn-toolbar/g)?.length).toBe(4);
expect(modal).toMatch(/class="btn-toolbar btn-primary"[^>]*sendReadMyMind\(true\)/);
// btn-toolbar is display:flex (block-level): without the desktop footer
// row rule the four buttons would stack vertically.
expect(styles).toMatch(/\.readmymind-modal \.modal-footer \{[^}]*display: flex/);
// The skin block's bare .btn-toolbar (0,2,1) greys out .btn-primary
// (0,2,0), so Send's accent must be re-asserted at higher specificity.
expect(styles).toMatch(/\.readmymind-modal \.modal-footer \.btn-toolbar\.btn-primary \{[^}]*var\(--accent\)/);
// The phone block sizes the same class for finger targets.
expect(phoneBlock).toMatch(/\.readmymind-modal \.modal-footer \.btn-toolbar/);
});
});