Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2025-01-05 18:57:10 -08:00
parent 3c53babb50
commit 09ef1d5f10
39 changed files with 388 additions and 349 deletions

View File

@ -12,9 +12,10 @@ import { CmdBarFixture } from './cmdBarFixture'
import { EditorFixture } from './editorFixture'
import { ToolbarFixture } from './toolbarFixture'
import { SceneFixture } from './sceneFixture'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { HomePageFixture } from './homePageFixture'
import { unsafeTypedKeys } from 'lib/utils'
import { DeepPartial } from 'lib/types'
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
export class AuthenticatedApp {
public readonly page: Page
@ -78,7 +79,7 @@ export class AuthenticatedTronApp {
fixtures: Partial<Fixtures>
folderSetupFn?: (projectDirName: string) => Promise<void>
cleanProjectDir?: boolean
appSettings?: Partial<SaveSettingsPayload>
appSettings?: DeepPartial<Settings>
} = { fixtures: {} }
) {
const { electronApp, page, context, dir } = await setupElectron({

View File

@ -1,7 +1,12 @@
import { test, expect } from './zoo-test'
import { join } from 'path'
import fsp from 'fs/promises'
import { getUtils, executorInputPath, createProject } from './test-utils'
import {
getUtils,
executorInputPath,
createProject,
settingsToToml,
} from './test-utils'
import { bracket } from 'lib/exampleKcl'
import { onboardingPaths } from 'routes/Onboarding/paths'
import {
@ -10,7 +15,6 @@ import {
TEST_SETTINGS_ONBOARDING_EXPORT,
TEST_SETTINGS_ONBOARDING_USER_MENU,
} from './storageStates'
import * as TOML from '@iarna/toml'
import { expectPixelColor } from './fixtures/sceneFixture'
// Because onboarding relies on an app setting we need to set it as incompletel
@ -22,7 +26,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
cleanProjectDir: true,
@ -63,7 +67,7 @@ test.describe('Onboarding tests', () => {
tag: '@electron',
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
cleanProjectDir: true,
@ -108,7 +112,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
cleanProjectDir: true,
@ -158,7 +162,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
},
@ -172,7 +176,7 @@ test.describe('Onboarding tests', () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: TEST_SETTINGS_ONBOARDING_START,
}),
}
@ -208,7 +212,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: '/export',
onboarding_status: '/export',
},
},
cleanProjectDir: true,
@ -225,7 +229,7 @@ test.describe('Onboarding tests', () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
}),
}
@ -263,7 +267,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: '/parametric-modeling',
onboarding_status: '/parametric-modeling',
},
},
cleanProjectDir: true,
@ -313,7 +317,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
cleanProjectDir: true,
@ -326,7 +330,7 @@ test.describe('Onboarding tests', () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
}),
}
@ -386,7 +390,7 @@ test.describe('Onboarding tests', () => {
{
appSettings: {
app: {
onboardingStatus: 'incomplete',
onboarding_status: 'incomplete',
},
},
cleanProjectDir: true,
@ -400,7 +404,7 @@ test.describe('Onboarding tests', () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
}),
}
@ -442,7 +446,7 @@ test.fixme(
{
appSettings: {
app: {
onboardingStatus: 'dismissed',
onboarding_status: 'dismissed',
},
},
cleanProjectDir: true,

View File

@ -1,6 +1,6 @@
import { test, expect } from '@playwright/test'
import { secrets } from './secrets'
import { Paths, doExport, getUtils } from './test-utils'
import { Paths, doExport, getUtils, settingsToToml } from './test-utils'
import { Models } from '@kittycad/lib'
import fsp from 'fs/promises'
import { spawn } from 'child_process'
@ -12,7 +12,6 @@ import {
TEST_SETTINGS,
TEST_SETTINGS_KEY,
} from './storageStates'
import * as TOML from '@iarna/toml'
test.beforeEach(async ({ page }) => {
// reducedMotion kills animations, which speeds up tests and reduces flakiness
@ -29,7 +28,7 @@ test.beforeEach(async ({ page }) => {
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({ settings: TEST_SETTINGS }),
settings: settingsToToml({ settings: TEST_SETTINGS }),
IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY,
}
)
@ -704,12 +703,12 @@ test.describe(
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: {
...TEST_SETTINGS,
modeling: {
...TEST_SETTINGS.modeling,
defaultUnit: 'mm',
base_unit: 'mm',
},
},
}),
@ -1062,12 +1061,12 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
},
{
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: {
...TEST_SETTINGS,
modeling: {
...TEST_SETTINGS.modeling,
showScaleGrid: true,
show_scale_grid: true,
},
},
}),

View File

@ -1,78 +1,83 @@
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Themes } from 'lib/theme'
import { DeepPartial } from 'lib/types'
import { onboardingPaths } from 'routes/Onboarding/paths'
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
export const IS_PLAYWRIGHT_KEY = 'playwright'
export const TEST_SETTINGS_KEY = '/settings.toml'
export const TEST_SETTINGS = {
app: {
onboarding_status: 'dismissed',
appearance: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: '',
enableSSAO: false,
},
},
modeling: {
defaultUnit: 'in',
mouseControls: 'Zoo',
cameraProjection: 'perspective',
showDebugPanel: true,
base_unit: 'in',
mouse_controls: 'zoo',
camera_projection: 'perspective',
show_debug_panel: true,
enable_ssao: false,
},
projects: {
defaultProjectName: 'project-$nnn',
project: {
default_project_name: 'project-$nnn',
directory: '',
},
textEditor: {
textWrapping: true,
text_editor: {
text_wrapping: true,
},
} satisfies Partial<SaveSettingsPayload>
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_ONBOARDING_USER_MENU = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.USER_MENU },
} satisfies Partial<SaveSettingsPayload>
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.USER_MENU },
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.EXPORT },
} satisfies Partial<SaveSettingsPayload>
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.EXPORT },
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING = {
...TEST_SETTINGS,
app: {
...TEST_SETTINGS.app,
onboardingStatus: onboardingPaths.PARAMETRIC_MODELING,
onboarding_status: onboardingPaths.PARAMETRIC_MODELING,
},
} satisfies Partial<SaveSettingsPayload>
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_ONBOARDING_START = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, onboardingStatus: '' },
} satisfies Partial<SaveSettingsPayload>
app: { ...TEST_SETTINGS.app, onboarding_status: '' },
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_DEFAULT_THEME = {
...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, theme: Themes.System },
} satisfies Partial<SaveSettingsPayload>
app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } },
} satisfies DeepPartial<Settings>
export const TEST_SETTINGS_CORRUPTED = {
app: {
onboarding_status: 'dismissed',
appearance: {
theme: Themes.Dark,
onboardingStatus: 'dismissed',
projectDirectory: 123 as any,
},
},
modeling: {
defaultUnit: 'invalid' as any,
mouseControls: `() => alert('hack the planet')` as any,
cameraProjection: 'perspective',
showDebugPanel: true,
base_unit: 'invalid' as any,
mouse_controls: `() => alert('hack the planet')` as any,
camera_projection: 'perspective',
show_debug_panel: true,
},
projects: {
defaultProjectName: false as any,
project: {
default_project_name: false as any,
directory: 123 as any,
},
textEditor: {
textWrapping: true,
text_editor: {
text_wrapping: true,
},
} satisfies Partial<SaveSettingsPayload>
} satisfies DeepPartial<Settings>
export const TEST_CODE_GIZMO = `part001 = startSketchOn('XZ')
|> startProfileAt([20, 0], %)

View File

@ -23,11 +23,13 @@ import {
IS_PLAYWRIGHT_KEY,
} from './storageStates'
import * as TOML from '@iarna/toml'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { SETTINGS_FILE_NAME } from 'lib/constants'
import { isErrorWhitelisted } from './lib/console-error-whitelist'
import { isArray } from 'lib/utils'
import { reportRejection } from 'lib/trap'
import { Configuration } from 'lang/wasm'
import { DeepPartial } from 'lib/types'
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
const toNormalizedCode = (text: string) => {
return text.replace(/\s+/g, '')
@ -884,19 +886,23 @@ export async function setup(
{
token: secrets.token,
settingsKey: TEST_SETTINGS_KEY,
settings: TOML.stringify({
settings: settingsToToml({
settings: {
...TEST_SETTINGS,
app: {
...TEST_SETTINGS.projects,
projectDirectory: TEST_SETTINGS.app.projectDirectory,
onboardingStatus: 'dismissed',
...TEST_SETTINGS.app,
onboarding_status: 'dismissed',
appearance: {
theme: 'dark',
},
} as Partial<SaveSettingsPayload>,
},
project: {
directory: TEST_SETTINGS.project.directory,
},
},
}),
IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project.directory,
PERSIST_MODELING_CONTEXT,
}
)
@ -931,7 +937,7 @@ export async function setupElectron({
testInfo: TestInfo
folderSetupFn?: (projectDirName: string) => Promise<void>
cleanProjectDir?: boolean
appSettings?: Partial<SaveSettingsPayload>
appSettings?: DeepPartial<Settings>
}): Promise<{
electronApp: ElectronApplication
context: BrowserContext
@ -978,7 +984,7 @@ export async function setupElectron({
if (cleanProjectDir) {
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
const settingsOverrides = TOML.stringify(
const settingsOverrides = settingsToToml(
appSettings
? {
settings: {
@ -986,9 +992,11 @@ export async function setupElectron({
...appSettings,
app: {
...TEST_SETTINGS.app,
projectDirectory: projectDirName,
...appSettings.app,
},
project: {
directory: projectDirName,
},
},
}
: {
@ -996,7 +1004,9 @@ export async function setupElectron({
...TEST_SETTINGS,
app: {
...TEST_SETTINGS.app,
projectDirectory: projectDirName,
},
project: {
directory: projectDirName,
},
},
}
@ -1190,3 +1200,7 @@ export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
})
.toBe(lines)
}
export function settingsToToml(settings: DeepPartial<Configuration>) {
return TOML.stringify(settings)
}

View File

@ -31,13 +31,13 @@ test.describe('Testing settings', () => {
)
) as { settings: SaveSettingsPayload }
expect(storedSettings.settings?.app?.theme).toBe('dark')
expect(storedSettings.settings?.app?.appearance?.theme).toBe('dark')
// Check that the invalid settings were changed to good defaults
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
expect(storedSettings.settings?.modeling?.mouseControls).toBe('Zoo')
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('Zoo')
expect(storedSettings.settings?.project?.directory).toBe('')
expect(storedSettings.settings?.project?.default_project_name).toBe(
'project-$nnn'
)
}
@ -374,7 +374,9 @@ test.describe('Testing settings', () => {
tag: '@electron',
appSettings: {
app: {
themeColor: '259',
appearance: {
color: 259,
},
},
},
},
@ -400,9 +402,11 @@ test.describe('Testing settings', () => {
tag: '@electron',
appSettings: {
app: {
appearance: {
// Doesn't matter what you set it to. It will
// default to 264.5
themeColor: '0',
color: 0,
},
},
},
},
@ -832,7 +836,7 @@ test.describe('Testing settings', () => {
// but "show debug panel" set to false
appSettings: {
...TEST_SETTINGS,
modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false },
modeling: { ...TEST_SETTINGS.modeling, show_debug_panel: false },
},
},
async ({ context, page, homePage }) => {

View File

@ -18,7 +18,8 @@ import {
AuthenticatedApp,
} from './fixtures/fixtureSetup'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
import { DeepPartial } from 'lib/types'
export { expect } from '@playwright/test'
declare module '@playwright/test' {
@ -45,7 +46,7 @@ export type BrowserContext = BrowserContextPlaywright
export type Page = PagePlaywright
export type TestDetails = TestDetailsPlaywright & {
cleanProjectDir?: boolean
appSettings?: Partial<SaveSettingsPayload>
appSettings?: DeepPartial<Settings>
}
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and

View File

@ -54,7 +54,7 @@ export function App() {
)
const {
app: { onboardingStatus },
app: { onboarding_status },
} = settings.context
useHotkeys('backspace', (e) => {
@ -69,7 +69,7 @@ export function App() {
)
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
(p) => p === onboardingStatus.current
(p) => p === onboarding_status.current
)
? 'opacity-20'
: ''

View File

@ -77,7 +77,7 @@ export const ClientSideScene = ({
}: {
cameraControls: ReturnType<
typeof useSettingsAuthContext
>['settings']['context']['modeling']['mouseControls']['current']
>['settings']['context']['modeling']['mouse_controls']['current']
}) => {
const canvasRef = useRef<HTMLDivElement>(null)
const { state, send, context } = useModelingContext()

View File

@ -5,21 +5,21 @@ import { useEffect, useState } from 'react'
export function CameraProjectionToggle() {
const { settings } = useSettingsAuthContext()
const isCameraProjectionPerspective =
settings.context.modeling.cameraProjection.current === 'perspective'
settings.context.modeling.camera_projection.current === 'perspective'
const [checked, setChecked] = useState(isCameraProjectionPerspective)
useEffect(() => {
setChecked(
settings.context.modeling.cameraProjection.current === 'perspective'
settings.context.modeling.camera_projection.current === 'perspective'
)
}, [settings.context.modeling.cameraProjection.current])
}, [settings.context.modeling.camera_projection.current])
return (
<Switch
checked={checked}
onChange={(newValue) => {
settings.send({
type: 'set.modeling.cameraProjection',
type: 'set.modeling.camera_projection',
data: {
level: 'user',
value: newValue ? 'perspective' : 'orthographic',

View File

@ -115,9 +115,9 @@ function CommandBarKclInput({
: defaultValue.length,
},
theme:
settings.context.app.theme.current === 'system'
settings.context.app.appearance.theme.current === 'system'
? getSystemTheme()
: settings.context.app.theme.current,
: settings.context.app.appearance.theme.current,
extensions: [
varMentionsExtension,
EditorView.updateListener.of((vu: ViewUpdate) => {

View File

@ -6,7 +6,7 @@ import { useState } from 'react'
const DownloadAppBanner = () => {
const { settings } = useSettingsAuthContext()
const [isBannerDismissed, setIsBannerDismissed] = useState(
settings.context.app.dismissWebBanner.current
settings.context.app.dismiss_web_banner.current
)
return (

View File

@ -315,7 +315,7 @@ export const FileMachineProvider = ({
// with the sample's setting.
if (data.sampleUnits) {
settings.send({
type: 'set.modeling.defaultUnit',
type: 'set.modeling.base_unit',
data: {
level: 'project',
value: data.sampleUnits,

View File

@ -107,7 +107,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
as="button"
onClick={() => {
settings.send({
type: 'set.app.onboardingStatus',
type: 'set.app.onboarding_status',
data: {
value: '',
level: 'user',

View File

@ -111,12 +111,15 @@ export const ModelingMachineProvider = ({
auth,
settings: {
context: {
app: { theme, enableSSAO },
app: {
appearance: { theme },
},
modeling: {
defaultUnit,
cameraProjection,
highlightEdges,
showScaleGrid,
base_unit,
camera_projection,
highlight_edges,
show_scale_grid,
enable_ssao,
},
},
},
@ -172,7 +175,7 @@ export const ModelingMachineProvider = ({
sceneInfra.camControls.syncDirection = 'clientToEngine'
if (cameraProjection.current === 'perspective') {
if (camera_projection.current === 'perspective') {
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
}
@ -508,7 +511,7 @@ export const ModelingMachineProvider = ({
format.type === 'stl' ||
format.type === 'ply'
) {
format.units = defaultUnit.current
format.units = base_unit.current
}
if (format.type === 'ply' || format.type === 'stl') {
@ -533,7 +536,7 @@ export const ModelingMachineProvider = ({
token,
settings: {
theme: theme.current,
highlightEdges: highlightEdges.current,
highlightEdges: highlight_edges.current,
},
}).catch(reportRejection)
},
@ -1134,10 +1137,10 @@ export const ModelingMachineProvider = ({
{
pool: pool,
theme: theme.current,
highlightEdges: highlightEdges.current,
enableSSAO: enableSSAO.current,
showScaleGrid: showScaleGrid.current,
cameraProjection: cameraProjection.current,
highlightEdges: highlight_edges.current,
enableSSAO: enable_ssao.current,
showScaleGrid: show_scale_grid.current,
cameraProjection: camera_projection.current,
},
token
)

View File

@ -69,7 +69,7 @@ export const ModelingPane = ({
...props
}: ModelingPaneProps) => {
const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus
const onboardingStatus = settings.context.app.onboarding_status
const pointerEventsCssClass =
onboardingStatus.current === onboardingPaths.CAMERA
? 'pointer-events-none '

View File

@ -69,9 +69,9 @@ export const KclEditorPane = () => {
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector)
const theme =
context.app.theme.current === Themes.System
context.app.appearance.theme.current === Themes.System
? getSystemTheme()
: context.app.theme.current
: context.app.appearance.theme.current
const { copilotLSP, kclLSP } = useLspContext()
// Since these already exist in the editor, we don't need to define them
@ -104,8 +104,8 @@ export const KclEditorPane = () => {
})
}, [editorIsMounted, lastSelectionEvent])
const textWrapping = context.textEditor.textWrapping
const cursorBlinking = context.textEditor.blinkingCursor
const textWrapping = context.text_editor.text_wrapping
const cursorBlinking = context.text_editor.blinking_cursor
// DO NOT ADD THE CODEMIRROR HOTKEYS HERE TO THE DEPENDENCY ARRAY
// It reloads the editor every time we do _anything_ in the editor
// I have no idea why.

View File

@ -210,6 +210,6 @@ export const sidebarPanes: SidebarPane[] = [
)
},
keybinding: 'Shift + D',
hide: ({ settings }) => !settings.modeling.showDebugPanel.current,
hide: ({ settings }) => !settings.modeling.show_debug_panel.current,
},
]

View File

@ -40,14 +40,14 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
const { commandBarSend } = useCommandsContext()
const kclContext = useKclContext()
const { settings } = useSettingsAuthContext()
const onboardingStatus = settings.context.app.onboardingStatus
const onboardingStatus = settings.context.app.onboarding_status
const { send, context } = useModelingContext()
const pointerEventsCssClass =
onboardingStatus.current === onboardingPaths.CAMERA ||
context.store?.openPanes.length === 0
? 'pointer-events-none '
: 'pointer-events-auto '
const showDebugPanel = settings.context.modeling.showDebugPanel
const showDebugPanel = settings.context.modeling.show_debug_panel
const paneCallbackProps = useMemo(
() => ({

View File

@ -79,11 +79,8 @@ const ProjectsContextDesktop = ({
} = useSettingsAuthContext()
useEffect(() => {
console.log(
'project directory changed',
settings.app.projectDirectory.current
)
}, [settings.app.projectDirectory.current])
console.log('project directory changed', settings.project.directory.current)
}, [settings.project.directory.current])
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
const { projectPaths, projectsDir } = useProjectsLoader([
@ -192,7 +189,7 @@ const ProjectsContextDesktop = ({
let name = (
input && 'name' in input && input.name
? input.name
: settings.projects.defaultProjectName.current
: settings.project.default_project_name.current
).trim()
if (doesProjectNameNeedInterpolated(name)) {
@ -257,8 +254,8 @@ const ProjectsContextDesktop = ({
{
input: {
projects: projectPaths,
defaultProjectName: settings.projects.defaultProjectName.current,
defaultDirectory: settings.app.projectDirectory.current,
defaultProjectName: settings.project.default_project_name.current,
defaultDirectory: settings.project.directory.current,
},
}
)

View File

@ -61,7 +61,7 @@ export const AllSettingsFields = forwardRef(
function restartOnboarding() {
send({
type: `set.app.onboardingStatus`,
type: `set.app.onboarding_status`,
data: { level: 'user', value: '' },
})
}
@ -73,7 +73,7 @@ export const AllSettingsFields = forwardRef(
useEffect(() => {
async function navigateToOnboardingStart() {
if (
state.context.app.onboardingStatus.user === '' &&
state.context.app.onboarding_status.user === '' &&
state.matches('idle')
) {
if (isFileSettings) {

View File

@ -122,18 +122,20 @@ export const SettingsAuthProviderBase = ({
setClientSideSceneUnits: ({ context, event }) => {
const newBaseUnit =
event.type === 'set.modeling.defaultUnit'
event.type === 'set.modeling.base_unit'
? (event.data.value as BaseUnit)
: context.modeling.defaultUnit.current
: context.modeling.base_unit.current
sceneInfra.baseUnit = newBaseUnit
},
setEngineTheme: ({ context }) => {
engineCommandManager
.setTheme(context.app.theme.current)
.setTheme(context.app.appearance.theme.current)
.catch(reportRejection)
},
setClientTheme: ({ context }) => {
const opposingTheme = getOppositeTheme(context.app.theme.current)
const opposingTheme = getOppositeTheme(
context.app.appearance.theme.current
)
sceneInfra.theme = opposingTheme
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
},
@ -164,12 +166,12 @@ export const SettingsAuthProviderBase = ({
try {
const relevantSetting = (s: typeof settings) => {
return (
s.modeling?.defaultUnit?.current !==
context.modeling.defaultUnit.current ||
s.modeling.showScaleGrid.current !==
context.modeling.showScaleGrid.current ||
s.modeling?.highlightEdges.current !==
context.modeling.highlightEdges.current
s.modeling?.base_unit.current !==
context.modeling.base_unit.current ||
s.modeling.show_scale_grid.current !==
context.modeling.show_scale_grid.current ||
s.modeling?.highlight_edges.current !==
context.modeling.highlight_edges.current
)
}
@ -180,9 +182,9 @@ export const SettingsAuthProviderBase = ({
event.type === 'Reset settings' && relevantSetting(settings)
if (
event.type === 'set.modeling.defaultUnit' ||
event.type === 'set.modeling.showScaleGrid' ||
event.type === 'set.modeling.highlightEdges' ||
event.type === 'set.modeling.base_unit' ||
event.type === 'set.modeling.show_scale_grid' ||
event.type === 'set.modeling.highlight_edges' ||
allSettingsIncludesUnitChange ||
resetSettingsIncludesUnitChange
) {
@ -258,7 +260,7 @@ export const SettingsAuthProviderBase = ({
useEffect(() => {
// If the user wants to hide the settings commands
//from the command bar don't add them.
if (settingsState.context.commandBar.includeSettings.current === false)
if (settingsState.context.command_bar.include_settings.current === false)
return
const commands = settingsWithCommandConfigs(settingsState.context)
@ -334,7 +336,8 @@ export const SettingsAuthProviderBase = ({
// events outside of the machine that also depend on the machine's context
useEffect(() => {
const listener = (e: MediaQueryListEvent) => {
if (settingsState.context.app.theme.current !== 'system') return
if (settingsState.context.app.appearance.theme.current !== 'system')
return
setThemeClass(e.matches ? Themes.Dark : Themes.Light)
}
@ -349,9 +352,9 @@ export const SettingsAuthProviderBase = ({
useEffect(() => {
document.documentElement.style.setProperty(
`--primary-hue`,
settingsState.context.app.themeColor.current
settingsState.context.app.appearance.color.current
)
}, [settingsState.context.app.themeColor.current])
}, [settingsState.context.app.appearance.color.current])
/**
* Update the --cursor-color CSS variable
@ -360,11 +363,11 @@ export const SettingsAuthProviderBase = ({
useEffect(() => {
document.documentElement.style.setProperty(
`--cursor-color`,
settingsState.context.textEditor.blinkingCursor.current
settingsState.context.text_editor.blinking_cursor.current
? 'auto'
: 'transparent'
)
}, [settingsState.context.textEditor.blinkingCursor.current])
}, [settingsState.context.text_editor.blinking_cursor.current])
// Auth machine setup
const [authState, authSend, authActor] = useMachine(

View File

@ -41,7 +41,7 @@ export const Stream = () => {
const [streamState, setStreamState] = useState(StreamState.Unset)
const { file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const IDLE = settings.context.app.streamIdleMode.current
const IDLE = settings.context.app.stream_idle_mode.current
const isNetworkOkay =
overallState === NetworkHealthState.Ok ||
@ -342,7 +342,7 @@ export const Stream = () => {
id="video-stream"
/>
<ClientSideScene
cameraControls={settings.context.modeling.mouseControls.current}
cameraControls={settings.context.modeling.mouse_controls.current}
/>
{(streamState === StreamState.Paused ||
streamState === StreamState.Resuming) && (

View File

@ -18,7 +18,7 @@ export function UnitsMenu() {
<div className="absolute w-[1px] h-[1em] bg-primary right-0 top-1/2 -translate-y-1/2"></div>
</div>
<span className="sr-only">Current units are:&nbsp;</span>
{settings.context.modeling.defaultUnit.current}
{settings.context.modeling.base_unit.current}
</Popover.Button>
<Popover.Panel
className={`absolute bottom-full right-0 mb-2 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
@ -32,7 +32,7 @@ export function UnitsMenu() {
className="flex items-center gap-2 m-0 py-1.5 px-2 cursor-pointer hover:bg-chalkboard-20 dark:hover:bg-chalkboard-80 border-none text-left"
onClick={() => {
settings.send({
type: 'set.modeling.defaultUnit',
type: 'set.modeling.base_unit',
data: {
level: 'project',
value: unit,
@ -42,7 +42,7 @@ export function UnitsMenu() {
}}
>
<span className="flex-1">{baseUnitLabels[unit]}</span>
{unit === settings.context.modeling.defaultUnit.current && (
{unit === settings.context.modeling.base_unit.current && (
<span className="text-chalkboard-60">current</span>
)}
</button>

View File

@ -10,7 +10,7 @@ export function useResolvedTheme() {
const {
settings: { context },
} = useSettingsAuthContext()
return context.app.theme.current === Themes.System
return context.app.appearance.theme.current === Themes.System
? getSystemTheme()
: context.app.theme.current
: context.app.appearance.theme.current
}

View File

@ -63,9 +63,7 @@ export async function renameProjectDirectory(
export async function ensureProjectDirectoryExists(
config: DeepPartial<Configuration>
): Promise<string | undefined> {
const projectDir =
config.settings?.app?.project_directory ||
config.settings?.project?.directory
const projectDir = config.settings?.project?.directory
if (!projectDir) {
console.error('projectDir is falsey', config)
return Promise.reject(new Error('projectDir is falsey'))
@ -520,8 +518,7 @@ export const readAppSettingsFile = async () => {
}
const hasProjectDirectorySetting =
parsedAppConfig.settings?.project?.directory ||
parsedAppConfig.settings?.app?.project_directory
parsedAppConfig.settings?.project?.directory
if (hasProjectDirectorySetting) {
return parsedAppConfig

View File

@ -86,8 +86,7 @@ export function kclCommands(
sampleName: data.sample,
code,
method: data.method,
sampleUnits:
projectSettingsPayload.modeling?.defaultUnit || 'mm',
sampleUnits: projectSettingsPayload.modeling?.base_unit || 'mm',
}
}
)

View File

@ -55,7 +55,7 @@ export const telemetryLoader: LoaderFunction = async ({
export const onboardingRedirectLoader: ActionFunction = async (args) => {
const { settings } = await loadAndValidateSettings()
const onboardingStatus: OnboardingStatus =
settings.app.onboardingStatus.current || ''
settings.app.onboarding_status.current || ''
const notEnRouteToOnboarding = !args.request.url.includes(
PATHS.ONBOARDING.INDEX
)

View File

@ -128,6 +128,7 @@ export function createSettings() {
/**
* The overall appearance of the app: light, dark, or system
*/
appearance: {
theme: new Setting<Themes>({
hideOnLevel: 'project',
defaultValue: Themes.System,
@ -148,7 +149,7 @@ export function createSettings() {
})),
},
}),
themeColor: new Setting<string>({
color: new Setting<string>({
defaultValue: '264.5',
description: 'The hue of the primary theme color for the app',
validate: (v) => Number(v) >= 0 && Number(v) < 360,
@ -172,17 +173,11 @@ export function createSettings() {
</div>
),
}),
enableSSAO: new Setting<boolean>({
defaultValue: true,
description:
'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled',
validate: (v) => typeof v === 'boolean',
hideOnPlatform: 'both', //for now
}),
},
/**
* Stream resource saving behavior toggle
*/
streamIdleMode: new Setting<boolean>({
stream_idle_mode: new Setting<boolean>({
defaultValue: false,
description: 'Toggle stream idling, saving bandwidth and battery',
validate: (v) => typeof v === 'boolean',
@ -190,7 +185,7 @@ export function createSettings() {
inputType: 'boolean',
},
}),
onboardingStatus: new Setting<OnboardingStatus>({
onboarding_status: new Setting<OnboardingStatus>({
defaultValue: '',
// TODO: this could be better but we don't have a TS side real enum
// for this yet
@ -198,62 +193,13 @@ export function createSettings() {
hideOnPlatform: 'both',
}),
/** Permanently dismiss the banner warning to download the desktop app. */
dismissWebBanner: new Setting<boolean>({
dismiss_web_banner: new Setting<boolean>({
defaultValue: false,
description:
'Permanently dismiss the banner warning to download the desktop app.',
validate: (v) => typeof v === 'boolean',
hideOnPlatform: 'desktop',
}),
projectDirectory: new Setting<string>({
defaultValue: '',
description: 'The directory to save and load projects from',
hideOnLevel: 'project',
hideOnPlatform: 'web',
validate: (v) =>
typeof v === 'string' && (v.length > 0 || !isDesktop()),
Component: ({ value, updateValue }) => {
const inputRef = useRef<HTMLInputElement>(null)
return (
<div className="flex gap-4 p-1 border rounded-sm border-chalkboard-30">
<input
className="flex-grow text-xs px-2 bg-transparent"
value={value}
disabled
data-testid="project-directory-input"
ref={inputRef}
/>
<button
onClick={toSync(async () => {
// In desktop end-to-end tests we can't control the file picker,
// so we seed the new directory value in the element's dataset
const inputRefVal = inputRef.current?.dataset.testValue
if (
inputRef.current &&
inputRefVal &&
!Array.isArray(inputRefVal)
) {
updateValue(inputRefVal)
} else {
const newPath = await window.electron.open({
properties: ['openDirectory', 'createDirectory'],
defaultPath: value,
title: 'Choose a new project directory',
})
if (newPath.canceled) return
updateValue(newPath.filePaths[0])
}
}, reportRejection)}
className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20"
data-testid="project-directory-button"
>
<CustomIcon name="folder" className="w-5 h-5" />
<Tooltip position="top-right">Choose a folder</Tooltip>
</button>
</div>
)
},
}),
},
/**
* Settings that affect the behavior while modeling.
@ -262,7 +208,7 @@ export function createSettings() {
/**
* The default unit to use in modeling dimensions
*/
defaultUnit: new Setting<BaseUnit>({
base_unit: new Setting<BaseUnit>({
defaultValue: 'mm',
description: 'The default unit to use in modeling dimensions',
validate: (v) => baseUnitsUnion.includes(v as BaseUnit),
@ -285,7 +231,7 @@ export function createSettings() {
/**
* The controls for how to navigate the 3D view
*/
mouseControls: new Setting<CameraSystem>({
mouse_controls: new Setting<CameraSystem>({
defaultValue: 'Zoo',
description: 'The controls for how to navigate the 3D view',
validate: (v) => cameraSystems.includes(v as CameraSystem),
@ -345,7 +291,7 @@ export function createSettings() {
/**
* Projection method applied to the 3D view, perspective or orthographic
*/
cameraProjection: new Setting<CameraProjectionType>({
camera_projection: new Setting<CameraProjectionType>({
defaultValue: 'orthographic',
hideOnLevel: 'project',
description:
@ -372,10 +318,17 @@ export function createSettings() {
})),
},
}),
enable_ssao: new Setting<boolean>({
defaultValue: true,
description:
'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled',
validate: (v) => typeof v === 'boolean',
hideOnPlatform: 'both', //for now
}),
/**
* Whether to highlight edges of 3D objects
*/
highlightEdges: new Setting<boolean>({
highlight_edges: new Setting<boolean>({
defaultValue: true,
description: 'Whether to highlight edges of 3D objects',
validate: (v) => typeof v === 'boolean',
@ -387,7 +340,7 @@ export function createSettings() {
/**
* Whether to show a scale grid in the 3D modeling view
*/
showScaleGrid: new Setting<boolean>({
show_scale_grid: new Setting<boolean>({
defaultValue: false,
description: 'Whether to show a scale grid in the 3D modeling view',
validate: (v) => typeof v === 'boolean',
@ -400,7 +353,7 @@ export function createSettings() {
* Whether to show the debug panel, which lets you see
* various states of the app to aid in development
*/
showDebugPanel: new Setting<boolean>({
show_debug_panel: new Setting<boolean>({
defaultValue: false,
description: 'Whether to show the debug panel, a development tool',
validate: (v) => typeof v === 'boolean',
@ -438,11 +391,11 @@ export function createSettings() {
/**
* Settings that affect the behavior of the KCL text editor.
*/
textEditor: {
text_editor: {
/**
* Whether to wrap text in the editor or overflow with scroll
*/
textWrapping: new Setting<boolean>({
text_wrapping: new Setting<boolean>({
defaultValue: true,
description:
'Whether to wrap text in the editor or overflow with scroll',
@ -454,7 +407,7 @@ export function createSettings() {
/**
* Whether to make the cursor blink in the editor
*/
blinkingCursor: new Setting<boolean>({
blinking_cursor: new Setting<boolean>({
defaultValue: true,
description: 'Whether to make the cursor blink in the editor',
validate: (v) => typeof v === 'boolean',
@ -466,11 +419,60 @@ export function createSettings() {
/**
* Settings that affect the behavior of project management.
*/
projects: {
project: {
directory: new Setting<string>({
defaultValue: '',
description: 'The directory to save and load projects from',
hideOnLevel: 'project',
hideOnPlatform: 'web',
validate: (v) =>
typeof v === 'string' && (v.length > 0 || !isDesktop()),
Component: ({ value, updateValue }) => {
const inputRef = useRef<HTMLInputElement>(null)
return (
<div className="flex gap-4 p-1 border rounded-sm border-chalkboard-30">
<input
className="flex-grow text-xs px-2 bg-transparent"
value={value}
disabled
data-testid="project-directory-input"
ref={inputRef}
/>
<button
onClick={toSync(async () => {
// In desktop end-to-end tests we can't control the file picker,
// so we seed the new directory value in the element's dataset
const inputRefVal = inputRef.current?.dataset.testValue
if (
inputRef.current &&
inputRefVal &&
!Array.isArray(inputRefVal)
) {
updateValue(inputRefVal)
} else {
const newPath = await window.electron.open({
properties: ['openDirectory', 'createDirectory'],
defaultPath: value,
title: 'Choose a new project directory',
})
if (newPath.canceled) return
updateValue(newPath.filePaths[0])
}
}, reportRejection)}
className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20"
data-testid="project-directory-button"
>
<CustomIcon name="folder" className="w-5 h-5" />
<Tooltip position="top-right">Choose a folder</Tooltip>
</button>
</div>
)
},
}),
/**
* The default project name to use when creating a new project
*/
defaultProjectName: new Setting<string>({
default_project_name: new Setting<string>({
defaultValue: DEFAULT_PROJECT_NAME,
description:
'The default project name to use when creating a new project',
@ -504,11 +506,11 @@ export function createSettings() {
/**
* Settings that affect the behavior of the command bar.
*/
commandBar: {
command_bar: {
/**
* Whether to include settings in the command bar
*/
includeSettings: new Setting<boolean>({
include_settings: new Setting<boolean>({
defaultValue: true,
description: 'Whether to include settings in the command bar',
validate: (v) => typeof v === 'boolean',

View File

@ -26,8 +26,8 @@ describe(`testing settings initialization`, () => {
setSettingsAtLevel(settings, 'user', appSettingsPayload)
expect(settings.app.theme.current).toBe('dark')
expect(settings.app.themeColor.current).toBe('190')
expect(settings.app.appearance.theme.current).toBe('dark')
expect(settings.app.appearance.color.current).toBe('190')
})
it(`doesn't read theme from project settings`, () => {
@ -61,9 +61,9 @@ describe(`testing settings initialization`, () => {
setSettingsAtLevel(settings, 'project', projectSettingsPayload)
// The 'project'-level for `theme` setting should be ignored completely
expect(settings.app.theme.current).toBe('dark')
expect(settings.app.appearance.theme.current).toBe('dark')
// But the 'project'-level for `themeColor` setting should be applied
expect(settings.app.themeColor.current).toBe('200')
expect(settings.app.appearance.color.current).toBe('200')
})
})
@ -106,8 +106,8 @@ describe(`testing getAllCurrentSettings`, () => {
const allCurrentSettings = getAllCurrentSettings(settings)
// This one gets the 'user'-level theme because it's ignored at the project level
// (see the test "doesn't read theme from project settings")
expect(allCurrentSettings.app.theme).toBe('dark')
expect(allCurrentSettings.app.themeColor).toBe('200')
expect(allCurrentSettings.modeling.defaultUnit).toBe('ft')
expect(allCurrentSettings.app.appearance?.theme).toBe('dark')
expect(allCurrentSettings.app.appearance?.color).toBe('200')
expect(allCurrentSettings.modeling.base_unit).toBe('ft')
})
})

View File

@ -34,36 +34,38 @@ export function configurationToSettingsPayload(
): DeepPartial<SaveSettingsPayload> {
return {
app: {
appearance: {
theme: appThemeToTheme(configuration?.settings?.app?.appearance?.theme),
themeColor: configuration?.settings?.app?.appearance?.color
color: configuration?.settings?.app?.appearance?.color
? configuration?.settings?.app?.appearance?.color.toString()
: undefined,
onboardingStatus: configuration?.settings?.app?.onboarding_status,
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
projectDirectory: configuration?.settings?.project?.directory,
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
},
onboarding_status: configuration?.settings?.app?.onboarding_status,
dismiss_web_banner: configuration?.settings?.app?.dismiss_web_banner,
stream_idle_mode: configuration?.settings?.app?.stream_idle_mode,
},
modeling: {
defaultUnit: configuration?.settings?.modeling?.base_unit,
cameraProjection: configuration?.settings?.modeling?.camera_projection,
mouseControls: mouseControlsToCameraSystem(
base_unit: configuration?.settings?.modeling?.base_unit,
camera_projection: configuration?.settings?.modeling?.camera_projection,
mouse_controls: mouseControlsToCameraSystem(
configuration?.settings?.modeling?.mouse_controls
),
highlightEdges: configuration?.settings?.modeling?.highlight_edges,
showDebugPanel: configuration?.settings?.modeling?.show_debug_panel,
showScaleGrid: configuration?.settings?.modeling?.show_scale_grid,
highlight_edges: configuration?.settings?.modeling?.highlight_edges,
show_debug_panel: configuration?.settings?.modeling?.show_debug_panel,
show_scale_grid: configuration?.settings?.modeling?.show_scale_grid,
enable_ssao: configuration?.settings?.modeling?.enable_ssao,
},
textEditor: {
textWrapping: configuration?.settings?.text_editor?.text_wrapping,
blinkingCursor: configuration?.settings?.text_editor?.blinking_cursor,
text_editor: {
text_wrapping: configuration?.settings?.text_editor?.text_wrapping,
blinking_cursor: configuration?.settings?.text_editor?.blinking_cursor,
},
projects: {
defaultProjectName:
project: {
default_project_name:
configuration?.settings?.project?.default_project_name,
directory: configuration?.settings?.project?.directory,
},
commandBar: {
includeSettings: configuration?.settings?.command_bar?.include_settings,
command_bar: {
include_settings: configuration?.settings?.command_bar?.include_settings,
},
}
}
@ -73,29 +75,31 @@ export function projectConfigurationToSettingsPayload(
): DeepPartial<SaveSettingsPayload> {
return {
app: {
appearance: {
// do not read in `theme`, because it is blocked on the project level
themeColor: configuration?.settings?.app?.appearance?.color
color: configuration?.settings?.app?.appearance?.color
? configuration?.settings?.app?.appearance?.color.toString()
: undefined,
onboardingStatus: configuration?.settings?.app?.onboarding_status,
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
},
onboarding_status: configuration?.settings?.app?.onboarding_status,
dismiss_web_banner: configuration?.settings?.app?.dismiss_web_banner,
stream_idle_mode: configuration?.settings?.app?.stream_idle_mode,
},
modeling: {
defaultUnit: configuration?.settings?.modeling?.base_unit,
mouseControls: mouseControlsToCameraSystem(
base_unit: configuration?.settings?.modeling?.base_unit,
mouse_controls: mouseControlsToCameraSystem(
configuration?.settings?.modeling?.mouse_controls
),
highlightEdges: configuration?.settings?.modeling?.highlight_edges,
showDebugPanel: configuration?.settings?.modeling?.show_debug_panel,
highlight_edges: configuration?.settings?.modeling?.highlight_edges,
show_debug_panel: configuration?.settings?.modeling?.show_debug_panel,
enable_ssao: configuration?.settings?.modeling?.enable_ssao,
},
textEditor: {
textWrapping: configuration?.settings?.text_editor?.text_wrapping,
blinkingCursor: configuration?.settings?.text_editor?.blinking_cursor,
text_editor: {
text_wrapping: configuration?.settings?.text_editor?.text_wrapping,
blinking_cursor: configuration?.settings?.text_editor?.blinking_cursor,
},
commandBar: {
includeSettings: configuration?.settings?.command_bar?.include_settings,
command_bar: {
include_settings: configuration?.settings?.command_bar?.include_settings,
},
}
}
@ -181,7 +185,7 @@ export async function loadAndValidateSettings(
// Because getting the default directory is async, we need to set it after
if (onDesktop) {
settings.app.projectDirectory.default = await getInitialDefaultDir()
settings.project.directory.default = await getInitialDefaultDir()
}
settingsNext = setSettingsAtLevel(

View File

@ -84,13 +84,13 @@ export const settingsMachine = setup({
return newContext
}),
setThemeClass: ({ context }) => {
const currentTheme = context.app.theme.current ?? Themes.System
const currentTheme = context.app.appearance.theme.current ?? Themes.System
setThemeClass(
currentTheme === Themes.System ? getSystemTheme() : currentTheme
)
},
setEngineCameraProjection: ({ context }) => {
const newCurrentProjection = context.modeling.cameraProjection.current
const newCurrentProjection = context.modeling.camera_projection.current
sceneInfra.camControls.setEngineCameraProjection(newCurrentProjection)
},
},

View File

@ -79,13 +79,13 @@ const Home = () => {
send({
type: 'assign',
data: {
defaultProjectName: settings.projects.defaultProjectName.current,
defaultDirectory: settings.app.projectDirectory.current,
defaultProjectName: settings.project.default_project_name.current,
defaultDirectory: settings.project.directory.current,
},
})
}, [
settings.app.projectDirectory.current,
settings.projects.defaultProjectName.current,
settings.project.directory.current,
settings.project.default_project_name.current,
send,
])
@ -134,7 +134,7 @@ const Home = () => {
groupId: 'projects',
name: 'Create project',
argDefaultValues: {
name: settings.projects.defaultProjectName.current,
name: settings.project.default_project_name.current,
},
},
})
@ -207,7 +207,7 @@ const Home = () => {
to={`${PATHS.HOME + PATHS.SETTINGS_USER}#projectDirectory`}
className="text-chalkboard-90 dark:text-chalkboard-20 underline underline-offset-2"
>
{settings.app.projectDirectory.current}
{settings.project.directory.current}
</Link>
.
</p>

View File

@ -16,7 +16,7 @@ export default function Units() {
send,
state: {
context: {
modeling: { mouseControls },
modeling: { mouse_controls },
},
},
},
@ -38,10 +38,10 @@ export default function Units() {
<select
id="camera-controls"
className="block w-full px-3 py-1 bg-transparent border border-chalkboard-30"
value={mouseControls.current}
value={mouse_controls.current}
onChange={(e) => {
send({
type: 'set.modeling.mouseControls',
type: 'set.modeling.mouse_controls',
data: {
level: 'user',
value: e.target.value as CameraSystem,
@ -58,15 +58,15 @@ export default function Units() {
<ul className="mx-4 my-2 text-sm leading-relaxed">
<li>
<strong>Pan:</strong>{' '}
{cameraMouseDragGuards[mouseControls.current].pan.description}
{cameraMouseDragGuards[mouse_controls.current].pan.description}
</li>
<li>
<strong>Zoom:</strong>{' '}
{cameraMouseDragGuards[mouseControls.current].zoom.description}
{cameraMouseDragGuards[mouse_controls.current].zoom.description}
</li>
<li>
<strong>Rotate:</strong>{' '}
{cameraMouseDragGuards[mouseControls.current].rotate.description}
{cameraMouseDragGuards[mouse_controls.current].rotate.description}
</li>
</ul>
</SettingsSection>

View File

@ -126,7 +126,9 @@ function OnboardingIntroductionInner() {
settings: {
state: {
context: {
app: { theme },
app: {
appearance: { theme },
},
},
},
},

View File

@ -11,10 +11,12 @@ export default function OnboardingParametricModeling() {
settings: {
context: {
app: {
appearance: {
theme: { current: theme },
},
},
},
},
} = useSettingsAuthContext()
const getImageTheme = () =>
theme === Themes.Light ||

View File

@ -13,7 +13,7 @@ export default function Units() {
settings: {
send,
context: {
modeling: { defaultUnit },
modeling: { base_unit },
},
},
} = useSettingsAuthContext()
@ -29,10 +29,10 @@ export default function Units() {
<select
id="base-unit"
className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
value={defaultUnit.user}
value={base_unit.user}
onChange={(e) => {
send({
type: 'set.modeling.defaultUnit',
type: 'set.modeling.base_unit',
data: {
level: 'user',
value: e.target.value as BaseUnit,

View File

@ -116,7 +116,7 @@ export function useNextClick(newStatus: string) {
return useCallback(() => {
send({
type: 'set.app.onboardingStatus',
type: 'set.app.onboarding_status',
data: { level: 'user', value: newStatus },
})
navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus)
@ -132,7 +132,7 @@ export function useDismiss() {
const settingsCallback = useCallback(() => {
send({
type: 'set.app.onboardingStatus',
type: 'set.app.onboarding_status',
data: { level: 'user', value: 'dismissed' },
})
}, [send])
@ -143,7 +143,7 @@ export function useDismiss() {
*/
useEffect(() => {
if (
state.context.app.onboardingStatus.user === 'dismissed' &&
state.context.app.onboarding_status.user === 'dismissed' &&
state.matches('idle')
) {
navigate(filePath)

View File

@ -26,7 +26,9 @@ const SignIn = () => {
settings: {
state: {
context: {
app: { theme },
app: {
appearance: { theme },
},
},
},
},