このコミットが含まれているのは:
2026-07-05 11:27:45 +09:00
コミット ca92c62049
11個のファイルの変更1137行の追加92行の削除
+1 -13
ファイルの表示
@@ -7,7 +7,7 @@ import { useOverlayStore } from '@/components/RouteBlockerOverlay'
import { prefetchForURL } from '@/lib/prefetchers'
import { cn } from '@/lib/utils'
import type { AnchorHTMLAttributes, KeyboardEvent, MouseEvent, TouchEvent } from 'react'
import type { AnchorHTMLAttributes, MouseEvent, TouchEvent } from 'react'
import type { To } from 'react-router-dom'
type Props = AnchorHTMLAttributes<HTMLAnchorElement> & {
@@ -25,7 +25,6 @@ export default forwardRef<HTMLAnchorElement, Props> (({
state,
onMouseEnter,
onTouchStart,
onKeyDown,
onClick,
cancelOnError = false,
...rest }, ref) => {
@@ -98,22 +97,11 @@ export default forwardRef<HTMLAnchorElement, Props> (({
}
}
const handleKeyDown = (ev: KeyboardEvent<HTMLAnchorElement>) => {
onKeyDown?.(ev)
if (ev.defaultPrevented)
return
if (ev.key === ' ' || ev.key === 'Spacebar')
ev.preventDefault ()
}
return (
<a ref={ref}
href={typeof to === 'string' ? to : createPath (to)}
onMouseEnter={handleMouseEnter}
onTouchStart={handleTouchStart}
onKeyDown={handleKeyDown}
onClick={handleClick}
className={cn ('cursor-pointer', className)}
{...rest}/>)
+143
ファイルの表示
@@ -0,0 +1,143 @@
import { useEffect, useMemo, useState } from 'react'
import FormField from '@/components/common/FormField'
import { Button } from '@/components/ui/button'
import { toast } from '@/components/ui/use-toast'
import {
getClientBehaviorSettings,
setClientBehaviorSettings,
} from '@/lib/settings'
import { inputClass } from '@/lib/utils'
import type {
ClientAnimationMode,
ClientBehaviorSettings,
ClientEmbedAutoLoadMode,
ClientThumbnailMode,
} from '@/lib/settings'
import type { FC } from 'react'
type Props = {
sectionClassName: string
}
const BehaviorSettingsSection: FC<Props> = ({ sectionClassName }) => {
const [savedSettings, setSavedSettings] =
useState<ClientBehaviorSettings> (() => getClientBehaviorSettings ())
const [draftSettings, setDraftSettings] =
useState<ClientBehaviorSettings> (() => getClientBehaviorSettings ())
useEffect (() => {
const current = getClientBehaviorSettings ()
setSavedSettings (current)
setDraftSettings (current)
}, [])
const hasUnsavedChanges = useMemo (
() => JSON.stringify (draftSettings) !== JSON.stringify (savedSettings),
[draftSettings, savedSettings],
)
const updateDraft = <Key extends keyof ClientBehaviorSettings,> (
key: Key,
value: ClientBehaviorSettings[Key],
) => {
setDraftSettings (current => ({ ...current, [key]: value }))
}
const handleSave = () => {
const next = setClientBehaviorSettings (draftSettings)
setSavedSettings (next)
setDraftSettings (next)
toast ({ title: '動作設定を保存しました' })
}
const handleDiscard = () => {
setDraftSettings (savedSettings)
}
return (
<section className={sectionClassName}>
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="space-y-1">
<h2 className="text-xl font-bold"></h2>
<p className="text-sm text-muted-foreground">
</p>
<p className="text-sm text-muted-foreground">
調
</p>
{hasUnsavedChanges && (
<p className="text-sm text-amber-700 dark:text-amber-300">
</p>)}
</div>
<div className="flex flex-wrap gap-2">
<Button
type="button"
variant="outline"
disabled={!(hasUnsavedChanges)}
onClick={handleDiscard}>
</Button>
<Button
type="button"
disabled={!(hasUnsavedChanges)}
onClick={handleSave}>
</Button>
</div>
</div>
<div className="grid gap-4 md:grid-cols-2">
<FormField label="アニメーション">
{() => (
<select
className={inputClass (false)}
value={draftSettings.animation ?? 'normal'}
onChange={ev => updateDraft (
'animation',
ev.target.value as ClientAnimationMode,
)}>
<option value="normal"></option>
<option value="reduced"></option>
<option value="off"></option>
</select>)}
</FormField>
<FormField label="埋め込み自動読込">
{() => (
<select
className={inputClass (false)}
value={draftSettings.embedAutoLoad ?? 'auto'}
onChange={ev => updateDraft (
'embedAutoLoad',
ev.target.value as ClientEmbedAutoLoadMode,
)}>
<option value="auto"></option>
<option value="manual"></option>
<option value="off"></option>
</select>)}
</FormField>
<FormField label="サムネイル表示">
{() => (
<select
className={inputClass (false)}
value={draftSettings.thumbnailMode ?? 'normal'}
onChange={ev => updateDraft (
'thumbnailMode',
ev.target.value as ClientThumbnailMode,
)}>
<option value="normal"></option>
<option value="light"></option>
<option value="off"></option>
</select>)}
</FormField>
</div>
</section>)
}
export default BehaviorSettingsSection
+13 -3
ファイルの表示
@@ -24,14 +24,24 @@ type Props = {
activeScope: ShortcutScope
effectiveBindings: Record<ShortcutActionId, KeyBinding | null>
conflictActionIds: Set<ShortcutActionId>
availableActionIds: Set<ShortcutActionId>
}
const ShortcutHelpDialog: FC<Props> = (
{ open, onOpenChange, activeScope, effectiveBindings, conflictActionIds },
{
open,
onOpenChange,
activeScope,
effectiveBindings,
conflictActionIds,
availableActionIds,
},
) => {
const rows = SHORTCUT_DEFINITIONS.filter (definition =>
definition.scope === 'global' || definition.scope === activeScope)
const rows = SHORTCUT_DEFINITIONS
.filter (definition =>
definition.scope === 'global' || definition.scope === activeScope)
.filter (definition => availableActionIds.has (definition.id))
return (
<Dialog open={open} onOpenChange={onOpenChange}>