diff --git a/src/app/components/editor/autocomplete/AutocompleteMenu.css.tsx b/src/app/components/editor/autocomplete/AutocompleteMenu.css.tsx index 98f653ed..5f4b66f3 100644 --- a/src/app/components/editor/autocomplete/AutocompleteMenu.css.tsx +++ b/src/app/components/editor/autocomplete/AutocompleteMenu.css.tsx @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { DefaultReset, config } from 'folds'; +import { DefaultReset, color, config, toRem } from 'folds'; export const AutocompleteMenuBase = style([ DefaultReset, @@ -19,17 +19,51 @@ export const AutocompleteMenuContainer = style([ }, ]); +// Dawn popover shell — a single hairline-bordered panel that floats above the +// composer, mirroring the cmdK result-list look (panel #181a20 + faint fleet ring). export const AutocompleteMenu = style([ DefaultReset, { - maxHeight: '30vh', + maxHeight: '40vh', height: '100%', display: 'flex', flexDirection: 'column', + backgroundColor: color.SurfaceVariant.Container, + border: `${toRem(1)} solid rgba(255, 255, 255, 0.07)`, + borderRadius: config.radii.R400, + boxShadow: '0 30px 80px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(149, 128, 255, 0.15)', + overflow: 'hidden', }, ]); +// UPPERCASE, letter-spaced, muted mono section label — replaces the folds Header. export const AutocompleteMenuHeader = style([ DefaultReset, - { padding: `0 ${config.space.S300}`, flexShrink: 0 }, + { + flexShrink: 0, + display: 'block', + padding: `${config.space.S200} ${config.space.S300} ${config.space.S100}`, + fontFamily: 'var(--font-mono)', + fontSize: toRem(10), + lineHeight: toRem(14), + fontWeight: config.fontWeight.W500, + letterSpacing: '0.12em', + textTransform: 'uppercase', + color: color.SurfaceVariant.OnContainer, + opacity: 0.5, + }, ]); + +// JetBrains Mono technical handle (@user:server, #alias:server, !id, /command sig) +// rendered at a muted tone inside an autocomplete row. +export const AutocompleteMono = style({ + fontFamily: 'var(--font-mono)', + opacity: 0.6, +}); + +// Fleet highlight for the first (Tab-target) row: faint violet wash + a 2px +// fleet left-border. Purely visual — reflects the onTabPress 'first item' logic. +export const AutocompleteActiveRow = style({ + backgroundColor: 'rgba(149, 128, 255, 0.08)', + boxShadow: `inset ${toRem(2)} 0 0 0 ${color.Primary.Main}`, +}); diff --git a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx index 452fa1b4..cfa024eb 100644 --- a/src/app/components/editor/autocomplete/AutocompleteMenu.tsx +++ b/src/app/components/editor/autocomplete/AutocompleteMenu.tsx @@ -1,7 +1,7 @@ import React, { ReactNode } from 'react'; import FocusTrap from 'focus-trap-react'; import { isKeyHotkey } from 'is-hotkey'; -import { Header, Menu, Scroll, config } from 'folds'; +import { Menu, Scroll, config } from 'folds'; import * as css from './AutocompleteMenu.css'; import { preventScrollWithArrowKey, stopPropagation } from '../../../utils/keyboard'; @@ -38,9 +38,7 @@ export function AutocompleteMenu({ headerContent, requestClose, children }: Auto }} > -
- {headerContent} -
+ {headerContent}
{children}
diff --git a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx index a2387eb8..46ebc9ac 100644 --- a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx @@ -2,9 +2,11 @@ import React, { KeyboardEvent as ReactKeyboardEvent, useEffect, useMemo } from ' import { Editor } from 'slate'; import { Box, MenuItem, Text, toRem } from 'folds'; import { Room } from 'matrix-js-sdk'; +import { useTranslation } from 'react-i18next'; import { AutocompleteQuery } from './autocompleteQuery'; import { AutocompleteMenu } from './AutocompleteMenu'; +import * as css from './AutocompleteMenu.css'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { UseAsyncSearchOptions, useAsyncSearch } from '../../../hooks/useAsyncSearch'; import { onTabPress } from '../../../utils/keyboard'; @@ -42,6 +44,7 @@ export function EmoticonAutocomplete({ query, requestClose, }: EmoticonAutocompleteProps) { + const { t } = useTranslation(); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); @@ -85,8 +88,8 @@ export function EmoticonAutocomplete({ }); return autoCompleteEmoticon.length === 0 ? null : ( - Emojis} requestClose={requestClose}> - {autoCompleteEmoticon.map((emoticon) => { + + {autoCompleteEmoticon.map((emoticon, index) => { const isCustomEmoji = 'url' in emoticon; const key = isCustomEmoji ? emoticon.url : emoticon.unicode; const customEmojiUrl = mxcUrlToHttp(mx, key, useAuthentication); @@ -94,6 +97,7 @@ export function EmoticonAutocomplete({ return ( ) => @@ -121,7 +125,7 @@ export function EmoticonAutocomplete({ ) } > - + :{emoticon.shortcode}: diff --git a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx index de5b9b10..8d8ad046 100644 --- a/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/RoomMentionAutocomplete.tsx @@ -3,12 +3,14 @@ import { Editor } from 'slate'; import { Avatar, Icon, Icons, MenuItem, Text } from 'folds'; import { JoinRule, MatrixClient } from 'matrix-js-sdk'; import { useAtomValue } from 'jotai'; +import { useTranslation } from 'react-i18next'; import { createMentionElement, moveCursor, replaceWithElement } from '../utils'; import { getDirectRoomAvatarUrl } from '../../../utils/room'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { AutocompleteQuery } from './autocompleteQuery'; import { AutocompleteMenu } from './AutocompleteMenu'; +import * as css from './AutocompleteMenu.css'; import { getMxIdServer, isRoomAlias } from '../../../utils/matrix'; import { UseAsyncSearchOptions, useAsyncSearch } from '../../../hooks/useAsyncSearch'; import { onTabPress } from '../../../utils/keyboard'; @@ -76,6 +78,7 @@ export function RoomMentionAutocomplete({ query, requestClose, }: RoomMentionAutocompleteProps) { + const { t } = useTranslation(); const mx = useMatrixClient(); const mDirects = useAtomValue(mDirectAtom); @@ -86,12 +89,12 @@ export function RoomMentionAutocomplete({ useCallback( (rId) => { const r = mx.getRoom(rId); - if (!r) return 'Unknown Room'; + if (!r) return t('Room.autocomplete_unknown_room'); const alias = r.getCanonicalAlias(); if (alias) return [r.name, alias]; return r.name; }, - [mx] + [mx, t] ), SEARCH_OPTIONS ); @@ -133,11 +136,11 @@ export function RoomMentionAutocomplete({ }); return ( - Rooms} requestClose={requestClose}> + {autoCompleteRoomIds.length === 0 ? ( ) : ( - autoCompleteRoomIds.map((rId) => { + autoCompleteRoomIds.map((rId, index) => { const room = mx.getRoom(rId); if (!room) return null; const dm = mDirects.has(room.roomId); @@ -147,6 +150,7 @@ export function RoomMentionAutocomplete({ return ( ) => @@ -154,7 +158,7 @@ export function RoomMentionAutocomplete({ } onClick={handleSelect} after={ - + {room.getCanonicalAlias() ?? ''} } diff --git a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx index dc446137..d3020310 100644 --- a/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx @@ -2,9 +2,11 @@ import React, { useEffect, KeyboardEvent as ReactKeyboardEvent } from 'react'; import { Editor } from 'slate'; import { Avatar, Icon, Icons, MenuItem, Text } from 'folds'; import { MatrixClient, Room, RoomMember } from 'matrix-js-sdk'; +import { useTranslation } from 'react-i18next'; import { AutocompleteQuery } from './autocompleteQuery'; import { AutocompleteMenu } from './AutocompleteMenu'; +import * as css from './AutocompleteMenu.css'; import { useRoomMembers } from '../../../hooks/useRoomMembers'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { @@ -90,6 +92,7 @@ export function UserMentionAutocomplete({ query, requestClose, }: UserMentionAutocompleteProps) { + const { t } = useTranslation(); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const { roomId } = room; @@ -137,7 +140,7 @@ export function UserMentionAutocomplete({ getMemberDisplayName(room, member.userId) ?? getMxIdLocalPart(member.userId) ?? member.userId; return ( - Mentions} requestClose={requestClose}> + {query.text === 'room' && ( ) : ( - autoCompleteMembers.map((roomMember) => { + autoCompleteMembers.map((roomMember, index) => { const avatarMxcUrl = roomMember.getMxcAvatarUrl(); const avatarUrl = avatarMxcUrl ? mx.mxcUrlToHttp(avatarMxcUrl, 32, 32, 'crop', undefined, false, useAuthentication) @@ -160,6 +163,9 @@ export function UserMentionAutocomplete({ return ( ) => @@ -167,7 +173,7 @@ export function UserMentionAutocomplete({ } onClick={() => handleAutocomplete(roomMember.userId, getName(roomMember))} after={ - + {roomMember.userId} } diff --git a/src/app/features/room/CommandAutocomplete.tsx b/src/app/features/room/CommandAutocomplete.tsx index 6b7ba56e..76e4a159 100644 --- a/src/app/features/room/CommandAutocomplete.tsx +++ b/src/app/features/room/CommandAutocomplete.tsx @@ -2,6 +2,7 @@ import React, { KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, use import { Editor } from 'slate'; import { Box, config, MenuItem, Text } from 'folds'; import { Room } from 'matrix-js-sdk'; +import { useTranslation } from 'react-i18next'; import { Command, useCommands } from '../../hooks/useCommands'; import { AutocompleteMenu, @@ -10,6 +11,7 @@ import { moveCursor, replaceWithElement, } from '../../components/editor'; +import * as css from '../../components/editor/autocomplete/AutocompleteMenu.css'; import { UseAsyncSearchOptions, useAsyncSearch } from '../../hooks/useAsyncSearch'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { useKeyDown } from '../../hooks/useKeyDown'; @@ -36,6 +38,7 @@ export function CommandAutocomplete({ query, requestClose, }: CommandAutocompleteProps) { + const { t } = useTranslation(); const mx = useMatrixClient(); const commands = useCommands(mx, room); const commandNames = useMemo(() => Object.keys(commands) as Command[], [commands]); @@ -71,17 +74,11 @@ export function CommandAutocomplete({ }); return autoCompleteNames.length === 0 ? null : ( - - Commands - - } - requestClose={requestClose} - > - {autoCompleteNames.map((commandName) => ( + + {autoCompleteNames.map((commandName, index) => ( - + {`/${commandName}`} diff --git a/src/app/styles/CustomHtml.css.ts b/src/app/styles/CustomHtml.css.ts index ba7b9214..6e27a4bf 100644 --- a/src/app/styles/CustomHtml.css.ts +++ b/src/app/styles/CustomHtml.css.ts @@ -147,9 +147,9 @@ export const Mention = recipe({ base: [ DefaultReset, { - backgroundColor: color.SurfaceVariant.Container, - color: color.SurfaceVariant.OnContainer, - boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.SurfaceVariant.ContainerLine}`, + // Flat Dawn pill — violet-tinted fill + lavender text, no boxShadow ring. + backgroundColor: 'rgba(149, 128, 255, 0.12)', + color: color.Primary.MainHover, padding: `0 ${toRem(2)}`, borderRadius: config.radii.R300, fontWeight: config.fontWeight.W500, @@ -158,14 +158,14 @@ export const Mention = recipe({ variants: { highlight: { true: { - backgroundColor: color.Success.Container, - color: color.Success.OnContainer, - boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.Success.ContainerLine}`, + // Self-mention reads as the fleet brand accent (not green status). + backgroundColor: color.Primary.Container, + color: color.Primary.OnContainer, }, }, focus: { true: { - boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.SurfaceVariant.OnContainer}`, + boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.Primary.Main}`, }, }, },