From 2a4def667c46f8ddb0aa50887bea7768ea08c614 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Sun, 12 Apr 2026 04:43:53 +0900 Subject: [PATCH] #95 --- frontend/package-lock.json | 46 ++++++-- frontend/package.json | 1 + frontend/src/App.tsx | 2 + frontend/src/components/TopNav.tsx | 125 ++++++++++++--------- frontend/src/components/WikiBody.tsx | 24 ++-- frontend/src/mdx-components.tsx | 7 +- frontend/src/pages/MorePage.tsx | 42 +++++++ frontend/src/pages/TOSPage.mdx | 20 ++-- frontend/src/pages/wiki/WikiDetailPage.tsx | 19 ++-- frontend/src/types.ts | 16 ++- frontend/tailwind.config.js | 4 +- 11 files changed, 196 insertions(+), 110 deletions(-) create mode 100644 frontend/src/pages/MorePage.tsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7f5d5f2..6123d0e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -42,6 +42,7 @@ }, "devDependencies": { "@eslint/js": "^9.25.0", + "@tailwindcss/typography": "^0.5.19", "@types/axios": "^0.14.4", "@types/markdown-it": "^14.1.2", "@types/mdx": "^2.0.13", @@ -2207,6 +2208,33 @@ "win32" ] }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@tanstack/query-core": { "version": "5.90.2", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.2.tgz", @@ -2863,9 +2891,9 @@ } }, "node_modules/axios": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", - "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", + "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -2904,9 +2932,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -7756,9 +7784,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index c240e46..1745d0f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,6 +44,7 @@ }, "devDependencies": { "@eslint/js": "^9.25.0", + "@tailwindcss/typography": "^0.5.19", "@types/axios": "^0.14.4", "@types/markdown-it": "^14.1.2", "@types/mdx": "^2.0.13", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 39de095..d4cd701 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -15,6 +15,7 @@ import MaterialDetailPage from '@/pages/materials/MaterialDetailPage' import MaterialListPage from '@/pages/materials/MaterialListPage' import MaterialNewPage from '@/pages/materials/MaterialNewPage' // import MaterialSearchPage from '@/pages/materials/MaterialSearchPage' +import MorePage from '@/pages/MorePage' import NicoTagListPage from '@/pages/tags/NicoTagListPage' import NotFound from '@/pages/NotFound' import TOSPage from '@/pages/TOSPage.mdx' @@ -72,6 +73,7 @@ const RouteTransitionWrapper = ({ user, setUser }: { }/> }/> }/> + }/> }/> diff --git a/frontend/src/components/TopNav.tsx b/frontend/src/components/TopNav.tsx index f13b3ec..3b79a05 100644 --- a/frontend/src/components/TopNav.tsx +++ b/frontend/src/components/TopNav.tsx @@ -14,11 +14,63 @@ import { fetchWikiPage } from '@/lib/wiki' import type { FC, MouseEvent } from 'react' -import type { Menu, User } from '@/types' +import type { Menu, Tag, User } from '@/types' type Props = { user: User | null } +export const menuOutline = ({ tag, wikiId, user }: { tag?: Tag | null + wikiId: number | null + user: User | null }): Menu => { + const postCount = tag?.postCount ?? 0 + + const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) && wikiId) + const wikiTitle = location.pathname.split ('/')[2] ?? '' + + return [ + { name: '広場', to: '/posts', subMenu: [ + { name: '一覧', to: '/posts' }, + { name: '検索', to: '/posts/search' }, + { name: '追加', to: '/posts/new' }, + { name: '履歴', to: '/posts/changes' }, + { name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] }, + { name: 'タグ', to: '/tags', subMenu: [ + { name: 'マスタ', to: '/tags' }, + { name: '別名タグ', to: '/tags/aliases', visible: false }, + { name: '上位タグ', to: '/tags/implications', visible: false }, + { name: 'ニコニコ連携', to: '/tags/nico' }, + { name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] }, + { name: '素材', to: '/materials', visible: false, subMenu: [ + { name: '一覧', to: '/materials' }, + { name: '検索', to: '/materials/search', visible: false }, + { name: '追加', to: '/materials/new' }, + { name: '履歴', to: '/materials/changes', visible: false }, + { name: 'ヘルプ', to: '/wiki/ヘルプ:素材集' }] }, + { name: '上映会', to: '/theatres/1', base: '/theatres', subMenu: [ + { name: <>第 1 会場, to: '/theatres/1' }, + { name: 'CyTube', to: '//cytube.mm428.net/r/deernijika' }, + { name: <>ニジカ放送局第 1 チャンネル, + to: '//www.youtube.com/watch?v=DCU3hL4Uu6A' }, + { name: 'ヘルプ', to: '/wiki/ヘルプ:上映会' }] }, + { name: 'Wiki', to: '/wiki/ヘルプ:ホーム', base: '/wiki', subMenu: [ + { name: '検索', to: '/wiki' }, + { name: '新規', to: '/wiki/new' }, + { name: '全体履歴', to: '/wiki/changes' }, + { name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' }, + { component: , visible: wikiPageFlg }, + { name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`, + visible: wikiPageFlg }, + { name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg }, + { name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] }, + { name: 'ユーザ', to: '/users/settings', visible: false, subMenu: [ + { name: '一覧', to: '/users', visible: false }, + { name: 'お前', to: `/users/${ user?.id }`, visible: false }, + { name: '設定', to: '/users/settings', visible: Boolean (user) }] }, + { name: '法規', visible: false, subMenu: [ + { name: '利用規約', to: '/tos' }] }] +} + + export default (({ user }: Props) => { const location = useLocation () @@ -66,51 +118,10 @@ export default (({ user }: Props) => { queryKey: tagsKeys.show (effectiveTitle), queryFn: () => fetchTagByName (effectiveTitle) }) - const postCount = tag?.postCount ?? 0 - - const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (location.pathname) && wikiId) - const wikiTitle = location.pathname.split ('/')[2] ?? '' - const menu: Menu = [ - { name: '広場', to: '/posts', subMenu: [ - { name: '一覧', to: '/posts' }, - { name: '検索', to: '/posts/search' }, - { name: '追加', to: '/posts/new' }, - { name: '履歴', to: '/posts/changes' }, - { name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] }, - { name: 'タグ', to: '/tags', subMenu: [ - { name: 'マスタ', to: '/tags' }, - { name: '別名タグ', to: '/tags/aliases', visible: false }, - { name: '上位タグ', to: '/tags/implications', visible: false }, - { name: 'ニコニコ連携', to: '/tags/nico' }, - { name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] }, - // { name: '素材', to: '/materials', subMenu: [ - // { name: '一覧', to: '/materials' }, - // { name: '検索', to: '/materials/search', visible: false }, - // { name: '追加', to: '/materials/new' }, - // { name: '履歴', to: '/materials/changes', visible: false }, - // { name: 'ヘルプ', to: '/wiki/ヘルプ:素材集' }] }, - { name: '上映会', to: '/theatres/1', base: '/theatres', subMenu: [ - { name: <>第 1 会場, to: '/theatres/1' }, - { name: 'CyTube', to: '//cytube.mm428.net/r/deernijika' }, - { name: <>ニジカ放送局第 1 チャンネル, - to: '//www.youtube.com/watch?v=DCU3hL4Uu6A' }, - { name: 'ヘルプ', to: '/wiki/ヘルプ:上映会' }] }, - { name: 'Wiki', to: '/wiki/ヘルプ:ホーム', base: '/wiki', subMenu: [ - { name: '検索', to: '/wiki' }, - { name: '新規', to: '/wiki/new' }, - { name: '全体履歴', to: '/wiki/changes' }, - { name: 'ヘルプ', to: '/wiki/ヘルプ:Wiki' }, - { component: , visible: wikiPageFlg }, - { name: `広場 (${ postCount || 0 })`, to: `/posts?tags=${ wikiTitle }`, - visible: wikiPageFlg }, - { name: '履歴', to: `/wiki/changes?id=${ wikiId }`, visible: wikiPageFlg }, - { name: '編輯', to: `/wiki/${ wikiId || wikiTitle }/edit`, visible: wikiPageFlg }] }, - { name: 'ユーザ', to: '/users/settings', subMenu: [ - { name: '一覧', to: '/users', visible: false }, - { name: 'お前', to: `/users/${ user?.id }`, visible: false }, - { name: '設定', to: '/users/settings', visible: Boolean (user) }] }] - - const activeIdx = menu.findIndex (item => location.pathname.startsWith (item.base || item.to)) + const menu = menuOutline ({ tag, wikiId, user }) + const activeIdx = (menu + .filter (item => item.visible ?? true) + .findIndex (item => location.pathname.startsWith (item.base || item.to!))) const prevActiveIdxRef = useRef (activeIdx) @@ -140,8 +151,8 @@ export default (({ user }: Props) => { useEffect (() => { setMenuOpen (false) - setOpenItemIdx (menu.findIndex (item => ( - location.pathname.startsWith (item.base || item.to)))) + setOpenItemIdx (menu.filter (item => item.visible ?? true).findIndex (item => ( + location.pathname.startsWith (item.base || item.to!)))) }, [location]) return ( @@ -168,10 +179,10 @@ export default (({ user }: Props) => { transform: `translateX(${ hl.left }px)`, opacity: hl.visible ? 1 : 0 }}/> - {menu.map ((item, i) => ( + {menu.filter (item => item.visible ?? true).map ((item, i) => ( { itemsRef.current[i] = el }} @@ -180,7 +191,7 @@ export default (({ user }: Props) => { {item.name} ))} { itemsRef.current[menu.length] = el }} @@ -262,10 +273,10 @@ export default (({ user }: Props) => { exit="closed" transition={{ duration: .2, ease: 'easeOut' }}> - {menu.map ((item, i) => ( + {menu.filter (item => item.visible ?? true).map ((item, i) => ( { )} ))} + { + itemsRef.current[menu.length] = el + }} + className="w-full min-h-[40px] flex items-center pl-8"> + その他 » + )} diff --git a/frontend/src/components/WikiBody.tsx b/frontend/src/components/WikiBody.tsx index 50316b2..49ac6e5 100644 --- a/frontend/src/components/WikiBody.tsx +++ b/frontend/src/components/WikiBody.tsx @@ -4,8 +4,6 @@ import ReactMarkdown from 'react-markdown' import remarkGFM from 'remark-gfm' import PrefetchLink from '@/components/PrefetchLink' -import SectionTitle from '@/components/common/SectionTitle' -import SubsectionTitle from '@/components/common/SubsectionTitle' import { wikiKeys } from '@/lib/queryKeys' import remarkWikiAutoLink from '@/lib/remark-wiki-autolink' import { fetchWikiPages } from '@/lib/wiki' @@ -16,19 +14,15 @@ import type { Components } from 'react-markdown' type Props = { title: string body?: string } -const mdComponents = { h1: ({ children }) => {children}, - h2: ({ children }) => {children}, - ol: ({ children }) =>
    {children}
, - ul: ({ children }) =>
    {children}
, - a: (({ href, children }) => ( - ['/', '.'].some (e => href?.startsWith (e)) - ? {children} - : ( - - {children} - ))) } as const satisfies Components +const mdComponents = { a: (({ href, children }) => ( + ['/', '.'].some (e => href?.startsWith (e)) + ? {children} + : ( + + {children} + ))) } as const satisfies Components export default (({ title, body }: Props) => { diff --git a/frontend/src/mdx-components.tsx b/frontend/src/mdx-components.tsx index 193c117..b49be36 100644 --- a/frontend/src/mdx-components.tsx +++ b/frontend/src/mdx-components.tsx @@ -1,9 +1,4 @@ import type { MDXComponents } from 'mdx/types' -import PageTitle from '@/components/common/PageTitle' -import SectionTitle from '@/components/common/SectionTitle' - -export const useMDXComponents = (): MDXComponents => ({ - h1: props => , - h2: props => }) +export const useMDXComponents = (): MDXComponents => ({ }) diff --git a/frontend/src/pages/MorePage.tsx b/frontend/src/pages/MorePage.tsx new file mode 100644 index 0000000..dd57ec1 --- /dev/null +++ b/frontend/src/pages/MorePage.tsx @@ -0,0 +1,42 @@ +import { Helmet } from 'react-helmet-async' + +import PrefetchLink from '@/components/PrefetchLink' +import { menuOutline } from '@/components/TopNav' +import SectionTitle from '@/components/common/SectionTitle' +import MainArea from '@/components/layout/MainArea' +import { SITE_TITLE } from '@/config' + +import type { FC } from 'react' + +import type { User } from '@/types' + + +export default (() => { + const menu = menuOutline ({ tag: null, wikiId: null, user: { } as User }) + + return ( + + + {`メニュー | ${ SITE_TITLE }`} + + + {menu.map ((item, i) => ( +
+ {item.name} +
    + {item.subMenu + .filter (subItem => (subItem.visible ?? true)) + .map ((subItem, j) => ('name' in subItem && ( +
  • + + {subItem.name} + +
  • )))} +
+
))} +
) +}) satisfies FC diff --git a/frontend/src/pages/TOSPage.mdx b/frontend/src/pages/TOSPage.mdx index aed2845..46b77cc 100644 --- a/frontend/src/pages/TOSPage.mdx +++ b/frontend/src/pages/TOSPage.mdx @@ -9,7 +9,7 @@ import { dateString } from '@/lib/utils' {`利用規約 | ${ SITE_TITLE }`} -
+
# 利用規約 最終更新日: {dateString ('2026-03-27', 'hour')} @@ -60,12 +60,12 @@ import { dateString } from '@/lib/utils' 7. 虚偽の情報、誤解を招く情報、出典を偽装した情報、意図的なミスリード、荒らし目的のタグづけ、関係のないタグの大量付与、分類妨碍、検索妨碍その他の品質破壊行為。 {/* 8. 自動化ツール、スクリプト、Bot その他の手段を用いて、運営の許可なく大量投稿、大量編集、大量アクセス、過剰なスクレイピング、過負荷送信を行う行為。 */} {/* 9. 脆弱性の探索、過度な負荷試験、リバースエンジニアリング、認可回避、BAN 回避、なりすまし、セッション奪取その他の不正アクセスに類する行為。 */} - 10. マルウェア、フィッシング、詐欺、誘導広告、悪質なリダイレクト、危険な外部リンクその他利用者または運営に危害を与える行為。 - 11. 本サービスの趣旨に照らして不相当な政治的扇動、宗教勧誘、商業宣伝、連鎖的勧誘、スパム、同一内容の反復送信。 - 12. 未成年の安全に反する行為、児童性的搾取、違法または著しく不適切な性的表現、過度に露骨な性表現や残虐表現を、一般公開導線に無警告で流し込む行為。 - 13. 運営、他の利用者、外部サービスまたは第 3 者に著しい負担、不利益、混乱を生じさせる行為。 - 14. 前各号のいずれかを試みる行為、教唆する行為、容易にする行為。 - 15. その他、運営が本サービスの目的または安全な運営に照らして不適切と判断する行為。 + 8. マルウェア、フィッシング、詐欺、誘導広告、悪質なリダイレクト、危険な外部リンクその他利用者または運営に危害を与える行為。 + 9. 本サービスの趣旨に照らして不相当な政治的扇動、宗教勧誘、商業宣伝、連鎖的勧誘、スパム、同一内容の反復送信。 + 10. 未成年の安全に反する行為、児童性的搾取、違法または著しく不適切な性的表現、過度に露骨な性表現や残虐表現を、一般公開導線に無警告で流し込む行為。 + 11. 運営、他の利用者、外部サービスまたは第 3 者に著しい負担、不利益、混乱を生じさせる行為。 + 12. 前各号のいずれかを試みる行為、教唆する行為、容易にする行為。 + 13. その他、運営が本サービスの目的または安全な運営に照らして不適切と判断する行為。 ## 第 6 条 投稿、タグ、Wiki 等の取扱い @@ -103,14 +103,14 @@ import { dateString } from '@/lib/utils' ## 第 10 条 未成年の利用 {/* 1. 未成年者は、法定代理人の同意を得たうえで本サービスを利用しなければなりません。 */} - 2. 運営は、未成年の安全確保の観点から、年齢に応じた表示制限、導線制御、非表示化、削除、申請拒否その他の措置を行えます。 - 3. 利用者は、未成年が閲覧しうる一般公開面において、未成年に不適切な内容を無警告で流し込まないものとします。 + 1. 運営は、未成年の安全確保の観点から、年齢に応じた表示制限、導線制御、非表示化、削除、申請拒否その他の措置を行えます。 + 2. 利用者は、未成年が閲覧しうる一般公開面において、未成年に不適切な内容を無警告で流し込まないものとします。 ## 第 11 条 お問い合わせ、通報、御意見番 1. 利用者は、本サービスが別途案内する問い合わせ、通報または御意見板の導線を通じて、バグ報告、問題報告、削除要請その他の聯絡を行えます。 {/* 2. 運営は、再現、調査、保守または安全確保のため、操作ログ、画面情報、環境情報その他の関連情報の提出または自動添付を求めることがあります。 */} - 3. 運営は、すべての問い合わせに回答する義務を負わず、回答期限、対応結果または対応方法を保証しません。 + 2. 運営は、すべての問い合わせに回答する義務を負わず、回答期限、対応結果または対応方法を保証しません。 ## 第 12 条 免責 diff --git a/frontend/src/pages/wiki/WikiDetailPage.tsx b/frontend/src/pages/wiki/WikiDetailPage.tsx index fd2c0b5..7eab461 100644 --- a/frontend/src/pages/wiki/WikiDetailPage.tsx +++ b/frontend/src/pages/wiki/WikiDetailPage.tsx @@ -7,7 +7,6 @@ import PostList from '@/components/PostList' import PrefetchLink from '@/components/PrefetchLink' import TagLink from '@/components/TagLink' import WikiBody from '@/components/WikiBody' -import PageTitle from '@/components/common/PageTitle' import TabGroup, { Tab } from '@/components/common/TabGroup' import MainArea from '@/components/layout/MainArea' import { SITE_TITLE } from '@/config' @@ -107,15 +106,15 @@ export default () => { ) : '(最新)'} )} - - - -
- {loading ? 'Loading...' : } -
+
+

+ +

+ {loading ?
Loading...
: } +
{(!(version) && posts.length > 0) && ( diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 2f71826..8037bde 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -63,11 +63,17 @@ export type Material = { export type Menu = MenuItem[] -export type MenuItem = { - name: ReactNode - to: string - base?: string - subMenu: SubMenuItem[] } +export type MenuItem = + | { name: ReactNode + to: string + base?: string + visible?: true + subMenu: SubMenuItem[] } + | { name: ReactNode + to?: string + base?: string + visible: false + subMenu: SubMenuItem[] } export type NicoTag = Tag & { category: 'nico' diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index fddc378..7982b0a 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -8,7 +8,7 @@ import { DARK_COLOUR_SHADE, const colours = Object.values (TAG_COLOUR) export default { - content: ['./src/**/*.{html,js,ts,jsx,tsx}'], + content: ['./src/**/*.{html,js,ts,jsx,tsx,mdx}'], safelist: [...colours.map (c => `text-${ c }-${ LIGHT_COLOUR_SHADE }`), ...colours.map (c => `hover:text-${ c }-${ LIGHT_COLOUR_SHADE - 200 }`), ...colours.map (c => `dark:text-${ c }-${ DARK_COLOUR_SHADE }`), @@ -24,4 +24,4 @@ export default { 'rainbow-scroll': { '0%': { backgroundPosition: '0% 50%' }, '100%': { backgroundPosition: '200% 50%' } } } } }, - plugins: [] } satisfies Config + plugins: [require ('@tailwindcss/typography')] } satisfies Config