Skip to content

Commit 8cde998

Browse files
Fix UI bugs
Signed-off-by: Leslie Lazzarino <leslie.lazzarino@gmail.com>
1 parent eea4923 commit 8cde998

6 files changed

Lines changed: 53 additions & 18 deletions

File tree

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
.claude
23
node_modules
34
/build
45
*.xpi

‎docs/USERGUIDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ keeping token use in check. A live counter shows LLM/tool calls while it works;
122122
**After a report is generated**
123123

124124
- **Linked emails** — the report cites the emails behind its statements as inline chips.
125-
Click **↗** on a chip to open that email in a tab, or **↩** to start a reply to it.
125+
Click a chip to open that email in a tab, or its **↩** button to start a reply to it.
126126
(The links work within the current Thunderbird session.)
127127
- **Refine** — type a follow-up instruction and send again; the report continues the
128128
same conversation (e.g. *"group these by sender"*).

‎public/organiseConfirm.html‎

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,37 @@
5353
min-height: 18px;
5454
margin: 0 0 8px;
5555
}
56-
/* Scrollable list filling the remaining height. */
56+
/* Everything except the list keeps its natural height. Without `flex: 0 0 auto` these are
57+
shrinkable, so on a full page (content taller than the window) the browser compresses the
58+
heading, status line, pager and footer — and lets them spring back on a short last page.
59+
That is what made the dialog "jump" when paging. */
60+
h1,
61+
.subtitle,
62+
#status,
63+
.pager,
64+
.footer {
65+
flex: 0 0 auto;
66+
}
67+
/* Scrollable list filling the remaining height. `flex-basis: 0` (not `auto`) makes its size
68+
depend only on the free space, never on how many rows the current page happens to have, and
69+
a reserved scrollbar gutter stops the rows from shifting sideways when a short page needs no
70+
scrollbar. Together these keep every page pixel-identical. */
5771
#move-list {
58-
flex: 1 1 auto;
72+
flex: 1 1 0;
5973
min-height: 120px;
6074
overflow-y: auto;
75+
scrollbar-gutter: stable;
6176
border: 1px solid var(--border);
6277
border-radius: 6px;
6378
background: var(--subtle-bg);
6479
}
80+
/* Fixed row height so a row can never be taller or shorter than its neighbours. */
6581
.move-row {
6682
display: flex;
6783
align-items: center;
6884
gap: 10px;
85+
height: 52px;
86+
box-sizing: border-box;
6987
padding: 8px 10px;
7088
border-bottom: 1px solid var(--border);
7189
}
@@ -78,12 +96,14 @@
7896
}
7997
.move-subject {
8098
font-size: 13px;
99+
line-height: 1.35;
81100
white-space: nowrap;
82101
overflow: hidden;
83102
text-overflow: ellipsis;
84103
}
85104
.move-author {
86105
font-size: 12px;
106+
line-height: 1.35;
87107
color: var(--muted);
88108
white-space: nowrap;
89109
overflow: hidden;

‎public/reports.html‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -448,28 +448,42 @@
448448
.report-placeholder {
449449
color: var(--muted);
450450
}
451-
/* Inline email citation chip: label + Open/Reply glyph buttons. */
451+
/* Inline email citation chip: the label opens the email, the trailing glyph replies to it. */
452452
.email-citation {
453453
display: inline-flex;
454454
align-items: center;
455-
gap: 4px;
455+
gap: 2px;
456456
margin: 0 1px;
457-
padding: 0 2px 0 6px;
457+
padding: 0 2px 0 0;
458458
border: 1px solid var(--border);
459459
border-radius: 999px;
460460
background: var(--field-bg);
461461
vertical-align: baseline;
462462
line-height: 1.6;
463463
}
464+
/* The label is a button so the whole chip body — not just a small glyph — opens the email. */
464465
.email-citation-label {
466+
display: inline-block;
465467
max-width: 220px;
466468
overflow: hidden;
467469
text-overflow: ellipsis;
468470
white-space: nowrap;
471+
padding: 0 4px 0 8px;
472+
margin: 0;
473+
border: none;
474+
border-radius: 999px 0 0 999px;
475+
background: transparent;
476+
font-family: inherit;
469477
font-size: 12px;
478+
line-height: inherit;
470479
color: var(--fg);
480+
cursor: pointer;
481+
}
482+
.email-citation-label:hover {
483+
background: var(--accent);
484+
color: var(--accent-fg);
471485
}
472-
.email-citation button {
486+
.email-citation .email-reply {
473487
display: inline-flex;
474488
align-items: center;
475489
justify-content: center;
@@ -484,7 +498,7 @@
484498
line-height: 1;
485499
cursor: pointer;
486500
}
487-
.email-citation button:hover {
501+
.email-citation .email-reply:hover {
488502
background: var(--accent);
489503
color: var(--accent-fg);
490504
}

‎src/__tests__/reportMarkdown.test.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@ describe("renderReportHtml", () => {
2525
expect(html.querySelector("code")?.textContent).toBe("code");
2626
});
2727

28-
test("turns an email citation into a chip with Open/Reply buttons carrying the id", () => {
28+
test("turns an email citation into a chip whose label opens and whose button replies", () => {
2929
const html = render('Resolved on Tuesday [Alice — "Re: Invoice"](email:12345).');
3030
const chip = html.querySelector(".email-citation");
3131
expect(chip).not.toBeNull();
32-
expect(chip?.querySelector(".email-citation-label")?.textContent).toBe('Alice — "Re: Invoice"');
3332

3433
const open = chip?.querySelector<HTMLButtonElement>(".email-open");
3534
const reply = chip?.querySelector<HTMLButtonElement>(".email-reply");
35+
// The label itself is the open control, so there is no separate arrow glyph on the chip.
36+
expect(open?.textContent).toBe('Alice — "Re: Invoice"');
37+
expect(open?.classList.contains("email-citation-label")).toBe(true);
38+
expect(chip?.querySelectorAll("button")).toHaveLength(2);
3639
expect(open?.dataset.emailId).toBe("12345");
3740
expect(reply?.dataset.emailId).toBe("12345");
3841
// The surrounding prose is preserved around the chip.

‎src/reportMarkdown.ts‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,20 @@ interface InlineMatch {
3333
build: (doc: Document) => Node;
3434
}
3535

36-
/** Build the email-citation chip: a label plus Open/Reply buttons tagged with the message id. */
36+
/** Build the email-citation chip: a clickable label (opens the email) plus a Reply button. */
3737
function buildEmailChip(doc: Document, id: string, label: string): Node {
3838
const chip = doc.createElement("span");
3939
chip.className = "email-citation";
4040

41-
const name = doc.createElement("span");
42-
name.className = "email-citation-label";
43-
name.textContent = label;
44-
chip.appendChild(name);
45-
41+
// The label itself is the "open" control: the whole chip (everything but the reply button)
42+
// opens the cited email, so there is no separate arrow glyph.
4643
const openBtn = doc.createElement("button");
4744
openBtn.type = "button";
48-
openBtn.className = "email-open";
45+
openBtn.className = "email-citation-label email-open";
4946
openBtn.dataset.emailId = id;
5047
openBtn.title = "Open this email";
5148
openBtn.setAttribute("aria-label", `Open email: ${label}`);
52-
openBtn.textContent = "↗";
49+
openBtn.textContent = label;
5350
chip.appendChild(openBtn);
5451

5552
const replyBtn = doc.createElement("button");

0 commit comments

Comments
 (0)