@micha.bigler/ui-core-micha 2.23.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/WORK_ORDERS.md +3 -2
- package/dist/i18n/messagingTranslations.js +6 -0
- package/dist/messaging/AttachmentList.js +65 -5
- package/dist/messaging/Composer.js +13 -3
- package/dist/messaging/MessageBubble.js +1 -1
- package/dist/messaging/MessagingProvider.js +0 -0
- package/dist/messaging/PollCard.js +19 -4
- package/dist/messaging/Thread.js +1 -1
- package/package.json +1 -1
- package/src/i18n/messagingTranslations.ts +6 -0
- package/src/messaging/AttachmentList.jsx +109 -8
- package/src/messaging/Composer.jsx +35 -5
- package/src/messaging/MessageBubble.jsx +7 -1
- package/src/messaging/MessagingProvider.jsx +0 -0
- package/src/messaging/PollCard.jsx +20 -5
- package/src/messaging/Thread.jsx +13 -2
- package/tests/messagingCompactBubble.test.jsx +4 -2
- package/tests/messagingComposerChunk5.test.jsx +4 -1
- package/tests/messagingMsg6h.test.jsx +136 -0
- package/tests/messagingMsg6i.test.jsx +129 -0
- package/work-orders/MSG-6h.md +238 -0
- package/work-orders/MSG-6i.md +271 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# WORK ORDER MSG-6i (ui-core-micha) — attachment tile size, poll spacing/denominator, wider emoji picker
|
|
2
|
+
|
|
3
|
+
**EXECUTION DIRECTIVE.** If you are the implementer reading this as your own spec, this section is not
|
|
4
|
+
addressed to you — it tells the Orchestrator how to invoke you; you ARE that invocation, do not shell
|
|
5
|
+
out to `codex exec`. Orchestrator: implement through `codex exec` in the background, invoked **directly
|
|
6
|
+
via Bash** (never the `debugger`/`*_coder` Agent wrappers) with **both** flags `--skip-git-repo-check`
|
|
7
|
+
and `--dangerously-bypass-approvals-and-sandbox`, prompt passed as a positional argument from this
|
|
8
|
+
file. Fall back to direct Claude implementation only on Codex quota / rate-limit / non-zero exit.
|
|
9
|
+
|
|
10
|
+
Feature-level WO (Expertenchat envelope). Operator polish requests 2026-08-03, gathered in a
|
|
11
|
+
requirements round after MSG-6h shipped; four independent, small scopes bundled the same way
|
|
12
|
+
MSG-6f bundled A/B/C.
|
|
13
|
+
|
|
14
|
+
## TIER
|
|
15
|
+
Tier 2 — shared-core UI consumed by every host app. Independent `reviewer` + `ui_reviewer` mandatory
|
|
16
|
+
(scope C changes poll math, not just styling).
|
|
17
|
+
|
|
18
|
+
## SCOPE
|
|
19
|
+
|
|
20
|
+
**A. Attachment tile: 64px → 120px.**
|
|
21
|
+
|
|
22
|
+
`AttachmentList.jsx`'s tile `sx={{ width: 64, height: 64, ... }}` (both the `ButtonBase` and its
|
|
23
|
+
internal thumbnail `Box component="img"`, which is `width: '100%', height: '100%'` and inherits).
|
|
24
|
+
Change both dimensions to `120`. Check the non-image tile's icon/caption layout still reads well at
|
|
25
|
+
the larger size (currently sized for 64px — `InsertDriveFileOutlinedIcon fontSize="small"` and a
|
|
26
|
+
`caption` typography may look sparse/small inside a 120px box; consider `fontSize="medium"` or larger
|
|
27
|
+
if it looks disproportionate — operator did not specify, use judgement, flag if genuinely unsure).
|
|
28
|
+
`Stack direction="row" flexWrap="wrap"` spacing stays as-is unless the larger tiles visibly break
|
|
29
|
+
wrapping at typical bubble widths.
|
|
30
|
+
|
|
31
|
+
**B. Poll card: more breathing room, not a wider bubble.**
|
|
32
|
+
|
|
33
|
+
Operator, verbatim (after seeing a screenshot of the current cramped layout — checkbox, option text,
|
|
34
|
+
and percentage squeezed onto one tight line, thin bars close to the text, minimal gap between options):
|
|
35
|
+
*"Nicht generell breiter, aber luftiger."* (Not generally wider, but airier.) This is **not** scope A
|
|
36
|
+
of a width change — the poll's outer bubble width stays exactly as-is (still shares
|
|
37
|
+
`MessageBubble.jsx`'s `maxWidth: min(75%, 680px)` with every other message type). Increase internal
|
|
38
|
+
spacing in `PollCard.jsx`: the `Stack spacing={0.75}` wrapping the whole card, the per-option
|
|
39
|
+
`Stack direction="row" spacing={0.5}` (control + label), and the `LinearProgress`'s `height: 6` are
|
|
40
|
+
the likely levers — raise spacing values and/or bar height/thickness so the card reads as roomier.
|
|
41
|
+
No prescribed exact values; operator did not send further numeric guidance beyond the "airier, not
|
|
42
|
+
wider" direction — use reasonable judgement (e.g. spacing bumped by ~50%, bar height 8-10px) and
|
|
43
|
+
**flag this specific change for a live screenshot check before considering it closed** — same
|
|
44
|
+
disclosed-limitation pattern as MSG-6h's reaction-spacing fix (no staging credentials this round).
|
|
45
|
+
|
|
46
|
+
**C. Poll percentage denominator: respondents of this poll, not sum of votes cast.**
|
|
47
|
+
|
|
48
|
+
**This reverses an explicit non-goal from `MSG-6g`'s own envelope**, confirmed deliberately by the
|
|
49
|
+
operator in this round (not an oversight): MSG-6g stated *"Denominator: total votes cast, not
|
|
50
|
+
participant count. A poll's turnout relative to group size is a different question and is not in
|
|
51
|
+
scope."* — operator's correction: for a **multi-select** poll, summing `vote_count` across options can
|
|
52
|
+
exceed the number of people who actually answered (one person can vote for several options), so a
|
|
53
|
+
per-option percentage computed against that inflated sum reads as misleadingly low and the bars
|
|
54
|
+
don't sum to a sensible 100%. The new denominator: **the number of distinct people who cast at least
|
|
55
|
+
one vote in this poll** — not total conversation participants (an un-started, never-voting group
|
|
56
|
+
member should not silently deflate everyone else's percentages further).
|
|
57
|
+
|
|
58
|
+
Compute this **client-side, no backend change**: `serialize_poll`'s existing `option.voters` arrays
|
|
59
|
+
(already present, already used for the viewer's-own-vote marking) already carry every voter's id per
|
|
60
|
+
option — the union of all options' `voters` arrays, deduplicated, is exactly "everyone who answered
|
|
61
|
+
this poll." Replace `PollCard.jsx`'s current
|
|
62
|
+
`totalVotes = options.reduce((sum, option) => sum + (option.vote_count ?? option.voters?.length ?? 0), 0)`
|
|
63
|
+
with a respondent count derived from the union of `option.voters` across all options (a `Set` of voter
|
|
64
|
+
ids). **Zero-vote rendering must stay `NaN`-free** (existing guard, do not regress it) — zero
|
|
65
|
+
respondents still means 0% for every option, not a division error.
|
|
66
|
+
|
|
67
|
+
**D. Composer emoji picker: its own, wider curated list, decoupled from reaction quick-picks.**
|
|
68
|
+
|
|
69
|
+
`Composer.jsx:144` currently reuses `QUICK_EMOJIS` from `ReactionBar.jsx` (`['👍', '❤️', '😂', '🎉',
|
|
70
|
+
'👀']`, 5 entries) — the same tiny set meant for quick message reactions, not for composing free-form
|
|
71
|
+
text. Operator confirmed direction after discussion: mobile users already have a full native emoji
|
|
72
|
+
keyboard (no in-app work needed there); desktop users lack an obvious equivalent, so a **wider curated
|
|
73
|
+
grid (~20-30 common emojis)** in the Composer's own picker adds real value. **Not** a full
|
|
74
|
+
search-based picker (out of scope — meaningfully more engineering for a need mobile already covers).
|
|
75
|
+
|
|
76
|
+
Define a new, separate constant (e.g. `COMPOSER_EMOJIS` in `Composer.jsx` itself, or a shared
|
|
77
|
+
`emojiSets.js` if that reads cleaner) with ~20-30 common emojis, grouped loosely by theme (smileys,
|
|
78
|
+
gestures, hearts, objects/travel — operator did not specify an exact list; use a reasonable, common
|
|
79
|
+
default and flag it as easily adjustable). Render as a grid inside the existing `Menu` (currently a
|
|
80
|
+
plain vertical `MenuItem` list — at 25-30 entries that would be a very long scroll; switch to a
|
|
81
|
+
wrapping grid layout, e.g. a `Box` with `display: grid` or `flexWrap`, inside the `Menu`). Keep
|
|
82
|
+
`ReactionBar.jsx`'s own `QUICK_EMOJIS` untouched (5, curated, unchanged) — reactions stay small and
|
|
83
|
+
fast, this is a Composer-only change.
|
|
84
|
+
|
|
85
|
+
## NON-GOALS / DO NOT TOUCH
|
|
86
|
+
- Do not touch `ReactionBar.jsx`'s own reaction UI or its 5-emoji `QUICK_EMOJIS` — scope D adds a
|
|
87
|
+
second, separate, larger set for the Composer only.
|
|
88
|
+
- Do not widen the message bubble's `maxWidth` (`MessageBubble.jsx`) — scope B is internal spacing
|
|
89
|
+
only, confirmed explicitly by the operator.
|
|
90
|
+
- Do not touch `MessageBubble.jsx`'s reaction-overlap fix from MSG-6h, or the lightbox/context-menu
|
|
91
|
+
logic from MSG-6h — this WO only resizes the tile (scope A), doesn't change its interaction model.
|
|
92
|
+
- No backend change for scope C — the data needed already exists in the current API response.
|
|
93
|
+
- Do not build a full emoji search/picker component (scope D) — a curated grid only.
|
|
94
|
+
|
|
95
|
+
## RISKS
|
|
96
|
+
- Scope C is a **behaviour change visible to every host app** the moment this version is consumed —
|
|
97
|
+
a poll's displayed percentages will differ from before for any multi-select poll where the same
|
|
98
|
+
person voted for multiple options. State this plainly; it is the operator's own explicit correction,
|
|
99
|
+
not a silent reinterpretation.
|
|
100
|
+
- Scope A's larger tile changes vertical rhythm inside a message bubble containing attachments —
|
|
101
|
+
check it doesn't visually crowd the meta row (timestamp/read-ticks) directly below it.
|
|
102
|
+
- Scope B and D have no prescribed exact values — implement a reasonable default, disclose the
|
|
103
|
+
specific choices made (spacing multipliers, the exact emoji list) in the completion note so the
|
|
104
|
+
operator can request a numeric tweak without re-deriving what was chosen.
|
|
105
|
+
|
|
106
|
+
## REQUIRED TESTS TO WRITE
|
|
107
|
+
Narrow and behavioural. Do NOT run the full suite.
|
|
108
|
+
|
|
109
|
+
1. Attachment tile renders at 120×120 (image and non-image cases).
|
|
110
|
+
2. Poll respondent-count denominator: a multi-select poll where one voter voted for two options must
|
|
111
|
+
show percentages based on the **respondent count** (e.g. 1 respondent → both their chosen options
|
|
112
|
+
show 100%, not summed-and-diluted), not `sum(vote_count)`. This must **fail against the current
|
|
113
|
+
`sum(vote_count)` code** — prove it with a case where the two differ (multi-select, one voter,
|
|
114
|
+
two options voted).
|
|
115
|
+
3. Poll respondent-count denominator: the existing single-choice zero-vote case still shows 0%, no
|
|
116
|
+
`NaN`, no regression (reuse/extend the existing MSG-6g zero-vote test's assertion shape).
|
|
117
|
+
4. Composer emoji picker: opening it shows more than 5 emoji options (the expanded set, not
|
|
118
|
+
`ReactionBar`'s `QUICK_EMOJIS`); selecting one still inserts it at the cursor position (existing
|
|
119
|
+
`insertEmoji` behaviour, unchanged — just fed from the new list).
|
|
120
|
+
5. `ReactionBar`'s own picker is unaffected — still exactly its original 5 `QUICK_EMOJIS` (regression
|
|
121
|
+
guard that scope D didn't leak into scope-unrelated reaction UI).
|
|
122
|
+
|
|
123
|
+
## TEST SCOPE FOR THE GATE (orchestrator)
|
|
124
|
+
Attachment-, poll-, and Composer-adjacent messaging test files. Not the full suite.
|
|
125
|
+
|
|
126
|
+
## TARGET REPO
|
|
127
|
+
`C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release flow;
|
|
128
|
+
the jg-ferien pin bump is part of THIS WO's completion, not a deferred step (standing correction this
|
|
129
|
+
session: publish alone is not delivery).
|
|
130
|
+
|
|
131
|
+
## MINI-HANDOVER (pastable)
|
|
132
|
+
|
|
133
|
+
> Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`). Work order:
|
|
134
|
+
> `work-orders/MSG-6i.md` — read it fully, then follow the `orchestrate-codex` skill.
|
|
135
|
+
>
|
|
136
|
+
> Four independent scopes: (A) attachment tile 64px→120px in `AttachmentList.jsx`. (B) `PollCard.jsx`
|
|
137
|
+
> internal spacing increased ("airier, not wider" — bubble maxWidth unchanged) — no exact numbers
|
|
138
|
+
> given, use judgement, flag for a live screenshot check. (C) poll percentage denominator changes from
|
|
139
|
+
> `sum(vote_count)` to the count of distinct respondents (union of all options' `voters` arrays,
|
|
140
|
+
> client-side, no backend change) — **this deliberately reverses an explicit MSG-6g non-goal**, operator
|
|
141
|
+
> re-confirmed. (D) Composer's emoji picker gets its own ~20-30-entry curated grid, decoupled from
|
|
142
|
+
> `ReactionBar`'s 5-entry `QUICK_EMOJIS` (untouched) — no full search picker, mobile already has a
|
|
143
|
+
> native one.
|
|
144
|
+
|
|
145
|
+
## PROGRESS CONTRACT
|
|
146
|
+
Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
|
|
147
|
+
relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
|
|
148
|
+
stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Part B — Implementation map (Orchestrator) / as-built record
|
|
153
|
+
|
|
154
|
+
**Codex was unavailable (out of credits) — implemented directly by the Orchestrator.**
|
|
155
|
+
|
|
156
|
+
### Scope A — `AttachmentList.jsx`
|
|
157
|
+
Tile `sx={{ width: 64, height: 64, ... }}` → `120, 120`. Non-image tile's icon bumped
|
|
158
|
+
`fontSize="small"` → `"large"` and its `Stack` spacing `0.25` → `0.75` — the original
|
|
159
|
+
small icon+caption pairing read sparse against the much larger tile.
|
|
160
|
+
|
|
161
|
+
### Scope B — `PollCard.jsx` spacing
|
|
162
|
+
Outer `Paper` padding `p: 1` → `1.5`; outer `Stack` (question/options/close-row) spacing
|
|
163
|
+
`0.75` → `1.5`; per-option `richLabel` `Stack` spacing `0.25` → `0.75`; `LinearProgress`
|
|
164
|
+
`height: 6` → `8`. No exact values were specified by the operator beyond "airier, not
|
|
165
|
+
wider" (confirmed via a screenshot of the cramped original) — **not independently
|
|
166
|
+
visually re-verified against a fresh screenshot before closing** (no staging/live
|
|
167
|
+
credentials for this specific check this round); flagging per the same disclosed-
|
|
168
|
+
limitation pattern as MSG-6h's reaction-spacing fix.
|
|
169
|
+
|
|
170
|
+
### Scope C — `PollCard.jsx` percentage denominator
|
|
171
|
+
`totalVotes = sum(option.vote_count)` replaced with
|
|
172
|
+
`respondentCount = new Set(options.flatMap(option => option.voters || [])).size` — the
|
|
173
|
+
count of distinct people who voted for *any* option, computed client-side from data
|
|
174
|
+
already present in the response (`voters` arrays, already used for the viewer's-own-vote
|
|
175
|
+
marking). `percent` now divides by `respondentCount`.
|
|
176
|
+
|
|
177
|
+
### Scope D — `Composer.jsx` emoji picker
|
|
178
|
+
New `COMPOSER_EMOJIS` (28 entries) defined locally in `Composer.jsx`, rendered as a
|
|
179
|
+
7-column CSS grid of `IconButton`s inside the existing `Menu` (replacing the old
|
|
180
|
+
`QUICK_EMOJIS.map(... <MenuItem>...)` vertical list — `MenuItem` import removed, now
|
|
181
|
+
unused). `ReactionBar.jsx`'s own `QUICK_EMOJIS` (5, curated) untouched.
|
|
182
|
+
|
|
183
|
+
### Companion fix found and fixed — `Thread.jsx` (not in this WO's original scope)
|
|
184
|
+
Discovered while live-verifying jg-ferien's `MSG-18` (mobile Composer hidden behind the
|
|
185
|
+
fixed bottom nav) in the *same* browser session as this WO's own work: after fixing every
|
|
186
|
+
jg-side layout offset, the Composer was **still** rendering behind the nav. Root cause
|
|
187
|
+
traced into this package: `Thread.jsx`'s root `<Stack>` had no `flex`/`minHeight`, and its
|
|
188
|
+
scroll `Box` used a **hardcoded `maxHeight: 560`** — neither participates in a host's
|
|
189
|
+
flex-column layout at all, so on a short mobile viewport the thread never shrinks to fit,
|
|
190
|
+
regardless of how correctly the *host* sizes its own containers. Fixed: both the root
|
|
191
|
+
`Stack` and the scroll `Box` gained `flex: 1, minHeight: 0` (the classic flex-item
|
|
192
|
+
`min-height: auto` trap — a flex item won't shrink below its content's natural size
|
|
193
|
+
without this). `maxHeight: 560` kept as an upper-bound fallback, so a host that does
|
|
194
|
+
**not** embed `Thread` in a flex-column context sees **zero behaviour change** (both new
|
|
195
|
+
properties are no-ops outside a flex context). Live-verified end-to-end (see MSG-18's own
|
|
196
|
+
as-built record in jg-ferien for the full trace): the composer moved from `top: 742`
|
|
197
|
+
(inside the fixed nav's own `740–812` range) to `top: 629.5` (a clean 64px gap above the
|
|
198
|
+
nav at `740`), by locally building this package's `dist/` and patching it into jg's
|
|
199
|
+
`node_modules` for verification — no publish needed to prove the fix, but IS needed to
|
|
200
|
+
actually ship it (bundled into this same release, not deferred).
|
|
201
|
+
|
|
202
|
+
### Tests
|
|
203
|
+
`tests/messagingMsg6i.test.jsx` (new) — 7 tests: all 5 WO-required cases, one extra
|
|
204
|
+
(emoji-insertion behaviour still works from the new grid), plus a 6th added post-review
|
|
205
|
+
(see R3 below) asserting `Thread.jsx`'s `flex`/`minHeight` are actually applied.
|
|
206
|
+
|
|
207
|
+
### Verification (pre-review)
|
|
208
|
+
Full suite 249 → 255 (6 new), `tsc` clean.
|
|
209
|
+
|
|
210
|
+
### Independent review — `reviewer` + `ui_reviewer`, both run in parallel, findings + fixes
|
|
211
|
+
|
|
212
|
+
Both reviewers independently converged on the same P2 keyboard-navigation regression
|
|
213
|
+
(scope D) — treated as one finding below (R1/U1).
|
|
214
|
+
|
|
215
|
+
- **R1/U1 (P2, confirmed by both reviewers) — Composer's emoji grid broke MUI `Menu`'s
|
|
216
|
+
arrow-key roving-focus.** Nesting the 28 `IconButton`s inside an intervening
|
|
217
|
+
`<Box role="group">` meant `MenuList`'s `moveFocus` (which only traverses **direct**
|
|
218
|
+
children) saw a single non-tabbable child, so ArrowDown/Up/Home/End all became dead —
|
|
219
|
+
none of the 28 buttons reachable via the menu's own keyboard model, and nesting
|
|
220
|
+
`role="button"` descendants under `role="menu"` isn't a conformant ARIA pattern either.
|
|
221
|
+
**Fixed**: swapped `<Menu>` for `<Popover>` (`Composer.jsx`) — a grid of plain buttons
|
|
222
|
+
was never actually a linear menu, so this stops advertising semantics it can't deliver
|
|
223
|
+
and leaves keyboard traversal to the browser's native Tab order, which does reach every
|
|
224
|
+
button. Replicated Menu's default `anchorOrigin`/`transformOrigin`
|
|
225
|
+
(`{vertical:'bottom',horizontal:'left'}` / `{vertical:'top',horizontal:'left'}`) so the
|
|
226
|
+
picker's on-screen position is unchanged.
|
|
227
|
+
- **R2 (P2, `reviewer`) — optimistic vote could desync `vote_count` from `voters`.**
|
|
228
|
+
`MessagingProvider.applyOptimisticVote` bumped `vote_count` unconditionally but only
|
|
229
|
+
updated `voters` when `userId` was known (a documented "brief window" before
|
|
230
|
+
`AuthContext` populates `user` — in practice also the default state for any host/test
|
|
231
|
+
that doesn't wrap `MessagingProvider` in `AuthContext` at all, confirmed while fixing
|
|
232
|
+
this). Scope C's new `respondentCount` denominator reads only from `voters`, so that
|
|
233
|
+
window could show a transiently inconsistent/>-100% percentage. **Fixed**: added a
|
|
234
|
+
sentinel (`OPTIMISTIC_ANONYMOUS_VOTER`, a space-prefixed string no real UUID can match)
|
|
235
|
+
used in place of `userId` in `voters` when it's unknown, so `vote_count` and `voters`
|
|
236
|
+
move together in every case — the real vote still overwrites this on
|
|
237
|
+
reconcile/revert. Re-ran every existing poll-vote test
|
|
238
|
+
(`messagingMsg6g/Interactions/PollRendering/CompactBubble/Api`) to confirm the
|
|
239
|
+
already-tested optimistic-count behaviour (which relies on the no-`AuthContext` test
|
|
240
|
+
shape) still holds — it does.
|
|
241
|
+
- **R3 (P2, `reviewer`) — `Thread.jsx`'s flex/minHeight companion fix had zero regression
|
|
242
|
+
coverage**, and the "not meaningfully assertable via jsdom" justification didn't hold up
|
|
243
|
+
(this WO's own tile-size test already proves resolved `sx` values ARE readable via
|
|
244
|
+
`getComputedStyle` without real layout). **Fixed**: added test 6 to
|
|
245
|
+
`tests/messagingMsg6i.test.jsx`, asserting `flexGrow`/`minHeight` on both `Thread`'s
|
|
246
|
+
root `Stack` and scroll `Box`, plus that `maxHeight: 560` is still present as the
|
|
247
|
+
fallback cap.
|
|
248
|
+
- **U2 (P3, `ui_reviewer`) — hardcoded `fontSize: '1.15rem'`** on the per-emoji
|
|
249
|
+
`IconButton`. No existing theme/typography token covers emoji-glyph sizing (checked
|
|
250
|
+
`ReactionBar.jsx`'s own emoji rendering for precedent — none exists there either).
|
|
251
|
+
**Not fixed** — P3, no clear correct token to switch to; noted here rather than
|
|
252
|
+
silently dropped.
|
|
253
|
+
- **U3 (P3, `ui_reviewer`) — the emoji grid reused `MessagingComposer.ADD_EMOJI`** as its
|
|
254
|
+
`role="group"` `aria-label`, identical to the trigger button's own label, so a screen
|
|
255
|
+
reader announces "Add emoji" twice back-to-back. **Fixed**: added a dedicated
|
|
256
|
+
`MessagingComposer.EMOJI_PICKER` key ("Choose an emoji" / "Emoji auswählen" / "Choisir
|
|
257
|
+
un emoji") for the grid.
|
|
258
|
+
- **R4 (P3, `reviewer`) — test 4b's assertion (`input.value.length > 2`) didn't prove
|
|
259
|
+
cursor-position insertion**, only that something got appended. **Fixed**: rewrote it to
|
|
260
|
+
match the exact expected string at a mid-string cursor position (same shape as the
|
|
261
|
+
pre-existing `messagingComposerChunk5.test.jsx` assertion).
|
|
262
|
+
- **R5 (P3, `reviewer`) — residual, not fixed**: the 120px tile stretches whatever
|
|
263
|
+
`getAttachmentThumbnail` returns via `objectFit: cover`; if that thumbnail is
|
|
264
|
+
server-sized for the old 64px target, the larger tile could show blur. Not verifiable
|
|
265
|
+
from this repo (backend-controlled, untouched by this WO) — flagged as disclosed
|
|
266
|
+
residual risk, consistent with scope B's own "no live screenshot check this round" note.
|
|
267
|
+
|
|
268
|
+
### Verification (post-review)
|
|
269
|
+
Full suite (single-forked to avoid an unrelated vitest worker-pool timeout seen under the
|
|
270
|
+
default parallel pool): **45 test files, 256/256 passed** (255 + 1 new). `tsc -p
|
|
271
|
+
tsconfig.build.json --noEmit` clean.
|