Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e713d47319 | |||
| b361d43088 | |||
| a33d28a7ae |
+11
-11
@@ -309,7 +309,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
|
|
||||||
| # | Area | File | Lines | Issue | Native Pattern |
|
| # | Area | File | Lines | Issue | Native Pattern |
|
||||||
| :-- | :------------------------- | :---------------------------------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| :-- | :------------------------- | :---------------------------------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| N5 | Read Receipts | `ReadReceiptAvatars.tsx` | 62–137 | Trigger button is raw `<button>` with `onMouseEnter`/`onMouseLeave` JS style mutation for hover state | All interactive elements use `useHover` from `react-aria` and folds variant system for hover; direct `.style` mutation used nowhere else on buttons |
|
| N5 | Read Receipts | `ReadReceiptAvatars.tsx` | 62–137 | Trigger button is raw `<button>` with `onMouseEnter`/`onMouseLeave` JS style mutation for hover state — **FIXED**: hover/focus emphasis moved to co-located `ReadReceiptAvatars.css.ts` (`:hover`/`:focus-visible`), no JS `.style` mutation | All interactive elements use `useHover` from `react-aria` and folds variant system for hover; direct `.style` mutation used nowhere else on buttons |
|
||||||
| N6 | Read Receipts | `ReadReceiptAvatars.tsx` & `Message.tsx` | 32–56 / 268–283 | Two code paths open `EventReaders`: avatar-pill path uses `useModalStyle(360)` for mobile fullscreen; context-menu path (`MessageReadReceiptItem`) does not — on mobile the context menu path opens a fixed-size non-fullscreen modal for the same content | All modals that share a layout variant use `useModalStyle` consistently; `MessageReadReceiptItem` was not updated when `useModalStyle` was added |
|
| N6 | Read Receipts | `ReadReceiptAvatars.tsx` & `Message.tsx` | 32–56 / 268–283 | Two code paths open `EventReaders`: avatar-pill path uses `useModalStyle(360)` for mobile fullscreen; context-menu path (`MessageReadReceiptItem`) does not — on mobile the context menu path opens a fixed-size non-fullscreen modal for the same content | All modals that share a layout variant use `useModalStyle` consistently; `MessageReadReceiptItem` was not updated when `useModalStyle` was added |
|
||||||
| N7 | Delivery Status | `Message.tsx` | 89–148 | `DeliveryStatus` renders Unicode glyphs (`⟳ ✓ ✕`) in a `<span>` with `fontSize: '10px'` instead of folds `<Icon>` components — **FIXED**: replaced with `Icons.Check/Cross/Send` via `<Icon size="100">` | `Icons.Check`, `Icons.Cross`, etc. are used for all other status glyphs; folds `Text` size tokens for all supplementary text |
|
| N7 | Delivery Status | `Message.tsx` | 89–148 | `DeliveryStatus` renders Unicode glyphs (`⟳ ✓ ✕`) in a `<span>` with `fontSize: '10px'` instead of folds `<Icon>` components — **FIXED**: replaced with `Icons.Check/Cross/Send` via `<Icon size="100">` | `Icons.Check`, `Icons.Cross`, etc. are used for all other status glyphs; folds `Text` size tokens for all supplementary text |
|
||||||
| N8 | GIF Picker | `GifPicker.tsx` | 83–124 | GIF picker container uses fully bespoke inline styles (`borderRadius: '12px'`, `boxShadow: '0 8px 32px rgba(0,0,0,0.4)'`, raw `rgba` border) — two separate style sets for TDS and non-TDS paths | `EmojiBoard` has no caller-applied container styling; folds components handle their own surface internally via design tokens |
|
| N8 | GIF Picker | `GifPicker.tsx` | 83–124 | GIF picker container uses fully bespoke inline styles (`borderRadius: '12px'`, `boxShadow: '0 8px 32px rgba(0,0,0,0.4)'`, raw `rgba` border) — two separate style sets for TDS and non-TDS paths | `EmojiBoard` has no caller-applied container styling; folds components handle their own surface internally via design tokens |
|
||||||
@@ -318,19 +318,19 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
| N11 | AvatarDecoration | `AvatarDecoration.tsx` | 5 / 38–41 | Fixed 8px inset on all sides regardless of avatar size — at folds size `"200"` (~32px) the decoration bleeds 50% of the avatar diameter, clipping against `overflow: hidden` parent containers in member lists. **Inset issue still OPEN.** _Related regression fixed in `useAvatarDecoration.ts`_: the decoration fetch cached **all** failures (including transient 429/5xx) as "no decoration" permanently for the session, so a single rate-limited burst (member list / timeline mount many avatars at once) would make decorations vanish until a full reload. Now only a genuine 404 is cached; transient errors retry on the next mount. | Folds `Avatar` and `PresenceRingAvatar` do not emit overflow outside their bounding box |
|
| N11 | AvatarDecoration | `AvatarDecoration.tsx` | 5 / 38–41 | Fixed 8px inset on all sides regardless of avatar size — at folds size `"200"` (~32px) the decoration bleeds 50% of the avatar diameter, clipping against `overflow: hidden` parent containers in member lists. **Inset issue still OPEN.** _Related regression fixed in `useAvatarDecoration.ts`_: the decoration fetch cached **all** failures (including transient 429/5xx) as "no decoration" permanently for the session, so a single rate-limited burst (member list / timeline mount many avatars at once) would make decorations vanish until a full reload. Now only a genuine 404 is cached; transient errors retry on the next mount. | Folds `Avatar` and `PresenceRingAvatar` do not emit overflow outside their bounding box |
|
||||||
| N12 | MediaGallery Drawer | `MediaGallery.tsx` | 651–661 | Drawer uses `position: 'fixed'` with hardcoded `width: '320px'` as inline styles on a `<Box>` — **FIXED**: moved positioning/width into co-located `MediaGallery.css.ts` using `toRem(320)` + a `max-width: 750px` full-screen media query (mirrors `MembersDrawer`); border/header now use `config.borderWidth`/`config.space` tokens. Added Escape-to-close on the panel (previously only the lightbox handled Escape). **Full chrome redesign (round 2)** to match native conventions: panel + header switched from `Surface` to `Background` variant (matching `MembersDrawer`/Saved Messages); header now `Text size="H4"` + plain close `IconButton` (dropped the bespoke tooltip-wrapped button); tabs moved to a bordered toolbar strip with the `variant={active?'Primary':'Secondary'} fill={active?'Solid':'Soft'}` pattern from `PolicyListViewer` and now show per-tab counts; the centered "lines + label" month divider replaced with a left-aligned group label (Cinny group-label pattern); thumbnail tiles moved hover/focus styling to CSS `:hover`/`:focus-visible` (no JS hover state) and into `MediaGallery.css.ts`; file rows + grid tokenized. **Docking fix (round 3)** — the core of the finding: the gallery was a `position: fixed` overlay floating over the timeline, mounted from `RoomViewHeader`. It is now a **docked flex sibling** in the room layout row, exactly like `MembersDrawer`: open state lifted to a `mediaGalleryAtom` (mirrors `bookmarksPanelAtom`), rendered in `Room.tsx` with a vertical `Line` separator on desktop and `key={room.roomId}` to reset per room; the CSS is static-width on desktop and only `position: fixed; inset: 0` full-screen on mobile (identical strategy to `MembersDrawer.css`). It now shares the row with the timeline instead of overlapping it. | `MembersDrawer` uses a vanilla-extract class with `width: toRem(266)` and is placed by the layout system, not `position: fixed`. 54px width discrepancy also breaks visual rhythm if both panels could be open |
|
| N12 | MediaGallery Drawer | `MediaGallery.tsx` | 651–661 | Drawer uses `position: 'fixed'` with hardcoded `width: '320px'` as inline styles on a `<Box>` — **FIXED**: moved positioning/width into co-located `MediaGallery.css.ts` using `toRem(320)` + a `max-width: 750px` full-screen media query (mirrors `MembersDrawer`); border/header now use `config.borderWidth`/`config.space` tokens. Added Escape-to-close on the panel (previously only the lightbox handled Escape). **Full chrome redesign (round 2)** to match native conventions: panel + header switched from `Surface` to `Background` variant (matching `MembersDrawer`/Saved Messages); header now `Text size="H4"` + plain close `IconButton` (dropped the bespoke tooltip-wrapped button); tabs moved to a bordered toolbar strip with the `variant={active?'Primary':'Secondary'} fill={active?'Solid':'Soft'}` pattern from `PolicyListViewer` and now show per-tab counts; the centered "lines + label" month divider replaced with a left-aligned group label (Cinny group-label pattern); thumbnail tiles moved hover/focus styling to CSS `:hover`/`:focus-visible` (no JS hover state) and into `MediaGallery.css.ts`; file rows + grid tokenized. **Docking fix (round 3)** — the core of the finding: the gallery was a `position: fixed` overlay floating over the timeline, mounted from `RoomViewHeader`. It is now a **docked flex sibling** in the room layout row, exactly like `MembersDrawer`: open state lifted to a `mediaGalleryAtom` (mirrors `bookmarksPanelAtom`), rendered in `Room.tsx` with a vertical `Line` separator on desktop and `key={room.roomId}` to reset per room; the CSS is static-width on desktop and only `position: fixed; inset: 0` full-screen on mobile (identical strategy to `MembersDrawer.css`). It now shares the row with the timeline instead of overlapping it. | `MembersDrawer` uses a vanilla-extract class with `width: toRem(266)` and is placed by the layout system, not `position: fixed`. 54px width discrepancy also breaks visual rhythm if both panels could be open |
|
||||||
| N13 | ScheduledMessagesTray | `ScheduledMessagesTray.tsx` | 108–126 | Collapsible tray header is `<Box as="button">` with `cursor: 'pointer'` inline style and no folds variant — no hover state, no focus ring — **FIXED**: replaced with folds `<Button variant="Secondary" fill="None" radii="0">` using `before`/`after` icon props (gains design-system hover/focus) | All clickable header/toggle elements in the room view use folds `<Button>` or `<IconButton>` with explicit variants for hover/focus; `<Box as="button">` with no variant is used nowhere else |
|
| N13 | ScheduledMessagesTray | `ScheduledMessagesTray.tsx` | 108–126 | Collapsible tray header is `<Box as="button">` with `cursor: 'pointer'` inline style and no folds variant — no hover state, no focus ring — **FIXED**: replaced with folds `<Button variant="Secondary" fill="None" radii="0">` using `before`/`after` icon props (gains design-system hover/focus) | All clickable header/toggle elements in the room view use folds `<Button>` or `<IconButton>` with explicit variants for hover/focus; `<Box as="button">` with no variant is used nowhere else |
|
||||||
| N14 | ForwardMessageDialog | `ForwardMessageDialog.tsx` | 137–154 | Dialog uses `<Modal>` but has no `<Header>` component and no close `<IconButton>` — only way to close is clicking outside | Every other modal using `<Modal>` or `<Box role="dialog">` includes a `<Header>` with a close `<IconButton>` in the top-right (EditHistoryModal, LeaveRoomPrompt, ScheduleMessageModal, RemindMeDialog, etc.) |
|
| N14 | ForwardMessageDialog | `ForwardMessageDialog.tsx` | 137–154 | Dialog uses `<Modal>` but has no `<Header>` component and no close `<IconButton>` — only way to close is clicking outside — **FIXED**: added a folds `<Header variant="Surface" size="500">` with the title + close `<IconButton radii="300">`, matching every other modal | Every other modal using `<Modal>` or `<Box role="dialog">` includes a `<Header>` with a close `<IconButton>` in the top-right (EditHistoryModal, LeaveRoomPrompt, ScheduleMessageModal, RemindMeDialog, etc.) |
|
||||||
| N15 | ScheduleMessageModal | `ScheduleMessageModal.tsx` | 180–193 | Modal root is `<Box as="form" role="dialog">` with manually assembled `borderRadius: config.radii.R400`/`boxShadow` | `ForwardMessageDialog` uses folds `<Modal size="400">` with `R500` radius; the R400 vs R500 mismatch is visible when both dialogs appear in the same session |
|
| N15 | ScheduleMessageModal | `ScheduleMessageModal.tsx` | 180–193 | Modal root is `<Box as="form" role="dialog">` with manually assembled `borderRadius: config.radii.R400`/`boxShadow` | `ForwardMessageDialog` uses folds `<Modal size="400">` with `R500` radius; the R400 vs R500 mismatch is visible when both dialogs appear in the same session |
|
||||||
| N16 | Presence Picker | `SettingsTab.tsx` | 118–144 | Presence trigger dot is raw `<button>` with `position: absolute; bottom: 2; right: 2` inline and no folds focus ring; no tooltip — **FIXED**: wrapped the trigger in a folds `TooltipProvider` (shows "Status: …"); replaced the undefined `var(--bg-surface)` with `color.Background.Container`. Kept the absolute-positioned `<button>` (it overlays the avatar corner; a full `IconButton` would be too large for the dot). | Every other sidebar icon button uses folds `IconButton` with `SidebarItemTooltip` and `TooltipProvider` |
|
| N16 | Presence Picker | `SettingsTab.tsx` | 118–144 | Presence trigger dot is raw `<button>` with `position: absolute; bottom: 2; right: 2` inline and no folds focus ring; no tooltip — **FIXED**: wrapped the trigger in a folds `TooltipProvider` (shows "Status: …"); replaced the undefined `var(--bg-surface)` with `color.Background.Container`. Kept the absolute-positioned `<button>` (it overlays the avatar corner; a full `IconButton` would be too large for the dot). | Every other sidebar icon button uses folds `IconButton` with `SidebarItemTooltip` and `TooltipProvider` |
|
||||||
| N17 | Presence Picker | `SettingsTab.tsx` | 80–86 | `PresencePicker` `FocusTrap` missing `escapeDeactivates: stopPropagation` and `isKeyForward`/`isKeyBackward` — **FIXED**: added all three options, matching the theme selector / sort menus | Every other `PopOut`+`FocusTrap`+`Menu` combo supplies both (theme selector `General.tsx:143–160`, `SettingsSelect`, sort menus) — without it Escape bubbles past the trap and arrow-key navigation is absent |
|
| N17 | Presence Picker | `SettingsTab.tsx` | 80–86 | `PresencePicker` `FocusTrap` missing `escapeDeactivates: stopPropagation` and `isKeyForward`/`isKeyBackward` — **FIXED**: added all three options, matching the theme selector / sort menus | Every other `PopOut`+`FocusTrap`+`Menu` combo supplies both (theme selector `General.tsx:143–160`, `SettingsSelect`, sort menus) — without it Escape bubbles past the trap and arrow-key navigation is absent |
|
||||||
| N18 | Profile Selects | `Profile.tsx` | 547–575 / 816–848 | `ProfileStatus` auto-clear and `ProfileTimezone` selectors are native `<select>` elements with hardcoded `colorScheme: 'dark'` — will render in dark mode on light themes | General.tsx uses folds `SettingsSelect<T>` (`Button`+`PopOut`+`Menu`) for all dropdowns; `colorScheme: 'dark'` breaks light/custom theme appearance |
|
| N18 | Profile Selects | `Profile.tsx` | 547–575 / 816–848 | `ProfileStatus` auto-clear and `ProfileTimezone` selectors are native `<select>` elements with hardcoded `colorScheme: 'dark'` — will render in dark mode on light themes | General.tsx uses folds `SettingsSelect<T>` (`Button`+`PopOut`+`Menu`) for all dropdowns; `colorScheme: 'dark'` breaks light/custom theme appearance |
|
||||||
| N19 | Presence Labels | `useUserPresence.ts` vs `SettingsTab.tsx` | 55–62 / 36–42 | `PresenceBadge` tooltip shows "Active / Busy / Away"; `PresencePicker` options read "Online / Idle / Do Not Disturb / Invisible" — a DND user shows tooltip "Busy", not "Do Not Disturb" — **FIXED**: aligned `usePresenceLabel` reader vocabulary to the setter (online→"Online", unavailable→"Idle", offline→"Offline") | Within the same Lotus feature set the user-facing vocabulary is inconsistent between the setter UI and the reader tooltip |
|
| N19 | Presence Labels | `useUserPresence.ts` vs `SettingsTab.tsx` | 55–62 / 36–42 | `PresenceBadge` tooltip shows "Active / Busy / Away"; `PresencePicker` options read "Online / Idle / Do Not Disturb / Invisible" — a DND user shows tooltip "Busy", not "Do Not Disturb" — **FIXED**: aligned `usePresenceLabel` reader vocabulary to the setter (online→"Online", unavailable→"Idle", offline→"Offline") | Within the same Lotus feature set the user-facing vocabulary is inconsistent between the setter UI and the reader tooltip |
|
||||||
| N20 | Notification Presets | `Notifications.tsx` | 57–107 | Gaming/Work/Sleep preset buttons are bare `<button>` elements with Lotus-specific CSS vars (`--border-interactive-normal`, `--bg-surface-low`) not defined in all themes | Grouped preset/action buttons elsewhere use folds `Chip variant="Primary/Secondary" outlined radii="Pill"` (e.g., Composer Toolbar toggles in `General.tsx:1100–1113`) |
|
| N20 | Notification Presets | `Notifications.tsx` | 57–107 | Gaming/Work/Sleep preset buttons are bare `<button>` elements with Lotus-specific CSS vars (`--border-interactive-normal`, `--bg-surface-low`) not defined in all themes — **FIXED**: converted to folds `<Button variant="Secondary" fill="Soft" radii="300">` (auto height) wrapping the emoji/label/description column; undefined vars removed | Grouped preset/action buttons elsewhere use folds `Chip variant="Primary/Secondary" outlined radii="Pill"` (e.g., Composer Toolbar toggles in `General.tsx:1100–1113`) |
|
||||||
| N21 | Notification Sound Selects | `SystemNotification.tsx` | 111–305 | Message sound, invite sound, and quiet-hours time pickers are bare `<select>`/`<input type="time">` with `colorScheme: 'dark'` workaround | All other dropdowns in settings use the `Button`+`PopOut`+`Menu`+`MenuItem` folds pattern; the native select renders OS-styled on all platforms |
|
| N21 | Notification Sound Selects | `SystemNotification.tsx` | 111–305 | Message sound, invite sound, and quiet-hours time pickers are bare `<select>`/`<input type="time">` with `colorScheme: 'dark'` workaround | All other dropdowns in settings use the `Button`+`PopOut`+`Menu`+`MenuItem` folds pattern; the native select renders OS-styled on all platforms |
|
||||||
| N22 | DM Preview Virtualizer | `RoomNavItem.tsx` / `Direct.tsx` | 608–627 / 232 | DM preview adds a second text row to each DM item, making it taller than 38px, but `useVirtualizer` in `Direct.tsx` still uses `estimateSize: () => 38` — causes layout jump/overlap on initial render | Non-DM rooms in Home.tsx also estimate 38px; DM items with a preview are now a different height, creating two visual densities in the same nav column |
|
| N22 | DM Preview Virtualizer | `RoomNavItem.tsx` / `Direct.tsx` | 608–627 / 232 | DM preview adds a second text row to each DM item, making it taller than 38px, but `useVirtualizer` in `Direct.tsx` still uses `estimateSize: () => 38` — causes layout jump/overlap on initial render | Non-DM rooms in Home.tsx also estimate 38px; DM items with a preview are now a different height, creating two visual densities in the same nav column |
|
||||||
| N23 | RoomServerACL | `RoomServerACL.tsx` | 100–115 / 298–309 | Server-name text input is a raw `<input type="text">` with inline style object; "Allow IP literal addresses" is a raw `<input type="checkbox">` with `style={{ width: 16, height: 16 }}` | All other text/boolean controls in room settings use folds `Input` and `Checkbox` components (`RoomAddress.tsx:163`, `RoomAddress.tsx:330`) |
|
| N23 | RoomServerACL | `RoomServerACL.tsx` | 100–115 / 298–309 | Server-name text input is a raw `<input type="text">` with inline style object; "Allow IP literal addresses" is a raw `<input type="checkbox">` with `style={{ width: 16, height: 16 }}` — **FIXED**: text input → folds `<Input variant={error?'Critical':'Secondary'}>`; checkbox → folds `<Checkbox variant="Primary">` | All other text/boolean controls in room settings use folds `Input` and `Checkbox` components (`RoomAddress.tsx:163`, `RoomAddress.tsx:330`) |
|
||||||
| N24 | PolicyListViewer | `PolicyListViewer.tsx` | 245–264 | Room-ID add input is a raw `<input type="text">` with manually replicated folds token values | Native pattern: folds `<Input variant="Secondary" size="300" radii="300">` — no inline style needed |
|
| N24 | PolicyListViewer | `PolicyListViewer.tsx` | 245–264 | Room-ID add input is a raw `<input type="text">` with manually replicated folds token values — **FIXED**: replaced with folds `<Input variant={error?'Critical':'Secondary'} size="400" radii="300">` | Native pattern: folds `<Input variant="Secondary" size="300" radii="300">` — no inline style needed |
|
||||||
| N25 | ExportRoomHistory Inputs | `ExportRoomHistory.tsx` | 258–292 | Both date range pickers are raw `<input type="date">` with inline styles | Native pattern: folds `Input` component; `<input type="date">` renders OS-native date picker, unstyled relative to the rest of the settings panel |
|
| N25 | ExportRoomHistory Inputs | `ExportRoomHistory.tsx` | 258–292 | Both date range pickers are raw `<input type="date">` with inline styles — **FIXED**: replaced with folds `<Input type="date" variant="Secondary" size="400" radii="300">` | Native pattern: folds `Input` component; `<input type="date">` renders OS-native date picker, unstyled relative to the rest of the settings panel |
|
||||||
| N26 | RoomShareInvite QR | `RoomShareInvite.tsx` | 66–73 | QR code `<img>` has no `onError` handler and no loading state — broken-image placeholder shown when the external API is unreachable | Cinny avatar components and MediaGallery use `onError` handlers; this is the only settings element making a request to a third-party server with no graceful degradation |
|
| N26 | RoomShareInvite QR | `RoomShareInvite.tsx` | 66–73 | QR code `<img>` has no `onError` handler and no loading state — broken-image placeholder shown when the external API is unreachable — **FIXED**: added `loading="lazy"` + `onError` that swaps to a folds "QR code unavailable" placeholder card | Cinny avatar components and MediaGallery use `onError` handlers; this is the only settings element making a request to a third-party server with no graceful degradation |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
|
|
||||||
| # | Area | File | Lines | Issue | Native Pattern |
|
| # | Area | File | Lines | Issue | Native Pattern |
|
||||||
| :------ | :--------------------------------- | :------------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| :------ | :--------------------------------- | :------------------------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| N27 | GIF Picker | `GifPicker.tsx` | 103–110 | `FocusTrap` omits `returnFocusOnDeactivate: false` — focus returns to GIF button on dismiss instead of staying in the editor | `EmojiBoard` in `RoomInput.tsx:978` explicitly sets `returnFocusOnDeactivate={false}`; GIF picker dismiss behaviour is inconsistent with emoji picker |
|
| N27 | GIF Picker | `GifPicker.tsx` | 103–110 | `FocusTrap` omits `returnFocusOnDeactivate: false` — focus returns to GIF button on dismiss instead of staying in the editor — **FIXED**: added `returnFocusOnDeactivate: false` (matches EmojiBoard) | `EmojiBoard` in `RoomInput.tsx:978` explicitly sets `returnFocusOnDeactivate={false}`; GIF picker dismiss behaviour is inconsistent with emoji picker |
|
||||||
| N28 | Character Counter | `RoomInput.tsx` | 1159–1174 | Composer character counter rendered with `color: 'var(--tc-surface-low)'` and raw pixel padding — a CSS variable not used anywhere else in the codebase — **FIXED**: removed undefined var and raw opacity; now `<Text priority="300">` with `config.space.S100` padding | Use `color.*` folds tokens or `priority="300"` on a `Text` component |
|
| N28 | Character Counter | `RoomInput.tsx` | 1159–1174 | Composer character counter rendered with `color: 'var(--tc-surface-low)'` and raw pixel padding — a CSS variable not used anywhere else in the codebase — **FIXED**: removed undefined var and raw opacity; now `<Text priority="300">` with `config.space.S100` padding | Use `color.*` folds tokens or `priority="300"` on a `Text` component |
|
||||||
| N29 | PollCreator Modal | `PollCreator.tsx` | 103–116 | Modal root is `<Box as="form" role="dialog" aria-modal="true">` with manually assembled surface styles instead of folds `<Dialog variant="Surface">` | `MessageDeleteItem` and `MessageReportItem` in `Message.tsx:506,635` use `<Dialog variant="Surface">` inside `OverlayCenter > FocusTrap` |
|
| N29 | PollCreator Modal | `PollCreator.tsx` | 103–116 | Modal root is `<Box as="form" role="dialog" aria-modal="true">` with manually assembled surface styles instead of folds `<Dialog variant="Surface">` | `MessageDeleteItem` and `MessageReportItem` in `Message.tsx:506,635` use `<Dialog variant="Surface">` inside `OverlayCenter > FocusTrap` |
|
||||||
| N30 | Playback Speed Chip | `AudioContent.tsx` | 163–189 | Speed chip uses `variant="SurfaceVariant" radii="Pill"` while adjacent Play/Pause chip uses `variant="Secondary" radii="300"` — mismatched shape and variant within the same `leftControl` row — **FIXED**: changed speed chip to `variant="Secondary" radii="300"` | Controls grouped in the same row should share variant and radii |
|
| N30 | Playback Speed Chip | `AudioContent.tsx` | 163–189 | Speed chip uses `variant="SurfaceVariant" radii="Pill"` while adjacent Play/Pause chip uses `variant="Secondary" radii="300"` — mismatched shape and variant within the same `leftControl` row — **FIXED**: changed speed chip to `variant="Secondary" radii="300"` | Controls grouped in the same row should share variant and radii |
|
||||||
@@ -418,7 +418,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
| N65 | EditHistoryModal — No "Load More" | `EditHistoryModal.tsx` | 253–259 | When `hasMore` is true the modal shows passive `<Text>"Showing the 50 most recent edits"</Text>` with no action; older edits are inaccessible — **FIXED**: implemented real pagination — edits accumulate across `next_batch` fetches (de-duped by event id, re-sorted by ts), with a folds `<Button>Load more</Button>` (spinner while loading) replacing the passive text | `RoomActivityLog.tsx:425` and `MessageSearch.tsx:129` both render a folds `<Button size="300" variant="Secondary">Load more</Button>` to fetch the next page |
|
| N65 | EditHistoryModal — No "Load More" | `EditHistoryModal.tsx` | 253–259 | When `hasMore` is true the modal shows passive `<Text>"Showing the 50 most recent edits"</Text>` with no action; older edits are inaccessible — **FIXED**: implemented real pagination — edits accumulate across `next_batch` fetches (de-duped by event id, re-sorted by ts), with a folds `<Button>Load more</Button>` (spinner while loading) replacing the passive text | `RoomActivityLog.tsx:425` and `MessageSearch.tsx:129` both render a folds `<Button size="300" variant="Secondary">Load more</Button>` to fetch the next page |
|
||||||
| N66 | DateRangeButton — Native `<input type="date">` | `SearchFilters.tsx` | 558–589 | "From" and "To" date fields are raw `<input type="date">` with inline style overrides including `fontSize: '0.82rem'` — **FIXED**: replaced both with folds `<Input type="date" variant="SurfaceVariant" size="300" radii="300">`; removed now-unused `color` import | `SelectRoomButton` (same file, line 224) and `SelectSenderButton` (line 424) both use folds `<Input size="300" radii="300">`; the date inputs are the only native browser inputs in the search filter row |
|
| N66 | DateRangeButton — Native `<input type="date">` | `SearchFilters.tsx` | 558–589 | "From" and "To" date fields are raw `<input type="date">` with inline style overrides including `fontSize: '0.82rem'` — **FIXED**: replaced both with folds `<Input type="date" variant="SurfaceVariant" size="300" radii="300">`; removed now-unused `color` import | `SelectRoomButton` (same file, line 224) and `SelectSenderButton` (line 424) both use folds `<Input size="300" radii="300">`; the date inputs are the only native browser inputs in the search filter row |
|
||||||
| N67 | SeasonalEffect / NightLight Z-Index Order | `SeasonalEffect.tsx` / `App.tsx` | 759 / 62–77 | `SeasonalEffect` mounts at `zIndex: 9999`; `NightLightOverlay` at `zIndex: 9998`. Seasonal particles render **above** Night Light so they are never tinted. `SeasonalEffect` also shares `z-index: 9999` with the skip-to-content link in `ClientLayout.tsx` — **FIXED**: lowered `SeasonalEffect` overlay to `zIndex: 9997` (below Night Light at 9998 and modals at 9999), so Night Light now tints the particles and dialogs are never obscured | Expected UX: Night Light tints all visible content including effects; requires either a higher Night Light z-index or a lower SeasonalEffect z-index |
|
| N67 | SeasonalEffect / NightLight Z-Index Order | `SeasonalEffect.tsx` / `App.tsx` | 759 / 62–77 | `SeasonalEffect` mounts at `zIndex: 9999`; `NightLightOverlay` at `zIndex: 9998`. Seasonal particles render **above** Night Light so they are never tinted. `SeasonalEffect` also shares `z-index: 9999` with the skip-to-content link in `ClientLayout.tsx` — **FIXED**: lowered `SeasonalEffect` overlay to `zIndex: 9997` (below Night Light at 9998 and modals at 9999), so Night Light now tints the particles and dialogs are never obscured | Expected UX: Night Light tints all visible content including effects; requires either a higher Night Light z-index or a lower SeasonalEffect z-index |
|
||||||
| N68 | Syntax Highlighting — `--lt-accent-*` Vars in Non-TDS Themes | `syntaxHighlight.ts` | 313–323 | `tokenStyle()` returns `var(--lt-accent-cyan/green/orange/purple, hardcoded-fallback)` — `--lt-*` vars only exist in TDS mode; fallbacks are Monokai dark colors that have poor contrast on light themes and no relationship to the existing `--prism-*` variables in `ReactPrism.css` | `ReactPrism.css` uses `--prism-keyword`, `--prism-selector` etc. which switch correctly between light and dark palettes; syntax highlighting should use the same variable family |
|
| N68 | Syntax Highlighting — `--lt-accent-*` Vars in Non-TDS Themes | `syntaxHighlight.ts` | 313–323 | `tokenStyle()` returns `var(--lt-accent-cyan/green/orange/purple, hardcoded-fallback)` — `--lt-*` vars only exist in TDS mode; fallbacks are Monokai dark colors that have poor contrast on light themes and no relationship to the existing `--prism-*` variables in `ReactPrism.css` — **FIXED**: `tokenStyle()` now maps to the `--prism-*` family (keyword/selector/boolean/atrule/comment) which has proper light/dark/TDS palettes; comment uses `--prism-comment` instead of an opacity hack | `ReactPrism.css` uses `--prism-keyword`, `--prism-selector` etc. which switch correctly between light and dark palettes; syntax highlighting should use the same variable family |
|
||||||
| N69 | Mention Highlight — `<input type="color">` Instead of `HexColorPickerPopOut` | `General.tsx` | 644–675 | Raw `<input type="color">` with hardcoded pixel dimensions; OS-native color picker chrome renders completely differently from the rest of settings UI | `PowersEditor.tsx:125–143` establishes `<HexColorPickerPopOut picker={<HexColorPicker ...>}>` as the codebase's color-picking pattern; Reset button should be `<Button size="300" variant="Secondary" radii="300">` |
|
| N69 | Mention Highlight — `<input type="color">` Instead of `HexColorPickerPopOut` | `General.tsx` | 644–675 | Raw `<input type="color">` with hardcoded pixel dimensions; OS-native color picker chrome renders completely differently from the rest of settings UI | `PowersEditor.tsx:125–143` establishes `<HexColorPickerPopOut picker={<HexColorPicker ...>}>` as the codebase's color-picking pattern; Reset button should be `<Button size="300" variant="Secondary" radii="300">` |
|
||||||
| N70 | ChatBgGrid / SeasonalBgGrid — Raw `<button>` Elements | `General.tsx` | 1648–1689 / 1711–1742 | Both pickers use raw HTML `<button>` elements with hardcoded `width: toRem(76)`, `height: toRem(50/56)`, `borderRadius: toRem(8)`, `border: 2px solid rgba(...)` — no focus ring via folds, no `variant` prop, no hover state from the design system | Native Cinny theme pickers use folds `<MenuItem>` or `<Chip>` which respond to theme and provide focus/hover states automatically |
|
| N70 | ChatBgGrid / SeasonalBgGrid — Raw `<button>` Elements | `General.tsx` | 1648–1689 / 1711–1742 | Both pickers use raw HTML `<button>` elements with hardcoded `width: toRem(76)`, `height: toRem(50/56)`, `borderRadius: toRem(8)`, `border: 2px solid rgba(...)` — no focus ring via folds, no `variant` prop, no hover state from the design system | Native Cinny theme pickers use folds `<MenuItem>` or `<Chip>` which respond to theme and provide focus/hover states automatically |
|
||||||
|
|
||||||
@@ -433,11 +433,11 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
| N73 | Pending Requests Header | `MembersDrawer.tsx` | 415–422 | "Pending Requests" section header is bare `<Text>` with inline padding instead of `className={css.MembersGroupLabel}` — **FIXED**: now uses `className={css.MembersGroupLabel}` like every other section header | Power-level group labels at lines 506–519 use `className={css.MembersGroupLabel}` for all other section headers in the same virtualizer list |
|
| N73 | Pending Requests Header | `MembersDrawer.tsx` | 415–422 | "Pending Requests" section header is bare `<Text>` with inline padding instead of `className={css.MembersGroupLabel}` — **FIXED**: now uses `className={css.MembersGroupLabel}` like every other section header | Power-level group labels at lines 506–519 use `className={css.MembersGroupLabel}` for all other section headers in the same virtualizer list |
|
||||||
| N74 | Emoji Prefix Span | `RoomNavItem.tsx` | 730–736 | Emoji prefix rendered as raw `<span style={{ fontSize: '1.15em', lineHeight: 1 }}>` inside a `<Text>` node — **FIXED**: removed the emoji-splitting span; the room name (including any leading emoji) now renders directly inside `<Text>` | All other nav item text uses folds `<Text size="Inherit">` or similar — no raw `<span>` with em-based font-size override exists elsewhere in the sidebar |
|
| N74 | Emoji Prefix Span | `RoomNavItem.tsx` | 730–736 | Emoji prefix rendered as raw `<span style={{ fontSize: '1.15em', lineHeight: 1 }}>` inside a `<Text>` node — **FIXED**: removed the emoji-splitting span; the room name (including any leading emoji) now renders directly inside `<Text>` | All other nav item text uses folds `<Text size="Inherit">` or similar — no raw `<span>` with em-based font-size override exists elsewhere in the sidebar |
|
||||||
| N75 | Room Name Override / Star Indicators | `RoomNavItem.tsx` | 741–757 | Pencil and star indicator icons are embedded inside the name `<Box as="span">`, giving them the same visual baseline as the room name text — **WON'T FIX (deliberate)**: an inline favorite-star / local-name marker adjacent to the name is a deliberate, common design (cf. Element/Slack pinned-name markers). Moving them to the far right would collide with the unread/notification indicators already there and risks layout regressions. Low value, real regression risk. | Native sidebar status indicators (unread count, notification mode icon) are placed to the far right of the item, never inside the name text span group |
|
| N75 | Room Name Override / Star Indicators | `RoomNavItem.tsx` | 741–757 | Pencil and star indicator icons are embedded inside the name `<Box as="span">`, giving them the same visual baseline as the room name text — **WON'T FIX (deliberate)**: an inline favorite-star / local-name marker adjacent to the name is a deliberate, common design (cf. Element/Slack pinned-name markers). Moving them to the far right would collide with the unread/notification indicators already there and risks layout regressions. Low value, real regression risk. | Native sidebar status indicators (unread count, notification mode icon) are placed to the far right of the item, never inside the name text span group |
|
||||||
| N76 | Report Modals — Extra Cancel Button | `ReportRoomModal.tsx` / `ReportUserModal.tsx` | 189–191 / 195–197 | Both custom report modals include a "Cancel" `<Button>` in the footer row | Native `MessageReportItem` (`Message.tsx:675–691`) has no Cancel button — dismissal is via `×` header button or click-outside only |
|
| N76 | Report Modals — Extra Cancel Button | `ReportRoomModal.tsx` / `ReportUserModal.tsx` | 189–191 / 195–197 | Both custom report modals include a "Cancel" `<Button>` in the footer row — **FIXED**: removed the Cancel button; dismissal is via the header `×` / click-outside, matching `MessageReportItem` | Native `MessageReportItem` (`Message.tsx:675–691`) has no Cancel button — dismissal is via `×` header button or click-outside only |
|
||||||
| N77 | Search Filter Inline Lambdas | `SearchFilters.tsx` | 480, 625 | `SelectSenderButton` and `DateRangeButton` trigger chips use inline `onClick` arrow functions — **WON'T FIX (deliberate)**: purely a code-style nit with zero user-facing or behavioural impact. Inline arrow handlers are idiomatic React and used throughout this very file; extracting them yields no functional benefit. | `OrderButton` (line 58) and `SelectRoomButton` (line 195) both extract a named `const handleOpenMenu: MouseEventHandler<HTMLButtonElement>` handler — bypassing the type annotation in the inline form |
|
| N77 | Search Filter Inline Lambdas | `SearchFilters.tsx` | 480, 625 | `SelectSenderButton` and `DateRangeButton` trigger chips use inline `onClick` arrow functions — **WON'T FIX (deliberate)**: purely a code-style nit with zero user-facing or behavioural impact. Inline arrow handlers are idiomatic React and used throughout this very file; extracting them yields no functional benefit. | `OrderButton` (line 58) and `SelectRoomButton` (line 195) both extract a named `const handleOpenMenu: MouseEventHandler<HTMLButtonElement>` handler — bypassing the type annotation in the inline form |
|
||||||
| N78 | HasLink Chip Active Color | `SearchFilters.tsx` | 755 | `HasLink` active state uses `variant="Primary"` (blue); all boolean scope-toggle chips in the same bar use `variant="Success"` (green) with `outlined` — **FIXED**: changed to `variant={containsUrl ? 'Success' : 'SurfaceVariant'} outlined={!!containsUrl}` | `variant="Success" outlined` is the established active-state pattern for boolean toggles in the filter bar |
|
| N78 | HasLink Chip Active Color | `SearchFilters.tsx` | 755 | `HasLink` active state uses `variant="Primary"` (blue); all boolean scope-toggle chips in the same bar use `variant="Success"` (green) with `outlined` — **FIXED**: changed to `variant={containsUrl ? 'Success' : 'SurfaceVariant'} outlined={!!containsUrl}` | `variant="Success" outlined` is the established active-state pattern for boolean toggles in the filter bar |
|
||||||
| N79 | Server Notice Chip Radii | `RoomViewHeader.tsx` | 570 | `<Chip size="400" radii="Pill">` — `Pill` radii on a room-type label — **FIXED**: changed to `radii="300"` | Room/space type labels in lobby (`RoomItem.tsx:83`, `SpaceItem.tsx:63`) use `radii="300"`; `radii="Pill"` is for filter/tag chips only |
|
| N79 | Server Notice Chip Radii | `RoomViewHeader.tsx` | 570 | `<Chip size="400" radii="Pill">` — `Pill` radii on a room-type label — **FIXED**: changed to `radii="300"` | Room/space type labels in lobby (`RoomItem.tsx:83`, `SpaceItem.tsx:63`) use `radii="300"`; `radii="Pill"` is for filter/tag chips only |
|
||||||
| N80 | Server Support Contact Layout | `About.tsx` | 172–239 | Homeserver support contacts rendered as raw `<Box direction="Column">` with `<Text as="a">` pairs — custom label/link layout | All other `<SequenceCard>` content in `About.tsx` and `General.tsx` uses `<SettingTile title="..." description="..." after={...}>` as the content unit |
|
| N80 | Server Support Contact Layout | `About.tsx` | 172–239 | Homeserver support contacts rendered as raw `<Box direction="Column">` with `<Text as="a">` pairs — custom label/link layout — **WON'T FIX (deliberate)**: a contact is `role → {matrix_id?, email?, …}` (one-to-many links per role), which doesn't map onto `SettingTile`'s single `title`/`description`/`after` slots without contortion. The current layout already uses folds `Box`/`Text`/`SequenceCard` + tokens and `Text as="a"` (a valid folds pattern); no undefined vars or raw HTML chrome. | All other `<SequenceCard>` content in `About.tsx` and `General.tsx` uses `<SettingTile title="..." description="..." after={...}>` as the content unit |
|
||||||
| N81 | Background Picker Grid — No Responsive Layout | `General.tsx` | 1707–1742 | Fixed `width: toRem(76)` flex-wrap cells with no `minWidth` floor or CSS grid `auto-fill` — SeasonalBgGrid's 13 items produce a visually lopsided orphan last row at any viewport width | Cinny's native grids use `grid-template-columns: repeat(auto-fill, minmax(N, 1fr))` or equivalent for responsive fill |
|
| N81 | Background Picker Grid — No Responsive Layout | `General.tsx` | 1707–1742 | Fixed `width: toRem(76)` flex-wrap cells with no `minWidth` floor or CSS grid `auto-fill` — SeasonalBgGrid's 13 items produce a visually lopsided orphan last row at any viewport width | Cinny's native grids use `grid-template-columns: repeat(auto-fill, minmax(N, 1fr))` or equivalent for responsive fill |
|
||||||
| N82 | Join/Leave Sounds Auto-Preview | `General.tsx` | 1592–1609 | Selecting a sound in the dropdown immediately plays a preview, but no UI affordance (button label, description text, or "▶ Preview" button) communicates this to the user | Settings tiles with side effects on selection (theme picker, chat background) show a live visual preview or a dedicated control explaining the side effect |
|
| N82 | Join/Leave Sounds Auto-Preview | `General.tsx` | 1592–1609 | Selecting a sound in the dropdown immediately plays a preview, but no UI affordance (button label, description text, or "▶ Preview" button) communicates this to the user | Settings tiles with side effects on selection (theme picker, chat background) show a live visual preview or a dedicated control explaining the side effect |
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -63,7 +63,7 @@ Status: `[ ]` pending · `[~]` in progress · `[x]` completed
|
|||||||
### Confirmed facts
|
### Confirmed facts
|
||||||
|
|
||||||
| Finding | Impact |
|
| Finding | Impact |
|
||||||
| ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||||
| **MSC flags ON:** `msc4140` · `msc3771` · `msc3440.stable` · `msc4133.stable` · `simplified_msc3575` · `msc4222` · `msc3266` · `msc3401_matrix_rtc` | All safe to use now |
|
| **MSC flags ON:** `msc4140` · `msc3771` · `msc3440.stable` · `msc4133.stable` · `simplified_msc3575` · `msc4222` · `msc3266` · `msc3401_matrix_rtc` | All safe to use now |
|
||||||
| **MSC flags OFF:** `msc4306` (thread subscriptions) · `msc3882` · `msc3912` · `msc4155` | These features are BLOCKED |
|
| **MSC flags OFF:** `msc4306` (thread subscriptions) · `msc3882` · `msc3912` · `msc4155` | These features are BLOCKED |
|
||||||
| **MSC3266** room summary: flag `msc3266_enabled: true` set but `GET /v1/rooms/{id}/summary` still returns 404 (M_UNRECOGNIZED) | Room Preview BLOCKED — endpoint not implemented in Synapse 1.155 |
|
| **MSC3266** room summary: flag `msc3266_enabled: true` set but `GET /v1/rooms/{id}/summary` still returns 404 (M_UNRECOGNIZED) | Room Preview BLOCKED — endpoint not implemented in Synapse 1.155 |
|
||||||
@@ -197,12 +197,14 @@ Features:
|
|||||||
|
|
||||||
**What:** Improve search filter UX in `SearchFilters.tsx`.
|
**What:** Improve search filter UX in `SearchFilters.tsx`.
|
||||||
**Completed 2026-06-18:**
|
**Completed 2026-06-18:**
|
||||||
|
|
||||||
- ✅ `SelectSenderButton` — picker UI for sender filter (previously required typing `from:@user` by hand)
|
- ✅ `SelectSenderButton` — picker UI for sender filter (previously required typing `from:@user` by hand)
|
||||||
- ✅ `DateRangeButton` — quick-pick presets: Today / Last week / Last month / Last year
|
- ✅ `DateRangeButton` — quick-pick presets: Today / Last week / Last month / Last year
|
||||||
- ✅ `Has link` chip — `contains_url: true` filter, wired to Matrix API and URL param
|
- ✅ `Has link` chip — `contains_url: true` filter, wired to Matrix API and URL param
|
||||||
**UNTESTED** — needs verification at chat.lotusguild.org.
|
**UNTESTED** — needs verification at chat.lotusguild.org.
|
||||||
|
|
||||||
**Remaining for parity with Discord/Slack:**
|
**Remaining for parity with Discord/Slack:**
|
||||||
|
|
||||||
- [ ] `has:image` / `has:file` / `has:video` — msgtype filters (require client-side post-filtering, no server API)
|
- [ ] `has:image` / `has:file` / `has:video` — msgtype filters (require client-side post-filtering, no server API)
|
||||||
- [ ] Pinned messages filter
|
- [ ] Pinned messages filter
|
||||||
- [ ] Saved searches / search history
|
- [ ] Saved searches / search history
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export function GifPicker({ apiKey, onSelect, requestClose }: GifPickerProps) {
|
|||||||
<FocusTrap
|
<FocusTrap
|
||||||
focusTrapOptions={{
|
focusTrapOptions={{
|
||||||
initialFocus: false,
|
initialFocus: false,
|
||||||
|
returnFocusOnDeactivate: false,
|
||||||
onDeactivate: requestClose,
|
onDeactivate: requestClose,
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
allowOutsideClick: true,
|
allowOutsideClick: true,
|
||||||
|
|||||||
@@ -237,7 +237,12 @@ export function RenderMessageContent({
|
|||||||
title={body}
|
title={body}
|
||||||
src={src}
|
src={src}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
style={{ objectFit: 'cover', objectPosition: 'center top', width: '100%', height: '100%' }}
|
style={{
|
||||||
|
objectFit: 'cover',
|
||||||
|
objectPosition: 'center top',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -103,7 +103,12 @@ export const Reply = as<'div', ReplyProps>(
|
|||||||
return (
|
return (
|
||||||
<Box direction="Row" gap="200" alignItems="Center" {...props} ref={ref}>
|
<Box direction="Row" gap="200" alignItems="Center" {...props} ref={ref}>
|
||||||
{threadRootId && (
|
{threadRootId && (
|
||||||
<ThreadIndicator as="button" data-event-id={threadRootId} onClick={onClick} aria-label="View thread" />
|
<ThreadIndicator
|
||||||
|
as="button"
|
||||||
|
data-event-id={threadRootId}
|
||||||
|
onClick={onClick}
|
||||||
|
aria-label="View thread"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<ReplyLayout
|
<ReplyLayout
|
||||||
as="button"
|
as="button"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
import { config } from 'folds';
|
||||||
|
|
||||||
|
// Hover/focus emphasis driven by CSS rather than JS style mutation, matching
|
||||||
|
// how every other interactive element in the app handles hover state.
|
||||||
|
export const ReceiptTrigger = style({
|
||||||
|
background: 'none',
|
||||||
|
border: 'none',
|
||||||
|
cursor: 'pointer',
|
||||||
|
padding: 0,
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginTop: config.space.S100,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: config.space.S100,
|
||||||
|
opacity: config.opacity.P500,
|
||||||
|
transition: 'opacity 150ms ease-in-out, transform 150ms ease-in-out',
|
||||||
|
selectors: {
|
||||||
|
'&:hover, &:focus-visible': {
|
||||||
|
opacity: 1,
|
||||||
|
transform: 'scale(1.04)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -23,6 +23,7 @@ import { StackedAvatar } from '../stacked-avatar';
|
|||||||
import { EventReaders } from '../event-readers';
|
import { EventReaders } from '../event-readers';
|
||||||
import { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
import { useModalStyle } from '../../hooks/useModalStyle';
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||||
|
import * as css from './ReadReceiptAvatars.css';
|
||||||
|
|
||||||
const MAX_DISPLAY = 5;
|
const MAX_DISPLAY = 5;
|
||||||
|
|
||||||
@@ -74,27 +75,7 @@ export function ReadReceiptAvatars({
|
|||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
title={tooltipNames}
|
title={tooltipNames}
|
||||||
aria-label={tooltipNames}
|
aria-label={tooltipNames}
|
||||||
style={{
|
className={css.ReceiptTrigger}
|
||||||
background: 'none',
|
|
||||||
border: 'none',
|
|
||||||
cursor: 'pointer',
|
|
||||||
padding: 0,
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginTop: '4px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '4px',
|
|
||||||
opacity: 0.85,
|
|
||||||
transition: 'opacity 0.15s, transform 0.15s',
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => {
|
|
||||||
e.currentTarget.style.opacity = '1';
|
|
||||||
e.currentTarget.style.transform = 'scale(1.04)';
|
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
e.currentTarget.style.opacity = '0.85';
|
|
||||||
e.currentTarget.style.transform = 'scale(1)';
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Pill wrapper ensures visibility on any wallpaper/background */}
|
{/* Pill wrapper ensures visibility on any wallpaper/background */}
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -63,7 +63,12 @@ export function CallStatus({ callEmbed }: CallStatusProps) {
|
|||||||
</Box>
|
</Box>
|
||||||
{memberVisible && (
|
{memberVisible && (
|
||||||
<Box shrink="No">
|
<Box shrink="No">
|
||||||
<MemberGlance room={room} members={callMembers} speakers={speakers} callEmbed={callEmbed} />
|
<MemberGlance
|
||||||
|
room={room}
|
||||||
|
members={callMembers}
|
||||||
|
speakers={speakers}
|
||||||
|
callEmbed={callEmbed}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,14 +1,4 @@
|
|||||||
import {
|
import { Box, config, Icon, Icons, Menu, MenuItem, PopOut, RectCords, Text } from 'folds';
|
||||||
Box,
|
|
||||||
config,
|
|
||||||
Icon,
|
|
||||||
Icons,
|
|
||||||
Menu,
|
|
||||||
MenuItem,
|
|
||||||
PopOut,
|
|
||||||
RectCords,
|
|
||||||
Text,
|
|
||||||
} from 'folds';
|
|
||||||
import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership';
|
import { CallMembership } from 'matrix-js-sdk/lib/matrixrtc/CallMembership';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Box, Button, config, Icon, Icons, Text } from 'folds';
|
import { Box, Button, color, config, Icon, Icons, Text } from 'folds';
|
||||||
import { SequenceCard } from '../../../components/sequence-card';
|
import { SequenceCard } from '../../../components/sequence-card';
|
||||||
import { SequenceCardStyle } from '../../room-settings/styles.css';
|
import { SequenceCardStyle } from '../../room-settings/styles.css';
|
||||||
import { SettingTile } from '../../../components/setting-tile';
|
import { SettingTile } from '../../../components/setting-tile';
|
||||||
@@ -12,6 +12,7 @@ export function RoomShareInvite() {
|
|||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const [qrError, setQrError] = useState(false);
|
||||||
|
|
||||||
const domain = mx.getDomain() ?? undefined;
|
const domain = mx.getDomain() ?? undefined;
|
||||||
const inviteUrl = getMatrixToRoom(room.roomId, domain ? [domain] : undefined);
|
const inviteUrl = getMatrixToRoom(room.roomId, domain ? [domain] : undefined);
|
||||||
@@ -63,13 +64,35 @@ export function RoomShareInvite() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box justifyContent="Center">
|
<Box justifyContent="Center">
|
||||||
|
{qrError ? (
|
||||||
|
<Box
|
||||||
|
direction="Column"
|
||||||
|
alignItems="Center"
|
||||||
|
justifyContent="Center"
|
||||||
|
gap="100"
|
||||||
|
style={{
|
||||||
|
width: 160,
|
||||||
|
height: 160,
|
||||||
|
borderRadius: config.radii.R300,
|
||||||
|
background: color.SurfaceVariant.Container,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon size="400" src={Icons.Warning} />
|
||||||
|
<Text size="T200" priority="300" align="Center">
|
||||||
|
QR code unavailable
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
) : (
|
||||||
<img
|
<img
|
||||||
src={qrSrc}
|
src={qrSrc}
|
||||||
alt="QR code for room invite link"
|
alt="QR code for room invite link"
|
||||||
width={160}
|
width={160}
|
||||||
height={160}
|
height={160}
|
||||||
|
loading="lazy"
|
||||||
|
onError={() => setQrError(true)}
|
||||||
style={{ display: 'block', borderRadius: config.radii.R300 }}
|
style={{ display: 'block', borderRadius: config.radii.R300 }}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</CutoutCard>
|
</CutoutCard>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { Box, Button, Icon, IconButton, Icons, Scroll, Spinner, Text, config, color } from 'folds';
|
import { Box, Button, Icon, IconButton, Icons, Input, Scroll, Spinner, Text } from 'folds';
|
||||||
import { EventType } from 'matrix-js-sdk';
|
import { EventType } from 'matrix-js-sdk';
|
||||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
@@ -255,40 +255,24 @@ ${msgRows}
|
|||||||
<Box gap="400" wrap="Wrap">
|
<Box gap="400" wrap="Wrap">
|
||||||
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
||||||
<Text size="T300">From</Text>
|
<Text size="T300">From</Text>
|
||||||
<input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
|
variant="Secondary"
|
||||||
|
size="400"
|
||||||
|
radii="300"
|
||||||
value={fromDate}
|
value={fromDate}
|
||||||
onChange={(e) => setFromDate(e.target.value)}
|
onChange={(e) => setFromDate(e.target.value)}
|
||||||
style={{
|
|
||||||
background: color.Surface.Container,
|
|
||||||
color: color.Surface.OnContainer,
|
|
||||||
border: `1px solid ${color.Surface.ContainerLine}`,
|
|
||||||
borderRadius: config.radii.R300,
|
|
||||||
padding: `${config.space.S200} ${config.space.S300}`,
|
|
||||||
fontSize: 'inherit',
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
||||||
<Text size="T300">To</Text>
|
<Text size="T300">To</Text>
|
||||||
<input
|
<Input
|
||||||
type="date"
|
type="date"
|
||||||
|
variant="Secondary"
|
||||||
|
size="400"
|
||||||
|
radii="300"
|
||||||
value={toDate}
|
value={toDate}
|
||||||
onChange={(e) => setToDate(e.target.value)}
|
onChange={(e) => setToDate(e.target.value)}
|
||||||
style={{
|
|
||||||
background: color.Surface.Container,
|
|
||||||
color: color.Surface.OnContainer,
|
|
||||||
border: `1px solid ${color.Surface.ContainerLine}`,
|
|
||||||
borderRadius: config.radii.R300,
|
|
||||||
padding: `${config.space.S200} ${config.space.S300}`,
|
|
||||||
fontSize: 'inherit',
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,5 +1,17 @@
|
|||||||
import React, { useCallback, useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import { Badge, Box, Button, Icon, IconButton, Icons, Scroll, Text, color, config } from 'folds';
|
import {
|
||||||
|
Badge,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
Icons,
|
||||||
|
Input,
|
||||||
|
Scroll,
|
||||||
|
Text,
|
||||||
|
color,
|
||||||
|
config,
|
||||||
|
} from 'folds';
|
||||||
import { EventTimeline, MatrixEvent, Room } from 'matrix-js-sdk';
|
import { EventTimeline, MatrixEvent, Room } from 'matrix-js-sdk';
|
||||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
@@ -242,7 +254,7 @@ export function PolicyListViewer({ requestClose }: PolicyListViewerProps) {
|
|||||||
gap="300"
|
gap="300"
|
||||||
>
|
>
|
||||||
<Box gap="200" alignItems="Center">
|
<Box gap="200" alignItems="Center">
|
||||||
<input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={roomIdInput}
|
value={roomIdInput}
|
||||||
onChange={(e) => setRoomIdInput(e.target.value)}
|
onChange={(e) => setRoomIdInput(e.target.value)}
|
||||||
@@ -250,17 +262,10 @@ export function PolicyListViewer({ requestClose }: PolicyListViewerProps) {
|
|||||||
if (e.key === 'Enter') handleLoad();
|
if (e.key === 'Enter') handleLoad();
|
||||||
}}
|
}}
|
||||||
placeholder="!roomId:server or #alias:server"
|
placeholder="!roomId:server or #alias:server"
|
||||||
style={{
|
variant={error ? 'Critical' : 'Secondary'}
|
||||||
flexGrow: 1,
|
size="400"
|
||||||
padding: `${config.space.S200} ${config.space.S300}`,
|
radii="300"
|
||||||
borderRadius: config.radii.R300,
|
style={{ flexGrow: 1 }}
|
||||||
border: `1px solid ${error ? color.Critical.Main : color.Surface.ContainerLine}`,
|
|
||||||
background: color.Surface.Container,
|
|
||||||
color: color.Surface.OnContainer,
|
|
||||||
fontSize: 'inherit',
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
outline: 'none',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleLoad}
|
onClick={handleLoad}
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
import React, { useCallback, useRef, useState } from 'react';
|
import React, { useCallback, useRef, useState } from 'react';
|
||||||
import { Box, Button, Icon, IconButton, Icons, Scroll, Spinner, Text, color, config } from 'folds';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
Icons,
|
||||||
|
Input,
|
||||||
|
Scroll,
|
||||||
|
Spinner,
|
||||||
|
Text,
|
||||||
|
color,
|
||||||
|
config,
|
||||||
|
} from 'folds';
|
||||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { useRoom } from '../../hooks/useRoom';
|
import { useRoom } from '../../hooks/useRoom';
|
||||||
@@ -97,22 +110,14 @@ function ServerList({ label, entries, canEdit, onAdd, onRemove }: ServerListProp
|
|||||||
|
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
<input
|
<Input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="e.g. *.example.com or badserver.org"
|
placeholder="e.g. *.example.com or badserver.org"
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
style={{
|
variant={error ? 'Critical' : 'Secondary'}
|
||||||
background: color.Surface.Container,
|
size="400"
|
||||||
color: color.Surface.OnContainer,
|
radii="300"
|
||||||
border: `1px solid ${error ? color.Critical.Main : color.Surface.ContainerLine}`,
|
|
||||||
borderRadius: config.radii.R300,
|
|
||||||
padding: `${config.space.S200} ${config.space.S300}`,
|
|
||||||
fontSize: 'inherit',
|
|
||||||
fontFamily: 'inherit',
|
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
{error && (
|
{error && (
|
||||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||||
@@ -295,18 +300,13 @@ export function RoomServerACL({ requestClose }: RoomServerACLProps) {
|
|||||||
gap="200"
|
gap="200"
|
||||||
>
|
>
|
||||||
<Box alignItems="Center" gap="300">
|
<Box alignItems="Center" gap="300">
|
||||||
<input
|
<Checkbox
|
||||||
type="checkbox"
|
|
||||||
id="allow-ip-literals"
|
id="allow-ip-literals"
|
||||||
checked={allowIpLiterals}
|
checked={allowIpLiterals}
|
||||||
disabled={!canEdit}
|
disabled={!canEdit}
|
||||||
onChange={(e) => setAllowIpLiterals(e.target.checked)}
|
onClick={() => setAllowIpLiterals(!allowIpLiterals)}
|
||||||
style={{
|
size="300"
|
||||||
width: 16,
|
variant="Primary"
|
||||||
height: 16,
|
|
||||||
flexShrink: 0,
|
|
||||||
cursor: canEdit ? 'pointer' : 'default',
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Box direction="Column" gap="0">
|
<Box direction="Column" gap="0">
|
||||||
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
||||||
|
|||||||
@@ -186,9 +186,6 @@ export function ReportRoomModal({ roomId, onClose }: ReportRoomModalProps) {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box gap="200" justifyContent="End">
|
<Box gap="200" justifyContent="End">
|
||||||
<Button type="button" variant="Secondary" fill="None" radii="300" onClick={onClose}>
|
|
||||||
<Text size="B400">Cancel</Text>
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="Critical"
|
variant="Critical"
|
||||||
|
|||||||
@@ -192,9 +192,6 @@ export function ReportUserModal({ userId, onClose }: ReportUserModalProps) {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box gap="200" justifyContent="End">
|
<Box gap="200" justifyContent="End">
|
||||||
<Button type="button" variant="Secondary" fill="None" radii="300" onClick={onClose}>
|
|
||||||
<Text size="B400">Cancel</Text>
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="Critical"
|
variant="Critical"
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
config,
|
config,
|
||||||
|
Header,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
Icons,
|
||||||
Input,
|
Input,
|
||||||
Line,
|
Line,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
@@ -134,14 +138,22 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) {
|
|||||||
...modalStyle,
|
...modalStyle,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Header
|
||||||
direction="Column"
|
variant="Surface"
|
||||||
gap="200"
|
size="500"
|
||||||
shrink="No"
|
style={{ padding: `0 ${config.space.S200} 0 ${config.space.S400}` }}
|
||||||
style={{ padding: config.space.S400, paddingBottom: config.space.S200 }}
|
|
||||||
>
|
>
|
||||||
<Text size="H5">Forward message</Text>
|
<Box grow="Yes">
|
||||||
|
<Text as="h2" size="H4" truncate>
|
||||||
|
Forward message
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<IconButton size="300" onClick={onClose} radii="300" aria-label="Close">
|
||||||
|
<Icon src={Icons.Cross} />
|
||||||
|
</IconButton>
|
||||||
|
</Header>
|
||||||
{!sentTo && (
|
{!sentTo && (
|
||||||
|
<Box shrink="No" style={{ padding: `${config.space.S200} ${config.space.S400}` }}>
|
||||||
<Input
|
<Input
|
||||||
variant="Background"
|
variant="Background"
|
||||||
size="400"
|
size="400"
|
||||||
@@ -151,8 +163,8 @@ export function ForwardMessageDialog({ mEvent, onClose }: Props) {
|
|||||||
value={query}
|
value={query}
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)}
|
onChange={(e: ChangeEvent<HTMLInputElement>) => setQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
)}
|
||||||
<Line size="300" />
|
<Line size="300" />
|
||||||
{sentTo ? (
|
{sentTo ? (
|
||||||
<Box
|
<Box
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import { Box, Text, IconButton, Icon, Icons, Scroll, config, toRem } from 'folds';
|
import { Box, Button, Text, IconButton, Icon, Icons, Scroll, config, toRem } from 'folds';
|
||||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||||
import { SystemNotification } from './SystemNotification';
|
import { SystemNotification } from './SystemNotification';
|
||||||
import { AllMessagesNotifications } from './AllMessages';
|
import { AllMessagesNotifications } from './AllMessages';
|
||||||
@@ -68,27 +68,23 @@ function NotificationPresets() {
|
|||||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
<Box gap="300" style={{ padding: config.space.S300, flexWrap: 'wrap' }}>
|
<Box gap="300" style={{ padding: config.space.S300, flexWrap: 'wrap' }}>
|
||||||
{PRESETS.map((preset) => (
|
{PRESETS.map((preset) => (
|
||||||
<button
|
<Button
|
||||||
key={preset.label}
|
key={preset.label}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => applyPreset(preset.patch)}
|
onClick={() => applyPreset(preset.patch)}
|
||||||
title={preset.description}
|
title={preset.description}
|
||||||
|
variant="Secondary"
|
||||||
|
fill="Soft"
|
||||||
|
radii="300"
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
height: 'unset',
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: toRem(4),
|
|
||||||
padding: `${toRem(8)} ${toRem(16)}`,
|
|
||||||
borderRadius: config.radii.R300,
|
|
||||||
border: '1px solid var(--border-interactive-normal)',
|
|
||||||
background: 'var(--bg-surface-low)',
|
|
||||||
color: 'inherit',
|
|
||||||
cursor: 'pointer',
|
|
||||||
minWidth: toRem(80),
|
minWidth: toRem(80),
|
||||||
|
padding: `${config.space.S200} ${config.space.S400}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box direction="Column" alignItems="Center" gap="100">
|
||||||
<span style={{ fontSize: toRem(24) }}>{preset.emoji}</span>
|
<span style={{ fontSize: toRem(24) }}>{preset.emoji}</span>
|
||||||
<Text size="T300" style={{ fontWeight: 600 }}>
|
<Text size="T300" style={{ fontWeight: config.fontWeight.W600 }}>
|
||||||
{preset.label}
|
{preset.label}
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
@@ -98,7 +94,8 @@ function NotificationPresets() {
|
|||||||
>
|
>
|
||||||
{preset.description}
|
{preset.description}
|
||||||
</Text>
|
</Text>
|
||||||
</button>
|
</Box>
|
||||||
|
</Button>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
|||||||
@@ -15,11 +15,7 @@ import { settingsAtom } from '../../state/settings';
|
|||||||
import { allInvitesAtom } from '../../state/room-list/inviteList';
|
import { allInvitesAtom } from '../../state/room-list/inviteList';
|
||||||
import { usePreviousValue } from '../../hooks/usePreviousValue';
|
import { usePreviousValue } from '../../hooks/usePreviousValue';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import {
|
import { getDirectRoomPath, getHomeRoomPath, getInboxInvitesPath } from '../pathUtils';
|
||||||
getDirectRoomPath,
|
|
||||||
getHomeRoomPath,
|
|
||||||
getInboxInvitesPath,
|
|
||||||
} from '../pathUtils';
|
|
||||||
import { mDirectAtom } from '../../state/mDirectList';
|
import { mDirectAtom } from '../../state/mDirectList';
|
||||||
import {
|
import {
|
||||||
getMemberDisplayName,
|
getMemberDisplayName,
|
||||||
|
|||||||
@@ -390,7 +390,10 @@ export class CallEmbed {
|
|||||||
if (this.call === null) return;
|
if (this.call === null) return;
|
||||||
const raw = ev.getEffectiveEvent();
|
const raw = ev.getEffectiveEvent();
|
||||||
this.call.feedStateUpdate(raw as IRoomEvent).catch((e) => {
|
this.call.feedStateUpdate(raw as IRoomEvent).catch((e) => {
|
||||||
console.error('Error sending state update to widget:', e instanceof Error ? e.message : 'unknown error');
|
console.error(
|
||||||
|
'Error sending state update to widget:',
|
||||||
|
e instanceof Error ? e.message : 'unknown error',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +499,10 @@ export class CallEmbed {
|
|||||||
} else {
|
} else {
|
||||||
const raw = ev.getEffectiveEvent();
|
const raw = ev.getEffectiveEvent();
|
||||||
this.call.feedEvent(raw as IRoomEvent).catch((e) => {
|
this.call.feedEvent(raw as IRoomEvent).catch((e) => {
|
||||||
console.error('Error sending event to widget:', e instanceof Error ? e.message : 'unknown error');
|
console.error(
|
||||||
|
'Error sending event to widget:',
|
||||||
|
e instanceof Error ? e.message : 'unknown error',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -306,19 +306,26 @@ export function tokenize(code: string, lang: string): SyntaxToken[] {
|
|||||||
|
|
||||||
// ── Inline style helpers ────────────────────────────────────────────────────
|
// ── Inline style helpers ────────────────────────────────────────────────────
|
||||||
|
|
||||||
/** Returns the React inline-style object for a given SyntaxToken type. */
|
/**
|
||||||
|
* Returns the React inline-style object for a given SyntaxToken type.
|
||||||
|
*
|
||||||
|
* Uses the `--prism-*` variable family (defined in `ReactPrism.css`), which has
|
||||||
|
* proper light / dark / TDS palettes — unlike the raw `--lt-accent-*` vars,
|
||||||
|
* which only exist in TDS mode and otherwise fall back to dark-only Monokai
|
||||||
|
* colors with poor contrast on light themes.
|
||||||
|
*/
|
||||||
export function tokenStyle(type: SyntaxToken['type']): CSSProperties {
|
export function tokenStyle(type: SyntaxToken['type']): CSSProperties {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'kw':
|
case 'kw':
|
||||||
return { color: 'var(--lt-accent-cyan, #66d9ef)' };
|
return { color: 'var(--prism-keyword)' };
|
||||||
case 'str':
|
case 'str':
|
||||||
return { color: 'var(--lt-accent-green, #a6e22e)' };
|
return { color: 'var(--prism-selector)' };
|
||||||
case 'num':
|
case 'num':
|
||||||
return { color: 'var(--lt-accent-orange, #fd971f)' };
|
return { color: 'var(--prism-boolean)' };
|
||||||
case 'cmt':
|
case 'cmt':
|
||||||
return { opacity: 0.5, fontStyle: 'italic' as const };
|
return { color: 'var(--prism-comment)', fontStyle: 'italic' as const };
|
||||||
case 'fn':
|
case 'fn':
|
||||||
return { color: 'var(--lt-accent-purple, #ae81ff)' };
|
return { color: 'var(--prism-atrule)' };
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user