このコミットが含まれているのは:
@@ -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 } } }))
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1445,4 +1445,5 @@ const SettingPage: FC<Props> = ({ user, setUser }) => {
|
|||||||
</MainArea>)
|
</MainArea>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default SettingPage
|
export default SettingPage
|
||||||
|
|||||||
新しい課題から参照
ユーザをブロックする