nicolium: style migrations

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-02-10 17:56:02 +01:00
parent 1b5afbbd23
commit 85fe8113c4
7 changed files with 209 additions and 124 deletions

View File

@ -1,6 +1,6 @@
import React from 'react';
import Card, { CardBody } from '@/components/ui/card';
import Card from '@/components/ui/card';
interface IBigCard {
title: React.ReactNode;
@ -10,16 +10,14 @@ interface IBigCard {
const BigCard: React.FC<IBigCard> = ({ title, subtitle, children }) => (
<Card variant='rounded' size='xl'>
<CardBody>
<div className='⁂-big-card__header'>
<h1>{title}</h1>
{subtitle && <p>{subtitle}</p>}
</div>
<div className='⁂-big-card__header'>
<h1>{title}</h1>
{subtitle && <p>{subtitle}</p>}
</div>
<div className='⁂-big-card__body'>
{children}
</div>
</CardBody>
<div className='⁂-big-card__body'>
{children}
</div>
</Card>
);

View File

@ -480,14 +480,14 @@ const ParsedMfm: React.FC<IParsedMfm> = React.memo(({ text, emojis, mentions, sp
const filename = emoji.static_url;
if (filename?.length > 0) {
return <img draggable={false} className='emojione !h-[2em] !w-[2em] transition-transform hover:scale-125' alt={token.props.name} title={token.props.name} src={filename} />;
return <img draggable={false} className='emojione ⁂-emoji !h-[2em] !w-[2em]' alt={token.props.name} title={token.props.name} src={filename} />;
}
return <bdi>{token.props.name}</bdi>;
}
case 'unicodeEmoji': {
return <Emoji emoji={token.props.emoji} className='emojione !h-[2em] !w-[2em]' />;
return <Emoji emoji={token.props.emoji} className='emojione ⁂-emoji !h-[2em] !w-[2em]' />;
}
// TODO

View File

@ -2,9 +2,6 @@ import React, { useEffect, useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { NODE_ENV } from '@/build-config';
import HStack from '@/components/ui/hstack';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Textarea from '@/components/ui/textarea';
import { useFrontendConfig } from '@/hooks/use-frontend-config';
import { useLogo } from '@/hooks/use-logo';
@ -81,18 +78,11 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
if (isNetworkError(error)) {
return (
<Column label={intl.formatMessage(messages.networkErrorTitle)}>
<Stack space={4} alignItems='center' justifyContent='center' className='min-h-[160px] rounded-lg p-10'>
{/* <IconButton
iconClassName='h-10 w-10'
title={intl.formatMessage(messages.networkErrorRetry)}
src={require('@phosphor-icons/core/regular/arrows-clockwise.svg')}
onClick={handleRetry}
/> */}
<Text align='center' theme='muted'>
<div className='⁂-network-error'>
<p>
<FormattedMessage id='bundle_column_error.body' defaultMessage='Something went wrong while loading this page.' />
</Text>
</Stack>
</p>
</div>
</Column>
);
}
@ -101,25 +91,23 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
<div className='⁂-site-error'>
<main>
{logoSrc && (
<div className='flex shrink-0 justify-center'>
<a href='/' className='inline-flex'>
<SiteLogo className='h-12 w-auto cursor-pointer' />
</a>
</div>
<a href='/' className='⁂-site-error__logo'>
<SiteLogo />
</a>
)}
<div className='py-8'>
<div className='mx-auto max-w-xl space-y-2 text-center'>
<h1 className='text-3xl font-extrabold tracking-tight text-gray-900 dark:text-gray-500 sm:text-4xl'>
<div className='⁂-site-error__body'>
<div className='⁂-site-error__message'>
<h1>
<FormattedMessage id='alert.unexpected.message' defaultMessage='Something went wrong.' />
</h1>
<p className='text-lg text-gray-700 dark:text-gray-600'>
<p className='⁂-site-error__message__body'>
<FormattedMessage
id='alert.unexpected.body'
defaultMessage="We're sorry for the interruption. If the problem persists, please report it in our {issueTracker}. You may also try to {clearCookies} (this will log you out)."
values={{
issueTracker: (
<a href={sourceCode.url + '/issues'} target='_blank' rel='noopener noreferrer' className='text-primary-600 hover:underline dark:text-primary-400'>
<a href={sourceCode.url + '/issues'} target='_blank' rel='noopener noreferrer'>
<FormattedMessage
id='alert.unexpected.issue_tracker'
defaultMessage='issue tracker'
@ -127,7 +115,7 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
</a>
),
clearCookies: (
<a href='/' onClick={clearCookies} className='text-primary-600 hover:underline dark:text-primary-400'>
<a href='/' onClick={clearCookies}>
<FormattedMessage
id='alert.unexpected.clear_cookies'
defaultMessage='clear cookies and browser data'
@ -138,64 +126,60 @@ const SiteError: ErrorRouteComponent = ({ error, info }) => {
/>
</p>
<Text theme='muted'>
<Text weight='medium' tag='span' theme='muted'>{sourceCode.displayName}:</Text>
<p className='⁂-site-error__message__version'>
<span>{sourceCode.displayName}:</span>
{' '}{sourceCode.version}
</Text>
</p>
</div>
<div className='mx-auto max-w-lg space-y-4 py-16'>
{(isProduction) ? (
(sentryEnabled && sentryEventId) && (
<SentryFeedbackForm eventId={sentryEventId} />
)
) : (
<>
{errorText && (
<Textarea
ref={textarea}
value={errorText}
onClick={handleCopy}
isCodeEditor
rows={12}
readOnly
/>
)}
<div className='⁂-site-error__form'>
{isProduction && sentryEnabled && sentryEventId && (
<SentryFeedbackForm eventId={sentryEventId} />
)}
{errorText && (
<Textarea
ref={textarea}
value={errorText}
onClick={handleCopy}
isCodeEditor
rows={12}
readOnly
/>
)}
{browser && (
<Stack>
<Text weight='semibold'><FormattedMessage id='alert.unexpected.browser' defaultMessage='Browser' /></Text>
<Text theme='muted'>{browser.getBrowserName()} {browser.getBrowserVersion()}</Text>
</Stack>
)}
</>
{browser && (
<p className='⁂-site-error__browser'>
<span><FormattedMessage id='alert.unexpected.browser' defaultMessage='Browser' />{': '}</span>{browser.getBrowserName()} {browser.getBrowserVersion()}
</p>
)}
</div>
</div>
</main>
<footer>
<HStack justifyContent='center' space={4} element='nav'>
{links.status && (
<SiteErrorBoundaryLink href={links.status}>
<FormattedMessage id='alert.unexpected.links.status' defaultMessage='Status' />
</SiteErrorBoundaryLink>
)}
{[links.status, links.help, links.support].some(Boolean) && (
<footer>
<nav>
{links.status && (
<SiteErrorBoundaryLink href={links.status}>
<FormattedMessage id='alert.unexpected.links.status' defaultMessage='Status' />
</SiteErrorBoundaryLink>
)}
{links.help && (
<SiteErrorBoundaryLink href={links.help}>
<FormattedMessage id='alert.unexpected.links.help' defaultMessage='Help Center' />
</SiteErrorBoundaryLink>
)}
{links.help && (
<SiteErrorBoundaryLink href={links.help}>
<FormattedMessage id='alert.unexpected.links.help' defaultMessage='Help Center' />
</SiteErrorBoundaryLink>
)}
{links.support && (
<SiteErrorBoundaryLink href={links.support}>
<FormattedMessage id='alert.unexpected.links.support' defaultMessage='Support' />
</SiteErrorBoundaryLink>
)}
</HStack>
</footer>
{links.support && (
<SiteErrorBoundaryLink href={links.support}>
<FormattedMessage id='alert.unexpected.links.support' defaultMessage='Support' />
</SiteErrorBoundaryLink>
)}
</nav>
</footer>
)}
</div>
);
};
@ -207,10 +191,8 @@ interface ISiteErrorBoundaryLink {
const SiteErrorBoundaryLink = ({ href, children }: ISiteErrorBoundaryLink) => (
<>
<span className='inline-block border-l border-gray-300' aria-hidden='true' />
<a href={href} className='text-sm font-medium text-gray-700 hover:underline dark:text-gray-600'>
{children}
</a>
<span aria-hidden='true' />
<a href={href}>{children}</a>
</>
);

View File

@ -5,9 +5,6 @@ import { FormattedMessage } from 'react-intl';
import { useLocale, useLocaleDirection } from '@/hooks/use-locale';
import { getTextDirection } from '@/utils/rtl';
import Stack from './stack';
import Text from './text';
interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'className' | 'id' | 'lang' | 'maxLength' | 'onChange' | 'onClick' | 'onKeyDown' | 'onKeyUp' | 'onPaste' | 'required' | 'disabled' | 'rows' | 'readOnly'> {
/** Put the cursor into the input on mount. */
autoFocus?: boolean;
@ -31,12 +28,10 @@ interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElemen
autoComplete?: string;
/** Whether to display the textarea in red. */
hasError?: boolean;
/** Whether or not you can resize the teztarea */
/** Whether or not you can resize the textarea */
isResizeable?: boolean;
/** Textarea theme. */
theme?: 'default' | 'transparent';
/** Whether to display a character counter below the textarea. */
withCounter?: boolean;
}
/** Textarea with custom styles. */
@ -84,37 +79,41 @@ const Textarea = React.forwardRef(({
}
};
const textarea = (
<textarea
{...props}
value={value}
ref={ref}
rows={rows}
onChange={handleChange}
className={clsx('⁂-textarea', {
'⁂-textarea--transparent': theme === 'transparent',
'⁂-textarea--mono': isCodeEditor,
'⁂-textarea--has-error': hasError,
'⁂-textarea--resizable': isResizeable,
}, className)}
dir={value?.length ? getTextDirection(value, { fallback: direction }) : undefined}
/>
);
if (!maxLength) {
return textarea;
}
return (
<Stack space={1.5}>
<textarea
{...props}
value={value}
ref={ref}
rows={rows}
onChange={handleChange}
className={clsx('block w-full rounded-md text-gray-900 placeholder:text-gray-600 dark:text-gray-100 dark:placeholder:text-gray-600 sm:text-sm', {
'bg-white dark:bg-transparent shadow-sm border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
theme === 'default',
'bg-transparent border-0 focus:border-0 focus:ring-0': theme === 'transparent',
'font-mono': isCodeEditor,
'text-red-600 border-red-600': hasError,
'resize-none': !isResizeable,
}, className)}
dir={value?.length ? getTextDirection(value, { fallback: direction }) : undefined}
/>
<div className='⁂-textarea__container'>
{textarea}
{maxLength && (
<div className='text-right rtl:text-left'>
<Text size='xs' theme={maxLength - length < 0 ? 'danger' : 'muted'}>
<FormattedMessage
id='textarea.counter.label'
defaultMessage='{count} characters remaining'
values={{ count: maxLength - length }}
/>
</Text>
</div>
<p className={clsx('⁂-textarea__max-length', { '⁂-textarea__max-length--exceeded': maxLength - length < 0 })}>
<FormattedMessage
id='textarea.counter.label'
defaultMessage='{count} characters remaining'
values={{ count: maxLength - length }}
/>
</p>
)}
</Stack>
</div>
);
},
);

View File

@ -24,8 +24,7 @@ const MaybeEmoji: React.FC<IMaybeEmoji> = ({ text, emojis }) => {
const filename = emoji.static_url;
if (filename?.length > 0) {
return <Emoji className='emojione transition-transform hover:scale-125' emoji={text} src={filename} />;
// return <img draggable={false} className='emojione transition-transform hover:scale-125' alt={text} title={text} src={filename} />;
return <Emoji className='emojione ⁂-emoji' emoji={text} src={filename} />;
}
}
@ -72,7 +71,7 @@ const Emojify: React.FC<IEmojify> = React.memo(({ text, emojis = {} }) => {
const { unified, shortcode } = unicodeMapping[c];
nodes.push(
<img key={index} draggable={false} className='emojione transition-transform hover:scale-125' alt={c} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
<img key={index} draggable={false} className='emojione ⁂-emoji' alt={c} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
);
} else if (!systemEmojiFont && unqualified in unicodeMapping) {
clearStack();
@ -80,7 +79,7 @@ const Emojify: React.FC<IEmojify> = React.memo(({ text, emojis = {} }) => {
const { unified, shortcode } = unicodeMapping[unqualified];
nodes.push(
<img key={index} draggable={false} className='emojione transition-transform hover:scale-125' alt={unqualified} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
<img key={index} draggable={false} className='emojione ⁂-emoji' alt={unqualified} title={`:${shortcode}:`} src={joinPublicPath(`packs/emoji/${unified}.svg`)} />,
);
} else if (!disableUserProvidedMedia && c === ':') {
if (!open) {

View File

@ -523,3 +523,44 @@ div[data-viewport-type="window"]:has(.⁂-empty-message) {
@apply mx-auto sm:w-2/3 sm:pt-10;
}
}
.-emoji {
@apply transition-transform hover:scale-125;
}
.-textarea {
@apply block w-full rounded-md text-gray-900 placeholder:text-gray-600 dark:text-gray-100 dark:placeholder:text-gray-600 sm:text-sm resize-none;
&--transparent {
@apply bg-transparent border-0 p-0 focus:ring-0;
}
&:not(&--transparent) {
@apply bg-white border border-gray-300 p-2 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 disabled:opacity-50 black:bg-black dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-1 dark:ring-gray-800 dark:focus:border-primary-500 dark:focus:ring-primary-500;
}
&--mono {
@apply font-mono;
}
&--has-error {
@apply text-red-600 border-red-600;
}
&--resizable {
@apply resize;
}
&__container {
@apply flex flex-col gap-1.5;
}
&__max-length {
@include mixins.text($size: xs, $theme: muted);
@apply text-right rtl:text-left;
&--exceeded {
@apply text-danger-700 dark:text-danger-500;
}
}
}

View File

@ -452,9 +452,75 @@ div:has(.⁂-background-shapes), .dark {
@apply mx-auto flex w-full max-w-7xl grow flex-col justify-center px-4 sm:px-6 lg:px-8;
}
&__body {
@apply py-8;
}
&__message {
@apply mx-auto max-w-xl space-y-2 text-center;
h1 {
@apply text-3xl font-extrabold tracking-tight text-gray-900 dark:text-gray-500 sm:text-4xl;
}
&__body {
@apply text-lg text-gray-700 dark:text-gray-600;
a {
@apply text-primary-600 hover:underline dark:text-primary-400;
}
}
}
&__form {
@apply mx-auto max-w-lg space-y-4 py-16;
&:last-child {
@apply pb-0;
}
}
&__message__version, &__browser {
@include mixins.text($theme: muted);
span {
@apply font-semibold;
}
}
footer {
@apply mx-auto w-full max-w-7xl shrink-0 px-4 sm:px-6 lg:px-8;
}
nav {
@apply flex justify-center gap-4;
span {
@apply inline-block border-l border-gray-300;
}
a {
@apply text-sm font-medium text-gray-700 hover:underline dark:text-gray-600;
}
}
&__logo {
@apply flex shrink-0 justify-center;
img {
@apply h-12 w-auto;
}
}
}
.-network-error {
@apply flex gap-4 items-center justify-center min-h-[160px] rounded-lg p-10;
p {
@include mixins.text($align: center, $theme: muted);
}
}
.-layout__content .-site-error {