Merge branch 'develop' of https://codeberg.org/mkljczk/pl-fe into develop
This commit is contained in:
44
.devcontainer/devcontainer.json
Normal file
44
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "Nicolium",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspaces/nicolium",
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "20",
|
||||
"nodeGypDependencies": true
|
||||
}
|
||||
},
|
||||
|
||||
"remoteUser": "vscode",
|
||||
"updateRemoteUserUID": true,
|
||||
|
||||
"forwardPorts": [7312],
|
||||
"portsAttributes": {
|
||||
"7312": {
|
||||
"label": "Nicolium",
|
||||
"onAutoForward": "openBrowser"
|
||||
}
|
||||
},
|
||||
|
||||
"postCreateCommand": "bash .devcontainer/post-create.sh",
|
||||
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"oxc.oxc-vscode",
|
||||
"stylelint.vscode-stylelint",
|
||||
"wix.vscode-import-cost"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"containerEnv": {
|
||||
"WAYLAND_DISPLAY": ""
|
||||
},
|
||||
"remoteEnv": {
|
||||
"WAYLAND_DISPLAY": ""
|
||||
}
|
||||
}
|
||||
10
.devcontainer/docker-compose.yml
Normal file
10
.devcontainer/docker-compose.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: mcr.microsoft.com/devcontainers/base:ubuntu
|
||||
command: sleep infinity
|
||||
volumes:
|
||||
- ..:/workspaces/nicolium:Z
|
||||
ports:
|
||||
- "127.0.0.1:7312:7312"
|
||||
24
.devcontainer/post-create.sh
Normal file
24
.devcontainer/post-create.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
corepack enable
|
||||
mkdir -p ~/.local/bin
|
||||
|
||||
platform="$(uname -s)-$(uname -m)"
|
||||
formatjs_url=""
|
||||
|
||||
case "$platform" in
|
||||
Linux-x86_64)
|
||||
formatjs_url="https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.1.0/formatjs_cli-linux-x64"
|
||||
;;
|
||||
Darwin-arm64)
|
||||
formatjs_url="https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.1.0/formatjs_cli-darwin-arm64"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n "$formatjs_url" ]]; then
|
||||
curl -fsSL "$formatjs_url" -o ~/.local/bin/formatjs
|
||||
chmod +x ~/.local/bin/formatjs
|
||||
fi
|
||||
|
||||
pnpm install --ignore-scripts
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
node_modules
|
||||
/node_modules/
|
||||
yarn-error.log*
|
||||
coverage/
|
||||
package-lock.json
|
||||
.pnpm-store
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-vscode-remote.remote-containers",
|
||||
"oxc.oxc-vscode",
|
||||
"stylelint.vscode-stylelint",
|
||||
"wix.vscode-import-cost"
|
||||
]
|
||||
|
||||
@@ -209,10 +209,16 @@ const TimelineGap: React.FC<ITimelineGap> = ({ gap, onFillGap, firstEntry }) =>
|
||||
interface ITimelineStatusInfo {
|
||||
status: SelectedStatus;
|
||||
rebloggedBy: Array<string>;
|
||||
reblogVisibility?: string;
|
||||
timelineId: string;
|
||||
}
|
||||
|
||||
const TimelineStatusInfo: React.FC<ITimelineStatusInfo> = ({ status, rebloggedBy, timelineId }) => {
|
||||
const TimelineStatusInfo: React.FC<ITimelineStatusInfo> = ({
|
||||
status,
|
||||
rebloggedBy,
|
||||
reblogVisibility,
|
||||
timelineId,
|
||||
}) => {
|
||||
const features = useFeatures();
|
||||
const isReblogged = rebloggedBy.length > 0;
|
||||
|
||||
@@ -256,19 +262,19 @@ const TimelineStatusInfo: React.FC<ITimelineStatusInfo> = ({ status, rebloggedBy
|
||||
avatarSize={42}
|
||||
icon={<Icon src={iconRepeat} className='size-4 text-green-600' aria-hidden />}
|
||||
text={
|
||||
// status.visibility === 'private' ? (
|
||||
// <FormattedMessage
|
||||
// id='status.reblogged_by_private'
|
||||
// defaultMessage='{name} reposted to followers'
|
||||
// values={values}
|
||||
// />
|
||||
// ) : (
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} reposted'
|
||||
values={values}
|
||||
/>
|
||||
// )
|
||||
reblogVisibility === 'private' ? (
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by_private'
|
||||
defaultMessage='{name} reposted to followers'
|
||||
values={values}
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} reposted'
|
||||
values={values}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
@@ -281,6 +287,7 @@ const TimelineStatusInfo: React.FC<ITimelineStatusInfo> = ({ status, rebloggedBy
|
||||
interface ITimelineStatus {
|
||||
id: string;
|
||||
rebloggedBy: Array<string>;
|
||||
reblogVisibility?: string;
|
||||
timelineId: string;
|
||||
contextType?: FilterContextType;
|
||||
isConnectedTop?: boolean;
|
||||
@@ -341,6 +348,7 @@ const TimelineStatus: React.FC<ITimelineStatus> = (props): React.JSX.Element =>
|
||||
<TimelineStatusInfo
|
||||
status={statusQuery.data!}
|
||||
rebloggedBy={props.rebloggedBy}
|
||||
reblogVisibility={props.reblogVisibility}
|
||||
timelineId={props.timelineId}
|
||||
/>
|
||||
)}
|
||||
@@ -440,6 +448,7 @@ const Timeline: React.FC<ITimeline> = ({
|
||||
onMoveUp={() => handleMoveUp(index)}
|
||||
onMoveDown={() => handleMoveDown(index)}
|
||||
rebloggedBy={entry.rebloggedBy}
|
||||
reblogVisibility={entry.reblogVisibility}
|
||||
timelineId={timelineId}
|
||||
// showGroup={showGroup}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Link } from '@tanstack/react-router';
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
|
||||
import Text from '@/components/ui/text';
|
||||
import { useSettings } from '@/stores/settings';
|
||||
import { shortNumberFormat } from '@/utils/numbers';
|
||||
|
||||
@@ -44,18 +43,11 @@ const ProfileStats: React.FC<IProfileStats> = ({ account, onClickHandler }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap items-center gap-x-3'>
|
||||
<div className='⁂-account-stats'>
|
||||
{!demetricator && (
|
||||
<div
|
||||
className='flex items-center gap-1'
|
||||
title={intl.formatMessage(messages.statusesCount, { count: account.statuses_count })}
|
||||
>
|
||||
<Text theme='primary' weight='bold' size='sm'>
|
||||
{shortNumberFormat(account.statuses_count)}
|
||||
</Text>
|
||||
<Text weight='bold' size='sm'>
|
||||
<FormattedMessage id='account.statuses' defaultMessage='Statuses' />
|
||||
</Text>
|
||||
<div title={intl.formatMessage(messages.statusesCount, { count: account.statuses_count })}>
|
||||
<strong>{shortNumberFormat(account.statuses_count)}</strong>
|
||||
<FormattedMessage id='account.statuses' defaultMessage='Statuses' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -63,44 +55,22 @@ const ProfileStats: React.FC<IProfileStats> = ({ account, onClickHandler }) => {
|
||||
to='/@{$username}/followers'
|
||||
params={{ username: account.acct }}
|
||||
onClick={onClickHandler}
|
||||
title={intl.formatNumber(account.followers_count)}
|
||||
className='hover:underline'
|
||||
title={intl.formatMessage(messages.followersCount, { count: account.followers_count })}
|
||||
>
|
||||
<div
|
||||
className='flex items-center gap-1'
|
||||
title={intl.formatMessage(messages.followersCount, { count: account.followers_count })}
|
||||
>
|
||||
{!demetricator && (
|
||||
<Text theme='primary' weight='bold' size='sm'>
|
||||
{shortNumberFormat(account.followers_count)}
|
||||
</Text>
|
||||
)}
|
||||
<Text weight='bold' size='sm'>
|
||||
<FormattedMessage id='account.followers' defaultMessage='Followers' />
|
||||
</Text>
|
||||
</div>
|
||||
{!demetricator && <strong>{shortNumberFormat(account.followers_count)}</strong>}
|
||||
|
||||
<FormattedMessage id='account.followers' defaultMessage='Followers' />
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
to='/@{$username}/following'
|
||||
params={{ username: account.acct }}
|
||||
onClick={onClickHandler}
|
||||
title={intl.formatNumber(account.following_count)}
|
||||
className='hover:underline'
|
||||
title={intl.formatMessage(messages.followingCount, { count: account.following_count })}
|
||||
>
|
||||
<div
|
||||
className='flex items-center gap-1'
|
||||
title={intl.formatMessage(messages.followingCount, { count: account.following_count })}
|
||||
>
|
||||
{!demetricator && (
|
||||
<Text theme='primary' weight='bold' size='sm'>
|
||||
{shortNumberFormat(account.following_count)}
|
||||
</Text>
|
||||
)}
|
||||
<Text weight='bold' size='sm'>
|
||||
<FormattedMessage id='account.follows' defaultMessage='Following' />
|
||||
</Text>
|
||||
</div>
|
||||
{!demetricator && <strong>{shortNumberFormat(account.following_count)}</strong>}
|
||||
|
||||
<FormattedMessage id='account.follows' defaultMessage='Following' />
|
||||
</Link>
|
||||
|
||||
{account.subscribers_count > 0 && (
|
||||
@@ -108,24 +78,13 @@ const ProfileStats: React.FC<IProfileStats> = ({ account, onClickHandler }) => {
|
||||
to='/@{$username}/subscribers'
|
||||
params={{ username: account.acct }}
|
||||
onClick={onClickHandler}
|
||||
title={intl.formatNumber(account.subscribers_count)}
|
||||
className='hover:underline'
|
||||
title={intl.formatMessage(messages.subscribersCount, {
|
||||
count: account.subscribers_count,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className='flex items-center gap-1'
|
||||
title={intl.formatMessage(messages.subscribersCount, {
|
||||
count: account.subscribers_count,
|
||||
})}
|
||||
>
|
||||
{!demetricator && (
|
||||
<Text theme='primary' weight='bold' size='sm'>
|
||||
{shortNumberFormat(account.subscribers_count)}
|
||||
</Text>
|
||||
)}
|
||||
<Text weight='bold' size='sm'>
|
||||
<FormattedMessage id='account.subscribers' defaultMessage='Subscribers' />
|
||||
</Text>
|
||||
</div>
|
||||
{!demetricator && <strong>{shortNumberFormat(account.subscribers_count)}</strong>}
|
||||
|
||||
<FormattedMessage id='account.subscribers' defaultMessage='Subscribers' />
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@ const SidebarNavigationLink: React.FC<ISidebarNavigationLink> = React.memo(
|
||||
|
||||
const LinkComponent = (to === undefined ? 'button' : Link) as typeof Link;
|
||||
|
||||
const isActive = matchRoute({ to }) !== false;
|
||||
const isActive = matchRoute({ to, ...rest }) !== false;
|
||||
|
||||
const handleClick: React.EventHandler<React.MouseEvent> = (e) => {
|
||||
if (onClick) {
|
||||
@@ -43,7 +43,7 @@ const SidebarNavigationLink: React.FC<ISidebarNavigationLink> = React.memo(
|
||||
return (
|
||||
<li>
|
||||
<LinkComponent
|
||||
activeOptions={{ exact: true }}
|
||||
activeOptions={{ exact: true, includeSearch: false }}
|
||||
activeProps={{ className: '⁂-sidebar-navigation-link--active' }}
|
||||
to={to}
|
||||
ref={ref}
|
||||
|
||||
@@ -105,7 +105,6 @@ const StatusList: React.FC<IStatusList> = ({
|
||||
showGroup={showGroup}
|
||||
variant='slim'
|
||||
fromBookmarks={other.scrollKey === 'bookmarked_statuses'}
|
||||
fromHomeTimeline={timelineId === 'home'}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import Text from '@/components/ui/text';
|
||||
import Emojify from '@/features/emoji/emojify';
|
||||
import StatusTypeIcon from '@/features/status/components/status-type-icon';
|
||||
import { Hotkeys } from '@/features/ui/components/hotkeys';
|
||||
import { useFeatures } from '@/hooks/use-features';
|
||||
import { useGroupQuery } from '@/queries/groups/use-group';
|
||||
import { useFollowedTags } from '@/queries/hashtags/use-followed-tags';
|
||||
import { useStatus, type SelectedStatus } from '@/queries/statuses/use-status';
|
||||
@@ -175,7 +174,6 @@ interface IStatus {
|
||||
showGroup?: boolean;
|
||||
showInfo?: boolean;
|
||||
fromBookmarks?: boolean;
|
||||
fromHomeTimeline?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -196,7 +194,6 @@ const Status: React.FC<IStatus> = React.memo((props) => {
|
||||
showGroup = true,
|
||||
showInfo = true,
|
||||
fromBookmarks = false,
|
||||
fromHomeTimeline = false,
|
||||
className,
|
||||
contextType,
|
||||
} = props;
|
||||
@@ -204,7 +201,6 @@ const Status: React.FC<IStatus> = React.memo((props) => {
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigate();
|
||||
const router = useRouter();
|
||||
const features = useFeatures();
|
||||
|
||||
const { toggleStatusesMediaHidden, unfilterStatus } = useStatusMetaActions();
|
||||
const { deleted, showFiltered } = useStatusMeta(status.id);
|
||||
@@ -479,12 +475,6 @@ const Status: React.FC<IStatus> = React.memo((props) => {
|
||||
}
|
||||
/>
|
||||
);
|
||||
} else if (fromHomeTimeline) {
|
||||
return (
|
||||
features.followHashtags && (
|
||||
<StatusFollowedTagInfo className='-mb-1' status={actualStatus} avatarSize={avatarSize} />
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [status.account, group?.id]);
|
||||
|
||||
|
||||
@@ -309,6 +309,7 @@ const EditEvent: React.FC<IEditEvent> = ({ statusId }) => {
|
||||
<ContentTypeButton composeId={composeId} />
|
||||
<ComposeEditor
|
||||
key={String(isDisabled)}
|
||||
className='⁂-edit-event__editor'
|
||||
placeholderClassName='⁂-compose-form__editor__placeholder'
|
||||
composeId={composeId}
|
||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import IconButton from '@/components/ui/icon-button';
|
||||
@@ -20,11 +19,7 @@ const ComposeFormButton: React.FC<IComposeFormButton> = ({
|
||||
}) => (
|
||||
<div>
|
||||
<IconButton
|
||||
className={clsx({
|
||||
'text-gray-600 hover:text-gray-700 dark:hover:text-gray-500': !active,
|
||||
'text-primary-500 hover:text-primary-600 dark:text-primary-500 dark:hover:text-primary-400':
|
||||
active,
|
||||
})}
|
||||
className='⁂-compose-form__button'
|
||||
src={icon}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
|
||||
@@ -109,14 +109,14 @@ const ComposeButton: React.FC<IComposeButton> = ({
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<div className='⁂-compose-form__button__container'>
|
||||
<button {...props} disabled={disabled} className='⁂-compose-form__button'>
|
||||
<div className='⁂-compose-form__send-button__container'>
|
||||
<button {...props} disabled={disabled} className='⁂-compose-form__send-button'>
|
||||
{icon ? <Icon src={icon} /> : null}
|
||||
<span>{text}</span>
|
||||
</button>
|
||||
<DropdownMenu items={actionsMenu} placement='bottom' disabled={disabled}>
|
||||
<button
|
||||
className='⁂-compose-form__button__actions'
|
||||
className='⁂-compose-form__send-button__actions'
|
||||
title={intl.formatMessage(messages.more)}
|
||||
>
|
||||
<SvgIcon src={iconCaretDown} aria-hidden />
|
||||
|
||||
@@ -193,20 +193,15 @@ const ComposeEditor = React.forwardRef<LexicalEditor, IComposeEditor>(
|
||||
|
||||
return (
|
||||
<LexicalComposer key={isWysiwyg ? 'wysiwyg' : 'no-wysiwyg'} initialConfig={initialConfig}>
|
||||
<div className={clsx('lexical relative', className)} data-markup>
|
||||
<div className={className} data-markup>
|
||||
<RichTextPlugin
|
||||
contentEditable={
|
||||
<div onFocus={onFocus} onPaste={handlePaste} ref={onRef}>
|
||||
<ContentEditable
|
||||
tabIndex={0}
|
||||
className={clsx(
|
||||
'relative z-10 text-[1rem] outline-none transition-[min-height] motion-reduce:transition-none',
|
||||
editableClassName,
|
||||
{
|
||||
'min-h-[39px]': condensed,
|
||||
'min-h-[99px]': !condensed,
|
||||
},
|
||||
)}
|
||||
className={clsx('⁂-compose-form__editor__editable', editableClassName, {
|
||||
'⁂-compose-form__editor__editable--condensed': condensed,
|
||||
})}
|
||||
lang={language ?? undefined}
|
||||
data-compose-id={composeId}
|
||||
aria-label={textareaPlaceholder}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useFloating, shift, flip, autoUpdate } from '@floating-ui/react';
|
||||
import iconSmiley from '@phosphor-icons/core/regular/smiley.svg';
|
||||
import clsx from 'clsx';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
@@ -22,11 +21,9 @@ interface IEmojiPickerDropdownContainer extends Pick<
|
||||
'onPickEmoji' | 'condensed' | 'withCustom'
|
||||
> {
|
||||
children?: React.JSX.Element;
|
||||
theme?: 'default' | 'inverse';
|
||||
}
|
||||
|
||||
const EmojiPickerDropdownContainer: React.FC<IEmojiPickerDropdownContainer> = ({
|
||||
theme = 'default',
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
@@ -87,12 +84,7 @@ const EmojiPickerDropdownContainer: React.FC<IEmojiPickerDropdownContainer> = ({
|
||||
{clonedChildren ?? (
|
||||
<IconButton
|
||||
theme='transparent'
|
||||
className={clsx('emoji-picker-dropdown -m-1 p-2', {
|
||||
'bg-transparent text-gray-600 hover:bg-primary-100 hover:text-gray-800 black:hover:bg-gray-800 dark:hover:bg-primary-800 dark:hover:text-white':
|
||||
theme === 'default',
|
||||
'bg-transparent text-white/80 hover:text-white dark:bg-transparent':
|
||||
theme === 'inverse',
|
||||
})}
|
||||
className='emoji-picker-dropdown -m-1 bg-transparent p-2 text-gray-600 hover:bg-primary-100 hover:text-gray-800 black:hover:bg-gray-800 dark:hover:bg-primary-800 dark:hover:text-white'
|
||||
ref={refs.setReference}
|
||||
src={iconSmiley}
|
||||
title={title}
|
||||
|
||||
@@ -77,7 +77,7 @@ const GroupTimelinePage: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
emptyMessageIcon={iconChatCenteredText}
|
||||
// showGroup={falsse}
|
||||
// showGroup={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ type TimelineEntry =
|
||||
accountId: string;
|
||||
rebloggedBy: Array<string>;
|
||||
reblogIds: Array<string>;
|
||||
reblogVisibility?: string;
|
||||
isConnectedTop?: boolean;
|
||||
isConnectedBottom?: boolean;
|
||||
isReply: boolean;
|
||||
@@ -125,6 +126,9 @@ const processPage = (statuses: Array<Status>): Array<TimelineEntry> => {
|
||||
if (!existingEntry.rebloggedBy.includes(status.account.id)) {
|
||||
existingEntry.rebloggedBy.push(status.account.id);
|
||||
existingEntry.reblogIds.push(status.id);
|
||||
if (existingEntry.reblogVisibility !== status.visibility) {
|
||||
existingEntry.reblogVisibility = undefined;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timelinePage.push({
|
||||
@@ -134,6 +138,7 @@ const processPage = (statuses: Array<Status>): Array<TimelineEntry> => {
|
||||
accountId: status.reblog.account.id,
|
||||
rebloggedBy: [status.account.id],
|
||||
reblogIds: [status.id],
|
||||
reblogVisibility: status.visibility,
|
||||
isConnectedTop,
|
||||
isReply: status.reblog.in_reply_to_id !== null,
|
||||
isReblog: true,
|
||||
|
||||
@@ -1,11 +1,36 @@
|
||||
.empty-column-indicator {
|
||||
@apply bg-primary-50 dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-center p-10 flex flex-1 items-center justify-center min-h-[160px] rounded-lg;
|
||||
display: flex;
|
||||
flex: 1 1 0%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
min-height: 10rem;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
color: rgb(var(--color-gray-900));
|
||||
text-align: center;
|
||||
|
||||
background-color: rgb(var(--color-primary-900));
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-gray-100));
|
||||
background-color: rgb(var(--color-primary-700));
|
||||
}
|
||||
|
||||
& > span {
|
||||
@apply max-w-[400px];
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary-600 dark:text-primary-400 no-underline hover:underline;
|
||||
color: rgb(var(--color-primary-600));
|
||||
text-decoration: none;
|
||||
|
||||
.dark & {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
.compose-form__upload-thumbnail {
|
||||
&.video {
|
||||
@apply bg-cover;
|
||||
|
||||
background-image: url('../assets/images/video-placeholder.png');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&.audio {
|
||||
@apply bg-cover;
|
||||
|
||||
background-image: url('../assets/images/audio-placeholder.png');
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
}
|
||||
|
||||
.react-datepicker__navigation--next {
|
||||
@apply right-4;
|
||||
right: 1rem;
|
||||
|
||||
&--with-time:not(.react-datepicker__navigation--next--with-today-button) {
|
||||
right: 100px;
|
||||
@@ -76,12 +76,13 @@
|
||||
}
|
||||
|
||||
.react-datepicker__header__dropdown {
|
||||
@apply py-4;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.react-datepicker__day-names,
|
||||
.react-datepicker__week {
|
||||
@apply flex justify-between;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.react-datepicker__time {
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
.svg-icon {
|
||||
@apply h-4 w-4 flex items-center justify-center transition duration-200;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
transition: all 200ms var(--ease-default);
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
|
||||
// Apparently this won't skew the image as long as it has a viewbox
|
||||
@apply h-full w-full transition duration-200;
|
||||
height: 100%;
|
||||
transition: all 200ms var(--ease-default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
.⁂-media-modal {
|
||||
.audio-player.detailed,
|
||||
.extended-video-player {
|
||||
@apply flex items-center justify-center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
@apply max-w-[800px] max-h-[600px];
|
||||
max-width: 50rem;
|
||||
max-height: 36rem;
|
||||
}
|
||||
|
||||
.extended-video-player {
|
||||
@apply w-full h-full;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
video {
|
||||
@apply max-w-full max-h-[80%];
|
||||
max-width: 100%;
|
||||
max-height: 80%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,17 +40,30 @@
|
||||
flex: 0 1 auto;
|
||||
|
||||
.player-button {
|
||||
@apply block outline-0 bg-transparent text-base border-0 text-black dark:text-white rounded-full;
|
||||
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
|
||||
padding: 4px;
|
||||
border-width: 0;
|
||||
border-radius: 9999px;
|
||||
|
||||
font-size: 0.9375rem;
|
||||
color: black;
|
||||
|
||||
background-color: transparent;
|
||||
outline-width: 0;
|
||||
|
||||
.dark {
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply dark:bg-primary-200/20 bg-primary-700/20 rounded-full;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
@apply w-5 h-5;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,19 @@
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
@apply text-gray-50 dark:text-gray-800 text-xs uppercase flex flex-col items-center justify-center overflow-visible;
|
||||
overflow: visible;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
font-size: 0.75rem;
|
||||
color: rgb(var(--color-gray-50));
|
||||
text-transform: uppercase;
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-gray-800));
|
||||
}
|
||||
}
|
||||
|
||||
.loading-indicator__container {
|
||||
@@ -19,10 +31,22 @@
|
||||
}
|
||||
|
||||
.loading-indicator__figure {
|
||||
@apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-12 h-12 rounded-full bg-transparent dark:bg-transparent border-[#e5e7eb] dark:border-gray-800;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
border: 0 solid;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: 0 solid #e5e7eb;
|
||||
border-width: 6px;
|
||||
border-radius: 9999px;
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
.dark {
|
||||
border-color: rgb(var(--color-gray-800));
|
||||
}
|
||||
}
|
||||
|
||||
.no-reduce-motion .loading-indicator__figure {
|
||||
@@ -30,29 +54,45 @@
|
||||
}
|
||||
|
||||
.loading-indicator-wrapper .loading-indicator {
|
||||
@apply h-screen w-screen items-center;
|
||||
align-items: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
&__figure {
|
||||
@apply border-gray-200 dark:border-gray-800;
|
||||
border-color: rgb(var(--color-gray-200));
|
||||
|
||||
.dark & {
|
||||
border-color: rgb(var(--color-gray-800));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loader-figure {
|
||||
0% {
|
||||
@apply bg-yellow-200 w-0 h-0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: var(--hex-yellow-200);
|
||||
}
|
||||
|
||||
29% {
|
||||
@apply bg-gray-200;
|
||||
background: rgb(var(--color-gray-200));
|
||||
}
|
||||
|
||||
30% {
|
||||
@apply w-12 h-12 bg-transparent opacity-100;
|
||||
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-width: 6px;
|
||||
|
||||
opacity: 1;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
100% {
|
||||
@apply w-12 h-12 border-0 opacity-0 bg-transparent;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-width: 0;
|
||||
|
||||
opacity: 0;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,61 +2,77 @@ $vertical-lr-langs: mn-mong, mnmong;
|
||||
|
||||
[data-markup],
|
||||
[data-lexical-editor] {
|
||||
@apply whitespace-pre-wrap;
|
||||
white-space: pre-wrap;
|
||||
|
||||
h1 {
|
||||
@apply text-3xl font-semibold;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 600;
|
||||
line-height: 2.25rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-2xl font-semibold;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-xl font-black;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
hr {
|
||||
&:not(:last-child) {
|
||||
@apply mb-4;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
@apply whitespace-pre-wrap;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:not(:last-child) {
|
||||
@apply mb-4;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-primary-600 dark:text-primary-400 hover:underline;
|
||||
color: rgb(var(--color-primary-600));
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
strong {
|
||||
@apply font-bold;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
em {
|
||||
@apply italic;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
@apply pl-10;
|
||||
padding-left: 2.5rem;
|
||||
|
||||
&:not(:last-child) {
|
||||
@apply mb-4;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply list-disc list-outside;
|
||||
list-style-position: outside;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
ol {
|
||||
@apply list-decimal list-outside;
|
||||
list-style-position: outside;
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
li {
|
||||
@@ -69,70 +85,119 @@ $vertical-lr-langs: mn-mong, mnmong;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
@apply py-1 pl-4 border-l-4 border-solid border-gray-400 text-gray-500 dark:text-gray-400;
|
||||
padding: 0.25rem 0 0.25rem 1rem;
|
||||
border-left: 4px solid rgb(var(--color-gray-400));
|
||||
color: rgb(var(--color-gray-500));
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-gray-400));
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
@apply mb-4;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
@apply table-auto w-full bg-gray-200 dark:bg-gray-900 my-4 rounded-md;
|
||||
table-layout: auto;
|
||||
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
border-radius: 0.375rem;
|
||||
|
||||
background-color: rgb(var(--color-gray-200));
|
||||
|
||||
.dark {
|
||||
background-color: rgb(var(--color-gray-900));
|
||||
}
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
@apply text-center px-2;
|
||||
align-items: center;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
table th {
|
||||
@apply border-b-2 border-gray-600;
|
||||
border: 2px solid rgb(var(--color-gray-600));
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
@apply cursor-text font-mono;
|
||||
cursor: text;
|
||||
font-family: "Roboto Mono", ui-monospace, monospace;
|
||||
}
|
||||
|
||||
p > code,
|
||||
pre {
|
||||
@apply bg-gray-100 dark:bg-primary-800;
|
||||
background: rgb(var(--color-gray-100));
|
||||
|
||||
.dark {
|
||||
background: rgb(var(--color-primary-800));
|
||||
}
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
p > code {
|
||||
@apply py-0.5 px-1 rounded-sm;
|
||||
padding: 0.125rem 0.25rem;
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
|
||||
/* Code block */
|
||||
pre {
|
||||
@apply py-2 px-3 leading-6 overflow-x-auto rounded-md break-all;
|
||||
overflow-x: auto;
|
||||
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 0.375rem;
|
||||
|
||||
line-height: 1.5rem;
|
||||
word-break: break-all;
|
||||
|
||||
&:not(:last-child) {
|
||||
@apply mb-4;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
pre:last-child {
|
||||
@apply mb-0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Emojis */
|
||||
img.emojione {
|
||||
@apply w-5 h-5 m-0;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Markdown inline images (Pleroma) */
|
||||
img:not(.emojione) {
|
||||
@apply max-h-[500px] mx-auto rounded-sm;
|
||||
max-height: 30rem;
|
||||
margin: 0 auto;
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
|
||||
&.big-emoji img.emojione {
|
||||
@apply inline w-9 h-9 p-1;
|
||||
display: inline;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.status-link {
|
||||
@apply hover:underline text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-400;
|
||||
color: rgb(var(--color-primary-600));
|
||||
|
||||
&:hover {
|
||||
color: rgb(var(--color-primary-800));
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-primary-400));
|
||||
|
||||
&:hover {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.invisible {
|
||||
@@ -180,7 +245,7 @@ body.underline-links {
|
||||
[data-markup],
|
||||
[data-lexical-editor] {
|
||||
a {
|
||||
@apply underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
.⁂-account-stats {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
|
||||
a, div {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
align-items: center;
|
||||
@include mixins.text($weight: bold, $size: sm);
|
||||
|
||||
strong {
|
||||
color: rgb(var(--color-primary-600));
|
||||
|
||||
.dark * & {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.⁂-account-card {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
|
||||
@@ -31,20 +31,43 @@
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
&__editor__placeholder {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
&__editor {
|
||||
position: relative;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
&__placeholder {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
||||
font-size: 1rem;
|
||||
color: rgb(var(--color-gray-600));
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
||||
visibility: visible !important;
|
||||
|
||||
.dark &::placeholder {
|
||||
font-size: 1rem;
|
||||
color: rgb(var(--color-gray-600));
|
||||
|
||||
visibility: visible !important;
|
||||
|
||||
.dark &::placeholder {
|
||||
color: rgb(var(--color-gray-600));
|
||||
}
|
||||
}
|
||||
|
||||
&__editable {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
min-height: 99px;
|
||||
font-size: 1rem;
|
||||
|
||||
&--condensed {
|
||||
min-height: 39px;
|
||||
}
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: min-height 150ms var(--ease-default);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +76,7 @@
|
||||
padding-right: 0.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
padding-left: 0.75rem;
|
||||
border-color: rgb(var(--color-gray-400));
|
||||
border: 1px solid rgb(var(--color-gray-400));
|
||||
border-radius: 0.375rem;
|
||||
|
||||
outline: 2px solid transparent;
|
||||
@@ -114,6 +137,34 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__button {
|
||||
color: rgb(var(--color-gray-600));
|
||||
|
||||
&:hover {
|
||||
color: rgb(var(--color-gray-700));
|
||||
}
|
||||
|
||||
.dark &:hover {
|
||||
color: rgb(var(--color-gray-500));
|
||||
}
|
||||
|
||||
&[aria-pressed='true'] {
|
||||
color: rgb(var(--color-primary-500));
|
||||
|
||||
&:hover {
|
||||
color: rgb(var(--color-primary-600));
|
||||
}
|
||||
|
||||
.dark & {
|
||||
color: rgb(var(--color-primary-500));
|
||||
|
||||
&:hover {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
@@ -132,7 +183,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__button {
|
||||
&__send-button {
|
||||
user-select: none;
|
||||
|
||||
display: inline-flex;
|
||||
|
||||
@@ -217,7 +217,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lexical {
|
||||
&__editor {
|
||||
position: relative;
|
||||
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
.divide-x-dot > *:not(:last-child)::after {
|
||||
@apply px-1;
|
||||
|
||||
content: '·';
|
||||
padding-right: 0.25rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
|
||||
.mention {
|
||||
@apply text-primary-600 dark:text-primary-400 hover:underline;
|
||||
color: rgb(var(--color-primary-600));
|
||||
|
||||
.dark {
|
||||
color: rgb(var(--color-primary-400));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-lg img.emojione {
|
||||
@apply h-9 w-9 #{!important};
|
||||
width: 2.25rem !important;
|
||||
height: 2.25rem !important;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ const config = defineConfig(() => ({
|
||||
},
|
||||
assetsInclude: ['**/*.oga'],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: Number(process.env.PORT ?? 7312),
|
||||
hmr: process.env.HMR_DISABLED === 'true' ? false : undefined,
|
||||
ws: process.env.WS_DISABLED === 'true' ? false : undefined,
|
||||
|
||||
@@ -910,6 +910,7 @@ const getFeatures = (instance: Instance) => {
|
||||
v.software === MASTODON,
|
||||
v.software === MITRA,
|
||||
v.software === PLEROMA,
|
||||
v.software === SNAC && gte(v.version, '2.78.0'),
|
||||
v.software === TOKI,
|
||||
]),
|
||||
|
||||
@@ -1398,6 +1399,7 @@ const getFeatures = (instance: Instance) => {
|
||||
v.software === MITRA && gte(v.version, '3.15.0'),
|
||||
v.software === NEODB,
|
||||
v.software === SHARKEY,
|
||||
v.software === SNAC && gte(v.version, '2.90.0'),
|
||||
v.software === TAKAHE && gte(v.version, '0.8.0'),
|
||||
instance.api_versions['polls.pleroma.pl-api'] >= 1,
|
||||
]),
|
||||
|
||||
Reference in New Issue
Block a user