From 75e02bb0888e876d7aae3ae681352c70b2332164 Mon Sep 17 00:00:00 2001 From: CatsJuice Date: Mon, 12 Aug 2024 03:56:56 +0000 Subject: [PATCH] feat(core): rewrite page-mode-switch with RadioGroup, bind hotkey with cmdk (#7758) close AF-1170 - bump `@toeverything/theme` - refactor page-mode-switch - use global `` - reuse for doc history - remove `styled` usage - bind hotkey via cmdk - Update `` color scheme with latest design system - Update right sidebar header tab style - Update tooltip with shortcut for app nav button --- packages/frontend/component/package.json | 2 +- .../frontend/component/src/ui/radio/radio.tsx | 38 ++-- .../component/src/ui/radio/styles.css.ts | 19 +- .../frontend/component/src/ui/radio/types.ts | 4 + packages/frontend/core/package.json | 2 +- .../page-history-modal/history-modal.tsx | 46 +---- .../block-suite-mode-switch/index.tsx | 185 ++++++++++-------- .../block-suite-mode-switch/style.css.ts | 10 + .../block-suite-mode-switch/style.ts | 62 ------ .../block-suite-mode-switch/switch-items.tsx | 22 +-- .../navigation/view/navigation-buttons.tsx | 26 +-- .../src/modules/quicksearch/views/cmdk.tsx | 6 + .../view/sidebar/sidebar-header-switcher.tsx | 3 +- .../core/src/pages/share/share-header.tsx | 7 +- .../detail-page/detail-page-header.tsx | 10 +- packages/frontend/i18n/src/resources/en.json | 2 + tests/kit/utils/editor.ts | 56 +++--- yarn.lock | 12 +- 18 files changed, 210 insertions(+), 302 deletions(-) create mode 100644 packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.css.ts delete mode 100644 packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index b9031b341..ba221bb67 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -47,7 +47,7 @@ "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-visually-hidden": "^1.1.0", - "@toeverything/theme": "^1.0.2", + "@toeverything/theme": "^1.0.4", "@vanilla-extract/dynamic": "^2.1.0", "bytes": "^3.1.2", "check-password-strength": "^2.0.10", diff --git a/packages/frontend/component/src/ui/radio/radio.tsx b/packages/frontend/component/src/ui/radio/radio.tsx index 20b99094f..f445b5381 100644 --- a/packages/frontend/component/src/ui/radio/radio.tsx +++ b/packages/frontend/component/src/ui/radio/radio.tsx @@ -1,7 +1,14 @@ import * as RadixRadioGroup from '@radix-ui/react-radio-group'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; -import { createRef, memo, useCallback, useMemo, useRef } from 'react'; +import { + createRef, + memo, + useCallback, + useEffect, + useMemo, + useRef, +} from 'react'; import { withUnit } from '../../utils/with-unit'; import * as styles from './styles.css'; @@ -71,6 +78,7 @@ export const RadioGroup = memo(function RadioGroup({ animationEasing = 'cubic-bezier(.18,.22,0,1)', activeItemClassName, activeItemStyle, + iconMode, onChange, }: RadioProps) { const animationTImerRef = useRef | null>(null); @@ -128,23 +136,25 @@ export const RadioGroup = memo(function RadioGroup({ [animationDuration, animationEasing, finalItems] ); - const onValueChange = useCallback( - (newValue: string) => { - const oldValue = value; - if (oldValue !== newValue) { - onChange(newValue); - animate(oldValue, newValue); - } - }, - [animate, onChange, value] - ); + // animate on value change + // useEffect: in case that value is changed from outside + const prevValue = useRef(value); + useEffect(() => { + const currentValue = value; + const previousValue = prevValue.current; + if (currentValue !== previousValue) { + animate(previousValue, currentValue); + prevValue.current = currentValue; + } + }, [animate, value]); return ( {finalItems.map(({ customRender, ...item }, index) => { const testId = item.testId ? { 'data-testid': item.testId } : {}; @@ -173,9 +183,7 @@ export const RadioGroup = memo(function RadioGroup({ ref={item.indicatorRef} /> - {customRender - ? customRender(item, index) - : (item.label ?? item.value)} + {customRender?.(item, index) ?? item.label ?? item.value} ); diff --git a/packages/frontend/component/src/ui/radio/styles.css.ts b/packages/frontend/component/src/ui/radio/styles.css.ts index 5812aabd8..cfdebbffa 100644 --- a/packages/frontend/component/src/ui/radio/styles.css.ts +++ b/packages/frontend/component/src/ui/radio/styles.css.ts @@ -1,4 +1,5 @@ import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; import { createVar, globalStyle, style } from '@vanilla-extract/css'; export const outerPadding = createVar('radio-outer-padding'); @@ -16,16 +17,22 @@ export const radioButton = style({ display: 'flex', justifyContent: 'center', alignItems: 'center', - color: cssVar('textSecondaryColor'), + color: cssVarV2('switch/fontColor/tertiary'), whiteSpace: 'nowrap', userSelect: 'none', fontWeight: 600, selectors: { '&[data-state="checked"]': { - color: cssVar('textPrimaryColor'), + color: cssVarV2('switch/fontColor/primary'), }, '&[data-state="unchecked"]:hover': { - background: cssVar('hoverColor'), + background: cssVarV2('switch/buttonBackground/hover'), + }, + '[data-icon-mode=true] &': { + color: cssVarV2('switch/iconColor/default'), + }, + '[data-icon-mode=true] &[data-state="checked"]': { + color: cssVarV2('switch/iconColor/active'), }, }, }); @@ -37,7 +44,7 @@ globalStyle(`${radioButtonContent} > svg`, { display: 'block' }); export const radioButtonGroup = style({ display: 'inline-flex', alignItems: 'center', - background: cssVar('hoverColorFilled'), + background: cssVarV2('switch/switchBackground/background'), borderRadius: outerRadius, padding: outerPadding, @@ -53,8 +60,8 @@ export const indicator = style({ height: '100%', left: 0, top: 0, - background: cssVar('white'), - filter: 'drop-shadow(0px 0px 4px rgba(0, 0, 0, 0.1))', + background: cssVarV2('switch/buttonBackground/active'), + boxShadow: cssVar('buttonShadow'), opacity: 0, transformOrigin: 'left', selectors: { diff --git a/packages/frontend/component/src/ui/radio/types.ts b/packages/frontend/component/src/ui/radio/types.ts index 8fe2c228e..4722d4ba7 100644 --- a/packages/frontend/component/src/ui/radio/types.ts +++ b/packages/frontend/component/src/ui/radio/types.ts @@ -47,6 +47,10 @@ export interface RadioProps extends RadioGroupItemProps { activeItemClassName?: string; /** Customize active item's style */ activeItemStyle?: CSSProperties; + /** + * This prop is used to use a different color scheme + */ + iconMode?: boolean; } export interface RadioItem { diff --git a/packages/frontend/core/package.json b/packages/frontend/core/package.json index 2ebef6828..1596edb73 100644 --- a/packages/frontend/core/package.json +++ b/packages/frontend/core/package.json @@ -48,7 +48,7 @@ "@sentry/integrations": "^7.109.0", "@sentry/react": "^8.0.0", "@sgtpooki/file-type": "^1.0.1", - "@toeverything/theme": "^1.0.2", + "@toeverything/theme": "^1.0.4", "@vanilla-extract/dynamic": "^2.1.0", "animejs": "^3.2.2", "async-call-rpc": "^6.4.2", diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx index 64bb74535..c18badec9 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -34,11 +34,7 @@ import { encodeStateAsUpdate } from 'yjs'; import { pageHistoryModalAtom } from '../../../atoms/page-history'; import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor'; -import { StyledEditorModeSwitch } from '../../blocksuite/block-suite-mode-switch/style'; -import { - EdgelessSwitchItem, - PageSwitchItem, -} from '../../blocksuite/block-suite-mode-switch/switch-items'; +import { PureEditorModeSwitch } from '../../blocksuite/block-suite-mode-switch'; import { AffineErrorBoundary } from '../affine-error-boundary'; import { historyListGroupByDay, @@ -108,15 +104,11 @@ const HistoryEditorPreview = ({ mode, title, }: HistoryEditorPreviewProps) => { - const { onSwitchToPageMode, onSwitchToEdgelessMode } = useMemo( - () => ({ - onSwitchToPageMode: () => { - onModeChange('page'); - }, - onSwitchToEdgelessMode: () => { - onModeChange('edgeless'); - }, - }), + const onModeChangeWithTrack = useCallback( + (mode: DocMode) => { + track.$.docHistory.$.switchPageMode({ mode }); + onModeChange(mode); + }, [onModeChange] ); @@ -124,22 +116,7 @@ const HistoryEditorPreview = ({ return (
- - - - +
{title}
{ts @@ -170,14 +147,7 @@ const HistoryEditorPreview = ({ )}
); - }, [ - mode, - onSwitchToEdgelessMode, - onSwitchToPageMode, - snapshotPage, - title, - ts, - ]); + }, [mode, onModeChangeWithTrack, snapshotPage, title, ts]); return (
diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx index f6b88222a..2f93df8c1 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx @@ -1,87 +1,69 @@ -import { Tooltip } from '@affine/component/ui/tooltip'; -import { useBlockSuiteDocMeta } from '@affine/core/hooks/use-block-suite-page-meta'; +import { RadioGroup, type RadioItem, toast, Tooltip } from '@affine/component'; +import { registerAffineCommand } from '@affine/core/commands'; import { track } from '@affine/core/mixpanel'; import { useI18n } from '@affine/i18n'; +import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc'; import { type DocMode, - DocService, + DocsService, useLiveData, useService, } from '@toeverything/infra'; -import type { CSSProperties } from 'react'; -import { useCallback, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; -import type { DocCollection } from '../../../shared'; -import { toast } from '../../../utils'; -import { StyledEditorModeSwitch } from './style'; +import { switchItem } from './style.css'; import { EdgelessSwitchItem, PageSwitchItem } from './switch-items'; -export type EditorModeSwitchProps = { - // todo(himself65): combine these two properties - docCollection: DocCollection; +export interface EditorModeSwitchProps { pageId: string; - style?: CSSProperties; isPublic?: boolean; publicMode?: DocMode; +} + +const EdgelessRadioItem: RadioItem = { + value: 'edgeless', + label: , + testId: 'switch-edgeless-mode-button', + className: switchItem, +}; +const PageRadioItem: RadioItem = { + value: 'page', + label: , + testId: 'switch-page-mode-button', + className: switchItem, }; export const EditorModeSwitch = ({ - style, - docCollection, pageId, isPublic, publicMode, }: EditorModeSwitchProps) => { const t = useI18n(); - const pageMeta = useBlockSuiteDocMeta(docCollection).find( - meta => meta.id === pageId - ); - const trash = pageMeta?.trash ?? false; - const doc = useService(DocService).doc; + const docsService = useService(DocsService); + const doc = useLiveData(docsService.list.doc$(pageId)); + const trash = useLiveData(doc?.trash$); + const currentMode = useLiveData(doc?.mode$); - const currentMode = useLiveData(doc.mode$); - - useEffect(() => { - if (trash || isPublic) { - return; - } - const keydown = (e: KeyboardEvent) => { - if (e.code === 'KeyS' && e.altKey) { - e.preventDefault(); - doc.toggleMode(); - toast( - currentMode === 'page' - ? t['com.affine.toastMessage.edgelessMode']() - : t['com.affine.toastMessage.pageMode']() - ); - } - }; - document.addEventListener('keydown', keydown, { capture: true }); - return () => - document.removeEventListener('keydown', keydown, { capture: true }); - }, [currentMode, isPublic, doc, pageId, t, trash]); - - const onSwitchToPageMode = useCallback(() => { - track.$.header.actions.switchPageMode({ - mode: 'page', - }); - if (currentMode === 'page' || isPublic) { - return; - } - doc.setMode('page'); + const togglePage = useCallback(() => { + if (currentMode === 'page' || isPublic || trash) return; + doc?.setMode('page'); toast(t['com.affine.toastMessage.pageMode']()); - }, [currentMode, isPublic, doc, t]); + track.$.header.actions.switchPageMode({ mode: 'page' }); + }, [currentMode, doc, isPublic, t, trash]); - const onSwitchToEdgelessMode = useCallback(() => { - track.$.header.actions.switchPageMode({ - mode: 'edgeless', - }); - if (currentMode === 'edgeless' || isPublic) { - return; - } - doc.setMode('edgeless'); + const toggleEdgeless = useCallback(() => { + if (currentMode === 'edgeless' || isPublic || trash) return; + doc?.setMode('edgeless'); toast(t['com.affine.toastMessage.edgelessMode']()); - }, [currentMode, isPublic, doc, t]); + track.$.header.actions.switchPageMode({ mode: 'edgeless' }); + }, [currentMode, doc, isPublic, t, trash]); + + const onModeChange = useCallback( + (mode: DocMode) => { + mode === 'page' ? togglePage() : toggleEdgeless(); + }, + [toggleEdgeless, togglePage] + ); const shouldHide = useCallback( (mode: DocMode) => @@ -89,40 +71,73 @@ export const EditorModeSwitch = ({ [currentMode, isPublic, publicMode, trash] ); - const shouldActive = useCallback( - (mode: DocMode) => (isPublic ? false : currentMode === mode), - [currentMode, isPublic] - ); + useEffect(() => { + if (trash || isPublic || currentMode === undefined) return; + return registerAffineCommand({ + id: 'affine:doc-mode-switch', + category: 'editor:page', + label: + currentMode === 'page' + ? t['com.affine.cmdk.switch-to-edgeless']() + : t['com.affine.cmdk.switch-to-page'](), + icon: currentMode === 'page' ? : , + keyBinding: { + binding: 'Alt+KeyS', + capture: true, + }, + run: () => onModeChange(currentMode === 'edgeless' ? 'page' : 'edgeless'), + }); + }, [currentMode, isPublic, onModeChange, t, trash]); return ( - - + - - +
); }; + +export interface PureEditorModeSwitchProps { + mode?: DocMode; + setMode: (mode: DocMode) => void; + hidePage?: boolean; + hideEdgeless?: boolean; +} + +export const PureEditorModeSwitch = ({ + mode, + setMode, + hidePage, + hideEdgeless, +}: PureEditorModeSwitchProps) => { + const items = useMemo( + () => [ + ...(hidePage ? [] : [PageRadioItem]), + ...(hideEdgeless ? [] : [EdgelessRadioItem]), + ], + [hideEdgeless, hidePage] + ); + return ( + + ); +}; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.css.ts b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.css.ts new file mode 100644 index 000000000..342a4bf5f --- /dev/null +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.css.ts @@ -0,0 +1,10 @@ +import { globalStyle, style } from '@vanilla-extract/css'; + +export const switchItem = style({ + width: 24, + height: 24, +}); +// a workaround to override lottie svg stroke with default radio button color schemes +globalStyle(`${switchItem} svg path`, { + stroke: 'currentColor', +}); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts deleted file mode 100644 index cb1645684..000000000 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/style.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { displayFlex, styled } from '@affine/component'; - -// TODO(@CatsJuice): refactor this component -export const StyledEditorModeSwitch = styled('div')<{ - switchLeft: boolean; - showAlone?: boolean; -}>(({ switchLeft, showAlone }) => { - return { - maxWidth: showAlone ? '40px' : '70px', - gap: '8px', - height: '32px', - background: showAlone - ? 'transparent' - : 'var(--affine-background-secondary-color)', - borderRadius: '8px', - ...displayFlex('space-between', 'center'), - padding: '4px 4px', - position: 'relative', - - '::after': { - content: '""', - display: showAlone ? 'none' : 'block', - width: '24px', - height: '24px', - background: 'var(--affine-background-primary-color)', - boxShadow: 'var(--affine-shadow-1)', - borderRadius: '4px', - zIndex: 1, - position: 'absolute', - transform: `translateX(${switchLeft ? '0' : '32px'})`, - transition: 'all .15s', - }, - }; -}); - -export const StyledSwitchItem = styled('button')<{ - active?: boolean; - hide?: boolean; - trash?: boolean; -}>(({ active = false, hide = false, trash = false }) => { - return { - width: '24px', - height: '24px', - borderRadius: '8px', - WebkitAppRegion: 'no-drag', - boxShadow: active ? 'var(--affine-shadow-1)' : 'none', - color: active - ? trash - ? 'var(--affine-error-color)' - : 'var(--affine-primary-color)' - : 'var(--affine-icon-color)', - display: hide ? 'none' : 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - position: 'relative', - zIndex: 2, - fontSize: '20px', - path: { - stroke: 'currentColor', - }, - }; -}); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx index c293225cc..1c0eff6a8 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/switch-items.tsx @@ -5,35 +5,23 @@ import { cloneElement, useState } from 'react'; import edgelessHover from './animation-data/edgeless-hover.json'; import pageHover from './animation-data/page-hover.json'; -import { StyledSwitchItem } from './style'; type HoverAnimateControllerProps = { active?: boolean; hide?: boolean; trash?: boolean; children: React.ReactElement; -} & HTMLAttributes; +} & HTMLAttributes; const HoverAnimateController = ({ - active, - hide, - trash, children, ...props }: HoverAnimateControllerProps) => { const [startAnimate, setStartAnimate] = useState(false); return ( - { - setStartAnimate(true); - }} - onMouseLeave={() => { - setStartAnimate(false); - }} +
setStartAnimate(true)} + onMouseLeave={() => setStartAnimate(false)} {...props} > {cloneElement(children, { @@ -42,7 +30,7 @@ const HoverAnimateController = ({ width: 20, height: 20, })} - +
); }; diff --git a/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx b/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx index d3da7221e..8499be33e 100644 --- a/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx +++ b/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx @@ -2,9 +2,8 @@ import { IconButton } from '@affine/component'; import { useI18n } from '@affine/i18n'; import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; -import { useCallback, useEffect, useMemo } from 'react'; +import { useCallback, useEffect } from 'react'; -import { useGeneralShortcuts } from '../../../hooks/affine/use-shortcuts'; import { NavigatorService } from '../services/navigator'; import * as styles from './navigation-buttons.css'; @@ -13,22 +12,6 @@ const tooltipSideBottom = { side: 'bottom' as const }; export const NavigationButtons = () => { const t = useI18n(); - const shortcuts = useGeneralShortcuts().shortcuts; - - const shortcutsObject = useMemo(() => { - const goBack = t['com.affine.keyboardShortcuts.goBack'](); - const goBackShortcut = shortcuts?.[goBack]; - - const goForward = t['com.affine.keyboardShortcuts.goForward'](); - const goForwardShortcut = shortcuts?.[goForward]; - return { - goBack, - goBackShortcut, - goForward, - goForwardShortcut, - }; - }, [shortcuts, t]); - const navigator = useService(NavigatorService).navigator; const backable = useLiveData(navigator.backable$); @@ -65,11 +48,11 @@ export const NavigationButtons = () => { return null; } - // TODO(@CatsJuice): tooltip with shortcut return (
{ { if (fragment === '$mod') { return isMacOS ? '⌘' : 'Ctrl'; } + if (fragment === 'Alt') { + return isMacOS ? '⌥' : 'Alt'; + } + if (fragment.startsWith('Key')) { + return fragment.slice(3); + } if (fragment === 'ArrowUp') { return '↑'; } diff --git a/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header-switcher.tsx b/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header-switcher.tsx index 34040ff97..179130e92 100644 --- a/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header-switcher.tsx +++ b/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header-switcher.tsx @@ -1,6 +1,5 @@ import { RadioGroup } from '@affine/component'; import { useLiveData, useService } from '@toeverything/infra'; -import { cssVar } from '@toeverything/theme'; import { useCallback } from 'react'; import { ViewService } from '../../services/view'; @@ -35,6 +34,7 @@ export const SidebarHeaderSwitcher = () => { return tabItems.length ? ( { items={tabItems} value={activeTab?.id} onChange={handleActiveTabChange} - activeItemStyle={{ color: cssVar('primaryColor') }} /> ) : null; }; diff --git a/packages/frontend/core/src/pages/share/share-header.tsx b/packages/frontend/core/src/pages/share/share-header.tsx index 8a09bb5d5..f4eccc41b 100644 --- a/packages/frontend/core/src/pages/share/share-header.tsx +++ b/packages/frontend/core/src/pages/share/share-header.tsx @@ -17,12 +17,7 @@ export function ShareHeader({ }) { return (
- + - +
- + { - if ( - await page - .locator('[data-testid="switch-page-mode-button"][data-active="true"]') - .isVisible() - ) { + if (await locateModeSwitchButton(page, 'page', true).isVisible()) { return 'page'; } - if ( - await page - .locator( - '[data-testid="switch-edgeless-mode-button"][data-active="true"]' - ) - .isVisible() - ) { + if (await locateModeSwitchButton(page, 'edgeless', true).isVisible()) { return 'edgeless'; } throw new Error('Unknown mode'); diff --git a/yarn.lock b/yarn.lock index 43a8e2212..07f5734fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -324,7 +324,7 @@ __metadata: "@storybook/test-runner": "npm:^0.19.0" "@storybook/testing-library": "npm:^0.2.2" "@testing-library/react": "npm:^16.0.0" - "@toeverything/theme": "npm:^1.0.2" + "@toeverything/theme": "npm:^1.0.4" "@types/bytes": "npm:^3.1.4" "@types/react": "npm:^18.2.75" "@types/react-dnd": "npm:^3.0.2" @@ -419,7 +419,7 @@ __metadata: "@sgtpooki/file-type": "npm:^1.0.1" "@swc/core": "npm:^1.4.13" "@testing-library/react": "npm:^16.0.0" - "@toeverything/theme": "npm:^1.0.2" + "@toeverything/theme": "npm:^1.0.4" "@types/animejs": "npm:^3.1.12" "@types/bytes": "npm:^3.1.4" "@types/image-blob-reduce": "npm:^4.1.4" @@ -14946,10 +14946,10 @@ __metadata: languageName: unknown linkType: soft -"@toeverything/theme@npm:^1.0.2": - version: 1.0.3 - resolution: "@toeverything/theme@npm:1.0.3" - checksum: 10/2e1df60e417717aee7525d1ec0432462f95cfaded3da6ad5d7c4b273cd97b77bd3948669f56be06b592e2c6a51cd892902b7b18ba2b71360ccc0a9daba18b315 +"@toeverything/theme@npm:^1.0.2, @toeverything/theme@npm:^1.0.4": + version: 1.0.4 + resolution: "@toeverything/theme@npm:1.0.4" + checksum: 10/88d7cb0c2f77e1632c9a25bb6605c8df22b8b41cd7f76435c0d64beb9f4758a56ed48fb35965588c004a97634b8097ea4ebc0ea44747ce46db0e88d9a0527d26 languageName: node linkType: hard