設定画面 (#34) #397

マージ済み
みてるぞ が 31 個のコミットを feature/034 から main へマージ 2026-07-07 00:48:41 +09:00
3個のファイルの変更338行の追加418行の削除
コミット 8ac56f74ce の変更だけを表示してゐます - すべてのコミットを表示
+103
ファイルの表示
@@ -376,6 +376,23 @@ const value =
short object literals, or short callback bodies that already fit the local short object literals, or short callback bodies that already fit the local
line-length and delimiter rules. Expanding compact local code without a line-length and delimiter rules. Expanding compact local code without a
readability need is a minor style violation. readability need is a minor style violation.
- In TypeScript and TSX, treat short object literals, short returned objects,
short typed object shapes, and short function-call object arguments as
compact associative syntax by default. If they fit locally, keep the opening
`{` on the same line as the first property and keep the closing `}` on the
same line as the final property. Do not explode them into vertical
Prettier-style blocks without a concrete readability or line-length reason.
- In TypeScript and TSX, when a compact associative form is broken across two
lines, keep the opening `{` with the first property and the closing `}` with
the final property. A shape like `{\n key: value,\n}` is wrong even when
line length would permit it.
- In TypeScript and TSX, when a short call such as `apiGet<...> (...)`,
`apiPatch<...> (...)`, `setState (...)`, `updateClientSettings (...)`, or
`window.dispatchEvent (...)` already fits within the local style, do not
add extra vertical layers around the object argument or callback body.
- In TypeScript and TSX, after editing a file, inspect the file tail and
remove meaningless blank lines before `export default` or the end of file.
A stray empty line before `export default` is a minor style offence.
- Use correct British English spelling for new identifiers, filenames, - Use correct British English spelling for new identifiers, filenames,
component names, helper names, comments, and developer-facing prose unless component names, helper names, comments, and developer-facing prose unless
editing an already established American-English API that must keep its editing an already established American-English API that must keep its
@@ -470,6 +487,92 @@ setDirtyStates (
Penalty: minor offence when semantics stay intact. It is not necessarily Penalty: minor offence when semantics stay intact. It is not necessarily
broken, but it ignores the repository's compact associative style. broken, but it ignores the repository's compact associative style.
#### Prettier-style exploded object literals that should stay compact
Bad:
```ts
const next = {
enabled: keyboard?.enabled !== false,
bindings: normaliseKeyboardBindings (keyboard?.bindings),
}
```
Good:
```ts
const next = {
enabled: keyboard?.enabled !== false,
bindings: normaliseKeyboardBindings (keyboard?.bindings) }
```
Also good when short enough:
```ts
const next = { enabled: keyboard?.enabled !== false,
bindings: normaliseKeyboardBindings (keyboard?.bindings) }
```
Rule: for compact associative syntax, keep `{` with the first property and `}`
with the final property unless line length truly forces a different shape.
Do not mechanically apply the common Prettier block form.
Penalty: medium-severity offence. The code still runs, but it violates a core
repository formatting habit that must be preserved consistently.
#### Exploded object arguments inside short calls
Bad:
```ts
updateClientSettings (settings => ({
...settings,
keyboard: next,
}))
```
Good:
```ts
updateClientSettings (settings => ({
...settings,
keyboard: next }))
```
Rule: a call-expression closing `)` must not drift onto its own line, and a
short object argument should not be exploded more than necessary. Keep the
callback's returned object compact when it fits.
Penalty: medium-severity offence. This usually means the edit was formatted
mechanically instead of by reading the local style.
#### Stray blank line before `export default`
Bad:
```ts
const Component: FC = () => {
return <div/>
}
export default Component
```
Good:
```ts
const Component: FC = () => {
return <div/>
}
export default Component
```
Rule: do not leave an extra empty line between the last top-level declaration
and `export default` unless some surrounding file structure genuinely requires
visual separation.
Penalty: minor offence. It is small, but it is easy to avoid and should not
recur.
### Frontend delimiter decision table ### Frontend delimiter decision table
Use this table before accepting any edited TypeScript or TSX hunk. The table is Use this table before accepting any edited TypeScript or TSX hunk. The table is
+132 -316
ファイルの表示
@@ -152,8 +152,7 @@ export type ClientSettings = {
keyboard?: ClientKeyboardSettings keyboard?: ClientKeyboardSettings
panes?: Record<string, ClientPaneSettings> panes?: Record<string, ClientPaneSettings>
lists?: Partial<Record<ClientListKey, ClientListSettings>> lists?: Partial<Record<ClientListKey, ClientListSettings>>
theatre?: { theatre?: { layoutMode?: TheatreLayoutMode
layoutMode?: TheatreLayoutMode
tagFlow?: TheatreTagFlow } tagFlow?: TheatreTagFlow }
gekanator?: { backgroundMotion?: GekanatorBackgroundMotionMode } gekanator?: { backgroundMotion?: GekanatorBackgroundMotionMode }
reducedMotion?: string reducedMotion?: string
@@ -273,14 +272,12 @@ export const DARK_THEME_TOKENS: ThemeTokens = {
topNavMenuLink: '#93c5fd', topNavMenuLink: '#93c5fd',
tagColours: DEFAULT_DARK_THEME_TAG_COLOURS } tagColours: DEFAULT_DARK_THEME_TAG_COLOURS }
export const BUILTIN_THEMES: Record<BuiltinThemeId, ClientTheme> = { export const BUILTIN_THEMES: Record<BuiltinThemeId, ClientTheme> = {
light: { light: { id: 'light',
id: 'light',
name: 'ライト・モード', name: 'ライト・モード',
builtin: true, builtin: true,
baseTheme: 'light', baseTheme: 'light',
tokens: LIGHT_THEME_TOKENS }, tokens: LIGHT_THEME_TOKENS },
dark: { dark: { id: 'dark',
id: 'dark',
name: 'ダーク・モード', name: 'ダーク・モード',
builtin: true, builtin: true,
baseTheme: 'dark', baseTheme: 'dark',
@@ -288,8 +285,7 @@ export const BUILTIN_THEMES: Record<BuiltinThemeId, ClientTheme> = {
const LEGACY_THEATRE_LAYOUT_STORAGE_KEY = 'theatre-layout-mode' const LEGACY_THEATRE_LAYOUT_STORAGE_KEY = 'theatre-layout-mode'
const LEGACY_THEATRE_TAG_FLOW_STORAGE_KEY = 'theatre-tag-flow' const LEGACY_THEATRE_TAG_FLOW_STORAGE_KEY = 'theatre-tag-flow'
const LEGACY_GEKANATOR_BACKGROUND_MOTION_STORAGE_KEY = const LEGACY_GEKANATOR_BACKGROUND_MOTION_STORAGE_KEY = 'gekanator:background-motion:v1'
'gekanator:background-motion:v1'
const LEGACY_MIGRATED_THEME_ID = 'custom:legacy-migrated' const LEGACY_MIGRATED_THEME_ID = 'custom:legacy-migrated'
@@ -307,15 +303,13 @@ export const fetchUserSettings = async (): Promise<UserSettings> => {
theme: UserSettings['theme'] theme: UserSettings['theme']
auto_fetch_title: UserSettings['autoFetchTitle'] auto_fetch_title: UserSettings['autoFetchTitle']
auto_fetch_thumbnail: UserSettings['autoFetchThumbnail'] auto_fetch_thumbnail: UserSettings['autoFetchThumbnail']
wiki_editor_mode: UserSettings['wikiEditorMode'] wiki_editor_mode: UserSettings['wikiEditorMode'] }> ('/users/settings')
}> ('/users/settings')
return { return {
theme: raw.theme, theme: raw.theme,
autoFetchTitle: raw.auto_fetch_title, autoFetchTitle: raw.auto_fetch_title,
autoFetchThumbnail: raw.auto_fetch_thumbnail, autoFetchThumbnail: raw.auto_fetch_thumbnail,
wikiEditorMode: raw.wiki_editor_mode, wikiEditorMode: raw.wiki_editor_mode }
}
} }
@@ -326,15 +320,13 @@ export const updateUserSettings = async (
theme: UserSettings['theme'] theme: UserSettings['theme']
auto_fetch_title: UserSettings['autoFetchTitle'] auto_fetch_title: UserSettings['autoFetchTitle']
auto_fetch_thumbnail: UserSettings['autoFetchThumbnail'] auto_fetch_thumbnail: UserSettings['autoFetchThumbnail']
wiki_editor_mode: UserSettings['wikiEditorMode'] wiki_editor_mode: UserSettings['wikiEditorMode'] }> ('/users/settings', settings)
}> ('/users/settings', settings)
return { return {
theme: raw.theme, theme: raw.theme,
autoFetchTitle: raw.auto_fetch_title, autoFetchTitle: raw.auto_fetch_title,
autoFetchThumbnail: raw.auto_fetch_thumbnail, autoFetchThumbnail: raw.auto_fetch_thumbnail,
wikiEditorMode: raw.wiki_editor_mode, wikiEditorMode: raw.wiki_editor_mode }
}
} }
@@ -364,8 +356,7 @@ export const saveClientSettings = (settings: ClientSettings): void => {
if (typeof window !== 'undefined') if (typeof window !== 'undefined')
{ {
window.dispatchEvent (new CustomEvent (CLIENT_SETTINGS_EVENT, { window.dispatchEvent (new CustomEvent (CLIENT_SETTINGS_EVENT, {
detail: settings, detail: settings }))
}))
} }
} }
@@ -428,10 +419,9 @@ const normaliseKeyboardBindings = (
if (!(bindings) || typeof bindings !== 'object') if (!(bindings) || typeof bindings !== 'object')
return { } return { }
return Object.keys (DEFAULT_KEY_BINDINGS).reduce< return (
Partial<Record<ShortcutActionId, KeyBinding | null>> Object.keys (DEFAULT_KEY_BINDINGS)
> ( .reduce<Partial<Record<ShortcutActionId, KeyBinding | null>>> ((normalised, actionId) => {
(normalised, actionId) => {
const value = (bindings as Record<string, unknown>)[actionId] const value = (bindings as Record<string, unknown>)[actionId]
if (value === null) if (value === null)
{ {
@@ -444,9 +434,7 @@ const normaliseKeyboardBindings = (
normalised[actionId as ShortcutActionId] = binding normalised[actionId as ShortcutActionId] = binding
return normalised return normalised
}, }, { }))
{ },
)
} }
@@ -455,8 +443,7 @@ export const getClientKeyboardSettings = (): ClientKeyboardSettings => {
return { return {
enabled: keyboard?.enabled !== false, enabled: keyboard?.enabled !== false,
bindings: normaliseKeyboardBindings (keyboard?.bindings), bindings: normaliseKeyboardBindings (keyboard?.bindings) }
}
} }
@@ -495,10 +482,7 @@ export const applyClientAnimationMode = (mode: ClientAnimationMode): void => {
export const setClientAnimationMode = ( export const setClientAnimationMode = (
animation: ClientAnimationMode, animation: ClientAnimationMode,
): ClientBehaviorSettings => ): ClientBehaviorSettings =>
setClientBehaviorSettings ({ setClientBehaviorSettings ({ ...getClientBehaviorSettings (), animation })
...getClientBehaviorSettings (),
animation,
})
export const getClientLinkPreloadMode = (): ClientLinkPreloadMode => export const getClientLinkPreloadMode = (): ClientLinkPreloadMode =>
@@ -509,10 +493,7 @@ export const getClientLinkPreloadMode = (): ClientLinkPreloadMode =>
export const setClientLinkPreloadMode = ( export const setClientLinkPreloadMode = (
linkPreload: ClientLinkPreloadMode, linkPreload: ClientLinkPreloadMode,
): ClientBehaviorSettings => ): ClientBehaviorSettings =>
setClientBehaviorSettings ({ setClientBehaviorSettings ({ ...getClientBehaviorSettings (), linkPreload })
...getClientBehaviorSettings (),
linkPreload,
})
export const getClientTagRelationDisplayMode = (): ClientTagRelationDisplayMode => export const getClientTagRelationDisplayMode = (): ClientTagRelationDisplayMode =>
@@ -523,10 +504,7 @@ export const getClientTagRelationDisplayMode = (): ClientTagRelationDisplayMode
export const setClientTagRelationDisplayMode = ( export const setClientTagRelationDisplayMode = (
tagRelationDisplay: ClientTagRelationDisplayMode, tagRelationDisplay: ClientTagRelationDisplayMode,
): ClientBehaviorSettings => ): ClientBehaviorSettings =>
setClientBehaviorSettings ({ setClientBehaviorSettings ({ ...getClientBehaviorSettings (), tagRelationDisplay })
...getClientBehaviorSettings (),
tagRelationDisplay,
})
export const getClientEmbedAutoLoadMode = (): ClientEmbedAutoLoadMode => export const getClientEmbedAutoLoadMode = (): ClientEmbedAutoLoadMode =>
@@ -538,10 +516,7 @@ export const getClientEmbedAutoLoadMode = (): ClientEmbedAutoLoadMode =>
export const setClientEmbedAutoLoadMode = ( export const setClientEmbedAutoLoadMode = (
embedAutoLoad: ClientEmbedAutoLoadMode, embedAutoLoad: ClientEmbedAutoLoadMode,
): ClientBehaviorSettings => ): ClientBehaviorSettings =>
setClientBehaviorSettings ({ setClientBehaviorSettings ({ ...getClientBehaviorSettings (), embedAutoLoad })
...getClientBehaviorSettings (),
embedAutoLoad,
})
export const getClientThumbnailMode = (): ClientThumbnailMode => export const getClientThumbnailMode = (): ClientThumbnailMode =>
@@ -553,10 +528,7 @@ export const getClientThumbnailMode = (): ClientThumbnailMode =>
export const setClientThumbnailMode = ( export const setClientThumbnailMode = (
thumbnailMode: ClientThumbnailMode, thumbnailMode: ClientThumbnailMode,
): ClientBehaviorSettings => ): ClientBehaviorSettings =>
setClientBehaviorSettings ({ setClientBehaviorSettings ({ ...getClientBehaviorSettings (), thumbnailMode })
...getClientBehaviorSettings (),
thumbnailMode,
})
export const getClientBehaviorSettings = (): ClientBehaviorSettings => ({ export const getClientBehaviorSettings = (): ClientBehaviorSettings => ({
@@ -564,8 +536,7 @@ export const getClientBehaviorSettings = (): ClientBehaviorSettings => ({
linkPreload: getClientLinkPreloadMode (), linkPreload: getClientLinkPreloadMode (),
tagRelationDisplay: getClientTagRelationDisplayMode (), tagRelationDisplay: getClientTagRelationDisplayMode (),
embedAutoLoad: getClientEmbedAutoLoadMode (), embedAutoLoad: getClientEmbedAutoLoadMode (),
thumbnailMode: getClientThumbnailMode (), thumbnailMode: getClientThumbnailMode () })
})
export const setClientBehaviorSettings = ( export const setClientBehaviorSettings = (
@@ -574,30 +545,21 @@ export const setClientBehaviorSettings = (
const next: ClientBehaviorSettings = { const next: ClientBehaviorSettings = {
animation: ( animation: (
normaliseAnimationMode (behavior.animation) normaliseAnimationMode (behavior.animation)
?? getClientAnimationMode () ?? getClientAnimationMode ()),
),
linkPreload: ( linkPreload: (
normaliseLinkPreloadMode (behavior.linkPreload) normaliseLinkPreloadMode (behavior.linkPreload)
?? getClientLinkPreloadMode () ?? getClientLinkPreloadMode ()),
),
tagRelationDisplay: ( tagRelationDisplay: (
normaliseTagRelationDisplayMode (behavior.tagRelationDisplay) normaliseTagRelationDisplayMode (behavior.tagRelationDisplay)
?? getClientTagRelationDisplayMode () ?? getClientTagRelationDisplayMode ()),
),
embedAutoLoad: ( embedAutoLoad: (
normaliseEmbedAutoLoadMode (behavior.embedAutoLoad) normaliseEmbedAutoLoadMode (behavior.embedAutoLoad)
?? getClientEmbedAutoLoadMode () ?? getClientEmbedAutoLoadMode ()),
),
thumbnailMode: ( thumbnailMode: (
normaliseThumbnailMode (behavior.thumbnailMode) normaliseThumbnailMode (behavior.thumbnailMode)
?? getClientThumbnailMode () ?? getClientThumbnailMode ()) }
),
}
updateClientSettings (settings => ({ updateClientSettings (settings => ({ ...settings, behavior: next }))
...settings,
behavior: next,
}))
applyClientAnimationMode (next.animation ?? 'normal') applyClientAnimationMode (next.animation ?? 'normal')
return next return next
} }
@@ -608,13 +570,9 @@ export const setClientKeyboardSettings = (
): ClientKeyboardSettings => { ): ClientKeyboardSettings => {
const next: ClientKeyboardSettings = { const next: ClientKeyboardSettings = {
enabled: keyboard.enabled !== false, enabled: keyboard.enabled !== false,
bindings: normaliseKeyboardBindings (keyboard.bindings), bindings: normaliseKeyboardBindings (keyboard.bindings) }
}
updateClientSettings (settings => ({ updateClientSettings (settings => ({ ...settings, keyboard: next }))
...settings,
keyboard: next,
}))
return next return next
} }
@@ -633,8 +591,7 @@ export const getEffectiveKeyBindings = (
: DEFAULT_KEY_BINDINGS[key] : DEFAULT_KEY_BINDINGS[key]
return effectiveBindings return effectiveBindings
}, },
{ ...DEFAULT_KEY_BINDINGS }, { ...DEFAULT_KEY_BINDINGS })
)
export const setClientKeyBinding = ( export const setClientKeyBinding = (
@@ -645,11 +602,7 @@ export const setClientKeyBinding = (
return setClientKeyboardSettings ({ return setClientKeyboardSettings ({
...current, ...current,
bindings: { bindings: { ...(current.bindings ?? { }), [actionId]: binding } })
...(current.bindings ?? { }),
[actionId]: binding,
},
})
} }
@@ -660,20 +613,14 @@ export const resetClientKeyBinding = (
const nextBindings = { ...(current.bindings ?? { }) } const nextBindings = { ...(current.bindings ?? { }) }
delete nextBindings[actionId] delete nextBindings[actionId]
return setClientKeyboardSettings ({ return setClientKeyboardSettings ({ ...current, bindings: nextBindings })
...current,
bindings: nextBindings,
})
} }
export const resetAllClientKeyBindings = (): ClientKeyboardSettings => { export const resetAllClientKeyBindings = (): ClientKeyboardSettings => {
const current = getClientKeyboardSettings () const current = getClientKeyboardSettings ()
return setClientKeyboardSettings ({ return setClientKeyboardSettings ({ ...current, bindings: { } })
...current,
bindings: { },
})
} }
@@ -696,21 +643,20 @@ const themeCopyName = (baseTheme: BuiltinThemeId): string =>
const appearanceFromSettings = (): ClientAppearanceSettings => const appearanceFromSettings = (): ClientAppearanceSettings =>
loadClientSettings ().appearance ?? { } loadClientSettings ().appearance ?? { }
let cachedUserThemeSlots: UserThemeSlotMap = { } let cachedUserThemeSlots: UserThemeSlotMap = { }
export const hasStoredClientThemeSelection = (): boolean => { export const hasStoredClientThemeSelection = (): boolean => {
const appearance = appearanceFromSettings () const appearance = appearanceFromSettings ()
return ( return (appearance.themeMode != null
appearance.themeMode != null
|| appearance.activeLightThemeSlotNo != null || appearance.activeLightThemeSlotNo != null
|| appearance.activeDarkThemeSlotNo != null || appearance.activeDarkThemeSlotNo != null
|| appearance.activeThemeSlot != null || appearance.activeThemeSlot != null
|| appearance.activeThemeId != null || appearance.activeThemeId != null
|| appearance.customThemes != null || appearance.customThemes != null
|| appearance.theme != null || appearance.theme != null
|| appearance.tagColours != null || appearance.tagColours != null)
)
} }
@@ -725,9 +671,7 @@ const mixHexColour = (
const channels = [0, 2, 4].map (offset => { const channels = [0, 2, 4].map (offset => {
const srcValue = parseInt (src.slice (offset, offset + 2), 16) const srcValue = parseInt (src.slice (offset, offset + 2), 16)
const dstValue = parseInt (dst.slice (offset, offset + 2), 16) const dstValue = parseInt (dst.slice (offset, offset + 2), 16)
const mixed = Math.round ( const mixed = Math.round (srcValue + (dstValue - srcValue) * clampedRatio)
srcValue + (dstValue - srcValue) * clampedRatio,
)
return mixed.toString (16).padStart (2, '0') return mixed.toString (16).padStart (2, '0')
}) })
@@ -749,10 +693,8 @@ const legacyThemeSlotSelection = (): ThemeSlotSelection => {
return 'builtin:light' return 'builtin:light'
if (appearance.activeThemeId === 'dark') if (appearance.activeThemeId === 'dark')
return 'builtin:dark' return 'builtin:dark'
if ( if (typeof appearance.activeThemeId === 'string'
typeof appearance.activeThemeId === 'string' && customThemes[appearance.activeThemeId] != null)
&& customThemes[appearance.activeThemeId] != null
)
return `builtin:${ customThemes[appearance.activeThemeId].baseTheme }` return `builtin:${ customThemes[appearance.activeThemeId].baseTheme }`
if (appearance.theme === 'light') if (appearance.theme === 'light')
return 'builtin:light' return 'builtin:light'
@@ -764,11 +706,9 @@ const legacyThemeSlotSelection = (): ThemeSlotSelection => {
const legacyThemeMode = (): ClientThemeMode => const legacyThemeMode = (): ClientThemeMode =>
normaliseThemeMode (appearanceFromSettings ().theme) normaliseThemeMode (appearanceFromSettings ().theme)
?? ( ?? (legacyThemeSlotSelection () === 'system'
legacyThemeSlotSelection () === 'system'
? 'system' ? 'system'
: (baseThemeOfSelection (legacyThemeSlotSelection ()) ?? 'system') : (baseThemeOfSelection (legacyThemeSlotSelection ()) ?? 'system'))
)
const legacyThemeSlotNoFor = ( const legacyThemeSlotNoFor = (
@@ -778,7 +718,7 @@ const legacyThemeSlotNoFor = (
if (isUserThemeSlotSelection (legacySelection)) if (isUserThemeSlotSelection (legacySelection))
{ {
const [_, selectionBaseTheme, slotNo] = legacySelection.split (':') const [, selectionBaseTheme, slotNo] = legacySelection.split (':')
if (selectionBaseTheme === baseTheme) if (selectionBaseTheme === baseTheme)
return Number (slotNo) as UserThemeSlotNo return Number (slotNo) as UserThemeSlotNo
} }
@@ -798,20 +738,12 @@ export const setClientActiveThemeSlot = (
): void => { ): void => {
updateClientSettings (settings => ({ updateClientSettings (settings => ({
...settings, ...settings,
appearance: { appearance: { ...(settings.appearance ?? { }), activeThemeSlot } }))
...(settings.appearance ?? { }),
activeThemeSlot,
},
}))
} }
export const getClientThemeMode = (): UserSettings['theme'] => { export const getClientThemeMode = (): UserSettings['theme'] =>
return ( normaliseThemeMode (appearanceFromSettings ().themeMode) ?? legacyThemeMode ()
normaliseThemeMode (appearanceFromSettings ().themeMode)
?? legacyThemeMode ()
)
}
export const getClientActiveLightThemeSlotNo = (): UserThemeSlotNo => export const getClientActiveLightThemeSlotNo = (): UserThemeSlotNo =>
@@ -827,8 +759,7 @@ export const getClientActiveDarkThemeSlotNo = (): UserThemeSlotNo =>
export const setClientThemeAppearance = ( export const setClientThemeAppearance = (
{ themeMode, { themeMode,
activeLightThemeSlotNo, activeLightThemeSlotNo,
activeDarkThemeSlotNo }: { activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
themeMode: ClientThemeMode
activeLightThemeSlotNo: UserThemeSlotNo activeLightThemeSlotNo: UserThemeSlotNo
activeDarkThemeSlotNo: UserThemeSlotNo }, activeDarkThemeSlotNo: UserThemeSlotNo },
): void => { ): void => {
@@ -838,9 +769,7 @@ export const setClientThemeAppearance = (
...(settings.appearance ?? { }), ...(settings.appearance ?? { }),
themeMode, themeMode,
activeLightThemeSlotNo, activeLightThemeSlotNo,
activeDarkThemeSlotNo, activeDarkThemeSlotNo } }))
},
}))
} }
@@ -848,8 +777,7 @@ export const setClientThemeMode = (theme: UserSettings['theme']): void => {
setClientThemeAppearance ({ setClientThemeAppearance ({
themeMode: theme, themeMode: theme,
activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (), activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (),
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo (), activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo () })
})
} }
@@ -894,8 +822,7 @@ const normaliseThemeTagColours = (
: fallback[category] : fallback[category]
return colours return colours
}, },
{ ...fallback }, { ...fallback })
)
const normaliseThemeTokenValue = ( const normaliseThemeTokenValue = (
@@ -916,9 +843,9 @@ const cssColourFromThemeToken = (
if (normalisedHex != null) if (normalisedHex != null)
return normalisedHex return normalisedHex
return /^-?\d+(?:\.\d+)?\s+\d+(?:\.\d+)?%\s+\d+(?:\.\d+)?%$/.test (value) return (/^-?\d+(?:\.\d+)?\s+\d+(?:\.\d+)?%\s+\d+(?:\.\d+)?%$/.test (value)
? `hsl(${ value })` ? `hsl(${ value })`
: fallback : fallback)
} }
@@ -928,16 +855,12 @@ const clamp = (value: number, min: number, max: number): number =>
const parseHslComponents = ( const parseHslComponents = (
value: string, value: string,
): { h: number ): { h: number; s: number; l: number } | null => {
s: number const raw = (value.startsWith ('hsl(') && value.endsWith (')')
l: number } | null => {
const raw = value.startsWith ('hsl(') && value.endsWith (')')
? value.slice (4, -1) ? value.slice (4, -1)
: value : value)
const match = const match =
raw.match ( raw.match (/^(-?\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)%\s+(\d+(?:\.\d+)?)%$/)
/^(-?\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)%\s+(\d+(?:\.\d+)?)%$/,
)
if (match == null) if (match == null)
return null return null
@@ -945,8 +868,7 @@ const parseHslComponents = (
return { return {
h: Number (match[1]), h: Number (match[1]),
s: Number (match[2]), s: Number (match[2]),
l: Number (match[3]), l: Number (match[3]) }
}
} }
@@ -987,9 +909,7 @@ const deriveMutedForeground = (
l: clamp ( l: clamp (
foregroundHsl.l + (backgroundHsl.l - foregroundHsl.l) * .35, foregroundHsl.l + (backgroundHsl.l - foregroundHsl.l) * .35,
0, 0,
100, 100) })
),
})
} }
@@ -1006,11 +926,9 @@ const deriveTopNavTokens = (
mobileActiveBackground: ( mobileActiveBackground: (
baseTheme === 'dark' baseTheme === 'dark'
? editable.activeBackground ? editable.activeBackground
: editable.rootBackgroundDesktop : editable.rootBackgroundDesktop),
),
brandLink: editable.brandLink, brandLink: editable.brandLink,
menuLink: link, menuLink: link })
})
export const buildThemeTokens = ( export const buildThemeTokens = (
@@ -1047,41 +965,33 @@ export const buildThemeTokens = (
tokens.topNavRootBackgroundMobile tokens.topNavRootBackgroundMobile
?? legacyTopNavTokens.topNavRootBackgroundMobile ?? legacyTopNavTokens.topNavRootBackgroundMobile
?? legacyTopNavTokens.topNavBackground, ?? legacyTopNavTokens.topNavBackground,
fallback.topNavRootBackgroundMobile, fallback.topNavRootBackgroundMobile)
)
const topNavRootBackgroundDesktop = normaliseTopNavColourValue ( const topNavRootBackgroundDesktop = normaliseTopNavColourValue (
tokens.topNavRootBackgroundDesktop tokens.topNavRootBackgroundDesktop
?? legacyTopNavTokens.topNavRootBackgroundDesktop ?? legacyTopNavTokens.topNavRootBackgroundDesktop
?? legacyTopNavTokens.topNavBackground, ?? legacyTopNavTokens.topNavBackground,
fallback.topNavRootBackgroundDesktop, fallback.topNavRootBackgroundDesktop)
)
const topNavActiveBackground = normaliseTopNavColourValue ( const topNavActiveBackground = normaliseTopNavColourValue (
tokens.topNavActiveBackground tokens.topNavActiveBackground
?? legacyTopNavTokens.topNavActiveBackground ?? legacyTopNavTokens.topNavActiveBackground
?? legacyTopNavTokens.topNavHighlightBackground ?? legacyTopNavTokens.topNavHighlightBackground
?? legacyTopNavTokens.topNavHighlightBackground, ?? legacyTopNavTokens.topNavHighlightBackground,
fallback.topNavActiveBackground, fallback.topNavActiveBackground)
)
const topNavBrandLink = normaliseTopNavColourValue ( const topNavBrandLink = normaliseTopNavColourValue (
tokens.topNavBrandLink tokens.topNavBrandLink
?? legacyTopNavTokens.topNavBrandLink ?? legacyTopNavTokens.topNavBrandLink
?? legacyTopNavTokens.topNavLink, ?? legacyTopNavTokens.topNavLink,
fallback.topNavBrandLink, fallback.topNavBrandLink)
)
const topNavMenuLink = cssColourFromThemeToken ( const topNavMenuLink = cssColourFromThemeToken (
link, link,
fallback.topNavMenuLink, fallback.topNavMenuLink)
)
const resolvedTopNav = deriveTopNavTokens ( const resolvedTopNav = deriveTopNavTokens (
baseTheme, baseTheme,
{ { rootBackgroundMobile: topNavRootBackgroundMobile,
rootBackgroundMobile: topNavRootBackgroundMobile,
rootBackgroundDesktop: topNavRootBackgroundDesktop, rootBackgroundDesktop: topNavRootBackgroundDesktop,
activeBackground: topNavActiveBackground, activeBackground: topNavActiveBackground,
brandLink: topNavBrandLink, brandLink: topNavBrandLink },
}, topNavMenuLink)
topNavMenuLink,
)
return { return {
background, background,
@@ -1098,8 +1008,7 @@ export const buildThemeTokens = (
mutedForeground: deriveMutedForeground ( mutedForeground: deriveMutedForeground (
foreground, foreground,
background, background,
fallback.mutedForeground, fallback.mutedForeground),
),
accent, accent,
accentForeground: readableForegroundFor (accent), accentForeground: readableForegroundFor (accent),
destructive: fallback.destructive, destructive: fallback.destructive,
@@ -1115,17 +1024,14 @@ export const buildThemeTokens = (
topNavMobileActiveBackground: resolvedTopNav.mobileActiveBackground, topNavMobileActiveBackground: resolvedTopNav.mobileActiveBackground,
topNavBrandLink: resolvedTopNav.brandLink, topNavBrandLink: resolvedTopNav.brandLink,
topNavMenuLink: resolvedTopNav.menuLink, topNavMenuLink: resolvedTopNav.menuLink,
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours), tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours) }
}
} }
const normaliseTopNavColourValue = ( const normaliseTopNavColourValue = (
value: unknown, value: unknown,
fallback: string, fallback: string,
): string => { ): string => cssColourFromThemeToken (value, fallback)
return cssColourFromThemeToken (value, fallback)
}
const normaliseThemeTokens = ( const normaliseThemeTokens = (
@@ -1161,8 +1067,7 @@ const normaliseThemeTokens = (
return buildThemeTokens ( return buildThemeTokens (
baseTheme, baseTheme,
{ { ...tokens,
...tokens,
background, background,
foreground, foreground,
card, card,
@@ -1191,9 +1096,7 @@ const normaliseThemeTokens = (
tokens.topNavMenuLink tokens.topNavMenuLink
?? legacyTopNavTokens.topNavMenuLink ?? legacyTopNavTokens.topNavMenuLink
?? link, ?? link,
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours), tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours) })
},
)
} }
@@ -1252,8 +1155,7 @@ export const getDefaultUserThemeSlot = (
): UserThemeSlot => ({ ): UserThemeSlot => ({
baseTheme, baseTheme,
slotNo, slotNo,
tokens: normaliseThemeTokens ({ }, baseTheme), tokens: normaliseThemeTokens ({ }, baseTheme) })
})
const normaliseUserThemeSlot = ( const normaliseUserThemeSlot = (
@@ -1266,12 +1168,10 @@ const normaliseUserThemeSlot = (
const baseTheme = slot.baseTheme const baseTheme = slot.baseTheme
const slotNo = slot.slotNo const slotNo = slot.slotNo
if ( if (baseTheme == null
baseTheme == null
|| !(isBuiltinThemeId (baseTheme)) || !(isBuiltinThemeId (baseTheme))
|| slotNo == null || slotNo == null
|| !(USER_THEME_SLOT_NOS.includes (slotNo as UserThemeSlotNo)) || !(USER_THEME_SLOT_NOS.includes (slotNo as UserThemeSlotNo)))
)
return null return null
return { return {
@@ -1279,8 +1179,7 @@ const normaliseUserThemeSlot = (
slotNo: slotNo as UserThemeSlotNo, slotNo: slotNo as UserThemeSlotNo,
tokens: normaliseThemeTokens (slot.tokens, baseTheme), tokens: normaliseThemeTokens (slot.tokens, baseTheme),
createdAt: typeof slot.createdAt === 'string' ? slot.createdAt : undefined, createdAt: typeof slot.createdAt === 'string' ? slot.createdAt : undefined,
updatedAt: typeof slot.updatedAt === 'string' ? slot.updatedAt : undefined, updatedAt: typeof slot.updatedAt === 'string' ? slot.updatedAt : undefined }
}
} }
@@ -1292,8 +1191,7 @@ export const normaliseUserThemeSlots = (
map[getUserThemeSlotSelection (slot.baseTheme, slot.slotNo)] = slot map[getUserThemeSlotSelection (slot.baseTheme, slot.slotNo)] = slot
return map return map
}, },
{ }, { })
)
const normaliseCustomTheme = ( const normaliseCustomTheme = (
@@ -1306,13 +1204,11 @@ const normaliseCustomTheme = (
const id = typeof theme.id === 'string' ? theme.id : null const id = typeof theme.id === 'string' ? theme.id : null
const name = typeof theme.name === 'string' ? theme.name : null const name = typeof theme.name === 'string' ? theme.name : null
const baseTheme = typeof theme.baseTheme === 'string' ? theme.baseTheme : null const baseTheme = typeof theme.baseTheme === 'string' ? theme.baseTheme : null
if ( if (id == null
id == null
|| !(isCustomThemeId (id)) || !(isCustomThemeId (id))
|| name == null || name == null
|| baseTheme == null || baseTheme == null
|| !(isBuiltinThemeId (baseTheme)) || !(isBuiltinThemeId (baseTheme)))
)
return null return null
return { return {
@@ -1321,21 +1217,16 @@ const normaliseCustomTheme = (
builtin: false, builtin: false,
baseTheme, baseTheme,
tokens: normaliseThemeTokens ( tokens: normaliseThemeTokens (
{ { ...(theme.tokens && typeof theme.tokens === 'object'
...(theme.tokens && typeof theme.tokens === 'object'
? theme.tokens as Partial<ThemeTokens> ? theme.tokens as Partial<ThemeTokens>
: { }), : { }),
tagColours: ( tagColours: (
theme.tokens (theme.tokens
&& typeof theme.tokens === 'object' && typeof theme.tokens === 'object'
&& 'tagColours' in theme.tokens && 'tagColours' in theme.tokens)
)
? (theme.tokens as { tagColours?: Partial<ThemeTagColours> }).tagColours ? (theme.tokens as { tagColours?: Partial<ThemeTagColours> }).tagColours
: theme.tagColours as Partial<ThemeTagColours> | undefined, : theme.tagColours as Partial<ThemeTagColours> | undefined) },
}, baseTheme) }
baseTheme,
),
}
} }
@@ -1352,8 +1243,7 @@ export const getClientCustomThemes = (): Record<string, CustomClientTheme> => {
themes[key] = theme themes[key] = theme
return themes return themes
}, },
{ }, { })
)
} }
const legacyTagColours = appearance.tagColours const legacyTagColours = appearance.tagColours
@@ -1371,11 +1261,7 @@ export const getClientCustomThemes = (): Record<string, CustomClientTheme> => {
...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS), ...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS),
tagColours: normaliseThemeTagColours ( tagColours: normaliseThemeTagColours (
legacyTagColours, legacyTagColours,
defaultThemeTagColoursFor (baseTheme), defaultThemeTagColoursFor (baseTheme)) } } }
),
},
},
}
} }
@@ -1385,12 +1271,11 @@ export const fetchUserThemeSlots = async (): Promise<UserThemeSlot[]> => {
slotNo: UserThemeSlotNo slotNo: UserThemeSlotNo
tokens: ThemeTokens tokens: ThemeTokens
createdAt: string createdAt: string
updatedAt: string updatedAt: string }>> ('/users/theme_slots')
}>> ('/users/theme_slots')
return raw return (raw
.map (slot => normaliseUserThemeSlot (slot)) .map (slot => normaliseUserThemeSlot (slot))
.filter ((slot): slot is UserThemeSlot => slot != null) .filter ((slot): slot is UserThemeSlot => slot != null))
} }
@@ -1404,8 +1289,7 @@ export const upsertUserThemeSlot = async (
slotNo: UserThemeSlotNo slotNo: UserThemeSlotNo
tokens: ThemeTokens tokens: ThemeTokens
createdAt: string createdAt: string
updatedAt: string updatedAt: string }> (`/users/theme_slots/${ baseTheme }/${ slotNo }`, { tokens })
}> (`/users/theme_slots/${ baseTheme }/${ slotNo }`, { tokens })
const slot = normaliseUserThemeSlot (raw) const slot = normaliseUserThemeSlot (raw)
if (slot == null) if (slot == null)
@@ -1427,8 +1311,7 @@ export const getCachedUserThemeSlots = (): UserThemeSlotMap =>
export const resolveDisplayedTheme = ( export const resolveDisplayedTheme = (
{ themeMode, { themeMode,
activeLightThemeSlotNo, activeLightThemeSlotNo,
activeDarkThemeSlotNo }: { activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
themeMode: ClientThemeMode
activeLightThemeSlotNo: UserThemeSlotNo activeLightThemeSlotNo: UserThemeSlotNo
activeDarkThemeSlotNo: UserThemeSlotNo }, activeDarkThemeSlotNo: UserThemeSlotNo },
themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (), themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (),
@@ -1445,9 +1328,8 @@ export const resolveDisplayedTheme = (
baseTheme, baseTheme,
selection, selection,
slotNo, slotNo,
tokens: themeSlots[selection]?.tokens tokens: (themeSlots[selection]?.tokens
?? getDefaultUserThemeSlot (baseTheme, slotNo).tokens, ?? getDefaultUserThemeSlot (baseTheme, slotNo).tokens) }
}
} }
@@ -1464,28 +1346,21 @@ export const getClientActiveThemeId = (): ActiveThemeId => {
const appearance = appearanceFromSettings () const appearance = appearanceFromSettings ()
const customThemes = getClientCustomThemes () const customThemes = getClientCustomThemes ()
if ( if (typeof appearance.activeThemeId === 'string'
typeof appearance.activeThemeId === 'string' && (appearance.activeThemeId === 'system'
&& (
appearance.activeThemeId === 'system'
|| isBuiltinThemeId (appearance.activeThemeId) || isBuiltinThemeId (appearance.activeThemeId)
|| customThemes[appearance.activeThemeId] != null || customThemes[appearance.activeThemeId] != null))
)
)
return appearance.activeThemeId return appearance.activeThemeId
if ( if (Object.keys (customThemes).length > 0
Object.keys (customThemes).length > 0
&& appearance.tagColours != null && appearance.tagColours != null
&& Object.keys (appearance.tagColours).length > 0 && Object.keys (appearance.tagColours).length > 0)
)
return LEGACY_MIGRATED_THEME_ID return LEGACY_MIGRATED_THEME_ID
if (typeof appearance.theme === 'string' && ( if (typeof appearance.theme === 'string'
appearance.theme === 'system' && (appearance.theme === 'system'
|| appearance.theme === 'light' || appearance.theme === 'light'
|| appearance.theme === 'dark' || appearance.theme === 'dark'))
))
return appearance.theme return appearance.theme
return DEFAULT_USER_SETTINGS.theme return DEFAULT_USER_SETTINGS.theme
@@ -1532,8 +1407,7 @@ export const resolveThemeFromSlotSelection = (
selection, selection,
baseTheme, baseTheme,
builtin: true, builtin: true,
tokens: BUILTIN_THEMES[baseTheme].tokens, tokens: BUILTIN_THEMES[baseTheme].tokens }
}
} }
if (isBuiltinThemeSlotSelection (selection)) if (isBuiltinThemeSlotSelection (selection))
@@ -1543,8 +1417,7 @@ export const resolveThemeFromSlotSelection = (
selection, selection,
baseTheme, baseTheme,
builtin: true, builtin: true,
tokens: BUILTIN_THEMES[baseTheme].tokens, tokens: BUILTIN_THEMES[baseTheme].tokens }
}
} }
const baseTheme = baseThemeOfSelection (selection) ?? 'light' const baseTheme = baseThemeOfSelection (selection) ?? 'light'
@@ -1552,9 +1425,8 @@ export const resolveThemeFromSlotSelection = (
selection, selection,
baseTheme, baseTheme,
builtin: false, builtin: false,
tokens: themeSlots[selection]?.tokens tokens: (themeSlots[selection]?.tokens
?? getDefaultUserThemeSlot (baseTheme, slotNoOfSelection (selection)).tokens, ?? getDefaultUserThemeSlot (baseTheme, slotNoOfSelection (selection)).tokens) }
}
} }
@@ -1576,21 +1448,15 @@ export const createCustomThemeFromBase = (
name: themeCopyName (baseTheme), name: themeCopyName (baseTheme),
builtin: false, builtin: false,
baseTheme, baseTheme,
tokens: tokens ?? { tokens: tokens ?? { ...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS) } }
...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS),
},
}
} }
export const getClientThemeTagColours = ( export const getClientThemeTagColours = (
activeThemeId: ActiveThemeId = getClientActiveThemeId (), activeThemeId: ActiveThemeId = getClientActiveThemeId (),
customThemes: Record<string, CustomClientTheme> = getClientCustomThemes (), customThemes: Record<string, CustomClientTheme> = getClientCustomThemes (),
): ThemeTagColours => { ): ThemeTagColours =>
return { ({ ...getResolvedThemeFromSelection (activeThemeId, customThemes).tokens.tagColours })
...getResolvedThemeFromSelection (activeThemeId, customThemes).tokens.tagColours,
}
}
export const setClientAppearanceThemes = ( export const setClientAppearanceThemes = (
@@ -1604,9 +1470,7 @@ export const setClientAppearanceThemes = (
activeThemeId, activeThemeId,
customThemes, customThemes,
theme: undefined, theme: undefined,
tagColours: undefined, tagColours: undefined } }))
},
}))
} }
@@ -1620,17 +1484,10 @@ export const setClientThemeTagColours = (
setClientAppearanceThemes ( setClientAppearanceThemes (
activeThemeId, activeThemeId,
{ { ...customThemes,
...customThemes,
[activeThemeId]: { [activeThemeId]: {
...customThemes[activeThemeId], ...customThemes[activeThemeId],
tokens: { tokens: { ...customThemes[activeThemeId].tokens, tagColours } } })
...customThemes[activeThemeId].tokens,
tagColours,
},
},
},
)
} }
@@ -1651,8 +1508,7 @@ export const applyThemeTagColours = (tagColours: ThemeTagColours): void => {
root.style.setProperty (`--tag-colour-${ category }`, colour) root.style.setProperty (`--tag-colour-${ category }`, colour)
root.style.setProperty ( root.style.setProperty (
`--tag-colour-${ category }-hover`, `--tag-colour-${ category }-hover`,
mixHexColour (colour, '#ffffff', .18), mixHexColour (colour, '#ffffff', .18))
)
}) })
} }
@@ -1680,38 +1536,14 @@ export const applyThemeTokens = (tokens: ThemeTokens): void => {
root.style.setProperty ('--input', tokens.input) root.style.setProperty ('--input', tokens.input)
root.style.setProperty ('--ring', tokens.ring) root.style.setProperty ('--ring', tokens.ring)
root.style.setProperty ('--theme-link', tokens.link) root.style.setProperty ('--theme-link', tokens.link)
root.style.setProperty ( root.style.setProperty ('--top-nav-root-bg-mobile', tokens.topNavRootBackgroundMobile)
'--top-nav-root-bg-mobile', root.style.setProperty ('--top-nav-root-bg-desktop', tokens.topNavRootBackgroundDesktop)
tokens.topNavRootBackgroundMobile, root.style.setProperty ('--top-nav-active-bg', tokens.topNavActiveBackground)
) root.style.setProperty ('--top-nav-submenu-bg', tokens.topNavSubmenuBackground)
root.style.setProperty ( root.style.setProperty ('--top-nav-mobile-menu-bg', tokens.topNavRootBackgroundMobile)
'--top-nav-root-bg-desktop', root.style.setProperty ('--top-nav-mobile-active-bg', tokens.topNavMobileActiveBackground)
tokens.topNavRootBackgroundDesktop, root.style.setProperty ('--top-nav-brand-link', tokens.topNavBrandLink)
) root.style.setProperty ('--top-nav-menu-link', tokens.topNavMenuLink)
root.style.setProperty (
'--top-nav-active-bg',
tokens.topNavActiveBackground,
)
root.style.setProperty (
'--top-nav-submenu-bg',
tokens.topNavSubmenuBackground,
)
root.style.setProperty (
'--top-nav-mobile-menu-bg',
tokens.topNavRootBackgroundMobile,
)
root.style.setProperty (
'--top-nav-mobile-active-bg',
tokens.topNavMobileActiveBackground,
)
root.style.setProperty (
'--top-nav-brand-link',
tokens.topNavBrandLink,
)
root.style.setProperty (
'--top-nav-menu-link',
tokens.topNavMenuLink,
)
applyThemeTagColours (tokens.tagColours) applyThemeTagColours (tokens.tagColours)
} }
@@ -1730,17 +1562,14 @@ export const applyThemeSelection = (
export const applyThemeAppearanceState = ( export const applyThemeAppearanceState = (
{ themeMode, { themeMode,
activeLightThemeSlotNo, activeLightThemeSlotNo,
activeDarkThemeSlotNo }: { activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
themeMode: ClientThemeMode
activeLightThemeSlotNo: UserThemeSlotNo activeLightThemeSlotNo: UserThemeSlotNo
activeDarkThemeSlotNo: UserThemeSlotNo }, activeDarkThemeSlotNo: UserThemeSlotNo },
themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (), themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (),
): void => { ): void => {
const theme = resolveDisplayedTheme ({ const theme = resolveDisplayedTheme ({ themeMode,
themeMode,
activeLightThemeSlotNo, activeLightThemeSlotNo,
activeDarkThemeSlotNo, activeDarkThemeSlotNo }, themeSlots)
}, themeSlots)
applyThemeMode (theme.baseTheme) applyThemeMode (theme.baseTheme)
applyThemeTokens (theme.tokens) applyThemeTokens (theme.tokens)
@@ -1753,8 +1582,7 @@ export const applyClientAppearance = (
applyThemeAppearanceState ({ applyThemeAppearanceState ({
themeMode: getClientThemeMode (), themeMode: getClientThemeMode (),
activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (), activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (),
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo (), activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo () }, themeSlots)
}, themeSlots)
} }
@@ -1786,20 +1614,17 @@ export const setClientListSettings = (
[listKey]: { [listKey]: {
...(settings.lists?.[listKey] ?? { }), ...(settings.lists?.[listKey] ?? { }),
...(limit == null ? { } : { limit }), ...(limit == null ? { } : { limit }),
...(order == null ? { } : { order }), ...(order == null ? { } : { order }) } } }))
},
},
}))
} }
const legacyTheatreLayoutMode = (): TheatreLayoutMode | null => { const legacyTheatreLayoutMode = (): TheatreLayoutMode | null => {
const value = localStorage.getItem (LEGACY_THEATRE_LAYOUT_STORAGE_KEY) const value = localStorage.getItem (LEGACY_THEATRE_LAYOUT_STORAGE_KEY)
return ( return ((value === 'threeColumns'
value === 'threeColumns'
|| value === 'tagsBottom' || value === 'tagsBottom'
|| value === 'commentsBottom' || value === 'commentsBottom')
) ? value : null ? value
: null)
} }
@@ -1841,8 +1666,7 @@ export const setClientTheatreTagFlow = (tagFlow: TheatreTagFlow): void => {
} }
export const getClientGekanatorBackgroundMotion = export const getClientGekanatorBackgroundMotion = (): GekanatorBackgroundMotionMode =>
(): GekanatorBackgroundMotionMode =>
loadClientSettings ().gekanator?.backgroundMotion loadClientSettings ().gekanator?.backgroundMotion
?? legacyGekanatorBackgroundMotion () ?? legacyGekanatorBackgroundMotion ()
?? 'on' ?? 'on'
@@ -1853,8 +1677,7 @@ export const setClientGekanatorBackgroundMotion = (
): void => { ): void => {
updateClientSettings (settings => ({ updateClientSettings (settings => ({
...settings, ...settings,
gekanator: { ...(settings.gekanator ?? { }), backgroundMotion }, gekanator: { ...(settings.gekanator ?? { }), backgroundMotion } }))
}))
} }
@@ -1880,11 +1703,7 @@ export const setClientPaneWidthPx = (
...(settings.panes?.[paneKey] ?? { }), ...(settings.panes?.[paneKey] ?? { }),
widthPxByBreakpoint: { widthPxByBreakpoint: {
...(settings.panes?.[paneKey]?.widthPxByBreakpoint ?? { }), ...(settings.panes?.[paneKey]?.widthPxByBreakpoint ?? { }),
[breakpoint]: widthPx, [breakpoint]: widthPx } } } }))
},
},
},
}))
} }
@@ -1898,8 +1717,5 @@ export const setClientPaneCollapsed = (
...(settings.panes ?? { }), ...(settings.panes ?? { }),
[paneKey]: { [paneKey]: {
...(settings.panes?.[paneKey] ?? { }), ...(settings.panes?.[paneKey] ?? { }),
collapsed, collapsed } } }))
},
},
}))
} }
+1
ファイルの表示
@@ -1445,4 +1445,5 @@ const SettingPage: FC<Props> = ({ user, setUser }) => {
</MainArea>) </MainArea>)
} }
export default SettingPage export default SettingPage