設定画面 (#34) #397
@@ -376,6 +376,23 @@ const value =
|
||||
short object literals, or short callback bodies that already fit the local
|
||||
line-length and delimiter rules. Expanding compact local code without a
|
||||
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,
|
||||
component names, helper names, comments, and developer-facing prose unless
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
panes?: Record<string, ClientPaneSettings>
|
||||
lists?: Partial<Record<ClientListKey, ClientListSettings>>
|
||||
theatre?: {
|
||||
layoutMode?: TheatreLayoutMode
|
||||
theatre?: { layoutMode?: TheatreLayoutMode
|
||||
tagFlow?: TheatreTagFlow }
|
||||
gekanator?: { backgroundMotion?: GekanatorBackgroundMotionMode }
|
||||
reducedMotion?: string
|
||||
@@ -273,14 +272,12 @@ export const DARK_THEME_TOKENS: ThemeTokens = {
|
||||
topNavMenuLink: '#93c5fd',
|
||||
tagColours: DEFAULT_DARK_THEME_TAG_COLOURS }
|
||||
export const BUILTIN_THEMES: Record<BuiltinThemeId, ClientTheme> = {
|
||||
light: {
|
||||
id: 'light',
|
||||
light: { id: 'light',
|
||||
name: 'ライト・モード',
|
||||
builtin: true,
|
||||
baseTheme: 'light',
|
||||
tokens: LIGHT_THEME_TOKENS },
|
||||
dark: {
|
||||
id: 'dark',
|
||||
dark: { id: 'dark',
|
||||
name: 'ダーク・モード',
|
||||
builtin: true,
|
||||
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_TAG_FLOW_STORAGE_KEY = 'theatre-tag-flow'
|
||||
const LEGACY_GEKANATOR_BACKGROUND_MOTION_STORAGE_KEY =
|
||||
'gekanator:background-motion:v1'
|
||||
const LEGACY_GEKANATOR_BACKGROUND_MOTION_STORAGE_KEY = 'gekanator:background-motion:v1'
|
||||
const LEGACY_MIGRATED_THEME_ID = 'custom:legacy-migrated'
|
||||
|
||||
|
||||
@@ -307,15 +303,13 @@ export const fetchUserSettings = async (): Promise<UserSettings> => {
|
||||
theme: UserSettings['theme']
|
||||
auto_fetch_title: UserSettings['autoFetchTitle']
|
||||
auto_fetch_thumbnail: UserSettings['autoFetchThumbnail']
|
||||
wiki_editor_mode: UserSettings['wikiEditorMode']
|
||||
}> ('/users/settings')
|
||||
wiki_editor_mode: UserSettings['wikiEditorMode'] }> ('/users/settings')
|
||||
|
||||
return {
|
||||
theme: raw.theme,
|
||||
autoFetchTitle: raw.auto_fetch_title,
|
||||
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']
|
||||
auto_fetch_title: UserSettings['autoFetchTitle']
|
||||
auto_fetch_thumbnail: UserSettings['autoFetchThumbnail']
|
||||
wiki_editor_mode: UserSettings['wikiEditorMode']
|
||||
}> ('/users/settings', settings)
|
||||
wiki_editor_mode: UserSettings['wikiEditorMode'] }> ('/users/settings', settings)
|
||||
|
||||
return {
|
||||
theme: raw.theme,
|
||||
autoFetchTitle: raw.auto_fetch_title,
|
||||
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')
|
||||
{
|
||||
window.dispatchEvent (new CustomEvent (CLIENT_SETTINGS_EVENT, {
|
||||
detail: settings,
|
||||
}))
|
||||
detail: settings }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,10 +419,9 @@ const normaliseKeyboardBindings = (
|
||||
if (!(bindings) || typeof bindings !== 'object')
|
||||
return { }
|
||||
|
||||
return Object.keys (DEFAULT_KEY_BINDINGS).reduce<
|
||||
Partial<Record<ShortcutActionId, KeyBinding | null>>
|
||||
> (
|
||||
(normalised, actionId) => {
|
||||
return (
|
||||
Object.keys (DEFAULT_KEY_BINDINGS)
|
||||
.reduce<Partial<Record<ShortcutActionId, KeyBinding | null>>> ((normalised, actionId) => {
|
||||
const value = (bindings as Record<string, unknown>)[actionId]
|
||||
if (value === null)
|
||||
{
|
||||
@@ -444,9 +434,7 @@ const normaliseKeyboardBindings = (
|
||||
normalised[actionId as ShortcutActionId] = binding
|
||||
|
||||
return normalised
|
||||
},
|
||||
{ },
|
||||
)
|
||||
}, { }))
|
||||
}
|
||||
|
||||
|
||||
@@ -455,8 +443,7 @@ export const getClientKeyboardSettings = (): ClientKeyboardSettings => {
|
||||
|
||||
return {
|
||||
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 = (
|
||||
animation: ClientAnimationMode,
|
||||
): ClientBehaviorSettings =>
|
||||
setClientBehaviorSettings ({
|
||||
...getClientBehaviorSettings (),
|
||||
animation,
|
||||
})
|
||||
setClientBehaviorSettings ({ ...getClientBehaviorSettings (), animation })
|
||||
|
||||
|
||||
export const getClientLinkPreloadMode = (): ClientLinkPreloadMode =>
|
||||
@@ -509,10 +493,7 @@ export const getClientLinkPreloadMode = (): ClientLinkPreloadMode =>
|
||||
export const setClientLinkPreloadMode = (
|
||||
linkPreload: ClientLinkPreloadMode,
|
||||
): ClientBehaviorSettings =>
|
||||
setClientBehaviorSettings ({
|
||||
...getClientBehaviorSettings (),
|
||||
linkPreload,
|
||||
})
|
||||
setClientBehaviorSettings ({ ...getClientBehaviorSettings (), linkPreload })
|
||||
|
||||
|
||||
export const getClientTagRelationDisplayMode = (): ClientTagRelationDisplayMode =>
|
||||
@@ -523,10 +504,7 @@ export const getClientTagRelationDisplayMode = (): ClientTagRelationDisplayMode
|
||||
export const setClientTagRelationDisplayMode = (
|
||||
tagRelationDisplay: ClientTagRelationDisplayMode,
|
||||
): ClientBehaviorSettings =>
|
||||
setClientBehaviorSettings ({
|
||||
...getClientBehaviorSettings (),
|
||||
tagRelationDisplay,
|
||||
})
|
||||
setClientBehaviorSettings ({ ...getClientBehaviorSettings (), tagRelationDisplay })
|
||||
|
||||
|
||||
export const getClientEmbedAutoLoadMode = (): ClientEmbedAutoLoadMode =>
|
||||
@@ -538,10 +516,7 @@ export const getClientEmbedAutoLoadMode = (): ClientEmbedAutoLoadMode =>
|
||||
export const setClientEmbedAutoLoadMode = (
|
||||
embedAutoLoad: ClientEmbedAutoLoadMode,
|
||||
): ClientBehaviorSettings =>
|
||||
setClientBehaviorSettings ({
|
||||
...getClientBehaviorSettings (),
|
||||
embedAutoLoad,
|
||||
})
|
||||
setClientBehaviorSettings ({ ...getClientBehaviorSettings (), embedAutoLoad })
|
||||
|
||||
|
||||
export const getClientThumbnailMode = (): ClientThumbnailMode =>
|
||||
@@ -553,10 +528,7 @@ export const getClientThumbnailMode = (): ClientThumbnailMode =>
|
||||
export const setClientThumbnailMode = (
|
||||
thumbnailMode: ClientThumbnailMode,
|
||||
): ClientBehaviorSettings =>
|
||||
setClientBehaviorSettings ({
|
||||
...getClientBehaviorSettings (),
|
||||
thumbnailMode,
|
||||
})
|
||||
setClientBehaviorSettings ({ ...getClientBehaviorSettings (), thumbnailMode })
|
||||
|
||||
|
||||
export const getClientBehaviorSettings = (): ClientBehaviorSettings => ({
|
||||
@@ -564,8 +536,7 @@ export const getClientBehaviorSettings = (): ClientBehaviorSettings => ({
|
||||
linkPreload: getClientLinkPreloadMode (),
|
||||
tagRelationDisplay: getClientTagRelationDisplayMode (),
|
||||
embedAutoLoad: getClientEmbedAutoLoadMode (),
|
||||
thumbnailMode: getClientThumbnailMode (),
|
||||
})
|
||||
thumbnailMode: getClientThumbnailMode () })
|
||||
|
||||
|
||||
export const setClientBehaviorSettings = (
|
||||
@@ -574,30 +545,21 @@ export const setClientBehaviorSettings = (
|
||||
const next: ClientBehaviorSettings = {
|
||||
animation: (
|
||||
normaliseAnimationMode (behavior.animation)
|
||||
?? getClientAnimationMode ()
|
||||
),
|
||||
?? getClientAnimationMode ()),
|
||||
linkPreload: (
|
||||
normaliseLinkPreloadMode (behavior.linkPreload)
|
||||
?? getClientLinkPreloadMode ()
|
||||
),
|
||||
?? getClientLinkPreloadMode ()),
|
||||
tagRelationDisplay: (
|
||||
normaliseTagRelationDisplayMode (behavior.tagRelationDisplay)
|
||||
?? getClientTagRelationDisplayMode ()
|
||||
),
|
||||
?? getClientTagRelationDisplayMode ()),
|
||||
embedAutoLoad: (
|
||||
normaliseEmbedAutoLoadMode (behavior.embedAutoLoad)
|
||||
?? getClientEmbedAutoLoadMode ()
|
||||
),
|
||||
?? getClientEmbedAutoLoadMode ()),
|
||||
thumbnailMode: (
|
||||
normaliseThumbnailMode (behavior.thumbnailMode)
|
||||
?? getClientThumbnailMode ()
|
||||
),
|
||||
}
|
||||
?? getClientThumbnailMode ()) }
|
||||
|
||||
updateClientSettings (settings => ({
|
||||
...settings,
|
||||
behavior: next,
|
||||
}))
|
||||
updateClientSettings (settings => ({ ...settings, behavior: next }))
|
||||
applyClientAnimationMode (next.animation ?? 'normal')
|
||||
return next
|
||||
}
|
||||
@@ -608,13 +570,9 @@ export const setClientKeyboardSettings = (
|
||||
): ClientKeyboardSettings => {
|
||||
const next: ClientKeyboardSettings = {
|
||||
enabled: keyboard.enabled !== false,
|
||||
bindings: normaliseKeyboardBindings (keyboard.bindings),
|
||||
}
|
||||
bindings: normaliseKeyboardBindings (keyboard.bindings) }
|
||||
|
||||
updateClientSettings (settings => ({
|
||||
...settings,
|
||||
keyboard: next,
|
||||
}))
|
||||
updateClientSettings (settings => ({ ...settings, keyboard: next }))
|
||||
|
||||
return next
|
||||
}
|
||||
@@ -633,8 +591,7 @@ export const getEffectiveKeyBindings = (
|
||||
: DEFAULT_KEY_BINDINGS[key]
|
||||
return effectiveBindings
|
||||
},
|
||||
{ ...DEFAULT_KEY_BINDINGS },
|
||||
)
|
||||
{ ...DEFAULT_KEY_BINDINGS })
|
||||
|
||||
|
||||
export const setClientKeyBinding = (
|
||||
@@ -645,11 +602,7 @@ export const setClientKeyBinding = (
|
||||
|
||||
return setClientKeyboardSettings ({
|
||||
...current,
|
||||
bindings: {
|
||||
...(current.bindings ?? { }),
|
||||
[actionId]: binding,
|
||||
},
|
||||
})
|
||||
bindings: { ...(current.bindings ?? { }), [actionId]: binding } })
|
||||
}
|
||||
|
||||
|
||||
@@ -660,20 +613,14 @@ export const resetClientKeyBinding = (
|
||||
const nextBindings = { ...(current.bindings ?? { }) }
|
||||
delete nextBindings[actionId]
|
||||
|
||||
return setClientKeyboardSettings ({
|
||||
...current,
|
||||
bindings: nextBindings,
|
||||
})
|
||||
return setClientKeyboardSettings ({ ...current, bindings: nextBindings })
|
||||
}
|
||||
|
||||
|
||||
export const resetAllClientKeyBindings = (): ClientKeyboardSettings => {
|
||||
const current = getClientKeyboardSettings ()
|
||||
|
||||
return setClientKeyboardSettings ({
|
||||
...current,
|
||||
bindings: { },
|
||||
})
|
||||
return setClientKeyboardSettings ({ ...current, bindings: { } })
|
||||
}
|
||||
|
||||
|
||||
@@ -696,21 +643,20 @@ const themeCopyName = (baseTheme: BuiltinThemeId): string =>
|
||||
const appearanceFromSettings = (): ClientAppearanceSettings =>
|
||||
loadClientSettings ().appearance ?? { }
|
||||
|
||||
|
||||
let cachedUserThemeSlots: UserThemeSlotMap = { }
|
||||
|
||||
|
||||
export const hasStoredClientThemeSelection = (): boolean => {
|
||||
const appearance = appearanceFromSettings ()
|
||||
return (
|
||||
appearance.themeMode != null
|
||||
return (appearance.themeMode != null
|
||||
|| appearance.activeLightThemeSlotNo != null
|
||||
|| appearance.activeDarkThemeSlotNo != null
|
||||
|| appearance.activeThemeSlot != null
|
||||
|| appearance.activeThemeId != null
|
||||
|| appearance.customThemes != null
|
||||
|| appearance.theme != null
|
||||
|| appearance.tagColours != null
|
||||
)
|
||||
|| appearance.tagColours != null)
|
||||
}
|
||||
|
||||
|
||||
@@ -725,9 +671,7 @@ const mixHexColour = (
|
||||
const channels = [0, 2, 4].map (offset => {
|
||||
const srcValue = parseInt (src.slice (offset, offset + 2), 16)
|
||||
const dstValue = parseInt (dst.slice (offset, offset + 2), 16)
|
||||
const mixed = Math.round (
|
||||
srcValue + (dstValue - srcValue) * clampedRatio,
|
||||
)
|
||||
const mixed = Math.round (srcValue + (dstValue - srcValue) * clampedRatio)
|
||||
return mixed.toString (16).padStart (2, '0')
|
||||
})
|
||||
|
||||
@@ -749,10 +693,8 @@ const legacyThemeSlotSelection = (): ThemeSlotSelection => {
|
||||
return 'builtin:light'
|
||||
if (appearance.activeThemeId === 'dark')
|
||||
return 'builtin:dark'
|
||||
if (
|
||||
typeof appearance.activeThemeId === 'string'
|
||||
&& customThemes[appearance.activeThemeId] != null
|
||||
)
|
||||
if (typeof appearance.activeThemeId === 'string'
|
||||
&& customThemes[appearance.activeThemeId] != null)
|
||||
return `builtin:${ customThemes[appearance.activeThemeId].baseTheme }`
|
||||
if (appearance.theme === 'light')
|
||||
return 'builtin:light'
|
||||
@@ -764,11 +706,9 @@ const legacyThemeSlotSelection = (): ThemeSlotSelection => {
|
||||
|
||||
const legacyThemeMode = (): ClientThemeMode =>
|
||||
normaliseThemeMode (appearanceFromSettings ().theme)
|
||||
?? (
|
||||
legacyThemeSlotSelection () === 'system'
|
||||
?? (legacyThemeSlotSelection () === 'system'
|
||||
? 'system'
|
||||
: (baseThemeOfSelection (legacyThemeSlotSelection ()) ?? 'system')
|
||||
)
|
||||
: (baseThemeOfSelection (legacyThemeSlotSelection ()) ?? 'system'))
|
||||
|
||||
|
||||
const legacyThemeSlotNoFor = (
|
||||
@@ -778,7 +718,7 @@ const legacyThemeSlotNoFor = (
|
||||
|
||||
if (isUserThemeSlotSelection (legacySelection))
|
||||
{
|
||||
const [_, selectionBaseTheme, slotNo] = legacySelection.split (':')
|
||||
const [, selectionBaseTheme, slotNo] = legacySelection.split (':')
|
||||
if (selectionBaseTheme === baseTheme)
|
||||
return Number (slotNo) as UserThemeSlotNo
|
||||
}
|
||||
@@ -798,20 +738,12 @@ export const setClientActiveThemeSlot = (
|
||||
): void => {
|
||||
updateClientSettings (settings => ({
|
||||
...settings,
|
||||
appearance: {
|
||||
...(settings.appearance ?? { }),
|
||||
activeThemeSlot,
|
||||
},
|
||||
}))
|
||||
appearance: { ...(settings.appearance ?? { }), activeThemeSlot } }))
|
||||
}
|
||||
|
||||
|
||||
export const getClientThemeMode = (): UserSettings['theme'] => {
|
||||
return (
|
||||
normaliseThemeMode (appearanceFromSettings ().themeMode)
|
||||
?? legacyThemeMode ()
|
||||
)
|
||||
}
|
||||
export const getClientThemeMode = (): UserSettings['theme'] =>
|
||||
normaliseThemeMode (appearanceFromSettings ().themeMode) ?? legacyThemeMode ()
|
||||
|
||||
|
||||
export const getClientActiveLightThemeSlotNo = (): UserThemeSlotNo =>
|
||||
@@ -827,8 +759,7 @@ export const getClientActiveDarkThemeSlotNo = (): UserThemeSlotNo =>
|
||||
export const setClientThemeAppearance = (
|
||||
{ themeMode,
|
||||
activeLightThemeSlotNo,
|
||||
activeDarkThemeSlotNo }: {
|
||||
themeMode: ClientThemeMode
|
||||
activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
|
||||
activeLightThemeSlotNo: UserThemeSlotNo
|
||||
activeDarkThemeSlotNo: UserThemeSlotNo },
|
||||
): void => {
|
||||
@@ -838,9 +769,7 @@ export const setClientThemeAppearance = (
|
||||
...(settings.appearance ?? { }),
|
||||
themeMode,
|
||||
activeLightThemeSlotNo,
|
||||
activeDarkThemeSlotNo,
|
||||
},
|
||||
}))
|
||||
activeDarkThemeSlotNo } }))
|
||||
}
|
||||
|
||||
|
||||
@@ -848,8 +777,7 @@ export const setClientThemeMode = (theme: UserSettings['theme']): void => {
|
||||
setClientThemeAppearance ({
|
||||
themeMode: theme,
|
||||
activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (),
|
||||
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo (),
|
||||
})
|
||||
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo () })
|
||||
}
|
||||
|
||||
|
||||
@@ -894,8 +822,7 @@ const normaliseThemeTagColours = (
|
||||
: fallback[category]
|
||||
return colours
|
||||
},
|
||||
{ ...fallback },
|
||||
)
|
||||
{ ...fallback })
|
||||
|
||||
|
||||
const normaliseThemeTokenValue = (
|
||||
@@ -916,9 +843,9 @@ const cssColourFromThemeToken = (
|
||||
if (normalisedHex != null)
|
||||
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 })`
|
||||
: fallback
|
||||
: fallback)
|
||||
}
|
||||
|
||||
|
||||
@@ -928,16 +855,12 @@ const clamp = (value: number, min: number, max: number): number =>
|
||||
|
||||
const parseHslComponents = (
|
||||
value: string,
|
||||
): { h: number
|
||||
s: number
|
||||
l: number } | null => {
|
||||
const raw = value.startsWith ('hsl(') && value.endsWith (')')
|
||||
): { h: number; s: number; l: number } | null => {
|
||||
const raw = (value.startsWith ('hsl(') && value.endsWith (')')
|
||||
? value.slice (4, -1)
|
||||
: value
|
||||
: value)
|
||||
const match =
|
||||
raw.match (
|
||||
/^(-?\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)%\s+(\d+(?:\.\d+)?)%$/,
|
||||
)
|
||||
raw.match (/^(-?\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)%\s+(\d+(?:\.\d+)?)%$/)
|
||||
|
||||
if (match == null)
|
||||
return null
|
||||
@@ -945,8 +868,7 @@ const parseHslComponents = (
|
||||
return {
|
||||
h: Number (match[1]),
|
||||
s: Number (match[2]),
|
||||
l: Number (match[3]),
|
||||
}
|
||||
l: Number (match[3]) }
|
||||
}
|
||||
|
||||
|
||||
@@ -987,9 +909,7 @@ const deriveMutedForeground = (
|
||||
l: clamp (
|
||||
foregroundHsl.l + (backgroundHsl.l - foregroundHsl.l) * .35,
|
||||
0,
|
||||
100,
|
||||
),
|
||||
})
|
||||
100) })
|
||||
}
|
||||
|
||||
|
||||
@@ -1006,11 +926,9 @@ const deriveTopNavTokens = (
|
||||
mobileActiveBackground: (
|
||||
baseTheme === 'dark'
|
||||
? editable.activeBackground
|
||||
: editable.rootBackgroundDesktop
|
||||
),
|
||||
: editable.rootBackgroundDesktop),
|
||||
brandLink: editable.brandLink,
|
||||
menuLink: link,
|
||||
})
|
||||
menuLink: link })
|
||||
|
||||
|
||||
export const buildThemeTokens = (
|
||||
@@ -1047,41 +965,33 @@ export const buildThemeTokens = (
|
||||
tokens.topNavRootBackgroundMobile
|
||||
?? legacyTopNavTokens.topNavRootBackgroundMobile
|
||||
?? legacyTopNavTokens.topNavBackground,
|
||||
fallback.topNavRootBackgroundMobile,
|
||||
)
|
||||
fallback.topNavRootBackgroundMobile)
|
||||
const topNavRootBackgroundDesktop = normaliseTopNavColourValue (
|
||||
tokens.topNavRootBackgroundDesktop
|
||||
?? legacyTopNavTokens.topNavRootBackgroundDesktop
|
||||
?? legacyTopNavTokens.topNavBackground,
|
||||
fallback.topNavRootBackgroundDesktop,
|
||||
)
|
||||
fallback.topNavRootBackgroundDesktop)
|
||||
const topNavActiveBackground = normaliseTopNavColourValue (
|
||||
tokens.topNavActiveBackground
|
||||
?? legacyTopNavTokens.topNavActiveBackground
|
||||
?? legacyTopNavTokens.topNavHighlightBackground
|
||||
?? legacyTopNavTokens.topNavHighlightBackground,
|
||||
fallback.topNavActiveBackground,
|
||||
)
|
||||
fallback.topNavActiveBackground)
|
||||
const topNavBrandLink = normaliseTopNavColourValue (
|
||||
tokens.topNavBrandLink
|
||||
?? legacyTopNavTokens.topNavBrandLink
|
||||
?? legacyTopNavTokens.topNavLink,
|
||||
fallback.topNavBrandLink,
|
||||
)
|
||||
fallback.topNavBrandLink)
|
||||
const topNavMenuLink = cssColourFromThemeToken (
|
||||
link,
|
||||
fallback.topNavMenuLink,
|
||||
)
|
||||
fallback.topNavMenuLink)
|
||||
const resolvedTopNav = deriveTopNavTokens (
|
||||
baseTheme,
|
||||
{
|
||||
rootBackgroundMobile: topNavRootBackgroundMobile,
|
||||
{ rootBackgroundMobile: topNavRootBackgroundMobile,
|
||||
rootBackgroundDesktop: topNavRootBackgroundDesktop,
|
||||
activeBackground: topNavActiveBackground,
|
||||
brandLink: topNavBrandLink,
|
||||
},
|
||||
topNavMenuLink,
|
||||
)
|
||||
brandLink: topNavBrandLink },
|
||||
topNavMenuLink)
|
||||
|
||||
return {
|
||||
background,
|
||||
@@ -1098,8 +1008,7 @@ export const buildThemeTokens = (
|
||||
mutedForeground: deriveMutedForeground (
|
||||
foreground,
|
||||
background,
|
||||
fallback.mutedForeground,
|
||||
),
|
||||
fallback.mutedForeground),
|
||||
accent,
|
||||
accentForeground: readableForegroundFor (accent),
|
||||
destructive: fallback.destructive,
|
||||
@@ -1115,17 +1024,14 @@ export const buildThemeTokens = (
|
||||
topNavMobileActiveBackground: resolvedTopNav.mobileActiveBackground,
|
||||
topNavBrandLink: resolvedTopNav.brandLink,
|
||||
topNavMenuLink: resolvedTopNav.menuLink,
|
||||
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours),
|
||||
}
|
||||
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours) }
|
||||
}
|
||||
|
||||
|
||||
const normaliseTopNavColourValue = (
|
||||
value: unknown,
|
||||
fallback: string,
|
||||
): string => {
|
||||
return cssColourFromThemeToken (value, fallback)
|
||||
}
|
||||
): string => cssColourFromThemeToken (value, fallback)
|
||||
|
||||
|
||||
const normaliseThemeTokens = (
|
||||
@@ -1161,8 +1067,7 @@ const normaliseThemeTokens = (
|
||||
|
||||
return buildThemeTokens (
|
||||
baseTheme,
|
||||
{
|
||||
...tokens,
|
||||
{ ...tokens,
|
||||
background,
|
||||
foreground,
|
||||
card,
|
||||
@@ -1191,9 +1096,7 @@ const normaliseThemeTokens = (
|
||||
tokens.topNavMenuLink
|
||||
?? legacyTopNavTokens.topNavMenuLink
|
||||
?? link,
|
||||
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours),
|
||||
},
|
||||
)
|
||||
tagColours: normaliseThemeTagColours (tokens.tagColours, fallback.tagColours) })
|
||||
}
|
||||
|
||||
|
||||
@@ -1252,8 +1155,7 @@ export const getDefaultUserThemeSlot = (
|
||||
): UserThemeSlot => ({
|
||||
baseTheme,
|
||||
slotNo,
|
||||
tokens: normaliseThemeTokens ({ }, baseTheme),
|
||||
})
|
||||
tokens: normaliseThemeTokens ({ }, baseTheme) })
|
||||
|
||||
|
||||
const normaliseUserThemeSlot = (
|
||||
@@ -1266,12 +1168,10 @@ const normaliseUserThemeSlot = (
|
||||
const baseTheme = slot.baseTheme
|
||||
const slotNo = slot.slotNo
|
||||
|
||||
if (
|
||||
baseTheme == null
|
||||
if (baseTheme == null
|
||||
|| !(isBuiltinThemeId (baseTheme))
|
||||
|| slotNo == null
|
||||
|| !(USER_THEME_SLOT_NOS.includes (slotNo as UserThemeSlotNo))
|
||||
)
|
||||
|| !(USER_THEME_SLOT_NOS.includes (slotNo as UserThemeSlotNo)))
|
||||
return null
|
||||
|
||||
return {
|
||||
@@ -1279,8 +1179,7 @@ const normaliseUserThemeSlot = (
|
||||
slotNo: slotNo as UserThemeSlotNo,
|
||||
tokens: normaliseThemeTokens (slot.tokens, baseTheme),
|
||||
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
|
||||
return map
|
||||
},
|
||||
{ },
|
||||
)
|
||||
{ })
|
||||
|
||||
|
||||
const normaliseCustomTheme = (
|
||||
@@ -1306,13 +1204,11 @@ const normaliseCustomTheme = (
|
||||
const id = typeof theme.id === 'string' ? theme.id : null
|
||||
const name = typeof theme.name === 'string' ? theme.name : null
|
||||
const baseTheme = typeof theme.baseTheme === 'string' ? theme.baseTheme : null
|
||||
if (
|
||||
id == null
|
||||
if (id == null
|
||||
|| !(isCustomThemeId (id))
|
||||
|| name == null
|
||||
|| baseTheme == null
|
||||
|| !(isBuiltinThemeId (baseTheme))
|
||||
)
|
||||
|| !(isBuiltinThemeId (baseTheme)))
|
||||
return null
|
||||
|
||||
return {
|
||||
@@ -1321,21 +1217,16 @@ const normaliseCustomTheme = (
|
||||
builtin: false,
|
||||
baseTheme,
|
||||
tokens: normaliseThemeTokens (
|
||||
{
|
||||
...(theme.tokens && typeof theme.tokens === 'object'
|
||||
{ ...(theme.tokens && typeof theme.tokens === 'object'
|
||||
? theme.tokens as Partial<ThemeTokens>
|
||||
: { }),
|
||||
tagColours: (
|
||||
theme.tokens
|
||||
(theme.tokens
|
||||
&& typeof theme.tokens === 'object'
|
||||
&& 'tagColours' in theme.tokens
|
||||
)
|
||||
&& 'tagColours' in theme.tokens)
|
||||
? (theme.tokens as { tagColours?: Partial<ThemeTagColours> }).tagColours
|
||||
: theme.tagColours as Partial<ThemeTagColours> | undefined,
|
||||
},
|
||||
baseTheme,
|
||||
),
|
||||
}
|
||||
: theme.tagColours as Partial<ThemeTagColours> | undefined) },
|
||||
baseTheme) }
|
||||
}
|
||||
|
||||
|
||||
@@ -1352,8 +1243,7 @@ export const getClientCustomThemes = (): Record<string, CustomClientTheme> => {
|
||||
themes[key] = theme
|
||||
return themes
|
||||
},
|
||||
{ },
|
||||
)
|
||||
{ })
|
||||
}
|
||||
|
||||
const legacyTagColours = appearance.tagColours
|
||||
@@ -1371,11 +1261,7 @@ export const getClientCustomThemes = (): Record<string, CustomClientTheme> => {
|
||||
...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS),
|
||||
tagColours: normaliseThemeTagColours (
|
||||
legacyTagColours,
|
||||
defaultThemeTagColoursFor (baseTheme),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
defaultThemeTagColoursFor (baseTheme)) } } }
|
||||
}
|
||||
|
||||
|
||||
@@ -1385,12 +1271,11 @@ export const fetchUserThemeSlots = async (): Promise<UserThemeSlot[]> => {
|
||||
slotNo: UserThemeSlotNo
|
||||
tokens: ThemeTokens
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}>> ('/users/theme_slots')
|
||||
updatedAt: string }>> ('/users/theme_slots')
|
||||
|
||||
return raw
|
||||
return (raw
|
||||
.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
|
||||
tokens: ThemeTokens
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}> (`/users/theme_slots/${ baseTheme }/${ slotNo }`, { tokens })
|
||||
updatedAt: string }> (`/users/theme_slots/${ baseTheme }/${ slotNo }`, { tokens })
|
||||
const slot = normaliseUserThemeSlot (raw)
|
||||
|
||||
if (slot == null)
|
||||
@@ -1427,8 +1311,7 @@ export const getCachedUserThemeSlots = (): UserThemeSlotMap =>
|
||||
export const resolveDisplayedTheme = (
|
||||
{ themeMode,
|
||||
activeLightThemeSlotNo,
|
||||
activeDarkThemeSlotNo }: {
|
||||
themeMode: ClientThemeMode
|
||||
activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
|
||||
activeLightThemeSlotNo: UserThemeSlotNo
|
||||
activeDarkThemeSlotNo: UserThemeSlotNo },
|
||||
themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (),
|
||||
@@ -1445,9 +1328,8 @@ export const resolveDisplayedTheme = (
|
||||
baseTheme,
|
||||
selection,
|
||||
slotNo,
|
||||
tokens: themeSlots[selection]?.tokens
|
||||
?? getDefaultUserThemeSlot (baseTheme, slotNo).tokens,
|
||||
}
|
||||
tokens: (themeSlots[selection]?.tokens
|
||||
?? getDefaultUserThemeSlot (baseTheme, slotNo).tokens) }
|
||||
}
|
||||
|
||||
|
||||
@@ -1464,28 +1346,21 @@ export const getClientActiveThemeId = (): ActiveThemeId => {
|
||||
const appearance = appearanceFromSettings ()
|
||||
const customThemes = getClientCustomThemes ()
|
||||
|
||||
if (
|
||||
typeof appearance.activeThemeId === 'string'
|
||||
&& (
|
||||
appearance.activeThemeId === 'system'
|
||||
if (typeof appearance.activeThemeId === 'string'
|
||||
&& (appearance.activeThemeId === 'system'
|
||||
|| isBuiltinThemeId (appearance.activeThemeId)
|
||||
|| customThemes[appearance.activeThemeId] != null
|
||||
)
|
||||
)
|
||||
|| customThemes[appearance.activeThemeId] != null))
|
||||
return appearance.activeThemeId
|
||||
|
||||
if (
|
||||
Object.keys (customThemes).length > 0
|
||||
if (Object.keys (customThemes).length > 0
|
||||
&& appearance.tagColours != null
|
||||
&& Object.keys (appearance.tagColours).length > 0
|
||||
)
|
||||
&& Object.keys (appearance.tagColours).length > 0)
|
||||
return LEGACY_MIGRATED_THEME_ID
|
||||
|
||||
if (typeof appearance.theme === 'string' && (
|
||||
appearance.theme === 'system'
|
||||
if (typeof appearance.theme === 'string'
|
||||
&& (appearance.theme === 'system'
|
||||
|| appearance.theme === 'light'
|
||||
|| appearance.theme === 'dark'
|
||||
))
|
||||
|| appearance.theme === 'dark'))
|
||||
return appearance.theme
|
||||
|
||||
return DEFAULT_USER_SETTINGS.theme
|
||||
@@ -1532,8 +1407,7 @@ export const resolveThemeFromSlotSelection = (
|
||||
selection,
|
||||
baseTheme,
|
||||
builtin: true,
|
||||
tokens: BUILTIN_THEMES[baseTheme].tokens,
|
||||
}
|
||||
tokens: BUILTIN_THEMES[baseTheme].tokens }
|
||||
}
|
||||
|
||||
if (isBuiltinThemeSlotSelection (selection))
|
||||
@@ -1543,8 +1417,7 @@ export const resolveThemeFromSlotSelection = (
|
||||
selection,
|
||||
baseTheme,
|
||||
builtin: true,
|
||||
tokens: BUILTIN_THEMES[baseTheme].tokens,
|
||||
}
|
||||
tokens: BUILTIN_THEMES[baseTheme].tokens }
|
||||
}
|
||||
|
||||
const baseTheme = baseThemeOfSelection (selection) ?? 'light'
|
||||
@@ -1552,9 +1425,8 @@ export const resolveThemeFromSlotSelection = (
|
||||
selection,
|
||||
baseTheme,
|
||||
builtin: false,
|
||||
tokens: themeSlots[selection]?.tokens
|
||||
?? getDefaultUserThemeSlot (baseTheme, slotNoOfSelection (selection)).tokens,
|
||||
}
|
||||
tokens: (themeSlots[selection]?.tokens
|
||||
?? getDefaultUserThemeSlot (baseTheme, slotNoOfSelection (selection)).tokens) }
|
||||
}
|
||||
|
||||
|
||||
@@ -1576,21 +1448,15 @@ export const createCustomThemeFromBase = (
|
||||
name: themeCopyName (baseTheme),
|
||||
builtin: false,
|
||||
baseTheme,
|
||||
tokens: tokens ?? {
|
||||
...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS),
|
||||
},
|
||||
}
|
||||
tokens: tokens ?? { ...(baseTheme === 'dark' ? DARK_THEME_TOKENS : LIGHT_THEME_TOKENS) } }
|
||||
}
|
||||
|
||||
|
||||
export const getClientThemeTagColours = (
|
||||
activeThemeId: ActiveThemeId = getClientActiveThemeId (),
|
||||
customThemes: Record<string, CustomClientTheme> = getClientCustomThemes (),
|
||||
): ThemeTagColours => {
|
||||
return {
|
||||
...getResolvedThemeFromSelection (activeThemeId, customThemes).tokens.tagColours,
|
||||
}
|
||||
}
|
||||
): ThemeTagColours =>
|
||||
({ ...getResolvedThemeFromSelection (activeThemeId, customThemes).tokens.tagColours })
|
||||
|
||||
|
||||
export const setClientAppearanceThemes = (
|
||||
@@ -1604,9 +1470,7 @@ export const setClientAppearanceThemes = (
|
||||
activeThemeId,
|
||||
customThemes,
|
||||
theme: undefined,
|
||||
tagColours: undefined,
|
||||
},
|
||||
}))
|
||||
tagColours: undefined } }))
|
||||
}
|
||||
|
||||
|
||||
@@ -1620,17 +1484,10 @@ export const setClientThemeTagColours = (
|
||||
|
||||
setClientAppearanceThemes (
|
||||
activeThemeId,
|
||||
{
|
||||
...customThemes,
|
||||
{ ...customThemes,
|
||||
[activeThemeId]: {
|
||||
...customThemes[activeThemeId],
|
||||
tokens: {
|
||||
...customThemes[activeThemeId].tokens,
|
||||
tagColours,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
tokens: { ...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 }-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 ('--ring', tokens.ring)
|
||||
root.style.setProperty ('--theme-link', tokens.link)
|
||||
root.style.setProperty (
|
||||
'--top-nav-root-bg-mobile',
|
||||
tokens.topNavRootBackgroundMobile,
|
||||
)
|
||||
root.style.setProperty (
|
||||
'--top-nav-root-bg-desktop',
|
||||
tokens.topNavRootBackgroundDesktop,
|
||||
)
|
||||
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,
|
||||
)
|
||||
root.style.setProperty ('--top-nav-root-bg-mobile', tokens.topNavRootBackgroundMobile)
|
||||
root.style.setProperty ('--top-nav-root-bg-desktop', tokens.topNavRootBackgroundDesktop)
|
||||
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)
|
||||
}
|
||||
@@ -1730,17 +1562,14 @@ export const applyThemeSelection = (
|
||||
export const applyThemeAppearanceState = (
|
||||
{ themeMode,
|
||||
activeLightThemeSlotNo,
|
||||
activeDarkThemeSlotNo }: {
|
||||
themeMode: ClientThemeMode
|
||||
activeDarkThemeSlotNo }: { themeMode: ClientThemeMode
|
||||
activeLightThemeSlotNo: UserThemeSlotNo
|
||||
activeDarkThemeSlotNo: UserThemeSlotNo },
|
||||
themeSlots: UserThemeSlotMap = getCachedUserThemeSlots (),
|
||||
): void => {
|
||||
const theme = resolveDisplayedTheme ({
|
||||
themeMode,
|
||||
const theme = resolveDisplayedTheme ({ themeMode,
|
||||
activeLightThemeSlotNo,
|
||||
activeDarkThemeSlotNo,
|
||||
}, themeSlots)
|
||||
activeDarkThemeSlotNo }, themeSlots)
|
||||
|
||||
applyThemeMode (theme.baseTheme)
|
||||
applyThemeTokens (theme.tokens)
|
||||
@@ -1753,8 +1582,7 @@ export const applyClientAppearance = (
|
||||
applyThemeAppearanceState ({
|
||||
themeMode: getClientThemeMode (),
|
||||
activeLightThemeSlotNo: getClientActiveLightThemeSlotNo (),
|
||||
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo (),
|
||||
}, themeSlots)
|
||||
activeDarkThemeSlotNo: getClientActiveDarkThemeSlotNo () }, themeSlots)
|
||||
}
|
||||
|
||||
|
||||
@@ -1786,20 +1614,17 @@ export const setClientListSettings = (
|
||||
[listKey]: {
|
||||
...(settings.lists?.[listKey] ?? { }),
|
||||
...(limit == null ? { } : { limit }),
|
||||
...(order == null ? { } : { order }),
|
||||
},
|
||||
},
|
||||
}))
|
||||
...(order == null ? { } : { order }) } } }))
|
||||
}
|
||||
|
||||
|
||||
const legacyTheatreLayoutMode = (): TheatreLayoutMode | null => {
|
||||
const value = localStorage.getItem (LEGACY_THEATRE_LAYOUT_STORAGE_KEY)
|
||||
return (
|
||||
value === 'threeColumns'
|
||||
return ((value === 'threeColumns'
|
||||
|| value === 'tagsBottom'
|
||||
|| value === 'commentsBottom'
|
||||
) ? value : null
|
||||
|| value === 'commentsBottom')
|
||||
? value
|
||||
: null)
|
||||
}
|
||||
|
||||
|
||||
@@ -1841,8 +1666,7 @@ export const setClientTheatreTagFlow = (tagFlow: TheatreTagFlow): void => {
|
||||
}
|
||||
|
||||
|
||||
export const getClientGekanatorBackgroundMotion =
|
||||
(): GekanatorBackgroundMotionMode =>
|
||||
export const getClientGekanatorBackgroundMotion = (): GekanatorBackgroundMotionMode =>
|
||||
loadClientSettings ().gekanator?.backgroundMotion
|
||||
?? legacyGekanatorBackgroundMotion ()
|
||||
?? 'on'
|
||||
@@ -1853,8 +1677,7 @@ export const setClientGekanatorBackgroundMotion = (
|
||||
): void => {
|
||||
updateClientSettings (settings => ({
|
||||
...settings,
|
||||
gekanator: { ...(settings.gekanator ?? { }), backgroundMotion },
|
||||
}))
|
||||
gekanator: { ...(settings.gekanator ?? { }), backgroundMotion } }))
|
||||
}
|
||||
|
||||
|
||||
@@ -1880,11 +1703,7 @@ export const setClientPaneWidthPx = (
|
||||
...(settings.panes?.[paneKey] ?? { }),
|
||||
widthPxByBreakpoint: {
|
||||
...(settings.panes?.[paneKey]?.widthPxByBreakpoint ?? { }),
|
||||
[breakpoint]: widthPx,
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
[breakpoint]: widthPx } } } }))
|
||||
}
|
||||
|
||||
|
||||
@@ -1898,8 +1717,5 @@ export const setClientPaneCollapsed = (
|
||||
...(settings.panes ?? { }),
|
||||
[paneKey]: {
|
||||
...(settings.panes?.[paneKey] ?? { }),
|
||||
collapsed,
|
||||
},
|
||||
},
|
||||
}))
|
||||
collapsed } } }))
|
||||
}
|
||||
|
||||
@@ -1445,4 +1445,5 @@ const SettingPage: FC<Props> = ({ user, setUser }) => {
|
||||
</MainArea>)
|
||||
}
|
||||
|
||||
|
||||
export default SettingPage
|
||||
|
||||
新しい課題から参照
ユーザをブロックする