Add support for system theme (#245)

* Add support for 'system' theme value

* Add ability to set theme to 'system' in settings

* Fix tsc errors for Theme
This commit is contained in:
Frank Noirot
2023-08-09 13:57:07 -04:00
committed by GitHub
parent 8ebb8b8b94
commit 968a67e654
7 changed files with 56 additions and 35 deletions

View File

@ -19,7 +19,7 @@ import {
lineHighlightField,
addLineHighlight,
} from './editor/highlightextension'
import { PaneType, Selections, useStore } from './useStore'
import { PaneType, Selections, Themes, useStore } from './useStore'
import { Logs, KCLErrors } from './components/Logs'
import { CollapsiblePanel } from './components/CollapsiblePanel'
import { MemoryPanel } from './components/MemoryPanel'
@ -42,6 +42,7 @@ import {
import { useHotkeys } from 'react-hotkeys-hook'
import { TEST } from './env'
import { getNormalisedCoordinates } from './lib/utils'
import { getSystemTheme } from './lib/getSystemTheme'
export function App() {
const streamRef = useRef<HTMLDivElement>(null)
@ -127,6 +128,8 @@ export function App() {
streamDimensions: s.streamDimensions,
}))
const editorTheme = theme === Themes.System ? getSystemTheme() : theme
// Pane toggling keyboard shortcuts
const togglePane = useCallback(
(newPane: PaneType) =>
@ -460,26 +463,26 @@ export function App() {
]}
onChange={onChange}
onUpdate={onUpdate}
theme={theme}
theme={editorTheme}
onCreateEditor={(_editorView) => setEditorView(_editorView)}
/>
</div>
</CollapsiblePanel>
<section className="flex flex-col">
<MemoryPanel
theme={theme}
theme={editorTheme}
open={openPanes.includes('variables')}
title="Variables"
icon={faSquareRootVariable}
/>
<Logs
theme={theme}
theme={editorTheme}
open={openPanes.includes('logs')}
title="Logs"
icon={faCodeCommit}
/>
<KCLErrors
theme={theme}
theme={editorTheme}
open={openPanes.includes('kclErrors')}
title="KCL Errors"
iconClassNames={{ icon: 'group-open:text-destroy-30' }}

View File

@ -1,15 +1,15 @@
import ReactJson from 'react-json-view'
import { useEffect } from 'react'
import { useStore } from '../useStore'
import { Themes, useStore } from '../useStore'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
const ReactJsonTypeHack = ReactJson as any
interface LogPanelProps extends CollapsiblePanelProps {
theme?: 'light' | 'dark'
theme?: Exclude<Themes, Themes.System>
}
export const Logs = ({ theme = 'light', ...props }: LogPanelProps) => {
export const Logs = ({ theme = Themes.Light, ...props }: LogPanelProps) => {
const { logs } = useStore(({ logs }) => ({
logs,
}))
@ -42,7 +42,10 @@ export const Logs = ({ theme = 'light', ...props }: LogPanelProps) => {
)
}
export const KCLErrors = ({ theme = 'light', ...props }: LogPanelProps) => {
export const KCLErrors = ({
theme = Themes.Light,
...props
}: LogPanelProps) => {
const { kclErrors } = useStore(({ kclErrors }) => ({
kclErrors,
}))

View File

@ -1,15 +1,15 @@
import ReactJson from 'react-json-view'
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
import { useStore } from '../useStore'
import { Themes, useStore } from '../useStore'
import { useMemo } from 'react'
import { ProgramMemory } from '../lang/executor'
interface MemoryPanelProps extends CollapsiblePanelProps {
theme?: 'light' | 'dark'
theme?: Exclude<Themes, Themes.System>
}
export const MemoryPanel = ({
theme = 'light',
theme = Themes.Light,
...props
}: MemoryPanelProps) => {
const { programMemory } = useStore((s) => ({

View File

@ -2,13 +2,15 @@ import ReactDOM from 'react-dom/client'
import './index.css'
import reportWebVitals from './reportWebVitals'
import { Toaster } from 'react-hot-toast'
import { useStore } from './useStore'
import { Themes, useStore } from './useStore'
import { Router } from './Router'
import { HotkeysProvider } from 'react-hotkeys-hook'
import { getSystemTheme } from './lib/getSystemTheme'
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
function setThemeClass(state: Partial<{ theme: string }>) {
if (state.theme === 'dark') {
function setThemeClass(state: Partial<{ theme: Themes }>) {
const systemTheme = state.theme === Themes.System && getSystemTheme()
if (state.theme === Themes.Dark || systemTheme === Themes.Dark) {
document.body.classList.add('dark')
} else {
document.body.classList.remove('dark')

View File

@ -0,0 +1,9 @@
import { Themes } from '../useStore'
export function getSystemTheme(): Exclude<Themes, 'system'> {
return typeof window !== 'undefined' &&
'matchMedia' in window &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? Themes.Dark
: Themes.Light
}

View File

@ -6,7 +6,7 @@ import {
import { ActionButton } from '../components/ActionButton'
import { AppHeader } from '../components/AppHeader'
import { open } from '@tauri-apps/api/dialog'
import { baseUnits, useStore } from '../useStore'
import { Themes, baseUnits, useStore } from '../useStore'
import { useRef } from 'react'
import { toast } from 'react-hot-toast'
import { Toggle } from '../components/Toggle/Toggle'
@ -184,21 +184,25 @@ export const Settings = () => {
title="Editor Theme"
description="Apply a light or dark theme to the editor"
>
<Toggle
name="settings-theme"
offLabel="Dark"
onLabel="Light"
checked={theme === 'light'}
<select
id="settings-theme"
className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
value={theme}
onChange={(e) => {
const newTheme = e.target.checked ? 'light' : 'dark'
setTheme(newTheme)
setTheme(e.target.value as Themes)
toast.success(
newTheme.slice(0, 1).toLocaleUpperCase() +
newTheme.slice(1) +
' mode activated'
'Theme changed to ' +
e.target.value.slice(0, 1).toLocaleUpperCase() +
e.target.value.slice(1)
)
}}
/>
>
{Object.entries(Themes).map(([label, value]) => (
<option key={value} value={value}>
{label}
</option>
))}
</select>
</SettingsSection>
<SettingsSection
title="Onboarding"

View File

@ -97,6 +97,11 @@ export type GuiModes =
}
type UnitSystem = 'imperial' | 'metric'
export enum Themes {
Light = 'light',
Dark = 'dark',
System = 'system',
}
export const baseUnits: Record<UnitSystem, string[]> = {
imperial: ['in', 'ft'],
@ -204,8 +209,8 @@ export interface StoreState {
setHomeShowMenu: (showMenu: boolean) => void
onboardingStatus: string
setOnboardingStatus: (status: string) => void
theme: 'light' | 'dark'
setTheme: (theme: 'light' | 'dark') => void
theme: Themes
setTheme: (theme: Themes) => void
openPanes: PaneType[]
setOpenPanes: (panes: PaneType[]) => void
homeMenuItems: {
@ -409,12 +414,7 @@ export const useStore = create<StoreState>()(
setDefaultBaseUnit: (defaultBaseUnit) => set({ defaultBaseUnit }),
onboardingStatus: '',
setOnboardingStatus: (onboardingStatus) => set({ onboardingStatus }),
theme:
typeof window !== 'undefined' &&
'matchMedia' in window &&
window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light',
theme: Themes.System,
setTheme: (theme) => set({ theme }),
openPanes: ['code'],
setOpenPanes: (openPanes) => set({ openPanes }),