@micha.bigler/ui-core-micha 3.14.4 → 4.0.1
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/CHANGELOG.md +29 -0
- package/README.md +7 -0
- package/dist/chat/kitSxRegistry.js +9 -0
- package/dist/components/charts/kitSxRegistry.js +6 -0
- package/dist/entries/charts.js +6 -0
- package/dist/entries/chat.js +8 -0
- package/dist/entries/markdown.js +1 -0
- package/dist/index.js +0 -14
- package/dist/pages/AccountPage.js +1 -2
- package/dist/pages/LoginPage.js +1 -2
- package/dist/pages/PasswordChangePage.js +1 -2
- package/dist/pages/PasswordInvitePage.js +1 -2
- package/dist/pages/PasswordResetRequestPage.js +1 -2
- package/dist/pages/SignUpPage.js +1 -2
- package/dist/pages/SignupConfirmPage.js +1 -2
- package/dist/theme/kitSxEntry.js +11 -0
- package/dist/theme/kitSxRegistry.js +2 -15
- package/package.json +31 -7
- package/.github/workflows/ci.yml +0 -35
- package/.github/workflows/publish.yml +0 -126
- package/DESIGN.md +0 -60
- package/WORK_ORDERS.md +0 -150
- package/dev/entries.jsx +0 -721
- package/dev/fixtures.js +0 -26
- package/dev/main.jsx +0 -75
- package/dev/mockTransport.jsx +0 -128
- package/docs/CHART-LAYOUT.md +0 -157
- package/docs/DARK-TOKENS.md +0 -140
- package/docs/TYPE-SCALE.md +0 -111
- package/docs/messaging-deviations.md +0 -118
- package/index.html +0 -12
- package/renovate.json +0 -10
- package/scripts/chart-api-census.mjs +0 -415
- package/src/auth/AuthContext.jsx +0 -158
- package/src/auth/ReauthModal.jsx +0 -82
- package/src/auth/UserMenu.jsx +0 -121
- package/src/auth/apiClient.jsx +0 -232
- package/src/auth/authApi.jsx +0 -539
- package/src/auth/authConfig.jsx +0 -34
- package/src/auth/reauth.js +0 -47
- package/src/chat/ChatComposer.jsx +0 -36
- package/src/chat/ChatList.jsx +0 -87
- package/src/chat/ChatThread.jsx +0 -128
- package/src/chat/ChatWorkspace.jsx +0 -244
- package/src/chat/MarkdownReply.jsx +0 -22
- package/src/chat/ModelPicker.jsx +0 -85
- package/src/chat/api.js +0 -18
- package/src/chat/useChatSocket.js +0 -32
- package/src/chat/wire.js +0 -7
- package/src/components/AccessCodeManager.jsx +0 -288
- package/src/components/AccessCodeSingleUseToggle.jsx +0 -92
- package/src/components/AllowedEmailDomainsManager.jsx +0 -92
- package/src/components/AuthFactorRequirementCard.jsx +0 -95
- package/src/components/BulkInviteCsvTab.jsx +0 -198
- package/src/components/EmptyState.jsx +0 -38
- package/src/components/LoginForm.jsx +0 -159
- package/src/components/MFAComponent.jsx +0 -350
- package/src/components/MfaLoginComponent.jsx +0 -252
- package/src/components/MobileBottomNav.jsx +0 -82
- package/src/components/PasskeysComponent.jsx +0 -252
- package/src/components/PasswordChangeForm.jsx +0 -87
- package/src/components/PasswordResetRequestForm.jsx +0 -43
- package/src/components/PasswordSetForm.jsx +0 -97
- package/src/components/ProfileComponent.jsx +0 -348
- package/src/components/QrSignupManager.jsx +0 -404
- package/src/components/QrSignupValidityManager.jsx +0 -106
- package/src/components/RegistrationMethodsManager.jsx +0 -150
- package/src/components/SecurityComponent.jsx +0 -141
- package/src/components/SocialLoginButtons.jsx +0 -83
- package/src/components/SoftChip.jsx +0 -93
- package/src/components/StatTile.jsx +0 -39
- package/src/components/SupportRecoveryRequestsTab.jsx +0 -482
- package/src/components/TurnstileWidget.jsx +0 -99
- package/src/components/UserInviteComponent.jsx +0 -76
- package/src/components/UserListComponent.jsx +0 -618
- package/src/components/charts/BarChart.jsx +0 -102
- package/src/components/charts/ChartFrame.jsx +0 -214
- package/src/components/charts/LineChart.jsx +0 -126
- package/src/components/charts/ScatterChart.jsx +0 -299
- package/src/components/charts/TimeSeriesChart.jsx +0 -300
- package/src/components/charts/chartDefaults.js +0 -644
- package/src/components/charts/chartLabels.js +0 -36
- package/src/components/charts/exportChart.js +0 -258
- package/src/components/charts/formatters.js +0 -39
- package/src/components/charts/palette.js +0 -33
- package/src/components/charts/yearTickInterval.js +0 -7
- package/src/i18n/authTranslations.ts +0 -2651
- package/src/i18n/chartsTranslations.ts +0 -91
- package/src/i18n/chatTranslations.ts +0 -55
- package/src/i18n/messagingTranslations.ts +0 -120
- package/src/i18n/notificationsTranslations.ts +0 -32
- package/src/i18n/onboardingTranslations.ts +0 -29
- package/src/i18n/sectionNavTranslations.ts +0 -20
- package/src/i18n/uiCoreTranslations.js +0 -29
- package/src/i18n/userMenuTranslations.ts +0 -5
- package/src/index.js +0 -158
- package/src/layout/PageLayout.jsx +0 -36
- package/src/layout/SectionNav.jsx +0 -320
- package/src/messaging/AttachmentList.jsx +0 -145
- package/src/messaging/Composer.jsx +0 -197
- package/src/messaging/ConversationLaunchers.jsx +0 -51
- package/src/messaging/ConversationList.jsx +0 -109
- package/src/messaging/DirectMessageLauncher.jsx +0 -112
- package/src/messaging/MessageBubble.jsx +0 -183
- package/src/messaging/MessagingProvider.jsx +0 -0
- package/src/messaging/MessagingScopeConfig.jsx +0 -24
- package/src/messaging/PollCard.jsx +0 -163
- package/src/messaging/ReactionBar.jsx +0 -43
- package/src/messaging/ReadTicks.jsx +0 -82
- package/src/messaging/Thread.jsx +0 -195
- package/src/messaging/api.js +0 -48
- package/src/messaging/composerImageCompression.js +0 -35
- package/src/messaging/linkifyText.jsx +0 -37
- package/src/notifications/NotificationBell.jsx +0 -84
- package/src/notifications/NotificationSettings.jsx +0 -330
- package/src/notifications/NotificationsProvider.jsx +0 -224
- package/src/notifications/PopupSurface.jsx +0 -105
- package/src/notifications/api.js +0 -55
- package/src/notifications/feedApi.js +0 -18
- package/src/notifications/realtime.jsx +0 -138
- package/src/notifications/serviceWorker/sw.js +0 -50
- package/src/onboarding/OnboardingProvider.jsx +0 -326
- package/src/onboarding/OnboardingWizard.jsx +0 -61
- package/src/onboarding/WizardDialogShell.jsx +0 -51
- package/src/onboarding/api.js +0 -13
- package/src/onboarding/stepSelection.js +0 -16
- package/src/onboarding/steps/BrowserPushStep.jsx +0 -121
- package/src/onboarding/steps/CompleteNameStep.jsx +0 -46
- package/src/onboarding/steps/CookieConsentStep.jsx +0 -133
- package/src/onboarding/steps/PwaInstallStep.jsx +0 -47
- package/src/pages/AccountPage.jsx +0 -394
- package/src/pages/LoginPage.jsx +0 -343
- package/src/pages/PasswordChangePage.jsx +0 -62
- package/src/pages/PasswordInvitePage.jsx +0 -177
- package/src/pages/PasswordResetRequestPage.jsx +0 -68
- package/src/pages/SignUpPage.jsx +0 -268
- package/src/pages/SignupConfirmPage.jsx +0 -155
- package/src/testing/checkKitIntegration.js +0 -233
- package/src/theme/createAppTheme.js +0 -423
- package/src/theme/fonts.js +0 -3
- package/src/theme/index.js +0 -21
- package/src/theme/kitSxRegistry.js +0 -285
- package/src/theme/themeCompleteness.js +0 -1045
- package/src/theme/tokens.js +0 -478
- package/src/utils/auth-errors.js +0 -76
- package/src/utils/authService.js +0 -223
- package/src/utils/resolveErrorText.js +0 -28
- package/src/utils/webauthn.js +0 -51
- package/tests/AccountPage.profileStatements.test.jsx +0 -162
- package/tests/AccountPage.test.jsx +0 -200
- package/tests/AuthContext.test.jsx +0 -129
- package/tests/BarChart.test.jsx +0 -112
- package/tests/BrowserPushStep.test.jsx +0 -110
- package/tests/ChartFrame.test.jsx +0 -336
- package/tests/ConversationList.test.jsx +0 -144
- package/tests/CookieConsentStep.test.jsx +0 -69
- package/tests/EmptyState.test.jsx +0 -62
- package/tests/LineChart.test.jsx +0 -80
- package/tests/MessagingProvider.test.jsx +0 -50
- package/tests/MobileBottomNav.test.jsx +0 -319
- package/tests/NotificationBell.test.jsx +0 -60
- package/tests/NotificationSettings.test.jsx +0 -276
- package/tests/NotificationsProvider.test.jsx +0 -240
- package/tests/OnboardingProvider.test.jsx +0 -294
- package/tests/OnboardingWizard.test.jsx +0 -143
- package/tests/PasswordChangeForm.test.jsx +0 -70
- package/tests/PasswordInvitePage.test.jsx +0 -142
- package/tests/PasswordSetForm.test.jsx +0 -70
- package/tests/PopupSurface.test.jsx +0 -142
- package/tests/ProfileComponent.refresh.test.jsx +0 -194
- package/tests/QrSignupManager.registrationContext.test.jsx +0 -106
- package/tests/QrSignupManagerInfoBox.test.jsx +0 -49
- package/tests/ScatterChart.test.jsx +0 -270
- package/tests/SectionNav.test.jsx +0 -363
- package/tests/SignUpPage.test.jsx +0 -208
- package/tests/SoftChip.test.jsx +0 -105
- package/tests/StatTile.test.jsx +0 -57
- package/tests/Thread.test.jsx +0 -151
- package/tests/TimeSeriesChart.test.jsx +0 -367
- package/tests/UserListComponent.test.jsx +0 -192
- package/tests/UserMenu.test.jsx +0 -171
- package/tests/apiClientLanguage.test.js +0 -33
- package/tests/apiClientPublicPaths.test.js +0 -178
- package/tests/authApiLogin.test.js +0 -42
- package/tests/authApiRegistration.test.js +0 -31
- package/tests/authErrorCodeRendering.test.jsx +0 -77
- package/tests/authErrors.test.js +0 -74
- package/tests/authTranslations.test.js +0 -74
- package/tests/chart-api-census.test.js +0 -366
- package/tests/chartAxisTickBand.test.jsx +0 -95
- package/tests/chartDefaults.test.js +0 -902
- package/tests/chartLabels.test.js +0 -50
- package/tests/chartsPalette.test.js +0 -99
- package/tests/chatApi.test.js +0 -48
- package/tests/chatDriverNames.test.jsx +0 -71
- package/tests/chatExports.test.js +0 -3
- package/tests/chatList.test.jsx +0 -84
- package/tests/chatPicker.test.jsx +0 -82
- package/tests/chatSocket.test.jsx +0 -26
- package/tests/chatThread.test.jsx +0 -116
- package/tests/chatTranslations.test.js +0 -47
- package/tests/chatWorkspace.test.jsx +0 -234
- package/tests/checkKitIntegration.test.jsx +0 -219
- package/tests/createAppTheme.test.js +0 -132
- package/tests/createAppThemeDark.test.js +0 -127
- package/tests/createAppThemeLightGuard.test.js +0 -40
- package/tests/createAppThemePageContrast.test.js +0 -108
- package/tests/createAppThemeTypography.test.js +0 -53
- package/tests/errorCallSiteGuard.test.js +0 -32
- package/tests/exportChart.test.js +0 -313
- package/tests/feedApi.test.js +0 -46
- package/tests/fixtures/light-theme-baseline.json +0 -2234
- package/tests/i18nAggregate.test.js +0 -120
- package/tests/i18nGermanVariant.test.js +0 -65
- package/tests/loginPageErrorResolution.test.jsx +0 -102
- package/tests/messagingApi.test.js +0 -32
- package/tests/messagingChunk6.test.jsx +0 -116
- package/tests/messagingCompactBubble.test.jsx +0 -64
- package/tests/messagingComposer.test.jsx +0 -136
- package/tests/messagingComposerChunk5.test.jsx +0 -113
- package/tests/messagingContractConformance.test.js +0 -69
- package/tests/messagingDirectMessageLauncher.test.jsx +0 -151
- package/tests/messagingExports.test.js +0 -21
- package/tests/messagingInteractions.test.jsx +0 -136
- package/tests/messagingLastMessagePreview.test.jsx +0 -93
- package/tests/messagingLinkify.test.jsx +0 -51
- package/tests/messagingMessageActions.test.jsx +0 -163
- package/tests/messagingMsg6.test.jsx +0 -105
- package/tests/messagingMsg6f.test.jsx +0 -155
- package/tests/messagingMsg6g.test.jsx +0 -166
- package/tests/messagingMsg6h.test.jsx +0 -136
- package/tests/messagingMsg6i.test.jsx +0 -129
- package/tests/messagingPollRendering.test.jsx +0 -142
- package/tests/messagingReactionMerge.test.jsx +0 -77
- package/tests/messagingReadTicksTooltip.test.jsx +0 -36
- package/tests/messagingThreadReceipts.test.jsx +0 -178
- package/tests/messagingThreadingQuoting.test.jsx +0 -87
- package/tests/messagingUnreadLifecycle.test.jsx +0 -78
- package/tests/notificationsApi.test.js +0 -41
- package/tests/notificationsExports.test.js +0 -34
- package/tests/onboardingDescriptors.test.js +0 -132
- package/tests/packageTreeShaking.test.js +0 -126
- package/tests/realtime.test.jsx +0 -219
- package/tests/resolveErrorText.test.js +0 -72
- package/tests/stepSelection.test.js +0 -53
- package/tests/themeCompleteness.test.js +0 -626
- package/tests/timeSeriesLegend.test.js +0 -99
- package/tests/userScreenTranslations.test.jsx +0 -218
- package/tsconfig.build.json +0 -18
- package/vite.config.mjs +0 -6
- package/work-orders/AUTH-1.md +0 -74
- package/work-orders/AUTH-2.md +0 -74
- package/work-orders/AUTH-4.md +0 -183
- package/work-orders/AUTH-5.md +0 -154
- package/work-orders/AUTH-7.md +0 -181
- package/work-orders/AUTH-8.md +0 -184
- package/work-orders/CHART-1.md +0 -285
- package/work-orders/CHART-2.md +0 -222
- package/work-orders/CHART-3.md +0 -163
- package/work-orders/CHART-4.md +0 -203
- package/work-orders/CHART-5.md +0 -226
- package/work-orders/CHART-6.md +0 -428
- package/work-orders/CHART-7.md +0 -175
- package/work-orders/DX-1.md +0 -169
- package/work-orders/DX-2.md +0 -188
- package/work-orders/DX-3.md +0 -195
- package/work-orders/I18N-1.md +0 -254
- package/work-orders/I18N-3.md +0 -207
- package/work-orders/MSG-3.md +0 -375
- package/work-orders/MSG-3b.md +0 -1002
- package/work-orders/MSG-3c.md +0 -378
- package/work-orders/MSG-3d.md +0 -268
- package/work-orders/MSG-3e.md +0 -164
- package/work-orders/MSG-3f.md +0 -391
- package/work-orders/MSG-6e.md +0 -308
- package/work-orders/MSG-6f.md +0 -214
- package/work-orders/MSG-6g.md +0 -244
- package/work-orders/MSG-6h.md +0 -238
- package/work-orders/MSG-6i.md +0 -271
- package/work-orders/MSG-6j.md +0 -117
- package/work-orders/NOTIF-12.md +0 -214
- package/work-orders/NOTIF-13.md +0 -142
- package/work-orders/NOTIF-14.md +0 -151
- package/work-orders/PRIM-1.md +0 -177
- package/work-orders/SHELL-1.md +0 -255
- package/work-orders/SHELL-2.md +0 -197
- package/work-orders/SHELL-3.md +0 -359
- package/work-orders/SHELL-4.md +0 -351
- package/work-orders/SHELL-5.md +0 -345
- package/work-orders/SHELL-6.md +0 -210
- package/work-orders/THEME-1.md +0 -587
- package/work-orders/THEME-10.md +0 -303
- package/work-orders/THEME-11.md +0 -208
- package/work-orders/THEME-2.md +0 -339
- package/work-orders/THEME-3.md +0 -210
- package/work-orders/THEME-4.md +0 -271
- package/work-orders/THEME-5.md +0 -341
- package/work-orders/THEME-6.md +0 -247
- package/work-orders/THEME-7.md +0 -82
- package/work-orders/THEME-8.md +0 -211
- package/work-orders/THEME-9.md +0 -194
- package/work-orders/UCM-AUTH-10.md +0 -490
- package/work-orders/UCM-AUTH-11.md +0 -185
- package/work-orders/UCM-AUTH-12.md +0 -98
- package/work-orders/UCM-AUTH-13.md +0 -47
- package/work-orders/UCM-AUTH-9.md +0 -207
- package/work-orders/UCM-CHART-10.md +0 -150
- package/work-orders/UCM-CHART-11.md +0 -132
- package/work-orders/UCM-CHART-12.md +0 -348
- package/work-orders/UCM-CHART-13.md +0 -122
- package/work-orders/UCM-CHART-14.md +0 -198
- package/work-orders/UCM-CHART-15.md +0 -187
- package/work-orders/UCM-CHART-16.md +0 -169
- package/work-orders/UCM-CHART-17.md +0 -195
- package/work-orders/UCM-CHART-18.md +0 -200
- package/work-orders/UCM-CHART-8.md +0 -147
- package/work-orders/UCM-CHART-9.md +0 -265
- package/work-orders/UCM-CHAT-1.md +0 -190
- package/work-orders/UCM-CHAT-2.md +0 -75
- package/work-orders/UCM-CI-1.md +0 -65
- package/work-orders/UCM-DEP-1.md +0 -63
- package/work-orders/UCM-I18N-4.md +0 -226
- package/work-orders/UCM-I18N-5.md +0 -211
- package/work-orders/UCM-MSG-20.md +0 -192
- package/work-orders/UCM-PRIM-2.md +0 -114
- package/work-orders/UCM-SHELL-7.md +0 -134
- package/work-orders/UCM-SHELL-8.md +0 -125
- package/work-orders/UCM-TEST-1.md +0 -304
- package/work-orders/UCM-THEME-13.md +0 -69
- package/work-orders/UCM-THEME-14.md +0 -312
- package/work-orders/UCM-THEME-15.md +0 -356
- package/work-orders/UCM-THEME-16.md +0 -80
- package/work-orders/UCM-THEME-17.md +0 -111
- package/work-orders/UCM-THEME-18.md +0 -184
- package/work-orders/assets/AUTH-4-user-list-card.html +0 -407
- package/work-orders/assets/PRIM-1-dashboard-primitives.html +0 -624
- package/work-orders/assets/SHELL-2-account-navigation.html +0 -1159
- package/work-orders/assets/THEME-1-baseline-reference-sheet.html +0 -1352
- package/work-orders/assets/UCM-CHAT-1-ai-chat.html +0 -447
- package/work-orders/assets/UCM-THEME-13-dark-prototype.html +0 -390
- package/work-orders/assets/UCM-THEME-17-type-scale.html +0 -391
package/work-orders/MSG-6g.md
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
# WORK ORDER MSG-6g (ui-core-micha) — poll UI: tap-to-vote with result bars, no separate vote button
|
|
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 request 2026-08-03: *"bei der Abstimmung wäre es
|
|
11
|
-
schön, wenn es ähnlicher wie WhatsApp mit dem Balken sowie ohne Abstimmungsbutton möglich wäre."*
|
|
12
|
-
|
|
13
|
-
This is a **UX improvement, not a defect** — the current poll works. It is scoped separately so it never
|
|
14
|
-
blocks the messaging defect WOs.
|
|
15
|
-
|
|
16
|
-
## TIER
|
|
17
|
-
Tier 2 — shared-core UI consumed by every app. Independent `reviewer` + `ui_reviewer` mandatory.
|
|
18
|
-
|
|
19
|
-
## CURRENT STATE
|
|
20
|
-
|
|
21
|
-
`PollCard.jsx` renders each option as a MUI `FormControlLabel` with a `Checkbox` (multi-select) or radio,
|
|
22
|
-
labelled `"<text> (<vote_count>)"`, plus a separate **"Abstimmen"** submit button and a
|
|
23
|
-
**"Umfrage schliessen"** action for the owner. Voting is therefore a two-step interaction — select, then
|
|
24
|
-
submit — and the result is a bare number in parentheses.
|
|
25
|
-
|
|
26
|
-
## GOAL
|
|
27
|
-
|
|
28
|
-
Voting is one tap on the option itself, and the distribution is readable at a glance from proportional
|
|
29
|
-
bars rather than parenthesised counts.
|
|
30
|
-
|
|
31
|
-
## SCOPE
|
|
32
|
-
|
|
33
|
-
**A. Tap-to-vote — remove the separate submit button.**
|
|
34
|
-
|
|
35
|
-
- **Single-choice poll** (`allow_multiple: false`): tapping an option casts the vote immediately.
|
|
36
|
-
Tapping a different option moves the vote. The "Abstimmen" button goes away entirely.
|
|
37
|
-
- **Multi-select poll** (`allow_multiple: true`): tapping an option toggles that option immediately —
|
|
38
|
-
each tap is its own vote update, not a batched submit. **Confirm dcm's vote endpoint accepts the full
|
|
39
|
-
option-id set per call** (it takes `option_ids`, so a toggle sends the new complete set, not a delta);
|
|
40
|
-
if it does not, stop and report rather than inventing a delta protocol.
|
|
41
|
-
- Every tap is an optimistic UI update reconciled against the server response, following the pattern
|
|
42
|
-
`MessagingProvider` already uses for sends. **A failed vote must visibly revert** — a silently
|
|
43
|
-
swallowed failure that leaves the bar showing a vote nobody recorded is worse than the current
|
|
44
|
-
two-step flow.
|
|
45
|
-
- Keep "Umfrage schliessen" — that is an owner action, not part of voting.
|
|
46
|
-
|
|
47
|
-
**B. Result bars.**
|
|
48
|
-
|
|
49
|
-
- Each option shows a horizontal proportional bar plus its count and percentage, in place of the
|
|
50
|
-
`(<n>)` suffix.
|
|
51
|
-
- The viewer's own choice(s) are visually marked. `voted_option_ids` already exists for this and is
|
|
52
|
-
carried in the REST vote/create/close response — note the standing constraint documented in
|
|
53
|
-
`MessagingProvider.jsx`: it is **viewer-specific and never present on a `poll_updated` frame**, so
|
|
54
|
-
frame handling must not wipe a locally-known selection. Do not regress that; it was a deliberate fix.
|
|
55
|
-
- Denominator: total votes cast, not participant count. A poll's turnout relative to group size is a
|
|
56
|
-
different question and is **not** in scope.
|
|
57
|
-
- Percentages must not mislead at zero votes — define and implement the zero-vote rendering explicitly
|
|
58
|
-
rather than letting a division produce `NaN`.
|
|
59
|
-
|
|
60
|
-
**C. Closed polls.** A closed poll shows final bars and is not interactive. Verify the closed state is
|
|
61
|
-
visually unmistakable — with the submit button gone, "closed" can no longer be inferred from a missing
|
|
62
|
-
button.
|
|
63
|
-
|
|
64
|
-
## NON-GOALS / DO NOT TOUCH
|
|
65
|
-
- No backend change. If a scope-A requirement appears to need one, stop and report — that is a dcm WO.
|
|
66
|
-
- Do not change `voted_option_ids` handling in `applyFrame` beyond what B needs to read it (see the
|
|
67
|
-
merge-trap note above).
|
|
68
|
-
- Do not add a "who voted for what" disclosure. Per-voter visibility is a privacy decision nobody has
|
|
69
|
-
taken, and the existing `voters` data in `serialize_poll` is not licence to surface it.
|
|
70
|
-
- Do not change poll creation (`Composer`'s dialog) — this WO is the display and voting surface only.
|
|
71
|
-
- Do not touch `ReadTicks`, conversation titles, or anything in the MSG-6f scope.
|
|
72
|
-
|
|
73
|
-
## RISKS
|
|
74
|
-
- **Tap-to-vote removes the "are you sure" step.** On a phone a mis-tap now votes. Mitigate by making
|
|
75
|
-
the vote reversible (tap again / tap another) and by ensuring the optimistic state is obviously
|
|
76
|
-
distinct from the confirmed state; do not add a confirmation dialog, which would reinstate the
|
|
77
|
-
two-step flow this WO removes.
|
|
78
|
-
- Rapid successive taps race. The last write must win deterministically — state how (request ordering,
|
|
79
|
-
or discard stale responses by comparing against the latest local intent).
|
|
80
|
-
- Accessibility: an option becomes an interactive control with a name, a pressed/checked state and
|
|
81
|
-
keyboard operability. Bars are decorative; the count and percentage must be in the accessible name or
|
|
82
|
-
adjacent text, not conveyed by width alone. `ui_reviewer` should check this specifically.
|
|
83
|
-
- Every host app inherits the new interaction — a visible behaviour change beyond jg-ferien.
|
|
84
|
-
|
|
85
|
-
## REQUIRED TESTS TO WRITE
|
|
86
|
-
Narrow and behavioural. Do NOT run the full suite.
|
|
87
|
-
|
|
88
|
-
1. Single-choice: tapping an option votes without any further interaction; tapping a second option
|
|
89
|
-
moves the vote (the first is no longer selected, the counts reflect it).
|
|
90
|
-
2. Multi-select: tapping toggles one option on and off, and each tap sends the complete option-id set.
|
|
91
|
-
3. A rejected vote reverts the optimistic state and surfaces an error — assert the bar returns to its
|
|
92
|
-
prior value, not merely that an error appeared.
|
|
93
|
-
4. Bars render proportionally, including the **zero-vote** case, with no `NaN` and no division error.
|
|
94
|
-
5. The viewer's own selection survives a `poll_updated` frame that does not carry `voted_option_ids`
|
|
95
|
-
(the documented merge trap — this must not regress).
|
|
96
|
-
6. A closed poll is non-interactive: tapping an option does nothing and issues no request.
|
|
97
|
-
|
|
98
|
-
**Non-vacuity:** test 5 must fail if the merge-trap guard is removed. Test 3 must assert the reverted
|
|
99
|
-
value, not the presence of an error message.
|
|
100
|
-
|
|
101
|
-
## TEST SCOPE FOR THE GATE (orchestrator)
|
|
102
|
-
`PollCard` and its neighbours in the messaging test files. Not the full suite.
|
|
103
|
-
|
|
104
|
-
## TARGET REPO
|
|
105
|
-
`C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main` (or `develop` if present). Publish per
|
|
106
|
-
the repo's release flow; the consuming pin bump is a separate step.
|
|
107
|
-
|
|
108
|
-
## MINI-HANDOVER (pastable)
|
|
109
|
-
|
|
110
|
-
> Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`, or `develop` if present).
|
|
111
|
-
> Work order: `work-orders/MSG-6g.md` — read it fully, then follow the `orchestrate-codex` skill.
|
|
112
|
-
>
|
|
113
|
-
> **UX improvement, not a defect — this blocks nothing and is blocked by nothing.** Do not let it hold
|
|
114
|
-
> up the messaging defect WOs (dcm MSG-10, ucm MSG-6f).
|
|
115
|
-
>
|
|
116
|
-
> Frontend only: no backend change is in scope. If tap-to-vote appears to need one, stop and report.
|
|
117
|
-
> Two things that are easy to break and are called out in the WO: a failed vote must visibly revert
|
|
118
|
-
> (not fail silently), and the viewer's `voted_option_ids` must survive a `poll_updated` frame that
|
|
119
|
-
> does not carry it — that merge-trap guard was a deliberate earlier fix and test 5 pins it.
|
|
120
|
-
|
|
121
|
-
## PROGRESS CONTRACT
|
|
122
|
-
Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
|
|
123
|
-
relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
|
|
124
|
-
stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Part B — Implementation map (Orchestrator) / as-built record
|
|
129
|
-
|
|
130
|
-
**Codex was unavailable (out of credits) — implemented directly by the Orchestrator.**
|
|
131
|
-
|
|
132
|
-
### Blocker found and resolved before implementation
|
|
133
|
-
|
|
134
|
-
Line 38-40's own contingency ("confirm dcm's vote endpoint accepts the full option-id set
|
|
135
|
-
per call... if it does not, stop and report") fired for real: `vote_poll` (dcm
|
|
136
|
-
`services.py:247-259`) only cleared prior votes for single-choice polls; multi-select only
|
|
137
|
-
ever `bulk_create`d, so a smaller `option_ids` set never retracted an omitted vote —
|
|
138
|
-
untoggling was silently a no-op. Reported to the operator, who confirmed fixing it
|
|
139
|
-
directly in dcm rather than working around it client-side. Fixed and published as dcm
|
|
140
|
-
`2.40.1` — see dcm `work-orders/MSG-11.md`. `option_ids` is now authoritative (delete
|
|
141
|
-
anything no longer in the set, then re-create the requested set) for both poll kinds.
|
|
142
|
-
|
|
143
|
-
### Files changed
|
|
144
|
-
|
|
145
|
-
- `src/messaging/MessagingProvider.jsx`:
|
|
146
|
-
- New module-level `applyOptimisticVote(poll, previousSelectedIds, nextSelectedIds, userId)`
|
|
147
|
-
— adjusts only the options whose membership in the vote actually changed (count +/-1,
|
|
148
|
-
`voters` array +/- the acting user), leaves everything else untouched.
|
|
149
|
-
- New `pollVoteTokensRef` (per-poll "latest tap wins" token) alongside the existing
|
|
150
|
-
`activeIdRef`/`seenEventsRef`/`filtersRef`.
|
|
151
|
-
- `castPollVote(messageId, poll, optionIds)` rewritten: captures `previousPoll` from
|
|
152
|
-
`cache.messages[messageId].poll` (falling back to the `poll` param), patches the
|
|
153
|
-
optimistic projection into the cache immediately (same `patchMessage` shape
|
|
154
|
-
`toggleReaction` already uses), then on success reconciles with the server's poll, on
|
|
155
|
-
failure reverts to `previousPoll` — both guarded by the token so a stale response from
|
|
156
|
-
a since-superseded tap can't stomp a newer one.
|
|
157
|
-
- `src/messaging/PollCard.jsx`: full rewrite.
|
|
158
|
-
- Single-choice (`allow_multiple: false`) → `Radio` per option (native "tap a different
|
|
159
|
-
option moves the vote, tap the same one is a no-op" semantics — matches the WO's
|
|
160
|
-
stated design directly, no extra toggle-off logic needed).
|
|
161
|
-
- Multi-select → `Checkbox` per option (existing toggle semantics preserved).
|
|
162
|
-
- Whole-option-row tap target (`Box onClick`, `stopPropagation` on the control's own
|
|
163
|
-
`onClick` to avoid a double-fire) — WhatsApp-style, matches the operator's own framing.
|
|
164
|
-
- `LinearProgress` bar + `{{count}} ({{percent}}%)` (`MessagingPoll.OPTION_RESULT`, new
|
|
165
|
-
key) next to each option; `percent = totalVotes > 0 ? round(count/totalVotes*100) : 0`
|
|
166
|
-
— never `NaN`.
|
|
167
|
-
- Accessible name (`aria-label` on the control) carries option text + result text +
|
|
168
|
-
"you voted" — never width-only.
|
|
169
|
-
- Closed state: `Chip` with a lock icon + `MessagingPoll.CLOSED`, replacing the old
|
|
170
|
-
caption-only text (now the *only* closed signal, since the submit button is gone).
|
|
171
|
-
- Local `selected` state + a `voteTokenRef` mirror the provider's own token guard, since
|
|
172
|
-
`PollCard` is a "dumb" prop-driven component (documented in
|
|
173
|
-
`tests/messagingPollRendering.test.jsx`) — a rejected vote must revert visibly even
|
|
174
|
-
when the host never re-passes a fresh `message` prop from its own cache.
|
|
175
|
-
- Removed: the "Abstimmen" submit button and its `SELECT_OPTION` validation entirely.
|
|
176
|
-
- `src/i18n/messagingTranslations.ts`: removed now-orphaned `MessagingPoll.VOTE` and
|
|
177
|
-
`MessagingPoll.SELECT_OPTION`; added `MessagingPoll.OPTION_RESULT`.
|
|
178
|
-
|
|
179
|
-
### Tests
|
|
180
|
-
|
|
181
|
-
- `tests/messagingMsg6g.test.jsx` (new) — the 6 WO-required cases verbatim.
|
|
182
|
-
- `tests/messagingPollRendering.test.jsx` — updated for radio (not checkbox) on
|
|
183
|
-
single-choice, the new label/aria-label shape, and the closed-chip instead of a
|
|
184
|
-
disabled button; its `t` mock upgraded to interpolate `{{count}}`/`{{percent}}` (was
|
|
185
|
-
bare-key-only, insufficient to assert rendered numbers).
|
|
186
|
-
`tests/messagingInteractions.test.jsx`'s poll test rewritten for tap-to-vote (no submit
|
|
187
|
-
click). `tests/messagingCompactBubble.test.jsx`'s poll smoke test needed no change
|
|
188
|
-
(empty `options: []`, no control interaction).
|
|
189
|
-
|
|
190
|
-
### Verification
|
|
191
|
-
Full suite 234 → 240, `tsc` clean (pre-review).
|
|
192
|
-
|
|
193
|
-
### Independent review: `reviewer` + `ui_reviewer`, both mandatory (author = Orchestrator, not Codex)
|
|
194
|
-
|
|
195
|
-
**`reviewer` — 3 findings, all fixed:**
|
|
196
|
-
- **R1 (P1, real bug):** `castPollVote`'s optimistic count projection was built from the
|
|
197
|
-
caller-supplied `poll` argument, not from `previousPoll` (the value already freshly read
|
|
198
|
-
from `cache.messages[messageId].poll` two lines above, and correctly used as the revert
|
|
199
|
-
target on failure). For a second rapid tap (e.g. switching a single-choice vote A→B)
|
|
200
|
-
where the caller's `poll` reference hadn't yet been refreshed — exactly how `PollCard`
|
|
201
|
-
is documented to behave, a "dumb" prop-driven component — the count math computed
|
|
202
|
-
against the stale pre-first-tap baseline, landing a silently wrong transient
|
|
203
|
-
`vote_count` in the **shared cache** (not just local render state) until the server's
|
|
204
|
-
response eventually reconciled it away. Fixed: project from `previousPoll`. Verified
|
|
205
|
-
non-vacuous: reverted the fix, confirmed the new regression test below fails with the
|
|
206
|
-
exact predicted wrong number (4 instead of 5), then restored it.
|
|
207
|
-
- **R2 (P2, coverage gap):** no test asserted vote-count values across a multi-tap
|
|
208
|
-
sequence with non-zero starting counts — the zero-vote fixtures used elsewhere
|
|
209
|
-
happened to mask R1 via the `Math.max(0, ...)` clamp. Closed by a new
|
|
210
|
-
`messagingMsg6g.test.jsx` case using non-zero seed counts (5/2) and a genuinely
|
|
211
|
-
cache-connected reader alongside a *statically-propped* `PollCard` (the scenario that
|
|
212
|
-
actually reproduces the staleness, per R1's own trace — a cache-connected `PollCard`
|
|
213
|
-
re-renders with a fresh `poll` prop between taps and would not have caught this).
|
|
214
|
-
- **R3 (P3):** `applyOptimisticVote` could push a literal `undefined` into an option's
|
|
215
|
-
`voters` array when `user?.id` is momentarily unset (`AuthContext` not yet populated).
|
|
216
|
-
Fixed with a `userId != null` guard.
|
|
217
|
-
|
|
218
|
-
**`ui_reviewer` — 2 findings, both fixed:**
|
|
219
|
-
- **U1 (P2):** no visual distinction between an in-flight optimistic vote and a
|
|
220
|
-
server-confirmed one, contradicting the WO's own risk-mitigation requirement. Fixed by
|
|
221
|
-
reusing `ReactionBar.jsx`'s established `pending` Set convention — dims the tapped
|
|
222
|
-
option's bar/text while its request is in flight, but (unlike `ReactionBar`) does
|
|
223
|
-
**not** disable the control, since the WO explicitly requires rapid re-taps to remain
|
|
224
|
-
possible and race deterministically (last-write-wins via the token guard), not be
|
|
225
|
-
blocked.
|
|
226
|
-
- **U2 (P2):** single-choice rendered bare, ungrouped `Radio` controls — no `RadioGroup`,
|
|
227
|
-
no shared `name`. Diverged from this repo's own established radio idiom
|
|
228
|
-
(`AuthFactorRequirementCard.jsx`'s `RadioGroup` + `FormControlLabel`) and lost native
|
|
229
|
-
arrow-key navigation between options plus the screen-reader group announcement. Fixed
|
|
230
|
-
by wrapping single-choice options in a real `RadioGroup`; multi-select's `Checkbox`
|
|
231
|
-
options are unaffected (no equivalent ARIA grouping concept applies). Switching to
|
|
232
|
-
`FormControlLabel` for both control types also let the manual `Box onClick` +
|
|
233
|
-
`stopPropagation()` whole-row-tap hack be removed entirely — `FormControlLabel`'s own
|
|
234
|
-
`<label>` wrapping already makes the whole row natively clickable.
|
|
235
|
-
- Found and fixed while addressing U2, not itself a reviewer finding: `RadioGroup`'s own
|
|
236
|
-
`onChange` only ever hands back `event.target.value`, a DOM string, which would coerce
|
|
237
|
-
a non-string option id (dcm's are UUID strings today, but the type isn't a guaranteed
|
|
238
|
-
contract) — caught by `tests/messagingInteractions.test.jsx`'s existing numeric-id
|
|
239
|
-
fixture failing after the `RadioGroup` change. Fixed by keeping each `Radio`'s own
|
|
240
|
-
`onChange` (closure-typed, matching the `Checkbox` branch) instead of relying on the
|
|
241
|
-
group-level handler.
|
|
242
|
-
|
|
243
|
-
Re-verified after all fixes: full suite 234 → **241** (7 new: 6 WO-required cases +
|
|
244
|
-
1 R1/R2 regression), `tsc` clean.
|
package/work-orders/MSG-6h.md
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
# WORK ORDER MSG-6h (ui-core-micha) — attachment gallery (thumbnail/lightbox/right-click download), reaction overlap
|
|
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 report 2026-08-03 from a staging screenshot, seen
|
|
11
|
-
now for the first time end-to-end because `django-core-micha` `MSG-10` just fixed attachment uploads
|
|
12
|
-
(they were unconditionally rejected before — this display code has effectively never been exercised
|
|
13
|
-
against a real upload on the platform path).
|
|
14
|
-
|
|
15
|
-
## TIER
|
|
16
|
-
Tier 2 — shared-core UI consumed by every app. Independent `reviewer` + `ui_reviewer` mandatory.
|
|
17
|
-
|
|
18
|
-
## SCOPE
|
|
19
|
-
|
|
20
|
-
**A. Attachment gallery.**
|
|
21
|
-
|
|
22
|
-
Current state (`AttachmentList.jsx`): each attachment renders as a `Button` containing a 36×36px
|
|
23
|
-
thumbnail (if the file is an image and its thumbnail loaded) plus a text label
|
|
24
|
-
`t('MessagingAttachments.DOWNLOAD', { name: nameOf(attachment) })`. `nameOf` is
|
|
25
|
-
`attachment.filename || attachment.name || attachment.id` — since dcm's `serialize_attachment` has
|
|
26
|
-
never returned `filename` (see `django-core-micha` `work-orders/MSG-12.md`, its companion, fixing
|
|
27
|
-
that), this **always** fell through to the raw UUID `id`. Clicking anywhere on the button downloads
|
|
28
|
-
the file — there is no preview step.
|
|
29
|
-
|
|
30
|
-
Operator's target shape, verbatim: *"bitte kleines Vorschaubild, bei Klick darauf grosse Vorschau,
|
|
31
|
-
rechtsklick zum Kontextmenu mit Download (analog Whatsapp)"* — a small thumbnail; clicking it opens a
|
|
32
|
-
large preview (lightbox); right-click opens a context menu with a download action.
|
|
33
|
-
|
|
34
|
-
Build this:
|
|
35
|
-
- **Image attachments**: small thumbnail (reuse the existing `getAttachmentThumbnail` fetch — already
|
|
36
|
-
wired). Click → a lightbox (MUI `Dialog` or `Modal`) showing the full-size image, fetched via the
|
|
37
|
-
existing `getAttachment` blob endpoint. Right-click (`onContextMenu`) → a small context menu (MUI
|
|
38
|
-
`Menu`, matching the pattern already used for message actions in `MessageBubble.jsx`) with a
|
|
39
|
-
Download action, calling the same download logic `AttachmentList` already has.
|
|
40
|
-
- **Non-image attachments** (no thumbnail available): keep a compact file-type indicator (icon +
|
|
41
|
-
filename, once `MSG-12` supplies a real one) as the tap target; single click downloads directly
|
|
42
|
-
(there is nothing to preview) — right-click still offers the same context menu for consistency, even
|
|
43
|
-
though its one item duplicates the click action.
|
|
44
|
-
- Filename: once `MSG-12` ships, `nameOf` should read the real `filename` first; keep the existing
|
|
45
|
-
`|| attachment.id` fallback for interop with a not-yet-bumped dcm pin (an older host may still be on
|
|
46
|
-
a dcm version that omits it) — never let a missing filename break rendering, just fall back visibly.
|
|
47
|
-
- Keep the download-only design principle: bytes never touch anything but the existing authenticated
|
|
48
|
-
`getAttachment`/`getAttachmentThumbnail` blob fetches already in `MessagingProvider`. No new
|
|
49
|
-
endpoints, no new byte-handling path.
|
|
50
|
-
|
|
51
|
-
**B. Reaction bar overlap.**
|
|
52
|
-
|
|
53
|
-
`MessageBubble.jsx:139` renders `ReactionBar` as a sibling *after* the bubble's `Paper`, with
|
|
54
|
-
`sx={{ mt: -0.5, ml: 0.75, position: 'relative', zIndex: 1 }}` — a small negative top margin
|
|
55
|
-
deliberately pulls it up to overlap the bubble's bottom edge (a common "chip peeking out" treatment).
|
|
56
|
-
Operator report: this overlap now reads as broken/unnatural, screenshotted on a message that has
|
|
57
|
-
attachments — worth checking specifically whether the fixed `-0.5` offset was tuned against a
|
|
58
|
-
plain-text bubble and no longer sits right against the bottom of an attachment row or (from `MSG-6g`)
|
|
59
|
-
a poll's result bars, both of which changed the bubble's actual bottom content since this offset was
|
|
60
|
-
last touched.
|
|
61
|
-
|
|
62
|
-
**Live-verify against the operator's original screenshot before considering this closed** — the
|
|
63
|
-
Orchestrator does not have staging credentials this round; if a local repro isn't conclusive, say so
|
|
64
|
-
and ask the operator to confirm the fix visually rather than guessing further.
|
|
65
|
-
|
|
66
|
-
## NON-GOALS / DO NOT TOUCH
|
|
67
|
-
- Do not touch `PollCard.jsx`, `ReadTicks.jsx`, or conversation titles — unrelated recent WOs.
|
|
68
|
-
- Do not add any new backend endpoint — scope A only consumes what already exists (plus `MSG-12`'s
|
|
69
|
-
additive `filename` field).
|
|
70
|
-
- Do not change the reaction *toggle* logic (`ReactionBar.jsx`'s `react`/`pending` mechanics) — only
|
|
71
|
-
its positioning relative to the bubble, scope B.
|
|
72
|
-
- Do not implement a generic lightbox/gallery component library dependency — build directly with MUI
|
|
73
|
-
primitives already used elsewhere in this codebase (`Dialog`, `Menu`), matching existing patterns.
|
|
74
|
-
|
|
75
|
-
## RISKS
|
|
76
|
-
- A right-click context menu on a message bubble already exists for message-level actions
|
|
77
|
-
(`MessageBubble.jsx`'s `openMenu`/`canOpenMenu`, triggered by `onContextMenu`). An attachment's own
|
|
78
|
-
right-click context menu must not fight with or accidentally trigger the message-level one when the
|
|
79
|
-
attachment sits inside the bubble — `event.stopPropagation()` where appropriate.
|
|
80
|
-
- Lightbox must be keyboard-dismissable (Escape) and trap focus reasonably — it is a modal.
|
|
81
|
-
- The thumbnail fetch already exists and works (`AttachmentList.jsx`'s current `useEffect`); reuse it,
|
|
82
|
-
don't re-invent — but the full-size lightbox fetch (`getAttachment`) is a new call path, verify it
|
|
83
|
-
doesn't double-fetch what the thumbnail effect already has cached.
|
|
84
|
-
|
|
85
|
-
## REQUIRED TESTS TO WRITE
|
|
86
|
-
Narrow and behavioural. Do NOT run the full suite.
|
|
87
|
-
|
|
88
|
-
1. An image attachment renders a thumbnail; clicking it opens a lightbox showing the full image
|
|
89
|
-
(fetched via `getAttachment`).
|
|
90
|
-
2. Right-clicking an image attachment opens a context menu with a Download action; selecting it
|
|
91
|
-
downloads (same assertion shape as the existing pre-redesign download test, if one exists — check).
|
|
92
|
-
3. A non-image attachment (no `thumbnail_url`) renders a file indicator, not a broken image; clicking
|
|
93
|
-
it downloads directly.
|
|
94
|
-
4. `nameOf` prefers `attachment.filename` when present, falls back to `attachment.id` when absent
|
|
95
|
-
(interop with a dcm pin that predates `MSG-12`) — assert both cases render without crashing.
|
|
96
|
-
5. The attachment's own context menu does not also trigger the message-level action menu (no
|
|
97
|
-
double-open, no propagation leak) — regression guard given `MessageBubble.jsx` already has its own
|
|
98
|
-
`onContextMenu`.
|
|
99
|
-
|
|
100
|
-
## TEST SCOPE FOR THE GATE (orchestrator)
|
|
101
|
-
Attachment- and reaction-bar-adjacent messaging test files. Not the full suite.
|
|
102
|
-
|
|
103
|
-
## TARGET REPO
|
|
104
|
-
`C:\Users\biglmi\Documents\webapps\ui-core-micha`. Branch `main`. Publish per the repo's release flow;
|
|
105
|
-
consuming pin bumps (jg-ferien, and `django-core-micha` `MSG-12` for the filename) are separate,
|
|
106
|
-
subsequent steps — **but per standing feedback this session, do not report this WO as "done" until
|
|
107
|
-
those consuming steps have actually landed**, not merely published.
|
|
108
|
-
|
|
109
|
-
## MINI-HANDOVER (pastable)
|
|
110
|
-
|
|
111
|
-
> Repo: `C:\Users\biglmi\Documents\webapps\ui-core-micha` (branch `main`). Work order:
|
|
112
|
-
> `work-orders/MSG-6h.md` — read it fully, then follow the `orchestrate-codex` skill.
|
|
113
|
-
>
|
|
114
|
-
> Two independent scopes, both operator-reported live on staging: (A) `AttachmentList.jsx` needs a
|
|
115
|
-
> real thumbnail-click-lightbox / right-click-download-menu gallery, WhatsApp-style, instead of a bare
|
|
116
|
-
> download button showing a raw UUID (the UUID-fallback root cause is fixed separately in
|
|
117
|
-
> `django-core-micha` `MSG-12`, a companion WO — consume its new `filename` field with a graceful
|
|
118
|
-
> fallback for an older pin). (B) `MessageBubble.jsx:139`'s `ReactionBar` has a fixed `mt: -0.5`
|
|
119
|
-
> negative-margin overlap trick that now reads as visually broken against certain bubble content
|
|
120
|
-
> (attachments, and possibly `MSG-6g`'s poll bars) — investigate and fix, but flag if it needs a live
|
|
121
|
-
> staging screenshot to confirm rather than guessing blind.
|
|
122
|
-
|
|
123
|
-
## PROGRESS CONTRACT
|
|
124
|
-
Emit `PLAN: <steps>` up front, then a single-line `PROGRESS: [<n>/<total>] <action>` before every
|
|
125
|
-
relevant action and `PROGRESS: [<n>/<total>] done` on completion, spaced so no gap exceeds ~2 min,
|
|
126
|
-
stdout unbuffered, and exactly one final `RESULT: DONE|BLOCKED <reason>`.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## Part B — Implementation map (Orchestrator) / as-built record
|
|
131
|
-
|
|
132
|
-
**Codex was unavailable (out of credits) — implemented directly by the Orchestrator.**
|
|
133
|
-
|
|
134
|
-
### Scope A — `src/messaging/AttachmentList.jsx` (full rewrite)
|
|
135
|
-
|
|
136
|
-
- Each attachment is now a 64×64 `ButtonBase` tile (not a full-width text button): an image
|
|
137
|
-
attachment shows its existing thumbnail fetch (unchanged `getAttachmentThumbnail` `useEffect`,
|
|
138
|
-
reused verbatim); a non-image attachment shows a generic file icon +
|
|
139
|
-
filename (`InsertDriveFileOutlinedIcon`).
|
|
140
|
-
- Click: image → `openLightbox` (fetches the full-resolution blob via the existing `getAttachment`,
|
|
141
|
-
cached per-attachment in a `fullUrlCacheRef` so a re-open within the same mount doesn't re-fetch);
|
|
142
|
-
non-image → `download` (unchanged logic, just relocated).
|
|
143
|
-
- Right-click (`onContextMenu`): opens a MUI `Menu` (anchored to click position) with one Download
|
|
144
|
-
item, for both image and non-image attachments. `event.stopPropagation()` inside the handler stops
|
|
145
|
-
it from also reaching `MessageBubble.jsx`'s own bubble-level `onContextMenu` (verified with a
|
|
146
|
-
dedicated regression test, item 5 below).
|
|
147
|
-
- Lightbox: a MUI `Dialog` with the full image + a close `IconButton` (also closes via the Dialog's
|
|
148
|
-
own backdrop-click/Escape handling, native to MUI).
|
|
149
|
-
- `nameOf(attachment)` unchanged in shape (`filename || name || id`), now actually resolves to a real
|
|
150
|
-
name once the host's dcm pin includes `MSG-12`; falls back to `id` for an older pin (test 4).
|
|
151
|
-
- 3 new i18n keys: `MessagingAttachments.PREVIEW`, `.CLOSE_PREVIEW`, `.DOWNLOAD_ACTION`. Existing
|
|
152
|
-
`.LABEL`/`.DOWNLOAD` kept — `.DOWNLOAD` is now also the accessible name for a non-image tile (no
|
|
153
|
-
visible "Download X" text on it any more, per the new compact-tile design — updated one pre-existing
|
|
154
|
-
test in `messagingCompactBubble.test.jsx` that asserted on the old visible text).
|
|
155
|
-
|
|
156
|
-
### Scope B — `src/messaging/MessageBubble.jsx:139`
|
|
157
|
-
|
|
158
|
-
- `ReactionBar`'s `sx` changed from `{ mt: -0.5, ml: 0.75, position: 'relative', zIndex: 1 }` to
|
|
159
|
-
`{ mt: 0.5, ml: 0.75 }` — a small **positive** gap instead of the negative-margin "peek from the
|
|
160
|
-
bubble's bottom edge" trick. That trick pulled the chip up into whatever sits immediately above it
|
|
161
|
-
in normal document flow, which used to always be plain text or the compact meta row; since MSG-6g
|
|
162
|
-
added poll bars and this WO's own gallery redesign changed the attachment row's height/borders, the
|
|
163
|
-
fixed offset started visually cutting into bordered, boxier content instead of text. Also dropped
|
|
164
|
-
`position: relative` / `zIndex: 1`, no longer needed without the overlap.
|
|
165
|
-
- **Not independently visually re-verified against the operator's original staging screenshot** — no
|
|
166
|
-
staging credentials this round (same limitation noted when the ucm pin bump was last reported). The
|
|
167
|
-
fix is a conservative, low-risk "stop overlapping anything" change; flagging per the WO's own
|
|
168
|
-
instruction to say so rather than claim a live-confirmed fix.
|
|
169
|
-
|
|
170
|
-
### Tests
|
|
171
|
-
`tests/messagingMsg6h.test.jsx` (new) — the 5 WO-required cases. `tests/messagingCompactBubble.test.jsx`
|
|
172
|
-
updated (pre-existing test asserted on now-removed visible DOWNLOAD text). No other existing test
|
|
173
|
-
needed a change (`messagingComposer.test.jsx`'s AttachmentList smoke test only checked the outer
|
|
174
|
-
`aria-label`, unaffected).
|
|
175
|
-
|
|
176
|
-
### Verification (pre-review)
|
|
177
|
-
Full suite 241 → 246 (5 new), `tsc` clean.
|
|
178
|
-
|
|
179
|
-
### Independent review: `reviewer` + `ui_reviewer`, both mandatory (author = Orchestrator, not Codex)
|
|
180
|
-
|
|
181
|
-
Both reviews converged on the same core gap from different angles — fixed together in one pass.
|
|
182
|
-
|
|
183
|
-
**`reviewer` — 3 findings:**
|
|
184
|
-
- **R1 (P2, real regression):** the pre-diff design let every attachment (image or not) download via a
|
|
185
|
-
direct click/Enter/Space on a plain `Button`. The redesign made an image's click open the lightbox
|
|
186
|
-
instead, which had only a Close button — the *only* way left to download an image was a real mouse
|
|
187
|
-
right-click, unreachable by keyboard or (with no long-press) touch. **Fixed**: the lightbox now has
|
|
188
|
-
its own Download `IconButton`, so download is reachable via click, Tab+Enter, or touch, without ever
|
|
189
|
-
needing the context menu.
|
|
190
|
-
- **R2 (P2):** the new lightbox fetch had no loading or error state — a click gave no feedback until
|
|
191
|
-
the network round trip resolved, and a failure silently no-opped. **Fixed**: `lightbox.status`
|
|
192
|
-
(`'loading' | 'ready' | 'error'`) drives a `CircularProgress` while fetching and an `Alert` on
|
|
193
|
-
failure; a stale in-flight fetch (opened, then a *different* attachment opened before the first
|
|
194
|
-
resolves) is guarded by comparing the resolved attachment's id against the current lightbox state
|
|
195
|
-
before applying the result.
|
|
196
|
-
- **R3 (P3):** the lightbox `Dialog` had no accessible name. **Fixed**: `aria-label` on the `Dialog`
|
|
197
|
-
itself, set to the previewed attachment's name.
|
|
198
|
-
|
|
199
|
-
Verified correct, no bug: object-URL lifecycle (no leak/double-revoke/revoke-while-displayed), the
|
|
200
|
-
context-menu `stopPropagation` (traced against React's synthetic event dispatch + the dedicated test),
|
|
201
|
-
no stale-closure risk in the menu's download handler, `fullUrlCacheRef` can't go stale across messages
|
|
202
|
-
(`Thread.jsx` keys `MessageBubble` by `message.id`, always unmounts on swap), i18n coverage.
|
|
203
|
-
|
|
204
|
-
**`ui_reviewer` — 6 findings:**
|
|
205
|
-
- **U1 (P1, same root cause as R1):** touch users had no way to download an image at all (no
|
|
206
|
-
long-press implemented, lightbox had no download control). **Fixed by the same lightbox Download
|
|
207
|
-
button as R1** — one fix closes both findings.
|
|
208
|
-
- **U2 (P2):** no visual affordance signals a tile is right-clickable, and right-click-for-download is
|
|
209
|
-
a non-obvious pattern without a fallback for a mouse user who doesn't know it. **Resolved by the R1/U1
|
|
210
|
-
fix, not a separate change**: right-click is no longer the *only* path to anything — an image is
|
|
211
|
-
fully reachable via primary click → lightbox → visible Download button, and a non-image already
|
|
212
|
-
downloads on primary click. Right-click remains a discoverable-if-you-know-it shortcut, not a
|
|
213
|
-
required, hidden one; adding a second, redundant hover-reveal affordance on top of an already-fully-
|
|
214
|
-
reachable primary interaction would be scope creep past what the finding's own root concern requires.
|
|
215
|
-
- **U3 (P2):** a non-image tile's filename truncates in a 64px box with no way to recover the full name
|
|
216
|
-
before downloading. **Fixed**: `title={nameOf(attachment)}` on the tile (native browser tooltip on
|
|
217
|
-
hover).
|
|
218
|
-
- **U4 (P3):** the French `PREVIEW` translation (`'Aperçu de {{name}}'`, noun-first) broke grammatical
|
|
219
|
-
parallelism with the sibling `DOWNLOAD`/`DOWNLOAD_ACTION` (verb-first). **Fixed**: `'Prévisualiser
|
|
220
|
-
{{name}}'`.
|
|
221
|
-
- **U5 (P3):** the lightbox close button omitted the `boxShadow` the codebase's analogous floating-icon
|
|
222
|
-
pattern (`MessageBubble.jsx`'s hover-revealed `MoreVertIcon`) uses for contrast. **Fixed**: both the
|
|
223
|
-
new Download button and the Close button now carry `boxShadow: 1`.
|
|
224
|
-
- **U6 (P3, not fixed, disclosed):** `openLightbox`'s failure path is a silent no-op if `getAttachment`
|
|
225
|
-
is bypassed some other way — the reviewer themselves noted this mirrors `download()`'s own
|
|
226
|
-
pre-existing silent-failure shape, not a new defect class. Left as-is, consistent with that existing
|
|
227
|
-
convention elsewhere in this component; R2's loading/error state above already covers the actual
|
|
228
|
-
reachable failure path (a real fetch rejection).
|
|
229
|
-
|
|
230
|
-
### Not independently visually re-verified
|
|
231
|
-
Scope B's `ReactionBar` spacing fix (`MessageBubble.jsx:145`) was not confirmed against the operator's
|
|
232
|
-
original staging screenshot — no staging credentials this round. Both reviewers examined it by static
|
|
233
|
-
code reading only and found the fix low-risk (a strict `mt` sign flip, no z-stacking or other side
|
|
234
|
-
effect traced), but this is a judgment call, not a live-rendered confirmation. Flagging per the WO's
|
|
235
|
-
own instruction rather than claiming a visual fix I have not actually seen.
|
|
236
|
-
|
|
237
|
-
### Re-verification after all fixes
|
|
238
|
-
Full suite 241 → 249 (8 new: 5 original + 3 closing R1/R2), `tsc` clean.
|