[Glitch] Add rendering of quote posts in web UI

Port 97b9e8849d to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
diondiondion
2025-05-21 17:50:45 +02:00
committed by Claire
parent 7efac8c025
commit e3b424aa02
13 changed files with 222 additions and 47 deletions

View File

@@ -69,6 +69,10 @@ export function importFetchedStatuses(statuses) {
processStatus(status.reblog); processStatus(status.reblog);
} }
if (status.quote?.quoted_status) {
processStatus(status.quote.quoted_status);
}
if (status.poll?.id) { if (status.poll?.id) {
pushUnique(polls, createPollFromServerJSON(status.poll, getState().polls[status.poll.id])); pushUnique(polls, createPollFromServerJSON(status.poll, getState().polls[status.poll.id]));
} }

View File

@@ -23,12 +23,20 @@ export function normalizeFilterResult(result) {
export function normalizeStatus(status, normalOldStatus, settings) { export function normalizeStatus(status, normalOldStatus, settings) {
const normalStatus = { ...status }; const normalStatus = { ...status };
normalStatus.account = status.account.id; normalStatus.account = status.account.id;
if (status.reblog && status.reblog.id) { if (status.reblog && status.reblog.id) {
normalStatus.reblog = status.reblog.id; normalStatus.reblog = status.reblog.id;
} }
if (status.quote?.quoted_status ?? status.quote?.quoted_status_id) {
normalStatus.quote = {
...status.quote,
quoted_status: status.quote.quoted_status?.id ?? status.quote?.quoted_status_id,
};
}
if (status.poll && status.poll.id) { if (status.poll && status.poll.id) {
normalStatus.poll = status.poll.id; normalStatus.poll = status.poll.id;
} }

View File

@@ -82,6 +82,7 @@ class Status extends ImmutablePureComponent {
id: PropTypes.string, id: PropTypes.string,
status: ImmutablePropTypes.map, status: ImmutablePropTypes.map,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
children: PropTypes.node,
previousId: PropTypes.string, previousId: PropTypes.string,
nextInReplyToId: PropTypes.string, nextInReplyToId: PropTypes.string,
rootId: PropTypes.string, rootId: PropTypes.string,
@@ -111,6 +112,7 @@ class Status extends ImmutablePureComponent {
withDismiss: PropTypes.bool, withDismiss: PropTypes.bool,
onMoveUp: PropTypes.func, onMoveUp: PropTypes.func,
onMoveDown: PropTypes.func, onMoveDown: PropTypes.func,
isQuotedPost: PropTypes.bool,
getScrollPosition: PropTypes.func, getScrollPosition: PropTypes.func,
updateScrollBottom: PropTypes.func, updateScrollBottom: PropTypes.func,
expanded: PropTypes.bool, expanded: PropTypes.bool,
@@ -440,7 +442,7 @@ class Status extends ImmutablePureComponent {
} }
render () { render () {
const { intl, hidden, featured, unfocusable, unread, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46 } = this.props; const { intl, hidden, featured, unfocusable, unread, pictureInPicture, previousId, nextInReplyToId, rootId, skipPrepend, avatarSize = 46, children } = this.props;
const { const {
status, status,
@@ -452,6 +454,7 @@ class Status extends ImmutablePureComponent {
onOpenMedia, onOpenMedia,
notification, notification,
history, history,
isQuotedPost,
...other ...other
} = this.props; } = this.props;
let attachments = null; let attachments = null;
@@ -684,7 +687,7 @@ class Status extends ImmutablePureComponent {
{!skipPrepend && prepend} {!skipPrepend && prepend}
<div <div
className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), 'status--in-thread': !!rootId, 'status--first-in-thread': previousId && (!connectUp || connectToRoot), muted: this.props.muted })} className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), 'status--in-thread': !!rootId, 'status--first-in-thread': previousId && (!connectUp || connectToRoot), muted: this.props.muted, 'status--is-quote': isQuotedPost })}
data-id={status.get('id')} data-id={status.get('id')}
> >
{(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />} {(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
@@ -722,6 +725,8 @@ class Status extends ImmutablePureComponent {
{...statusContentProps} {...statusContentProps}
/> />
{children}
{media} {media}
{hashtagBar} {hashtagBar}
</> </>
@@ -730,13 +735,15 @@ class Status extends ImmutablePureComponent {
{/* This is a glitch-soc addition to have a placeholder */} {/* This is a glitch-soc addition to have a placeholder */}
{!expanded && <MentionsPlaceholder status={status} />} {!expanded && <MentionsPlaceholder status={status} />}
<StatusActionBar {!isQuotedPost &&
status={status} <StatusActionBar
account={status.get('account')} status={status}
showReplyCount={settings.get('show_reply_count')} account={status.get('account')}
onFilter={matchedFilters ? this.handleFilterClick : null} showReplyCount={settings.get('show_reply_count')}
{...other} onFilter={matchedFilters ? this.handleFilterClick : null}
/> {...other}
/>
}
</div> </div>
</div> </div>
</HotKeys> </HotKeys>

View File

@@ -9,7 +9,7 @@ import { TIMELINE_GAP, TIMELINE_SUGGESTIONS } from 'flavours/glitch/actions/time
import { RegenerationIndicator } from 'flavours/glitch/components/regeneration_indicator'; import { RegenerationIndicator } from 'flavours/glitch/components/regeneration_indicator';
import { InlineFollowSuggestions } from 'flavours/glitch/features/home_timeline/components/inline_follow_suggestions'; import { InlineFollowSuggestions } from 'flavours/glitch/features/home_timeline/components/inline_follow_suggestions';
import StatusContainer from '../containers/status_container'; import { StatusQuoteManager } from '../components/status_quoted';
import { LoadGap } from './load_gap'; import { LoadGap } from './load_gap';
import ScrollableList from './scrollable_list'; import ScrollableList from './scrollable_list';
@@ -114,7 +114,7 @@ export default class StatusList extends ImmutablePureComponent {
); );
default: default:
return ( return (
<StatusContainer <StatusQuoteManager
key={statusId} key={statusId}
id={statusId} id={statusId}
onMoveUp={this.handleMoveUp} onMoveUp={this.handleMoveUp}
@@ -130,7 +130,7 @@ export default class StatusList extends ImmutablePureComponent {
if (scrollableContent && featuredStatusIds) { if (scrollableContent && featuredStatusIds) {
scrollableContent = featuredStatusIds.map(statusId => ( scrollableContent = featuredStatusIds.map(statusId => (
<StatusContainer <StatusQuoteManager
key={`f-${statusId}`} key={`f-${statusId}`}
id={statusId} id={statusId}
featured featured

View File

@@ -0,0 +1,118 @@
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import type { Map as ImmutableMap } from 'immutable';
import QuoteIcon from '@/images/quote.svg?react';
import { Icon } from 'flavours/glitch/components/icon';
import StatusContainer from 'flavours/glitch/containers/status_container';
import { useAppSelector } from 'flavours/glitch/store';
const QuoteWrapper: React.FC<{
isError?: boolean;
children: React.ReactNode;
}> = ({ isError, children }) => {
return (
<div
className={classNames('status__quote', {
'status__quote--error': isError,
})}
>
<Icon id='quote' icon={QuoteIcon} className='status__quote-icon' />
{children}
</div>
);
};
type QuoteMap = ImmutableMap<'state' | 'quoted_status', string | null>;
export const QuotedStatus: React.FC<{ quote: QuoteMap }> = ({ quote }) => {
const quotedStatusId = quote.get('quoted_status');
const state = quote.get('state');
const status = useAppSelector((state) =>
quotedStatusId ? state.statuses.get(quotedStatusId) : undefined,
);
let quoteError: React.ReactNode | null = null;
if (state === 'deleted') {
quoteError = (
<FormattedMessage
id='status.quote_error.removed'
defaultMessage='This post was removed by its author.'
/>
);
} else if (state === 'unauthorized') {
quoteError = (
<FormattedMessage
id='status.quote_error.unauthorized'
defaultMessage='This post cannot be displayed as you are not authorized to view it.'
/>
);
} else if (state === 'pending') {
quoteError = (
<FormattedMessage
id='status.quote_error.pending_approval'
defaultMessage='This post is pending approval from the original author.'
/>
);
} else if (state === 'rejected' || state === 'revoked') {
quoteError = (
<FormattedMessage
id='status.quote_error.rejected'
defaultMessage='This post cannot be displayed as the original author does not allow it to be quoted.'
/>
);
} else if (!status || !quotedStatusId) {
quoteError = (
<FormattedMessage
id='status.quote_error.not_found'
defaultMessage='This post cannot be displayed.'
/>
);
}
if (quoteError) {
return <QuoteWrapper isError>{quoteError}</QuoteWrapper>;
}
return (
<QuoteWrapper>
<StatusContainer
// @ts-expect-error Status isn't typed yet
isQuotedPost
id={quotedStatusId}
avatarSize={40}
/>
</QuoteWrapper>
);
};
interface StatusQuoteManagerProps {
id: string;
[key: string]: unknown;
}
/**
* This wrapper component takes a status ID and, if the associated status
* is a quote post, it renders the quote into `StatusContainer` as a child.
* It passes all other props through to `StatusContainer`.
*/
export const StatusQuoteManager = (props: StatusQuoteManagerProps) => {
const status = useAppSelector((state) => state.statuses.get(props.id));
const quote = status?.get('quote') as QuoteMap | undefined;
if (quote) {
return (
/* @ts-expect-error Status is not yet typed */
<StatusContainer {...props}>
<QuotedStatus quote={quote} />
</StatusContainer>
);
}
/* @ts-expect-error Status is not yet typed */
return <StatusContainer {...props} />;
};

View File

@@ -14,7 +14,7 @@ import { Account } from 'flavours/glitch/components/account';
import { ColumnBackButton } from 'flavours/glitch/components/column_back_button'; import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import { RemoteHint } from 'flavours/glitch/components/remote_hint'; import { RemoteHint } from 'flavours/glitch/components/remote_hint';
import StatusContainer from 'flavours/glitch/containers/status_container'; import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
import { AccountHeader } from 'flavours/glitch/features/account_timeline/components/account_header'; import { AccountHeader } from 'flavours/glitch/features/account_timeline/components/account_header';
import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error'; import BundleColumnError from 'flavours/glitch/features/ui/components/bundle_column_error';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
@@ -142,9 +142,8 @@ const AccountFeatured: React.FC<{ multiColumn: boolean }> = ({
/> />
</h4> </h4>
{featuredStatusIds.map((statusId) => ( {featuredStatusIds.map((statusId) => (
<StatusContainer <StatusQuoteManager
key={`f-${statusId}`} key={`f-${statusId}`}
// @ts-expect-error inferred props are wrong
id={statusId} id={statusId}
contextType='account' contextType='account'
/> />

View File

@@ -16,7 +16,7 @@ import PersonAddIcon from '@/material-icons/400-24px/person_add-fill.svg?react';
import { Account } from 'flavours/glitch/components/account'; import { Account } from 'flavours/glitch/components/account';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { Permalink } from 'flavours/glitch/components/permalink'; import { Permalink } from 'flavours/glitch/components/permalink';
import StatusContainer from 'flavours/glitch/containers/status_container'; import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import FollowRequestContainer from '../containers/follow_request_container'; import FollowRequestContainer from '../containers/follow_request_container';
@@ -159,7 +159,7 @@ class Notification extends ImmutablePureComponent {
renderMention (notification) { renderMention (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
id={notification.get('status')} id={notification.get('status')}
containerId={notification.get('id')} containerId={notification.get('id')}
withDismiss withDismiss
@@ -181,7 +181,7 @@ class Notification extends ImmutablePureComponent {
renderFavourite (notification) { renderFavourite (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
containerId={notification.get('id')} containerId={notification.get('id')}
hidden={!!this.props.hidden} hidden={!!this.props.hidden}
id={notification.get('status')} id={notification.get('status')}
@@ -206,7 +206,7 @@ class Notification extends ImmutablePureComponent {
renderReblog (notification) { renderReblog (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
containerId={notification.get('id')} containerId={notification.get('id')}
hidden={!!this.props.hidden} hidden={!!this.props.hidden}
id={notification.get('status')} id={notification.get('status')}
@@ -231,7 +231,7 @@ class Notification extends ImmutablePureComponent {
renderStatus (notification) { renderStatus (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
containerId={notification.get('id')} containerId={notification.get('id')}
hidden={!!this.props.hidden} hidden={!!this.props.hidden}
id={notification.get('status')} id={notification.get('status')}
@@ -256,7 +256,7 @@ class Notification extends ImmutablePureComponent {
renderUpdate (notification) { renderUpdate (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
containerId={notification.get('id')} containerId={notification.get('id')}
hidden={!!this.props.hidden} hidden={!!this.props.hidden}
id={notification.get('status')} id={notification.get('status')}
@@ -281,7 +281,7 @@ class Notification extends ImmutablePureComponent {
renderPoll (notification) { renderPoll (notification) {
return ( return (
<StatusContainer <StatusQuoteManager
containerId={notification.get('id')} containerId={notification.get('id')}
hidden={!!this.props.hidden} hidden={!!this.props.hidden}
id={notification.get('status')} id={notification.get('status')}

View File

@@ -15,7 +15,7 @@ import {
} from 'flavours/glitch/actions/statuses'; } from 'flavours/glitch/actions/statuses';
import type { IconProp } from 'flavours/glitch/components/icon'; import type { IconProp } from 'flavours/glitch/components/icon';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import Status from 'flavours/glitch/containers/status_container'; import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
import { getStatusHidden } from 'flavours/glitch/selectors/filters'; import { getStatusHidden } from 'flavours/glitch/selectors/filters';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
@@ -106,8 +106,7 @@ export const NotificationWithStatus: React.FC<{
{label} {label}
</div> </div>
<Status <StatusQuoteManager
// @ts-expect-error -- <Status> is not yet typed
id={statusId} id={statusId}
contextType='notifications' contextType='notifications'
withDismiss withDismiss

View File

@@ -17,7 +17,7 @@ import { ColumnHeader } from 'flavours/glitch/components/column_header';
import { CompatibilityHashtag as Hashtag } from 'flavours/glitch/components/hashtag'; import { CompatibilityHashtag as Hashtag } from 'flavours/glitch/components/hashtag';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import ScrollableList from 'flavours/glitch/components/scrollable_list'; import ScrollableList from 'flavours/glitch/components/scrollable_list';
import Status from 'flavours/glitch/containers/status_container'; import { StatusQuoteManager } from 'flavours/glitch/components/status_quoted';
import { Search } from 'flavours/glitch/features/compose/components/search'; import { Search } from 'flavours/glitch/features/compose/components/search';
import { useSearchParam } from 'flavours/glitch/hooks/useSearchParam'; import { useSearchParam } from 'flavours/glitch/hooks/useSearchParam';
import type { Hashtag as HashtagType } from 'flavours/glitch/models/tags'; import type { Hashtag as HashtagType } from 'flavours/glitch/models/tags';
@@ -53,8 +53,7 @@ const renderHashtags = (hashtags: HashtagType[]) =>
const renderStatuses = (statusIds: string[]) => const renderStatuses = (statusIds: string[]) =>
hidePeek<string>(statusIds).map((id) => ( hidePeek<string>(statusIds).map((id) => (
// @ts-expect-error inferred props are wrong <StatusQuoteManager key={id} id={id} />
<Status key={id} id={id} />
)); ));
type SearchType = 'all' | ApiSearchType; type SearchType = 'all' | ApiSearchType;
@@ -190,8 +189,7 @@ export const SearchResults: React.FC<{ multiColumn: boolean }> = ({
onClickMore={handleSelectStatuses} onClickMore={handleSelectStatuses}
> >
{results.statuses.slice(0, INITIAL_DISPLAY).map((id) => ( {results.statuses.slice(0, INITIAL_DISPLAY).map((id) => (
// @ts-expect-error inferred props are wrong <StatusQuoteManager key={id} id={id} />
<Status key={id} id={id} />
))} ))}
</SearchSection> </SearchSection>
)} )}

View File

@@ -27,6 +27,7 @@ import { MentionsPlaceholder } from 'flavours/glitch/components/mentions_placeho
import { Permalink } from 'flavours/glitch/components/permalink'; import { Permalink } from 'flavours/glitch/components/permalink';
import { PictureInPicturePlaceholder } from 'flavours/glitch/components/picture_in_picture_placeholder'; import { PictureInPicturePlaceholder } from 'flavours/glitch/components/picture_in_picture_placeholder';
import StatusContent from 'flavours/glitch/components/status_content'; import StatusContent from 'flavours/glitch/components/status_content';
import { QuotedStatus } from 'flavours/glitch/components/status_quoted';
import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon'; import { VisibilityIcon } from 'flavours/glitch/components/visibility_icon';
import { Audio } from 'flavours/glitch/features/audio'; import { Audio } from 'flavours/glitch/features/audio';
import scheduleIdleTask from 'flavours/glitch/features/ui/util/schedule_idle_task'; import scheduleIdleTask from 'flavours/glitch/features/ui/util/schedule_idle_task';
@@ -410,6 +411,10 @@ export const DetailedStatus: React.FC<{
{...(statusContentProps as any)} {...(statusContentProps as any)}
/> />
{status.get('quote') && (
<QuotedStatus quote={status.get('quote')} />
)}
{media} {media}
{hashtagBar} {hashtagBar}
</> </>

View File

@@ -6,8 +6,6 @@ import classNames from 'classnames';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { createSelector } from '@reduxjs/toolkit';
import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@@ -56,7 +54,7 @@ import {
} from '../../actions/statuses'; } from '../../actions/statuses';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { textForScreenReader, defaultMediaVisibility } from '../../components/status'; import { textForScreenReader, defaultMediaVisibility } from '../../components/status';
import StatusContainer from '../../containers/status_container'; import { StatusQuoteManager } from '../../components/status_quoted';
import { deleteModal } from '../../initial_state'; import { deleteModal } from '../../initial_state';
import { makeGetStatus, makeGetPictureInPicture } from '../../selectors'; import { makeGetStatus, makeGetPictureInPicture } from '../../selectors';
import { getAncestorsIds, getDescendantsIds } from 'flavours/glitch/selectors/contexts'; import { getAncestorsIds, getDescendantsIds } from 'flavours/glitch/selectors/contexts';
@@ -492,7 +490,7 @@ class Status extends ImmutablePureComponent {
const { params: { statusId } } = this.props; const { params: { statusId } } = this.props;
return list.map((id, i) => ( return list.map((id, i) => (
<StatusContainer <StatusQuoteManager
key={id} key={id}
id={id} id={id}
expanded={this.state.threadExpanded} expanded={this.state.threadExpanded}

View File

@@ -1550,8 +1550,12 @@ body > [data-popper-placement] {
} }
} }
&--is-quote {
border: none;
}
&--in-thread { &--in-thread {
$thread-margin: 46px + 10px; --thread-margin: calc(46px + 8px);
border-bottom: 0; border-bottom: 0;
@@ -1567,16 +1571,16 @@ body > [data-popper-placement] {
.hashtag-bar, .hashtag-bar,
.content-warning, .content-warning,
.filter-warning { .filter-warning {
margin-inline-start: $thread-margin; margin-inline-start: var(--thread-margin);
width: calc(100% - $thread-margin); width: calc(100% - var(--thread-margin));
} }
.more-from-author { .more-from-author {
width: calc(100% - $thread-margin + 2px); width: calc(100% - var(--thread-margin) + 2px);
} }
.status__content__read-more-button { .status__content__read-more-button {
margin-inline-start: $thread-margin; margin-inline-start: var(--thread-margin);
} }
} }
@@ -1939,6 +1943,41 @@ body > [data-popper-placement] {
} }
} }
.status__quote {
position: relative;
margin-block-start: 16px;
margin-inline-start: 56px;
border-radius: 8px;
color: var(--nested-card-text);
background: var(--nested-card-background);
border: var(--nested-card-border);
}
.status__quote--error {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
font-size: 15px;
}
.status__quote-icon {
position: absolute;
inset-block-start: 18px;
inset-inline-start: -50px;
display: block;
width: 26px;
height: 26px;
padding: 5px;
color: #6a49ba;
z-index: 10;
.status__quote--error & {
inset-block-start: 50%;
transform: translateY(-50%);
}
}
.detailed-status__link { .detailed-status__link {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -2375,11 +2414,6 @@ a.account__display-name {
} }
} }
.status__avatar {
width: 46px;
height: 46px;
}
.muted { .muted {
.status__content, .status__content,
.status__content p, .status__content p,
@@ -10837,6 +10871,7 @@ noscript {
line-height: 22px; line-height: 22px;
color: $darker-text-color; color: $darker-text-color;
-webkit-line-clamp: 4; -webkit-line-clamp: 4;
line-clamp: 4;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
max-height: none; max-height: none;
overflow: hidden; overflow: hidden;
@@ -11147,9 +11182,9 @@ noscript {
.content-warning { .content-warning {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
background: rgba($ui-highlight-color, 0.05); background: var(--nested-card-background);
color: $secondary-text-color; color: var(--nested-card-text);
border: 1px solid rgba($ui-highlight-color, 0.15); border: var(--nested-card-border);
border-radius: 8px; border-radius: 8px;
padding: 8px (5px + 8px); padding: 8px (5px + 8px);
position: relative; position: relative;

View File

@@ -27,6 +27,10 @@
--rich-text-container-color: rgba(87, 24, 60, 100%); --rich-text-container-color: rgba(87, 24, 60, 100%);
--rich-text-text-color: rgba(255, 175, 212, 100%); --rich-text-text-color: rgba(255, 175, 212, 100%);
--rich-text-decorations-color: rgba(128, 58, 95, 100%); --rich-text-decorations-color: rgba(128, 58, 95, 100%);
--nested-card-background: color(from #{$ui-highlight-color} srgb r g b / 5%);
--nested-card-text: #{$secondary-text-color};
--nested-card-border: 1px solid
color(from #{$ui-highlight-color} srgb r g b / 15%);
--input-placeholder-color: #{$dark-text-color}; --input-placeholder-color: #{$dark-text-color};
--input-background-color: var(--surface-variant-background-color); --input-background-color: var(--surface-variant-background-color);
--on-input-color: #{$secondary-text-color}; --on-input-color: #{$secondary-text-color};