Path: blob/master/src/packages/frontend/account/account-preferences-appearance.tsx
2209 views
/*1* This file is part of CoCalc: Copyright © 2025 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Button, Card, Slider } from "antd";6import { debounce } from "lodash";7import { ReactElement, useMemo } from "react";8import { FormattedMessage, defineMessages, useIntl } from "react-intl";910import { Panel, Switch } from "@cocalc/frontend/antd-bootstrap";11import { redux, useTypedRedux } from "@cocalc/frontend/app-framework";12import {13A,14HelpIcon,15Icon,16IconName,17LabeledRow,18} from "@cocalc/frontend/components";19import { labels } from "@cocalc/frontend/i18n";20import { DARK_MODE_ICON } from "@cocalc/util/consts/ui";21import { DARK_MODE_DEFAULTS } from "@cocalc/util/db-schema/accounts";22import { COLORS } from "@cocalc/util/theme";23import {24DARK_MODE_KEYS,25DARK_MODE_MINS,26get_dark_mode_config,27} from "./dark-mode";28import { EditorSettingsColorScheme } from "./editor-settings/color-schemes";29import { I18NSelector, I18N_MESSAGE, I18N_TITLE } from "./i18n-selector";30import { OtherSettings } from "./other-settings";31import { TerminalSettings } from "./terminal-settings";3233// Icon constant for account preferences section34export const APPEARANCE_ICON_NAME: IconName = "eye";3536// See https://github.com/sagemathinc/cocalc/issues/562037// There are weird bugs with relying only on mathjax, whereas our38// implementation of katex with a fallback to mathjax works very well.39// This makes it so katex can't be disabled.40const ALLOW_DISABLE_KATEX = false;4142export function katexIsEnabled() {43if (!ALLOW_DISABLE_KATEX) {44return true;45}46return redux.getStore("account")?.getIn(["other_settings", "katex"]) ?? true;47}4849const DARK_MODE_LABELS = defineMessages({50brightness: {51id: "account.other-settings.theme.dark_mode.brightness",52defaultMessage: "Brightness",53},54contrast: {55id: "account.other-settings.theme.dark_mode.contrast",56defaultMessage: "Contrast",57},58sepia: {59id: "account.other-settings.theme.dark_mode.sepia",60defaultMessage: "Sepia",61},62});6364export function AccountPreferencesAppearance() {65const intl = useIntl();66const other_settings = useTypedRedux("account", "other_settings");67const editor_settings = useTypedRedux("account", "editor_settings");68const font_size = useTypedRedux("account", "font_size");69const stripe_customer = useTypedRedux("account", "stripe_customer");70const kucalc = useTypedRedux("customize", "kucalc");7172function on_change(name: string, value: any): void {73redux.getActions("account").set_other_settings(name, value);74}7576function on_change_editor_settings(name: string, value: any): void {77redux.getActions("account").set_editor_settings(name, value);78}7980// Debounced version for dark mode sliders to reduce CPU usage81const on_change_dark_mode = useMemo(82() =>83debounce((name: string, value: any) => on_change(name, value), 50, {84trailing: true,85leading: false,86}),87[],88);8990function render_katex() {91if (!ALLOW_DISABLE_KATEX) {92return null;93}94return (95<Switch96checked={!!other_settings.get("katex")}97onChange={(e) => on_change("katex", e.target.checked)}98>99<FormattedMessage100id="account.other-settings.katex"101defaultMessage={`<strong>KaTeX:</strong> attempt to render formulas102using {katex} (much faster, but missing context menu options)`}103values={{ katex: <A href={"https://katex.org/"}>KaTeX</A> }}104/>105</Switch>106);107}108109function renderDarkModePanel(): ReactElement {110const checked = !!other_settings.get("dark_mode");111const config = get_dark_mode_config(other_settings.toJS());112return (113<Panel114size="small"115header={116<>117<Icon unicode={DARK_MODE_ICON} /> Dark Mode118</>119}120styles={{121header: {122color: COLORS.GRAY_LLL,123backgroundColor: COLORS.GRAY_DD,124},125body: {126color: COLORS.GRAY_LLL,127backgroundColor: COLORS.GRAY_D,128},129}}130>131<div>132<Switch133checked={checked}134onChange={(e) => on_change("dark_mode", e.target.checked)}135labelStyle={{ color: COLORS.GRAY_LLL }}136>137<FormattedMessage138id="account.other-settings.theme.dark_mode.compact"139defaultMessage={`Dark mode: reduce eye strain by showing a dark background (via {DR})`}140values={{141DR: (142<A143style={{ color: "#e96c4d", fontWeight: 700 }}144href="https://darkreader.org/"145>146DARK READER147</A>148),149}}150/>151</Switch>152{checked ? (153<Card154size="small"155title={156<>157<Icon unicode={DARK_MODE_ICON} />{" "}158{intl.formatMessage({159id: "account.other-settings.theme.dark_mode.configuration",160defaultMessage: "Dark Mode Configuration",161})}162</>163}164>165<div style={{ display: "flex", flexDirection: "column", gap: 5 }}>166{DARK_MODE_KEYS.map((key) => (167<div168key={key}169style={{ display: "flex", gap: 10, alignItems: "center" }}170>171<div style={{ width: 100 }}>172{intl.formatMessage(DARK_MODE_LABELS[key])}173</div>174<Slider175min={DARK_MODE_MINS[key]}176max={100}177value={config[key]}178onChange={(x) =>179on_change_dark_mode(`dark_mode_${key}`, x)180}181marks={{182[DARK_MODE_DEFAULTS[key]]: String(183DARK_MODE_DEFAULTS[key],184),185}}186style={{ flex: 1, width: 0 }}187/>188<Button189size="small"190style={{ marginLeft: "20px" }}191onClick={() =>192on_change_dark_mode(193`dark_mode_${key}`,194DARK_MODE_DEFAULTS[key],195)196}197>198{intl.formatMessage(labels.reset)}199</Button>200</div>201))}202</div>203</Card>204) : undefined}205</div>206</Panel>207);208}209210function renderUserInterfacePanel(): ReactElement {211return (212<Panel213size="small"214header={215<>216<Icon name="desktop" />{" "}217<FormattedMessage218id="account.appearance.user_interface.title"219defaultMessage="User Interface"220/>221</>222}223>224<LabeledRow225label={226<>227<Icon name="translation-outlined" />{" "}228{intl.formatMessage(labels.language)}229</>230}231>232<div>233<I18NSelector />{" "}234<HelpIcon title={intl.formatMessage(I18N_TITLE)}>235{intl.formatMessage(I18N_MESSAGE)}236</HelpIcon>237</div>238</LabeledRow>239<Switch240checked={!!other_settings.get("hide_file_popovers")}241onChange={(e) => on_change("hide_file_popovers", e.target.checked)}242>243<FormattedMessage244id="account.other-settings.file_popovers"245defaultMessage={`<strong>Hide File Tab Popovers:</strong>246do not show the popovers over file tabs`}247/>248</Switch>249<Switch250checked={!!other_settings.get("hide_project_popovers")}251onChange={(e) => on_change("hide_project_popovers", e.target.checked)}252>253<FormattedMessage254id="account.other-settings.project_popovers"255defaultMessage={`<strong>Hide Project Tab Popovers:</strong>256do not show the popovers over the project tabs`}257/>258</Switch>259<Switch260checked={!!other_settings.get("hide_button_tooltips")}261onChange={(e) => on_change("hide_button_tooltips", e.target.checked)}262>263<FormattedMessage264id="account.other-settings.button_tooltips"265defaultMessage={`<strong>Hide Button Tooltips:</strong>266hides some button tooltips (this is only partial)`}267/>268</Switch>269<Switch270checked={!!other_settings.get("time_ago_absolute")}271onChange={(e) => on_change("time_ago_absolute", e.target.checked)}272>273<FormattedMessage274id="account.other-settings.time_ago_absolute"275defaultMessage={`<strong>Display Timestamps as absolute points in time</strong>276instead of relative to the current time`}277/>278</Switch>279<Switch280checked={!!other_settings.get("hide_navbar_balance")}281onChange={(e) => on_change("hide_navbar_balance", e.target.checked)}282>283<FormattedMessage284id="account.other-settings.hide_navbar_balance"285defaultMessage={`<strong>Hide Account Balance</strong> in navigation bar`}286/>287</Switch>288{render_katex()}289</Panel>290);291}292293return (294<>295{renderUserInterfacePanel()}296<OtherSettings297other_settings={other_settings}298is_stripe_customer={299!!stripe_customer?.getIn(["subscriptions", "total_count"])300}301kucalc={kucalc}302mode="appearance"303/>304{renderDarkModePanel()}305<EditorSettingsColorScheme306size="small"307theme={editor_settings?.get("theme") ?? "default"}308on_change={(value) => on_change_editor_settings("theme", value)}309editor_settings={editor_settings}310font_size={font_size}311/>312<TerminalSettings />313</>314);315}316317318