Rearchitect settings system to be scoped (#1956)

* BROKEN: start of scopes for each setting

* Clean up later: mostly-functional scoped settings!
Broken command bar, unimplemented generated settings components

* Working persisted project settings in-folder

* Start working toward automatic commands and settings UI

* Relatively stable, settings-menu-editable

* Settings persistence tweaks after merge

* Custom settings UI working properly, cleaner types

* Allow boolean command types, create Settings UI for them

* Add support for option and string Settings input types

* Proof of concept settings from command bar

* Add all settings to command bar

* Allow settings to be hidden on a level

* Better command titles for settings

* Hide the settings the settings from the commands bar

* Derive command defaultValue from *current* settingsMachine context

* Fix generated settings UI for 'options' type settings

* Pretty settings modal 💅

* Allow for rollback to parent level setting

* fmt

* Fix tsc errors not related to loading from localStorage

* Better setting descriptions, better buttons

* Make displayName searchable in command bar

* Consolidate constants, get working in browser

* Start fixing tests, better types for saved settings payloads

* Fix playwright tests

* Add a test for the settings modal

* Add AtLeast to codespell ignore list

* Goofed merge of codespellrc

* Try fixing linux E2E tests

* Make codespellrc word lowercase

* fmt

* Fix data-testid in Tauri test

* Don't set text settings if nothing changed

* Turn off unimplemented settings

* Allow for multiple "execution-done" messages to have appeared in snapshot tests

* Try fixing up snapshot tests

* Switch from .json to .toml settings file format

* Use a different method for overriding the default units

* Try to force using the new common storage state in snapshot tests

* Update tests to use TOML

* fmt and remove console logs

* Restore units to export

* tsc errors, make snapshot tests use TOML

* Ensure that snapshot tests use the basicStorageState

* Re-organize use of test.use()

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Update snapshots one more time since lighting changed

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Fix broken "Show in folder" for project-level settings

* Fire all relevant actions after settings reset

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Properly reset the default directory

* Hide settings by platform

* Actually honor showDebugPanel

* Unify settings hiding logic

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* fix first extrusion snapshot

* another attempt to fix extrustion snapshot

* Rerun test suite

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* trigger CI

* more extrusion stuff

* Replace resetSettings console log with comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
This commit is contained in:
Frank Noirot
2024-04-02 10:29:34 -04:00
committed by GitHub
parent 77f51530f9
commit d605d4a029
67 changed files with 2470 additions and 1392 deletions

View File

@ -10,25 +10,17 @@ import { appConfigDir, documentDir, homeDir, sep } from '@tauri-apps/api/path'
import { isTauri } from './isTauri'
import { type ProjectWithEntryPointMetadata } from 'lib/types'
import { metadata } from 'tauri-plugin-fs-extra-api'
import { settingsMachine } from 'machines/settingsMachine'
import { ContextFrom } from 'xstate'
import { SETTINGS_FILE_NAME } from 'lib/constants'
const PROJECT_FOLDER = 'zoo-modeling-app-projects'
export const FILE_EXT = '.kcl'
export const PROJECT_ENTRYPOINT = 'main' + FILE_EXT
const INDEX_IDENTIFIER = '$n' // $nn.. will pad the number with 0s
export const MAX_PADDING = 7
const RELEVANT_FILE_TYPES = [
'kcl',
'fbx',
'gltf',
'glb',
'obj',
'ply',
'step',
'stl',
]
import {
FILE_EXT,
INDEX_IDENTIFIER,
MAX_PADDING,
PROJECT_ENTRYPOINT,
PROJECT_FOLDER,
RELEVANT_FILE_TYPES,
SETTINGS_FILE_EXT,
} from 'lib/constants'
import { SaveSettingsPayload, SettingsLevel } from './settings/settingsTypes'
import * as TOML from '@iarna/toml'
type PathWithPossibleError = {
path: string | null
@ -375,25 +367,18 @@ function getPaddedIdentifierRegExp() {
return new RegExp(`${escapedIdentifier}(${escapedIdentifier.slice(-1)}*)`)
}
export async function getSettingsFilePath() {
const dir = await appConfigDir()
return dir + SETTINGS_FILE_NAME
}
export async function writeToSettingsFile(
settings: ContextFrom<typeof settingsMachine>
export async function getUserSettingsFilePath(
filename: string = SETTINGS_FILE_EXT
) {
return writeTextFile(
await getSettingsFilePath(),
JSON.stringify(settings, null, 2)
)
const dir = await appConfigDir()
return dir + filename
}
export async function readSettingsFile(): Promise<ContextFrom<
typeof settingsMachine
> | null> {
const dir = await appConfigDir()
const path = dir + SETTINGS_FILE_NAME
export async function readSettingsFile(
path: string
): Promise<Partial<SaveSettingsPayload>> {
const dir = path.slice(0, path.lastIndexOf(sep))
const dirExists = await exists(dir)
if (!dirExists) {
await createDir(dir, { recursive: true })
@ -403,15 +388,39 @@ export async function readSettingsFile(): Promise<ContextFrom<
if (!settingsExist) {
console.log(`Settings file does not exist at ${path}`)
await writeToSettingsFile(settingsMachine.initialState.context)
return null
return {}
}
try {
const settings = await readTextFile(path)
return JSON.parse(settings)
// We expect the settings to be under a top-level [settings] key
return TOML.parse(settings).settings as Partial<SaveSettingsPayload>
} catch (e) {
console.error('Error reading settings file:', e)
return null
return {}
}
}
export async function getSettingsFilePaths(
projectPath?: string
): Promise<Partial<Record<SettingsLevel, string>>> {
const { user, project } = await getSettingsFolderPaths(projectPath)
return {
user: user + 'user' + SETTINGS_FILE_EXT,
project:
project !== undefined
? project + (isTauri() ? sep : '/') + 'project' + SETTINGS_FILE_EXT
: undefined,
}
}
export async function getSettingsFolderPaths(projectPath?: string) {
const user = isTauri() ? await appConfigDir() : '/'
const project = projectPath !== undefined ? projectPath : undefined
return {
user,
project,
}
}