このコミットが含まれているのは:
2026-10-18 00:20:30 +09:00
コミット 9be0c7b1d9
45個のファイルの変更、355行の追加、199行の削除
+6 -1
ファイルの表示
@@ -8,6 +8,8 @@ const COLUMN_COUNT = 12
const DevModeWatermark: FC = () => {
const { t } = useTranslation ('shared')
const text = t ('labels.development', { returnDetails: true })
return (
<div
aria-hidden="true"
@@ -29,13 +31,14 @@ const DevModeWatermark: FC = () => {
{Array.from ({ length: COLUMN_COUNT }, (_, columnIndex) => (
<span
key={columnIndex}
lang={text.usedLng}
className={
'whitespace-nowrap text-3xl font-bold '
+ 'tracking-[0.3em] text-neutral-950/5 '
+ 'dark:text-white/10'
}
style={{ fontFamily: 'Nikumaru' }}>
開発モード
{text.res}
</span>))}
</div>))}
</div>
@@ -44,3 +47,5 @@ const DevModeWatermark: FC = () => {
export default DevModeWatermark
import { useTranslation } from 'react-i18next'
+10 -10
ファイルの表示
@@ -4,6 +4,7 @@ import nikumaru from '@/assets/fonts/nikumaru.otf'
import errorImg from '@/assets/images/not-found.gif'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -11,17 +12,16 @@ type Props = { status: number }
const ErrorScreen: FC<Props> = ({ status }) => {
const [message, rightMsg, leftMsg]: [string, string, string] = (() => {
const { message: translate, label } = useUITranslation ('errors')
const [message]: [string, string, string] = (() => {
switch (status)
{
case 403:
return ['権限ないよ(笑)', '帰れ!', '帰れ!']
case 404:
return ['ページないよ(笑)', '帰れ!', '帰れ!']
case 500:
return ['鯖でエラー出たって(嘲笑)', '管理人に', '聯絡を!']
case 503:
return ['鯖死んでるよ(泣)', '管理人に', '聯絡を!']
return [translate (`${ status }.message`).text,
translate (`${ status }.right`).text, translate (`${ status }.left`).text]
default:
throw new Error
}
@@ -51,13 +51,13 @@ const ErrorScreen: FC<Props> = ({ status }) => {
style={{ fontFamily: 'Nikumaru' }}>
<p>{status}</p>
<div className="flex flex-row space-x-1 sm:space-x-2 md:space-x-4">
<p style={{ writingMode: 'vertical-rl' }}>{leftMsg}</p>
<img className="max-w-[70vw]" src={errorImg} alt="逃げたギター"/>
<p style={{ writingMode: 'vertical-rl' }}>{rightMsg}</p>
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.left`)}</p>
<img className="max-w-[70vw]" src={errorImg} alt={translate ('imageAlt').text}/>
<p style={{ writingMode: 'vertical-rl' }}>{label (`${ status }.right`)}</p>
</div>
<p className="mr-[-.5em]">{message}</p>
<p className="mr-[-.5em]">{label (`${ status }.message`)}</p>
</div>
</MainArea>)
}
export default ErrorScreen
export default ErrorScreen
+3 -1
ファイルの表示
@@ -3,6 +3,7 @@
import { useRef, useState } from 'react'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import TagSearchBox from '@/components/TagSearchBox'
import FormField from '@/components/common/FormField'
import TextArea from '@/components/common/TextArea'
@@ -39,6 +40,7 @@ type Props = Omit<ComponentPropsWithoutRef<'textarea'>, 'value' | 'onChange' | '
const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
const { label } = useUITranslation ('navigation')
const locale = useLocale ()
const ref = useRef<HTMLTextAreaElement> (null)
@@ -79,7 +81,7 @@ const PostFormTagsArea: FC<Props> = ({ tags, setTags, errors, ...rest }) => {
}
return (
<FormField className="relative w-full" label="タグ" messages={errors}>
<FormField className="relative w-full" label={label ('tags')} messages={errors}>
{({ describedBy, invalid }) => (
<>
<TextArea
+3 -1
ファイルの表示
@@ -3,6 +3,7 @@ import { useRef } from 'react'
import { useLocation } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import { useUITranslation } from '@/lib/useUITranslation'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { cn } from '@/lib/utils'
import { useSharedTransitionStore } from '@/stores/sharedTransitionStore'
@@ -16,6 +17,7 @@ type Props = { posts: Post[]
const PostList: FC<Props> = ({ posts, onClick }) => {
const { label } = useUITranslation ('shared')
const location = useLocation ()
const behaviourSettings = useClientBehaviourSettings ()
const thumbnailMode = behaviourSettings.thumbnailMode ?? 'normal'
@@ -75,7 +77,7 @@ const PostList: FC<Props> = ({ posts, onClick }) => {
? (
<div className="flex h-full w-full items-center justify-center
bg-muted text-center text-xs text-muted-foreground">
サムネイルなし
{label ('labels.noThumbnail')}
</div>)
: (
<img
+9 -6
ファイルの表示
@@ -1,6 +1,7 @@
import DateTimeField, { toMinutePrecisionIsoUtc } from '@/components/common/DateTimeField'
import FormField from '@/components/common/FormField'
import { Button } from '@/components/ui/button'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -19,9 +20,11 @@ const PostOriginalCreatedTimeField: FC<Props> = (
setOriginalCreatedFrom,
originalCreatedBefore,
setOriginalCreatedBefore,
errors }: Props) => {
errors }: Props,
) => {
const { label } = useUITranslation ('shared')
return (
<FormField label="オリジナルの作成日時" messages={errors}>
<FormField label={label ('labels.originalCreatedAt')} messages={errors}>
{({ describedBy, invalid }) => (
<>
<div className="my-1 flex">
@@ -46,7 +49,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
d.setMinutes (d.getMinutes () + 1)
setOriginalCreatedBefore (toMinutePrecisionIsoUtc (d.toISOString ()))
}}/>
以降
{label ('labels.since')}
</div>
<div>
<Button
@@ -56,7 +59,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
onClick={() => {
setOriginalCreatedFrom (null)
}}>
リセット
{label ('actions.reset')}
</Button>
</div>
</div>
@@ -71,7 +74,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
invalid={invalid}
value={originalCreatedBefore ?? undefined}
onChange={setOriginalCreatedBefore}/>
より前
{label ('labels.before')}
</div>
<div>
<Button
@@ -81,7 +84,7 @@ const PostOriginalCreatedTimeField: FC<Props> = (
onClick={() => {
setOriginalCreatedBefore (null)
}}>
リセット
{label ('actions.reset')}
</Button>
</div>
</div>
+3 -1
ファイルの表示
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { apiGet } from '@/lib/api'
import { inputClass } from '@/lib/utils'
@@ -15,6 +16,7 @@ import type { Tag } from '@/types'
const TagSearch: FC = () => {
const { message } = useUITranslation ('shared')
const locale = useLocale ()
const location = useLocation ()
const navigate = useLocaleNavigate ()
@@ -108,7 +110,7 @@ const TagSearch: FC = () => {
<div className="relative w-full">
<input type="text"
data-shortcut-focus-search="true"
placeholder="タグ検索..."
placeholder={message ('labels.tagSearch').text}
value={search}
onChange={whenChanged}
onFocus={() => setSuggestionsVsbl (true)}
+6 -4
ファイルの表示
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocale, useLocaleNavigate } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import TagLink from '@/components/TagLink'
import TagSearch from '@/components/TagSearch'
import SectionTitle from '@/components/common/SectionTitle'
@@ -23,6 +24,7 @@ type Props = { posts: Post[]
const TagSidebar: FC<Props> = ({ posts, onClick }) => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const navigate = useLocaleNavigate ()
const behaviourSettings = useClientBehaviourSettings ()
@@ -71,7 +73,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
const TagBlock = (
<>
<SectionTitle>タグ</SectionTitle>
<SectionTitle>{label ('navigation:tags')}</SectionTitle>
<ul>
{CATEGORIES.flatMap (cat => cat in tags ? (
tags[cat].map (tag => (
@@ -90,7 +92,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
</motion.div>)}
</li>))) : [])}
</ul>
<SectionTitle>関聯</SectionTitle>
<SectionTitle>{label ('labels.relations')}</SectionTitle>
<a href="#"
onClick={ev => {
ev.preventDefault ()
@@ -108,7 +110,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
}
}) ())
}}>
ランダム
{label ('actions.random')}
</a>
</>)
@@ -148,7 +150,7 @@ const TagSidebar: FC<Props> = ({ posts, onClick }) => {
ev.preventDefault ()
setTagsVsbl (v => !(v))
}}>
{tagsVsbl ? '▲▲▲ タグ一覧を閉じる ▲▲▲' : '▼▼▼ タグ一覧を表示 ▼▼▼'}
{tagsVsbl ? label ('labels.hideTags') : label ('labels.showTags')}
</a>
</SidebarComponent>)
}
+12 -11
ファイルの表示
@@ -16,6 +16,7 @@ import { isLocaleCode,
switchLocalePath,
unlocalisedPath } from '@/lib/locales'
import { uiMessage, uiMessageLanguage } from '@/lib/messages'
import { useUITranslation } from '@/lib/useUITranslation'
import { materialsKeys, tagsKeys, wikiKeys } from '@/lib/queryKeys'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { canEditContent } from '@/lib/users'
@@ -125,6 +126,7 @@ export const menuOutline = (
const TopNav: FC<Props> = ({ user, locales }) => {
const { label } = useUITranslation ('shared')
const locale = useLocale ()
const localeName = locales.find (entry => entry.code === locale)?.name
const navigate = useLocaleNavigate ()
@@ -206,21 +208,21 @@ const TopNav: FC<Props> = ({ user, locales }) => {
const topNavTransition =
animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08, ease: 'linear' as const }
: { duration: .2, ease: 'easeOut' as const }
: (reducedAnimations
? { duration: .08, ease: 'linear' as const }
: { duration: .2, ease: 'easeOut' as const })
const opacityTransition =
animationsOff
? { duration: 0 }
: reducedAnimations
? { duration: .08 }
: { duration: .12 }
: (reducedAnimations
? { duration: .08 }
: { duration: .12 })
const highlightTransitionClass =
animationsOff
? undefined
: reducedAnimations
? 'transition-[transform,width] duration-75 ease-linear'
: 'transition-[transform,width] duration-200 ease-out'
: (reducedAnimations
? 'transition-[transform,width] duration-75 ease-linear'
: 'transition-[transform,width] duration-200 ease-out')
const prevActiveIdxRef = useRef<number> (activeIdx)
@@ -382,8 +384,7 @@ const TopNav: FC<Props> = ({ user, locales }) => {
onClick={() => {
scroll (0, 0)
}}>
{/* TODO: ここも外出ししろ. */}
ぼざクリ タグ広場
{label ('labels.brand')}
</PrefetchLink>
<div ref={navRef}
+3 -1
ファイルの表示
@@ -1,6 +1,7 @@
import Separator from '@/components/MenuSeparator'
import PrefetchLink from '@/components/PrefetchLink'
import { cn } from '@/lib/utils'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -11,6 +12,7 @@ type Props = { user: User | null,
const TopNavUser: FC<Props> = ({ user, sp }) => {
const { label } = useUITranslation ('shared')
if (!(user))
return
@@ -24,7 +26,7 @@ const TopNavUser: FC<Props> = ({ user, sp }) => {
<>
{sp && <Separator/>}
<PrefetchLink to="/users/settings" className={className}>
{user.name || '名もなきニジラー'}
{user.name || label ('labels.anonymous')}
</PrefetchLink>
</>)
}
+5 -2
ファイルの表示
@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'
import remarkGFM from 'remark-gfm'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import PrefetchLink from '@/components/PrefetchLink'
import { wikiKeys } from '@/lib/queryKeys'
import remarkWikiAutoLink from '@/lib/remark-wiki-autolink'
@@ -27,6 +28,7 @@ const mdComponents = { a: (({ href, children }) => (
const WikiBody: FC<Props> = ({ title, body }) => {
const { message } = useUITranslation ('wiki')
const locale = useLocale ()
const { data } = useQuery ({
enabled: Boolean (body),
@@ -39,8 +41,9 @@ const WikiBody: FC<Props> = ({ title, body }) => {
return (
<ReactMarkdown components={mdComponents} remarkPlugins={remarkPlugins}>
{body || `このページは存在しません。[新規作成してください](/wiki/new?title=${ encodeURIComponent (title) })。`}
{body || message ('missingPage', {
url: `/wiki/new?title=${ encodeURIComponent (title) }` }).text}
</ReactMarkdown>)
}
export default WikiBody
export default WikiBody
+3 -2
ファイルの表示
@@ -1,6 +1,7 @@
import PostOriginalCreatedTimeField from '@/components/PostOriginalCreatedTimeField'
import PostTagsField from '@/components/posts/PostTagsField'
import PostTextField from '@/components/posts/PostTextField'
import TranslatedText from '@/components/TranslatedText'
import type { FC, ReactNode } from 'react'
@@ -54,7 +55,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
) => (
<>
<PostTextField
label="タイトル"
label={<TranslatedText namespace="shared" messageKey="labels.title"/>}
value={title.value}
disabled={title.disabled}
warnings={title.warnings}
@@ -82,7 +83,7 @@ const PostCoreDataFields: FC<PostCoreDataFieldsProps> = (
rows={tags.rows}/>
<PostTextField
label="親投稿"
label={<TranslatedText namespace="shared" messageKey="labels.parentPosts"/>}
value={parentPostIds.value}
disabled={parentPostIds.disabled}
warnings={parentPostIds.warnings}
+3 -3
ファイルの表示
@@ -1,4 +1,5 @@
import PostTextField from '@/components/posts/PostTextField'
import TranslatedText from '@/components/TranslatedText'
import type { FC } from 'react'
@@ -16,12 +17,11 @@ const PostDurationField: FC<Props> = (
disabled },
) => (
<PostTextField
label="動画時間"
label={<TranslatedText namespace="shared" messageKey="labels.duration"/>}
value={value}
onChange={onChange}
errors={errors}
disabled={disabled}
type="text"/>
)
type="text"/>)
export default PostDurationField
+1 -1
ファイルの表示
@@ -5,7 +5,7 @@ import { inputClass } from '@/lib/utils'
import type { FC, ReactNode } from 'react'
type Props = {
label: string
label: ReactNode
value: string
onChange: (value: string) => void
warnings?: string[]
+4 -2
ファイルの表示
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { cn } from '@/lib/utils'
import { useUITranslation } from '@/lib/useUITranslation'
import type { FC } from 'react'
@@ -15,10 +16,11 @@ type Props = {
const PostThumbnailPreview: FC<Props> = (
{ url,
file,
alt = 'サムネール',
alt,
className = 'h-16 w-16',
referrerPolicy },
) => {
const { message } = useUITranslation ('shared')
const [failed, setFailed] = useState (false)
const [fileUrl, setFileUrl] = useState<string | null> (null)
@@ -55,7 +57,7 @@ const PostThumbnailPreview: FC<Props> = (
return (
<img
src={resolvedUrl}
alt={alt}
alt={alt ?? message ('labels.thumbnail').text}
referrerPolicy={referrerPolicy}
className={cn (className, 'rounded border border-border object-cover')}
onError={() => setFailed (true)}/>)
+2 -9
ファイルの表示
@@ -1,4 +1,5 @@
import StatusBadge from '@/components/common/StatusBadge'
import TranslatedText from '@/components/TranslatedText'
import type { FC } from 'react'
@@ -8,14 +9,6 @@ import type { PostImportBadgeValue } from '@/components/posts/import/postImportR
type Props = {
value: PostImportBadgeValue }
const LABELS: Record<PostImportBadgeValue, string> = {
ready: '登録可能',
error: '登録不可',
warning: '警告',
skipped: 'スキップ',
created: '登録済み',
failed: '登録失敗' }
const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
ready: 'success',
error: 'warning',
@@ -27,7 +20,7 @@ const TONES: Record<PostImportBadgeValue, StatusBadgeTone> = {
const PostImportStatusBadge: FC<Props> = ({ value }) => (
<StatusBadge tone={TONES[value]}>
{LABELS[value]}
<TranslatedText namespace="imports" messageKey={`status.${ value }`}/>
</StatusBadge>)
export default PostImportStatusBadge
+1 -1
ファイルの表示
@@ -12,7 +12,7 @@ type Props = {
const PostImportThumbnailPreview: FC<Props> = (
{ url,
file,
alt = 'サムネール',
alt,
className = 'h-16 w-16' },
) => (
<PostThumbnailPreview