Compare commits
4 Commits
codex/fix-
...
remove-old
Author | SHA1 | Date | |
---|---|---|---|
0f8dfe528f | |||
006663d83c | |||
8e248f43ab | |||
09ef1d5f10 |
@ -12,9 +12,10 @@ import { CmdBarFixture } from './cmdBarFixture'
|
|||||||
import { EditorFixture } from './editorFixture'
|
import { EditorFixture } from './editorFixture'
|
||||||
import { ToolbarFixture } from './toolbarFixture'
|
import { ToolbarFixture } from './toolbarFixture'
|
||||||
import { SceneFixture } from './sceneFixture'
|
import { SceneFixture } from './sceneFixture'
|
||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
|
||||||
import { HomePageFixture } from './homePageFixture'
|
import { HomePageFixture } from './homePageFixture'
|
||||||
import { unsafeTypedKeys } from 'lib/utils'
|
import { unsafeTypedKeys } from 'lib/utils'
|
||||||
|
import { DeepPartial } from 'lib/types'
|
||||||
|
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
||||||
|
|
||||||
export class AuthenticatedApp {
|
export class AuthenticatedApp {
|
||||||
public readonly page: Page
|
public readonly page: Page
|
||||||
@ -78,7 +79,7 @@ export class AuthenticatedTronApp {
|
|||||||
fixtures: Partial<Fixtures>
|
fixtures: Partial<Fixtures>
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: DeepPartial<Settings>
|
||||||
} = { fixtures: {} }
|
} = { fixtures: {} }
|
||||||
) {
|
) {
|
||||||
const { electronApp, page, context, dir } = await setupElectron({
|
const { electronApp, page, context, dir } = await setupElectron({
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from './zoo-test'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fsp from 'fs/promises'
|
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 { bracket } from 'lib/exampleKcl'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
import {
|
import {
|
||||||
@ -10,7 +15,6 @@ import {
|
|||||||
TEST_SETTINGS_ONBOARDING_EXPORT,
|
TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
|
||||||
import { expectPixelColor } from './fixtures/sceneFixture'
|
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||||
|
|
||||||
// Because onboarding relies on an app setting we need to set it as incompletel
|
// Because onboarding relies on an app setting we need to set it as incompletel
|
||||||
@ -22,7 +26,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -63,7 +67,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -108,7 +112,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -158,7 +162,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -172,7 +176,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_START,
|
settings: TEST_SETTINGS_ONBOARDING_START,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -208,7 +212,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: '/export',
|
onboarding_status: '/export',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -225,7 +229,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -263,7 +267,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: '/parametric-modeling',
|
onboarding_status: '/parametric-modeling',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -313,7 +317,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -326,7 +330,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -386,7 +390,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'incomplete',
|
onboarding_status: 'incomplete',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
@ -400,7 +404,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
settings: TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
@ -442,7 +446,7 @@ test.fixme(
|
|||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
onboardingStatus: 'dismissed',
|
onboarding_status: 'dismissed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
import { secrets } from './secrets'
|
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 { Models } from '@kittycad/lib'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
@ -12,7 +12,6 @@ import {
|
|||||||
TEST_SETTINGS,
|
TEST_SETTINGS,
|
||||||
TEST_SETTINGS_KEY,
|
TEST_SETTINGS_KEY,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
// reducedMotion kills animations, which speeds up tests and reduces flakiness
|
// reducedMotion kills animations, which speeds up tests and reduces flakiness
|
||||||
@ -29,7 +28,7 @@ test.beforeEach(async ({ page }) => {
|
|||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({ settings: TEST_SETTINGS }),
|
settings: settingsToToml({ settings: TEST_SETTINGS }),
|
||||||
IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY: IS_PLAYWRIGHT_KEY,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -704,12 +703,12 @@ test.describe(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
modeling: {
|
modeling: {
|
||||||
...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,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
modeling: {
|
modeling: {
|
||||||
...TEST_SETTINGS.modeling,
|
...TEST_SETTINGS.modeling,
|
||||||
showScaleGrid: true,
|
show_scale_grid: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
@ -1,78 +1,83 @@
|
|||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
|
import { DeepPartial } from 'lib/types'
|
||||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||||
|
import { Settings } from 'wasm-lib/kcl/bindings/Settings'
|
||||||
|
|
||||||
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
export const IS_PLAYWRIGHT_KEY = 'playwright'
|
||||||
|
|
||||||
export const TEST_SETTINGS_KEY = '/settings.toml'
|
export const TEST_SETTINGS_KEY = '/settings.toml'
|
||||||
export const TEST_SETTINGS = {
|
export const TEST_SETTINGS = {
|
||||||
app: {
|
app: {
|
||||||
|
onboarding_status: 'dismissed',
|
||||||
|
appearance: {
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
onboardingStatus: 'dismissed',
|
},
|
||||||
projectDirectory: '',
|
show_debug_panel: true,
|
||||||
enableSSAO: false,
|
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: 'in',
|
base_unit: 'in',
|
||||||
mouseControls: 'Zoo',
|
mouse_controls: 'zoo',
|
||||||
cameraProjection: 'perspective',
|
camera_projection: 'perspective',
|
||||||
showDebugPanel: true,
|
enable_ssao: false,
|
||||||
},
|
},
|
||||||
projects: {
|
project: {
|
||||||
defaultProjectName: 'project-$nnn',
|
default_project_name: 'project-$nnn',
|
||||||
|
directory: '',
|
||||||
},
|
},
|
||||||
textEditor: {
|
text_editor: {
|
||||||
textWrapping: true,
|
text_wrapping: true,
|
||||||
},
|
},
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_USER_MENU = {
|
export const TEST_SETTINGS_ONBOARDING_USER_MENU = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.USER_MENU },
|
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.USER_MENU },
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
|
export const TEST_SETTINGS_ONBOARDING_EXPORT = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboardingStatus: onboardingPaths.EXPORT },
|
app: { ...TEST_SETTINGS.app, onboarding_status: onboardingPaths.EXPORT },
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING = {
|
export const TEST_SETTINGS_ONBOARDING_PARAMETRIC_MODELING = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...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 = {
|
export const TEST_SETTINGS_ONBOARDING_START = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, onboardingStatus: '' },
|
app: { ...TEST_SETTINGS.app, onboarding_status: '' },
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_SETTINGS_DEFAULT_THEME = {
|
export const TEST_SETTINGS_DEFAULT_THEME = {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: { ...TEST_SETTINGS.app, theme: Themes.System },
|
app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } },
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_SETTINGS_CORRUPTED = {
|
export const TEST_SETTINGS_CORRUPTED = {
|
||||||
app: {
|
app: {
|
||||||
|
onboarding_status: 'dismissed',
|
||||||
|
appearance: {
|
||||||
theme: Themes.Dark,
|
theme: Themes.Dark,
|
||||||
onboardingStatus: 'dismissed',
|
},
|
||||||
projectDirectory: 123 as any,
|
show_debug_panel: true,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: 'invalid' as any,
|
base_unit: 'invalid' as any,
|
||||||
mouseControls: `() => alert('hack the planet')` as any,
|
mouse_controls: `() => alert('hack the planet')` as any,
|
||||||
cameraProjection: 'perspective',
|
camera_projection: 'perspective',
|
||||||
showDebugPanel: true,
|
|
||||||
},
|
},
|
||||||
projects: {
|
project: {
|
||||||
defaultProjectName: false as any,
|
default_project_name: false as any,
|
||||||
|
directory: 123 as any,
|
||||||
},
|
},
|
||||||
textEditor: {
|
text_editor: {
|
||||||
textWrapping: true,
|
text_wrapping: true,
|
||||||
},
|
},
|
||||||
} satisfies Partial<SaveSettingsPayload>
|
} satisfies DeepPartial<Settings>
|
||||||
|
|
||||||
export const TEST_CODE_GIZMO = `part001 = startSketchOn('XZ')
|
export const TEST_CODE_GIZMO = `part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([20, 0], %)
|
|> startProfileAt([20, 0], %)
|
||||||
|
@ -23,11 +23,13 @@ import {
|
|||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
|
||||||
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
import { isErrorWhitelisted } from './lib/console-error-whitelist'
|
||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
import { reportRejection } from 'lib/trap'
|
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) => {
|
const toNormalizedCode = (text: string) => {
|
||||||
return text.replace(/\s+/g, '')
|
return text.replace(/\s+/g, '')
|
||||||
@ -884,19 +886,23 @@ export async function setup(
|
|||||||
{
|
{
|
||||||
token: secrets.token,
|
token: secrets.token,
|
||||||
settingsKey: TEST_SETTINGS_KEY,
|
settingsKey: TEST_SETTINGS_KEY,
|
||||||
settings: TOML.stringify({
|
settings: settingsToToml({
|
||||||
settings: {
|
settings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.projects,
|
...TEST_SETTINGS.app,
|
||||||
projectDirectory: TEST_SETTINGS.app.projectDirectory,
|
onboarding_status: 'dismissed',
|
||||||
onboardingStatus: 'dismissed',
|
appearance: {
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
},
|
},
|
||||||
} as Partial<SaveSettingsPayload>,
|
},
|
||||||
|
project: {
|
||||||
|
directory: TEST_SETTINGS.project.directory,
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
IS_PLAYWRIGHT_KEY,
|
IS_PLAYWRIGHT_KEY,
|
||||||
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app.projectDirectory,
|
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project.directory,
|
||||||
PERSIST_MODELING_CONTEXT,
|
PERSIST_MODELING_CONTEXT,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -931,7 +937,7 @@ export async function setupElectron({
|
|||||||
testInfo: TestInfo
|
testInfo: TestInfo
|
||||||
folderSetupFn?: (projectDirName: string) => Promise<void>
|
folderSetupFn?: (projectDirName: string) => Promise<void>
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: DeepPartial<Settings>
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
electronApp: ElectronApplication
|
electronApp: ElectronApplication
|
||||||
context: BrowserContext
|
context: BrowserContext
|
||||||
@ -978,7 +984,7 @@ export async function setupElectron({
|
|||||||
|
|
||||||
if (cleanProjectDir) {
|
if (cleanProjectDir) {
|
||||||
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
const tempSettingsFilePath = path.join(projectDirName, SETTINGS_FILE_NAME)
|
||||||
const settingsOverrides = TOML.stringify(
|
const settingsOverrides = settingsToToml(
|
||||||
appSettings
|
appSettings
|
||||||
? {
|
? {
|
||||||
settings: {
|
settings: {
|
||||||
@ -986,9 +992,11 @@ export async function setupElectron({
|
|||||||
...appSettings,
|
...appSettings,
|
||||||
app: {
|
app: {
|
||||||
...TEST_SETTINGS.app,
|
...TEST_SETTINGS.app,
|
||||||
projectDirectory: projectDirName,
|
|
||||||
...appSettings.app,
|
...appSettings.app,
|
||||||
},
|
},
|
||||||
|
project: {
|
||||||
|
directory: projectDirName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@ -996,7 +1004,9 @@ export async function setupElectron({
|
|||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
app: {
|
app: {
|
||||||
...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)
|
.toBe(lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function settingsToToml(settings: DeepPartial<Configuration>) {
|
||||||
|
return TOML.stringify(settings)
|
||||||
|
}
|
||||||
|
@ -31,13 +31,13 @@ test.describe('Testing settings', () => {
|
|||||||
)
|
)
|
||||||
) as { settings: SaveSettingsPayload }
|
) 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
|
// Check that the invalid settings were changed to good defaults
|
||||||
expect(storedSettings.settings?.modeling?.defaultUnit).toBe('in')
|
expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
|
||||||
expect(storedSettings.settings?.modeling?.mouseControls).toBe('Zoo')
|
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('Zoo')
|
||||||
expect(storedSettings.settings?.app?.projectDirectory).toBe('')
|
expect(storedSettings.settings?.project?.directory).toBe('')
|
||||||
expect(storedSettings.settings?.projects?.defaultProjectName).toBe(
|
expect(storedSettings.settings?.project?.default_project_name).toBe(
|
||||||
'project-$nnn'
|
'project-$nnn'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -374,7 +374,9 @@ test.describe('Testing settings', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
themeColor: '259',
|
appearance: {
|
||||||
|
color: 259,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -400,9 +402,11 @@ test.describe('Testing settings', () => {
|
|||||||
tag: '@electron',
|
tag: '@electron',
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
|
appearance: {
|
||||||
// Doesn't matter what you set it to. It will
|
// Doesn't matter what you set it to. It will
|
||||||
// default to 264.5
|
// default to 264.5
|
||||||
themeColor: '0',
|
color: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -832,7 +836,7 @@ test.describe('Testing settings', () => {
|
|||||||
// but "show debug panel" set to false
|
// but "show debug panel" set to false
|
||||||
appSettings: {
|
appSettings: {
|
||||||
...TEST_SETTINGS,
|
...TEST_SETTINGS,
|
||||||
modeling: { ...TEST_SETTINGS.modeling, showDebugPanel: false },
|
app: { ...TEST_SETTINGS.app, show_debug_panel: false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async ({ context, page, homePage }) => {
|
async ({ context, page, homePage }) => {
|
||||||
|
@ -18,7 +18,8 @@ import {
|
|||||||
AuthenticatedApp,
|
AuthenticatedApp,
|
||||||
} from './fixtures/fixtureSetup'
|
} 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'
|
export { expect } from '@playwright/test'
|
||||||
|
|
||||||
declare module '@playwright/test' {
|
declare module '@playwright/test' {
|
||||||
@ -45,7 +46,7 @@ export type BrowserContext = BrowserContextPlaywright
|
|||||||
export type Page = PagePlaywright
|
export type Page = PagePlaywright
|
||||||
export type TestDetails = TestDetailsPlaywright & {
|
export type TestDetails = TestDetailsPlaywright & {
|
||||||
cleanProjectDir?: boolean
|
cleanProjectDir?: boolean
|
||||||
appSettings?: Partial<SaveSettingsPayload>
|
appSettings?: DeepPartial<Settings>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
// Our custom decorated Zoo test object. Makes it easier to add fixtures, and
|
||||||
|
@ -54,7 +54,7 @@ export function App() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
app: { onboardingStatus },
|
app: { onboarding_status },
|
||||||
} = settings.context
|
} = settings.context
|
||||||
|
|
||||||
useHotkeys('backspace', (e) => {
|
useHotkeys('backspace', (e) => {
|
||||||
@ -69,7 +69,7 @@ export function App() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
||||||
(p) => p === onboardingStatus.current
|
(p) => p === onboarding_status.current
|
||||||
)
|
)
|
||||||
? 'opacity-20'
|
? 'opacity-20'
|
||||||
: ''
|
: ''
|
||||||
|
@ -77,7 +77,7 @@ export const ClientSideScene = ({
|
|||||||
}: {
|
}: {
|
||||||
cameraControls: ReturnType<
|
cameraControls: ReturnType<
|
||||||
typeof useSettingsAuthContext
|
typeof useSettingsAuthContext
|
||||||
>['settings']['context']['modeling']['mouseControls']['current']
|
>['settings']['context']['modeling']['mouse_controls']['current']
|
||||||
}) => {
|
}) => {
|
||||||
const canvasRef = useRef<HTMLDivElement>(null)
|
const canvasRef = useRef<HTMLDivElement>(null)
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
|
@ -5,21 +5,21 @@ import { useEffect, useState } from 'react'
|
|||||||
export function CameraProjectionToggle() {
|
export function CameraProjectionToggle() {
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const isCameraProjectionPerspective =
|
const isCameraProjectionPerspective =
|
||||||
settings.context.modeling.cameraProjection.current === 'perspective'
|
settings.context.modeling.camera_projection.current === 'perspective'
|
||||||
const [checked, setChecked] = useState(isCameraProjectionPerspective)
|
const [checked, setChecked] = useState(isCameraProjectionPerspective)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChecked(
|
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 (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={(newValue) => {
|
onChange={(newValue) => {
|
||||||
settings.send({
|
settings.send({
|
||||||
type: 'set.modeling.cameraProjection',
|
type: 'set.modeling.camera_projection',
|
||||||
data: {
|
data: {
|
||||||
level: 'user',
|
level: 'user',
|
||||||
value: newValue ? 'perspective' : 'orthographic',
|
value: newValue ? 'perspective' : 'orthographic',
|
||||||
|
@ -115,9 +115,9 @@ function CommandBarKclInput({
|
|||||||
: defaultValue.length,
|
: defaultValue.length,
|
||||||
},
|
},
|
||||||
theme:
|
theme:
|
||||||
settings.context.app.theme.current === 'system'
|
settings.context.app.appearance.theme.current === 'system'
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: settings.context.app.theme.current,
|
: settings.context.app.appearance.theme.current,
|
||||||
extensions: [
|
extensions: [
|
||||||
varMentionsExtension,
|
varMentionsExtension,
|
||||||
EditorView.updateListener.of((vu: ViewUpdate) => {
|
EditorView.updateListener.of((vu: ViewUpdate) => {
|
||||||
|
@ -6,7 +6,7 @@ import { useState } from 'react'
|
|||||||
const DownloadAppBanner = () => {
|
const DownloadAppBanner = () => {
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||||
settings.context.app.dismissWebBanner.current
|
settings.context.app.dismiss_web_banner.current
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -315,7 +315,7 @@ export const FileMachineProvider = ({
|
|||||||
// with the sample's setting.
|
// with the sample's setting.
|
||||||
if (data.sampleUnits) {
|
if (data.sampleUnits) {
|
||||||
settings.send({
|
settings.send({
|
||||||
type: 'set.modeling.defaultUnit',
|
type: 'set.modeling.base_unit',
|
||||||
data: {
|
data: {
|
||||||
level: 'project',
|
level: 'project',
|
||||||
value: data.sampleUnits,
|
value: data.sampleUnits,
|
||||||
|
@ -107,7 +107,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
|
|||||||
as="button"
|
as="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
settings.send({
|
settings.send({
|
||||||
type: 'set.app.onboardingStatus',
|
type: 'set.app.onboarding_status',
|
||||||
data: {
|
data: {
|
||||||
value: '',
|
value: '',
|
||||||
level: 'user',
|
level: 'user',
|
||||||
|
@ -111,12 +111,15 @@ export const ModelingMachineProvider = ({
|
|||||||
auth,
|
auth,
|
||||||
settings: {
|
settings: {
|
||||||
context: {
|
context: {
|
||||||
app: { theme, enableSSAO },
|
app: {
|
||||||
|
appearance: { theme },
|
||||||
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit,
|
base_unit,
|
||||||
cameraProjection,
|
camera_projection,
|
||||||
highlightEdges,
|
highlight_edges,
|
||||||
showScaleGrid,
|
show_scale_grid,
|
||||||
|
enable_ssao,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -172,7 +175,7 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
|
|
||||||
if (cameraProjection.current === 'perspective') {
|
if (camera_projection.current === 'perspective') {
|
||||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,7 +511,7 @@ export const ModelingMachineProvider = ({
|
|||||||
format.type === 'stl' ||
|
format.type === 'stl' ||
|
||||||
format.type === 'ply'
|
format.type === 'ply'
|
||||||
) {
|
) {
|
||||||
format.units = defaultUnit.current
|
format.units = base_unit.current
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.type === 'ply' || format.type === 'stl') {
|
if (format.type === 'ply' || format.type === 'stl') {
|
||||||
@ -533,7 +536,7 @@ export const ModelingMachineProvider = ({
|
|||||||
token,
|
token,
|
||||||
settings: {
|
settings: {
|
||||||
theme: theme.current,
|
theme: theme.current,
|
||||||
highlightEdges: highlightEdges.current,
|
highlightEdges: highlight_edges.current,
|
||||||
},
|
},
|
||||||
}).catch(reportRejection)
|
}).catch(reportRejection)
|
||||||
},
|
},
|
||||||
@ -1134,10 +1137,10 @@ export const ModelingMachineProvider = ({
|
|||||||
{
|
{
|
||||||
pool: pool,
|
pool: pool,
|
||||||
theme: theme.current,
|
theme: theme.current,
|
||||||
highlightEdges: highlightEdges.current,
|
highlightEdges: highlight_edges.current,
|
||||||
enableSSAO: enableSSAO.current,
|
enableSSAO: enable_ssao.current,
|
||||||
showScaleGrid: showScaleGrid.current,
|
showScaleGrid: show_scale_grid.current,
|
||||||
cameraProjection: cameraProjection.current,
|
cameraProjection: camera_projection.current,
|
||||||
},
|
},
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
|
@ -69,7 +69,7 @@ export const ModelingPane = ({
|
|||||||
...props
|
...props
|
||||||
}: ModelingPaneProps) => {
|
}: ModelingPaneProps) => {
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const onboardingStatus = settings.context.app.onboardingStatus
|
const onboardingStatus = settings.context.app.onboarding_status
|
||||||
const pointerEventsCssClass =
|
const pointerEventsCssClass =
|
||||||
onboardingStatus.current === onboardingPaths.CAMERA
|
onboardingStatus.current === onboardingPaths.CAMERA
|
||||||
? 'pointer-events-none '
|
? 'pointer-events-none '
|
||||||
|
@ -69,9 +69,9 @@ export const KclEditorPane = () => {
|
|||||||
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
|
const lastSelectionEvent = useSelector(kclEditorActor, selectionEventSelector)
|
||||||
const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector)
|
const editorIsMounted = useSelector(kclEditorActor, editorIsMountedSelector)
|
||||||
const theme =
|
const theme =
|
||||||
context.app.theme.current === Themes.System
|
context.app.appearance.theme.current === Themes.System
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: context.app.theme.current
|
: context.app.appearance.theme.current
|
||||||
const { copilotLSP, kclLSP } = useLspContext()
|
const { copilotLSP, kclLSP } = useLspContext()
|
||||||
|
|
||||||
// Since these already exist in the editor, we don't need to define them
|
// Since these already exist in the editor, we don't need to define them
|
||||||
@ -104,8 +104,8 @@ export const KclEditorPane = () => {
|
|||||||
})
|
})
|
||||||
}, [editorIsMounted, lastSelectionEvent])
|
}, [editorIsMounted, lastSelectionEvent])
|
||||||
|
|
||||||
const textWrapping = context.textEditor.textWrapping
|
const textWrapping = context.text_editor.text_wrapping
|
||||||
const cursorBlinking = context.textEditor.blinkingCursor
|
const cursorBlinking = context.text_editor.blinking_cursor
|
||||||
// DO NOT ADD THE CODEMIRROR HOTKEYS HERE TO THE DEPENDENCY ARRAY
|
// DO NOT ADD THE CODEMIRROR HOTKEYS HERE TO THE DEPENDENCY ARRAY
|
||||||
// It reloads the editor every time we do _anything_ in the editor
|
// It reloads the editor every time we do _anything_ in the editor
|
||||||
// I have no idea why.
|
// I have no idea why.
|
||||||
|
@ -210,6 +210,6 @@ export const sidebarPanes: SidebarPane[] = [
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
keybinding: 'Shift + D',
|
keybinding: 'Shift + D',
|
||||||
hide: ({ settings }) => !settings.modeling.showDebugPanel.current,
|
hide: ({ settings }) => !settings.app.show_debug_panel.current,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -40,14 +40,14 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
|||||||
const { commandBarSend } = useCommandsContext()
|
const { commandBarSend } = useCommandsContext()
|
||||||
const kclContext = useKclContext()
|
const kclContext = useKclContext()
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const onboardingStatus = settings.context.app.onboardingStatus
|
const onboardingStatus = settings.context.app.onboarding_status
|
||||||
const { send, context } = useModelingContext()
|
const { send, context } = useModelingContext()
|
||||||
const pointerEventsCssClass =
|
const pointerEventsCssClass =
|
||||||
onboardingStatus.current === onboardingPaths.CAMERA ||
|
onboardingStatus.current === onboardingPaths.CAMERA ||
|
||||||
context.store?.openPanes.length === 0
|
context.store?.openPanes.length === 0
|
||||||
? 'pointer-events-none '
|
? 'pointer-events-none '
|
||||||
: 'pointer-events-auto '
|
: 'pointer-events-auto '
|
||||||
const showDebugPanel = settings.context.modeling.showDebugPanel
|
const showDebugPanel = settings.context.app.show_debug_panel
|
||||||
|
|
||||||
const paneCallbackProps = useMemo(
|
const paneCallbackProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -79,11 +79,8 @@ const ProjectsContextDesktop = ({
|
|||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(
|
console.log('project directory changed', settings.project.directory.current)
|
||||||
'project directory changed',
|
}, [settings.project.directory.current])
|
||||||
settings.app.projectDirectory.current
|
|
||||||
)
|
|
||||||
}, [settings.app.projectDirectory.current])
|
|
||||||
|
|
||||||
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
|
const [projectsLoaderTrigger, setProjectsLoaderTrigger] = useState(0)
|
||||||
const { projectPaths, projectsDir } = useProjectsLoader([
|
const { projectPaths, projectsDir } = useProjectsLoader([
|
||||||
@ -192,7 +189,7 @@ const ProjectsContextDesktop = ({
|
|||||||
let name = (
|
let name = (
|
||||||
input && 'name' in input && input.name
|
input && 'name' in input && input.name
|
||||||
? input.name
|
? input.name
|
||||||
: settings.projects.defaultProjectName.current
|
: settings.project.default_project_name.current
|
||||||
).trim()
|
).trim()
|
||||||
|
|
||||||
if (doesProjectNameNeedInterpolated(name)) {
|
if (doesProjectNameNeedInterpolated(name)) {
|
||||||
@ -257,8 +254,8 @@ const ProjectsContextDesktop = ({
|
|||||||
{
|
{
|
||||||
input: {
|
input: {
|
||||||
projects: projectPaths,
|
projects: projectPaths,
|
||||||
defaultProjectName: settings.projects.defaultProjectName.current,
|
defaultProjectName: settings.project.default_project_name.current,
|
||||||
defaultDirectory: settings.app.projectDirectory.current,
|
defaultDirectory: settings.project.directory.current,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -61,7 +61,7 @@ export const AllSettingsFields = forwardRef(
|
|||||||
|
|
||||||
function restartOnboarding() {
|
function restartOnboarding() {
|
||||||
send({
|
send({
|
||||||
type: `set.app.onboardingStatus`,
|
type: `set.app.onboarding_status`,
|
||||||
data: { level: 'user', value: '' },
|
data: { level: 'user', value: '' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ export const AllSettingsFields = forwardRef(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function navigateToOnboardingStart() {
|
async function navigateToOnboardingStart() {
|
||||||
if (
|
if (
|
||||||
state.context.app.onboardingStatus.user === '' &&
|
state.context.app.onboarding_status.user === '' &&
|
||||||
state.matches('idle')
|
state.matches('idle')
|
||||||
) {
|
) {
|
||||||
if (isFileSettings) {
|
if (isFileSettings) {
|
||||||
|
@ -122,18 +122,20 @@ export const SettingsAuthProviderBase = ({
|
|||||||
|
|
||||||
setClientSideSceneUnits: ({ context, event }) => {
|
setClientSideSceneUnits: ({ context, event }) => {
|
||||||
const newBaseUnit =
|
const newBaseUnit =
|
||||||
event.type === 'set.modeling.defaultUnit'
|
event.type === 'set.modeling.base_unit'
|
||||||
? (event.data.value as BaseUnit)
|
? (event.data.value as BaseUnit)
|
||||||
: context.modeling.defaultUnit.current
|
: context.modeling.base_unit.current
|
||||||
sceneInfra.baseUnit = newBaseUnit
|
sceneInfra.baseUnit = newBaseUnit
|
||||||
},
|
},
|
||||||
setEngineTheme: ({ context }) => {
|
setEngineTheme: ({ context }) => {
|
||||||
engineCommandManager
|
engineCommandManager
|
||||||
.setTheme(context.app.theme.current)
|
.setTheme(context.app.appearance.theme.current)
|
||||||
.catch(reportRejection)
|
.catch(reportRejection)
|
||||||
},
|
},
|
||||||
setClientTheme: ({ context }) => {
|
setClientTheme: ({ context }) => {
|
||||||
const opposingTheme = getOppositeTheme(context.app.theme.current)
|
const opposingTheme = getOppositeTheme(
|
||||||
|
context.app.appearance.theme.current
|
||||||
|
)
|
||||||
sceneInfra.theme = opposingTheme
|
sceneInfra.theme = opposingTheme
|
||||||
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
|
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
|
||||||
},
|
},
|
||||||
@ -164,12 +166,12 @@ export const SettingsAuthProviderBase = ({
|
|||||||
try {
|
try {
|
||||||
const relevantSetting = (s: typeof settings) => {
|
const relevantSetting = (s: typeof settings) => {
|
||||||
return (
|
return (
|
||||||
s.modeling?.defaultUnit?.current !==
|
s.modeling?.base_unit.current !==
|
||||||
context.modeling.defaultUnit.current ||
|
context.modeling.base_unit.current ||
|
||||||
s.modeling.showScaleGrid.current !==
|
s.modeling.show_scale_grid.current !==
|
||||||
context.modeling.showScaleGrid.current ||
|
context.modeling.show_scale_grid.current ||
|
||||||
s.modeling?.highlightEdges.current !==
|
s.modeling?.highlight_edges.current !==
|
||||||
context.modeling.highlightEdges.current
|
context.modeling.highlight_edges.current
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,9 +182,9 @@ export const SettingsAuthProviderBase = ({
|
|||||||
event.type === 'Reset settings' && relevantSetting(settings)
|
event.type === 'Reset settings' && relevantSetting(settings)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
event.type === 'set.modeling.defaultUnit' ||
|
event.type === 'set.modeling.base_unit' ||
|
||||||
event.type === 'set.modeling.showScaleGrid' ||
|
event.type === 'set.modeling.show_scale_grid' ||
|
||||||
event.type === 'set.modeling.highlightEdges' ||
|
event.type === 'set.modeling.highlight_edges' ||
|
||||||
allSettingsIncludesUnitChange ||
|
allSettingsIncludesUnitChange ||
|
||||||
resetSettingsIncludesUnitChange
|
resetSettingsIncludesUnitChange
|
||||||
) {
|
) {
|
||||||
@ -258,7 +260,7 @@ export const SettingsAuthProviderBase = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If the user wants to hide the settings commands
|
// If the user wants to hide the settings commands
|
||||||
//from the command bar don't add them.
|
//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
|
return
|
||||||
|
|
||||||
const commands = settingsWithCommandConfigs(settingsState.context)
|
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
|
// events outside of the machine that also depend on the machine's context
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = (e: MediaQueryListEvent) => {
|
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)
|
setThemeClass(e.matches ? Themes.Dark : Themes.Light)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,9 +352,9 @@ export const SettingsAuthProviderBase = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
`--primary-hue`,
|
`--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
|
* Update the --cursor-color CSS variable
|
||||||
@ -360,11 +363,11 @@ export const SettingsAuthProviderBase = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
`--cursor-color`,
|
`--cursor-color`,
|
||||||
settingsState.context.textEditor.blinkingCursor.current
|
settingsState.context.text_editor.blinking_cursor.current
|
||||||
? 'auto'
|
? 'auto'
|
||||||
: 'transparent'
|
: 'transparent'
|
||||||
)
|
)
|
||||||
}, [settingsState.context.textEditor.blinkingCursor.current])
|
}, [settingsState.context.text_editor.blinking_cursor.current])
|
||||||
|
|
||||||
// Auth machine setup
|
// Auth machine setup
|
||||||
const [authState, authSend, authActor] = useMachine(
|
const [authState, authSend, authActor] = useMachine(
|
||||||
|
@ -41,7 +41,7 @@ export const Stream = () => {
|
|||||||
const [streamState, setStreamState] = useState(StreamState.Unset)
|
const [streamState, setStreamState] = useState(StreamState.Unset)
|
||||||
const { file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
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 =
|
const isNetworkOkay =
|
||||||
overallState === NetworkHealthState.Ok ||
|
overallState === NetworkHealthState.Ok ||
|
||||||
@ -342,7 +342,7 @@ export const Stream = () => {
|
|||||||
id="video-stream"
|
id="video-stream"
|
||||||
/>
|
/>
|
||||||
<ClientSideScene
|
<ClientSideScene
|
||||||
cameraControls={settings.context.modeling.mouseControls.current}
|
cameraControls={settings.context.modeling.mouse_controls.current}
|
||||||
/>
|
/>
|
||||||
{(streamState === StreamState.Paused ||
|
{(streamState === StreamState.Paused ||
|
||||||
streamState === StreamState.Resuming) && (
|
streamState === StreamState.Resuming) && (
|
||||||
|
@ -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 className="absolute w-[1px] h-[1em] bg-primary right-0 top-1/2 -translate-y-1/2"></div>
|
||||||
</div>
|
</div>
|
||||||
<span className="sr-only">Current units are: </span>
|
<span className="sr-only">Current units are: </span>
|
||||||
{settings.context.modeling.defaultUnit.current}
|
{settings.context.modeling.base_unit.current}
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
<Popover.Panel
|
<Popover.Panel
|
||||||
className={`absolute bottom-full right-0 mb-2 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
|
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"
|
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={() => {
|
onClick={() => {
|
||||||
settings.send({
|
settings.send({
|
||||||
type: 'set.modeling.defaultUnit',
|
type: 'set.modeling.base_unit',
|
||||||
data: {
|
data: {
|
||||||
level: 'project',
|
level: 'project',
|
||||||
value: unit,
|
value: unit,
|
||||||
@ -42,7 +42,7 @@ export function UnitsMenu() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="flex-1">{baseUnitLabels[unit]}</span>
|
<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>
|
<span className="text-chalkboard-60">current</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
@ -10,7 +10,7 @@ export function useResolvedTheme() {
|
|||||||
const {
|
const {
|
||||||
settings: { context },
|
settings: { context },
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
return context.app.theme.current === Themes.System
|
return context.app.appearance.theme.current === Themes.System
|
||||||
? getSystemTheme()
|
? getSystemTheme()
|
||||||
: context.app.theme.current
|
: context.app.appearance.theme.current
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,7 @@ export async function renameProjectDirectory(
|
|||||||
export async function ensureProjectDirectoryExists(
|
export async function ensureProjectDirectoryExists(
|
||||||
config: DeepPartial<Configuration>
|
config: DeepPartial<Configuration>
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const projectDir =
|
const projectDir = config.settings?.project?.directory
|
||||||
config.settings?.app?.project_directory ||
|
|
||||||
config.settings?.project?.directory
|
|
||||||
if (!projectDir) {
|
if (!projectDir) {
|
||||||
console.error('projectDir is falsey', config)
|
console.error('projectDir is falsey', config)
|
||||||
return Promise.reject(new Error('projectDir is falsey'))
|
return Promise.reject(new Error('projectDir is falsey'))
|
||||||
@ -520,8 +518,7 @@ export const readAppSettingsFile = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hasProjectDirectorySetting =
|
const hasProjectDirectorySetting =
|
||||||
parsedAppConfig.settings?.project?.directory ||
|
parsedAppConfig.settings?.project?.directory
|
||||||
parsedAppConfig.settings?.app?.project_directory
|
|
||||||
|
|
||||||
if (hasProjectDirectorySetting) {
|
if (hasProjectDirectorySetting) {
|
||||||
return parsedAppConfig
|
return parsedAppConfig
|
||||||
|
@ -86,8 +86,7 @@ export function kclCommands(
|
|||||||
sampleName: data.sample,
|
sampleName: data.sample,
|
||||||
code,
|
code,
|
||||||
method: data.method,
|
method: data.method,
|
||||||
sampleUnits:
|
sampleUnits: projectSettingsPayload.modeling?.base_unit || 'mm',
|
||||||
projectSettingsPayload.modeling?.defaultUnit || 'mm',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -55,7 +55,7 @@ export const telemetryLoader: LoaderFunction = async ({
|
|||||||
export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
||||||
const { settings } = await loadAndValidateSettings()
|
const { settings } = await loadAndValidateSettings()
|
||||||
const onboardingStatus: OnboardingStatus =
|
const onboardingStatus: OnboardingStatus =
|
||||||
settings.app.onboardingStatus.current || ''
|
settings.app.onboarding_status.current || ''
|
||||||
const notEnRouteToOnboarding = !args.request.url.includes(
|
const notEnRouteToOnboarding = !args.request.url.includes(
|
||||||
PATHS.ONBOARDING.INDEX
|
PATHS.ONBOARDING.INDEX
|
||||||
)
|
)
|
||||||
|
@ -128,6 +128,7 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* The overall appearance of the app: light, dark, or system
|
* The overall appearance of the app: light, dark, or system
|
||||||
*/
|
*/
|
||||||
|
appearance: {
|
||||||
theme: new Setting<Themes>({
|
theme: new Setting<Themes>({
|
||||||
hideOnLevel: 'project',
|
hideOnLevel: 'project',
|
||||||
defaultValue: Themes.System,
|
defaultValue: Themes.System,
|
||||||
@ -148,7 +149,7 @@ export function createSettings() {
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
themeColor: new Setting<string>({
|
color: new Setting<string>({
|
||||||
defaultValue: '264.5',
|
defaultValue: '264.5',
|
||||||
description: 'The hue of the primary theme color for the app',
|
description: 'The hue of the primary theme color for the app',
|
||||||
validate: (v) => Number(v) >= 0 && Number(v) < 360,
|
validate: (v) => Number(v) >= 0 && Number(v) < 360,
|
||||||
@ -172,17 +173,23 @@ export function createSettings() {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
enableSSAO: new Setting<boolean>({
|
},
|
||||||
defaultValue: true,
|
/**
|
||||||
description:
|
* Whether to show the debug panel, which lets you see
|
||||||
'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled',
|
* various states of the app to aid in development
|
||||||
|
*/
|
||||||
|
show_debug_panel: new Setting<boolean>({
|
||||||
|
defaultValue: false,
|
||||||
|
description: 'Whether to show the debug panel, a development tool',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
hideOnPlatform: 'both', //for now
|
commandConfig: {
|
||||||
|
inputType: 'boolean',
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
/**
|
/**
|
||||||
* Stream resource saving behavior toggle
|
* Stream resource saving behavior toggle
|
||||||
*/
|
*/
|
||||||
streamIdleMode: new Setting<boolean>({
|
stream_idle_mode: new Setting<boolean>({
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
description: 'Toggle stream idling, saving bandwidth and battery',
|
description: 'Toggle stream idling, saving bandwidth and battery',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
@ -190,7 +197,7 @@ export function createSettings() {
|
|||||||
inputType: 'boolean',
|
inputType: 'boolean',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
onboardingStatus: new Setting<OnboardingStatus>({
|
onboarding_status: new Setting<OnboardingStatus>({
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
// TODO: this could be better but we don't have a TS side real enum
|
// TODO: this could be better but we don't have a TS side real enum
|
||||||
// for this yet
|
// for this yet
|
||||||
@ -198,62 +205,13 @@ export function createSettings() {
|
|||||||
hideOnPlatform: 'both',
|
hideOnPlatform: 'both',
|
||||||
}),
|
}),
|
||||||
/** Permanently dismiss the banner warning to download the desktop app. */
|
/** Permanently dismiss the banner warning to download the desktop app. */
|
||||||
dismissWebBanner: new Setting<boolean>({
|
dismiss_web_banner: new Setting<boolean>({
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
description:
|
description:
|
||||||
'Permanently dismiss the banner warning to download the desktop app.',
|
'Permanently dismiss the banner warning to download the desktop app.',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
hideOnPlatform: 'desktop',
|
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.
|
* Settings that affect the behavior while modeling.
|
||||||
@ -262,7 +220,7 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* The default unit to use in modeling dimensions
|
* The default unit to use in modeling dimensions
|
||||||
*/
|
*/
|
||||||
defaultUnit: new Setting<BaseUnit>({
|
base_unit: new Setting<BaseUnit>({
|
||||||
defaultValue: 'mm',
|
defaultValue: 'mm',
|
||||||
description: 'The default unit to use in modeling dimensions',
|
description: 'The default unit to use in modeling dimensions',
|
||||||
validate: (v) => baseUnitsUnion.includes(v as BaseUnit),
|
validate: (v) => baseUnitsUnion.includes(v as BaseUnit),
|
||||||
@ -285,7 +243,7 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* The controls for how to navigate the 3D view
|
* The controls for how to navigate the 3D view
|
||||||
*/
|
*/
|
||||||
mouseControls: new Setting<CameraSystem>({
|
mouse_controls: new Setting<CameraSystem>({
|
||||||
defaultValue: 'Zoo',
|
defaultValue: 'Zoo',
|
||||||
description: 'The controls for how to navigate the 3D view',
|
description: 'The controls for how to navigate the 3D view',
|
||||||
validate: (v) => cameraSystems.includes(v as CameraSystem),
|
validate: (v) => cameraSystems.includes(v as CameraSystem),
|
||||||
@ -345,7 +303,7 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* Projection method applied to the 3D view, perspective or orthographic
|
* Projection method applied to the 3D view, perspective or orthographic
|
||||||
*/
|
*/
|
||||||
cameraProjection: new Setting<CameraProjectionType>({
|
camera_projection: new Setting<CameraProjectionType>({
|
||||||
defaultValue: 'orthographic',
|
defaultValue: 'orthographic',
|
||||||
hideOnLevel: 'project',
|
hideOnLevel: 'project',
|
||||||
description:
|
description:
|
||||||
@ -372,10 +330,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
|
* Whether to highlight edges of 3D objects
|
||||||
*/
|
*/
|
||||||
highlightEdges: new Setting<boolean>({
|
highlight_edges: new Setting<boolean>({
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
description: 'Whether to highlight edges of 3D objects',
|
description: 'Whether to highlight edges of 3D objects',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
@ -387,7 +352,7 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* Whether to show a scale grid in the 3D modeling view
|
* Whether to show a scale grid in the 3D modeling view
|
||||||
*/
|
*/
|
||||||
showScaleGrid: new Setting<boolean>({
|
show_scale_grid: new Setting<boolean>({
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
description: 'Whether to show a scale grid in the 3D modeling view',
|
description: 'Whether to show a scale grid in the 3D modeling view',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
@ -396,18 +361,6 @@ export function createSettings() {
|
|||||||
},
|
},
|
||||||
hideOnLevel: 'project',
|
hideOnLevel: 'project',
|
||||||
}),
|
}),
|
||||||
/**
|
|
||||||
* Whether to show the debug panel, which lets you see
|
|
||||||
* various states of the app to aid in development
|
|
||||||
*/
|
|
||||||
showDebugPanel: new Setting<boolean>({
|
|
||||||
defaultValue: false,
|
|
||||||
description: 'Whether to show the debug panel, a development tool',
|
|
||||||
validate: (v) => typeof v === 'boolean',
|
|
||||||
commandConfig: {
|
|
||||||
inputType: 'boolean',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
/**
|
/**
|
||||||
* TODO: This setting is not yet implemented.
|
* TODO: This setting is not yet implemented.
|
||||||
* Whether to turn off animations and other motion effects
|
* Whether to turn off animations and other motion effects
|
||||||
@ -438,11 +391,11 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* Settings that affect the behavior of the KCL text editor.
|
* Settings that affect the behavior of the KCL text editor.
|
||||||
*/
|
*/
|
||||||
textEditor: {
|
text_editor: {
|
||||||
/**
|
/**
|
||||||
* Whether to wrap text in the editor or overflow with scroll
|
* Whether to wrap text in the editor or overflow with scroll
|
||||||
*/
|
*/
|
||||||
textWrapping: new Setting<boolean>({
|
text_wrapping: new Setting<boolean>({
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
description:
|
description:
|
||||||
'Whether to wrap text in the editor or overflow with scroll',
|
'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
|
* Whether to make the cursor blink in the editor
|
||||||
*/
|
*/
|
||||||
blinkingCursor: new Setting<boolean>({
|
blinking_cursor: new Setting<boolean>({
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
description: 'Whether to make the cursor blink in the editor',
|
description: 'Whether to make the cursor blink in the editor',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
@ -466,11 +419,60 @@ export function createSettings() {
|
|||||||
/**
|
/**
|
||||||
* Settings that affect the behavior of project management.
|
* 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
|
* 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,
|
defaultValue: DEFAULT_PROJECT_NAME,
|
||||||
description:
|
description:
|
||||||
'The default project name to use when creating a new project',
|
'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.
|
* Settings that affect the behavior of the command bar.
|
||||||
*/
|
*/
|
||||||
commandBar: {
|
command_bar: {
|
||||||
/**
|
/**
|
||||||
* Whether to include settings in the command bar
|
* Whether to include settings in the command bar
|
||||||
*/
|
*/
|
||||||
includeSettings: new Setting<boolean>({
|
include_settings: new Setting<boolean>({
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
description: 'Whether to include settings in the command bar',
|
description: 'Whether to include settings in the command bar',
|
||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
|
@ -26,8 +26,8 @@ describe(`testing settings initialization`, () => {
|
|||||||
|
|
||||||
setSettingsAtLevel(settings, 'user', appSettingsPayload)
|
setSettingsAtLevel(settings, 'user', appSettingsPayload)
|
||||||
|
|
||||||
expect(settings.app.theme.current).toBe('dark')
|
expect(settings.app.appearance.theme.current).toBe('dark')
|
||||||
expect(settings.app.themeColor.current).toBe('190')
|
expect(settings.app.appearance.color.current).toBe('190')
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`doesn't read theme from project settings`, () => {
|
it(`doesn't read theme from project settings`, () => {
|
||||||
@ -61,9 +61,9 @@ describe(`testing settings initialization`, () => {
|
|||||||
setSettingsAtLevel(settings, 'project', projectSettingsPayload)
|
setSettingsAtLevel(settings, 'project', projectSettingsPayload)
|
||||||
|
|
||||||
// The 'project'-level for `theme` setting should be ignored completely
|
// 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
|
// 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)
|
const allCurrentSettings = getAllCurrentSettings(settings)
|
||||||
// This one gets the 'user'-level theme because it's ignored at the project level
|
// 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")
|
// (see the test "doesn't read theme from project settings")
|
||||||
expect(allCurrentSettings.app.theme).toBe('dark')
|
expect(allCurrentSettings.app.appearance?.theme).toBe('dark')
|
||||||
expect(allCurrentSettings.app.themeColor).toBe('200')
|
expect(allCurrentSettings.app.appearance?.color).toBe('200')
|
||||||
expect(allCurrentSettings.modeling.defaultUnit).toBe('ft')
|
expect(allCurrentSettings.modeling.base_unit).toBe('ft')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -34,36 +34,38 @@ export function configurationToSettingsPayload(
|
|||||||
): DeepPartial<SaveSettingsPayload> {
|
): DeepPartial<SaveSettingsPayload> {
|
||||||
return {
|
return {
|
||||||
app: {
|
app: {
|
||||||
|
appearance: {
|
||||||
theme: appThemeToTheme(configuration?.settings?.app?.appearance?.theme),
|
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()
|
? configuration?.settings?.app?.appearance?.color.toString()
|
||||||
: undefined,
|
: undefined,
|
||||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
},
|
||||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
onboarding_status: configuration?.settings?.app?.onboarding_status,
|
||||||
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
dismiss_web_banner: configuration?.settings?.app?.dismiss_web_banner,
|
||||||
projectDirectory: configuration?.settings?.project?.directory,
|
stream_idle_mode: configuration?.settings?.app?.stream_idle_mode,
|
||||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
show_debug_panel: configuration?.settings?.app?.show_debug_panel,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
base_unit: configuration?.settings?.modeling?.base_unit,
|
||||||
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
camera_projection: configuration?.settings?.modeling?.camera_projection,
|
||||||
mouseControls: mouseControlsToCameraSystem(
|
mouse_controls: mouseControlsToCameraSystem(
|
||||||
configuration?.settings?.modeling?.mouse_controls
|
configuration?.settings?.modeling?.mouse_controls
|
||||||
),
|
),
|
||||||
highlightEdges: configuration?.settings?.modeling?.highlight_edges,
|
highlight_edges: configuration?.settings?.modeling?.highlight_edges,
|
||||||
showDebugPanel: configuration?.settings?.modeling?.show_debug_panel,
|
show_scale_grid: configuration?.settings?.modeling?.show_scale_grid,
|
||||||
showScaleGrid: configuration?.settings?.modeling?.show_scale_grid,
|
enable_ssao: configuration?.settings?.modeling?.enable_ssao,
|
||||||
},
|
},
|
||||||
textEditor: {
|
text_editor: {
|
||||||
textWrapping: configuration?.settings?.text_editor?.text_wrapping,
|
text_wrapping: configuration?.settings?.text_editor?.text_wrapping,
|
||||||
blinkingCursor: configuration?.settings?.text_editor?.blinking_cursor,
|
blinking_cursor: configuration?.settings?.text_editor?.blinking_cursor,
|
||||||
},
|
},
|
||||||
projects: {
|
project: {
|
||||||
defaultProjectName:
|
default_project_name:
|
||||||
configuration?.settings?.project?.default_project_name,
|
configuration?.settings?.project?.default_project_name,
|
||||||
|
directory: configuration?.settings?.project?.directory,
|
||||||
},
|
},
|
||||||
commandBar: {
|
command_bar: {
|
||||||
includeSettings: configuration?.settings?.command_bar?.include_settings,
|
include_settings: configuration?.settings?.command_bar?.include_settings,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,29 +75,28 @@ export function projectConfigurationToSettingsPayload(
|
|||||||
): DeepPartial<SaveSettingsPayload> {
|
): DeepPartial<SaveSettingsPayload> {
|
||||||
return {
|
return {
|
||||||
app: {
|
app: {
|
||||||
|
appearance: {
|
||||||
// do not read in `theme`, because it is blocked on the project level
|
// 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()
|
? configuration?.settings?.app?.appearance?.color.toString()
|
||||||
: undefined,
|
: undefined,
|
||||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
},
|
||||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
onboarding_status: configuration?.settings?.app?.onboarding_status,
|
||||||
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
dismiss_web_banner: configuration?.settings?.app?.dismiss_web_banner,
|
||||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
stream_idle_mode: configuration?.settings?.app?.stream_idle_mode,
|
||||||
|
show_debug_panel: configuration?.settings?.app?.show_debug_panel,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
base_unit: configuration?.settings?.modeling?.base_unit,
|
||||||
mouseControls: mouseControlsToCameraSystem(
|
highlight_edges: configuration?.settings?.modeling?.highlight_edges,
|
||||||
configuration?.settings?.modeling?.mouse_controls
|
enable_ssao: configuration?.settings?.modeling?.enable_ssao,
|
||||||
),
|
|
||||||
highlightEdges: configuration?.settings?.modeling?.highlight_edges,
|
|
||||||
showDebugPanel: configuration?.settings?.modeling?.show_debug_panel,
|
|
||||||
},
|
},
|
||||||
textEditor: {
|
text_editor: {
|
||||||
textWrapping: configuration?.settings?.text_editor?.text_wrapping,
|
text_wrapping: configuration?.settings?.text_editor?.text_wrapping,
|
||||||
blinkingCursor: configuration?.settings?.text_editor?.blinking_cursor,
|
blinking_cursor: configuration?.settings?.text_editor?.blinking_cursor,
|
||||||
},
|
},
|
||||||
commandBar: {
|
command_bar: {
|
||||||
includeSettings: configuration?.settings?.command_bar?.include_settings,
|
include_settings: configuration?.settings?.command_bar?.include_settings,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +182,7 @@ export async function loadAndValidateSettings(
|
|||||||
|
|
||||||
// Because getting the default directory is async, we need to set it after
|
// Because getting the default directory is async, we need to set it after
|
||||||
if (onDesktop) {
|
if (onDesktop) {
|
||||||
settings.app.projectDirectory.default = await getInitialDefaultDir()
|
settings.project.directory.default = await getInitialDefaultDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsNext = setSettingsAtLevel(
|
settingsNext = setSettingsAtLevel(
|
||||||
|
@ -84,13 +84,13 @@ export const settingsMachine = setup({
|
|||||||
return newContext
|
return newContext
|
||||||
}),
|
}),
|
||||||
setThemeClass: ({ context }) => {
|
setThemeClass: ({ context }) => {
|
||||||
const currentTheme = context.app.theme.current ?? Themes.System
|
const currentTheme = context.app.appearance.theme.current ?? Themes.System
|
||||||
setThemeClass(
|
setThemeClass(
|
||||||
currentTheme === Themes.System ? getSystemTheme() : currentTheme
|
currentTheme === Themes.System ? getSystemTheme() : currentTheme
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
setEngineCameraProjection: ({ context }) => {
|
setEngineCameraProjection: ({ context }) => {
|
||||||
const newCurrentProjection = context.modeling.cameraProjection.current
|
const newCurrentProjection = context.modeling.camera_projection.current
|
||||||
sceneInfra.camControls.setEngineCameraProjection(newCurrentProjection)
|
sceneInfra.camControls.setEngineCameraProjection(newCurrentProjection)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -79,13 +79,13 @@ const Home = () => {
|
|||||||
send({
|
send({
|
||||||
type: 'assign',
|
type: 'assign',
|
||||||
data: {
|
data: {
|
||||||
defaultProjectName: settings.projects.defaultProjectName.current,
|
defaultProjectName: settings.project.default_project_name.current,
|
||||||
defaultDirectory: settings.app.projectDirectory.current,
|
defaultDirectory: settings.project.directory.current,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, [
|
}, [
|
||||||
settings.app.projectDirectory.current,
|
settings.project.directory.current,
|
||||||
settings.projects.defaultProjectName.current,
|
settings.project.default_project_name.current,
|
||||||
send,
|
send,
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ const Home = () => {
|
|||||||
groupId: 'projects',
|
groupId: 'projects',
|
||||||
name: 'Create project',
|
name: 'Create project',
|
||||||
argDefaultValues: {
|
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`}
|
to={`${PATHS.HOME + PATHS.SETTINGS_USER}#projectDirectory`}
|
||||||
className="text-chalkboard-90 dark:text-chalkboard-20 underline underline-offset-2"
|
className="text-chalkboard-90 dark:text-chalkboard-20 underline underline-offset-2"
|
||||||
>
|
>
|
||||||
{settings.app.projectDirectory.current}
|
{settings.project.directory.current}
|
||||||
</Link>
|
</Link>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
@ -16,7 +16,7 @@ export default function Units() {
|
|||||||
send,
|
send,
|
||||||
state: {
|
state: {
|
||||||
context: {
|
context: {
|
||||||
modeling: { mouseControls },
|
modeling: { mouse_controls },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -38,10 +38,10 @@ export default function Units() {
|
|||||||
<select
|
<select
|
||||||
id="camera-controls"
|
id="camera-controls"
|
||||||
className="block w-full px-3 py-1 bg-transparent border border-chalkboard-30"
|
className="block w-full px-3 py-1 bg-transparent border border-chalkboard-30"
|
||||||
value={mouseControls.current}
|
value={mouse_controls.current}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
send({
|
send({
|
||||||
type: 'set.modeling.mouseControls',
|
type: 'set.modeling.mouse_controls',
|
||||||
data: {
|
data: {
|
||||||
level: 'user',
|
level: 'user',
|
||||||
value: e.target.value as CameraSystem,
|
value: e.target.value as CameraSystem,
|
||||||
@ -58,15 +58,15 @@ export default function Units() {
|
|||||||
<ul className="mx-4 my-2 text-sm leading-relaxed">
|
<ul className="mx-4 my-2 text-sm leading-relaxed">
|
||||||
<li>
|
<li>
|
||||||
<strong>Pan:</strong>{' '}
|
<strong>Pan:</strong>{' '}
|
||||||
{cameraMouseDragGuards[mouseControls.current].pan.description}
|
{cameraMouseDragGuards[mouse_controls.current].pan.description}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Zoom:</strong>{' '}
|
<strong>Zoom:</strong>{' '}
|
||||||
{cameraMouseDragGuards[mouseControls.current].zoom.description}
|
{cameraMouseDragGuards[mouse_controls.current].zoom.description}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<strong>Rotate:</strong>{' '}
|
<strong>Rotate:</strong>{' '}
|
||||||
{cameraMouseDragGuards[mouseControls.current].rotate.description}
|
{cameraMouseDragGuards[mouse_controls.current].rotate.description}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
@ -126,7 +126,9 @@ function OnboardingIntroductionInner() {
|
|||||||
settings: {
|
settings: {
|
||||||
state: {
|
state: {
|
||||||
context: {
|
context: {
|
||||||
app: { theme },
|
app: {
|
||||||
|
appearance: { theme },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -11,10 +11,12 @@ export default function OnboardingParametricModeling() {
|
|||||||
settings: {
|
settings: {
|
||||||
context: {
|
context: {
|
||||||
app: {
|
app: {
|
||||||
|
appearance: {
|
||||||
theme: { current: theme },
|
theme: { current: theme },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
const getImageTheme = () =>
|
const getImageTheme = () =>
|
||||||
theme === Themes.Light ||
|
theme === Themes.Light ||
|
||||||
|
@ -13,7 +13,7 @@ export default function Units() {
|
|||||||
settings: {
|
settings: {
|
||||||
send,
|
send,
|
||||||
context: {
|
context: {
|
||||||
modeling: { defaultUnit },
|
modeling: { base_unit },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
@ -29,10 +29,10 @@ export default function Units() {
|
|||||||
<select
|
<select
|
||||||
id="base-unit"
|
id="base-unit"
|
||||||
className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
|
className="block w-full px-3 py-1 border border-chalkboard-30 bg-transparent"
|
||||||
value={defaultUnit.user}
|
value={base_unit.user}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
send({
|
send({
|
||||||
type: 'set.modeling.defaultUnit',
|
type: 'set.modeling.base_unit',
|
||||||
data: {
|
data: {
|
||||||
level: 'user',
|
level: 'user',
|
||||||
value: e.target.value as BaseUnit,
|
value: e.target.value as BaseUnit,
|
||||||
|
@ -116,7 +116,7 @@ export function useNextClick(newStatus: string) {
|
|||||||
|
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
send({
|
send({
|
||||||
type: 'set.app.onboardingStatus',
|
type: 'set.app.onboarding_status',
|
||||||
data: { level: 'user', value: newStatus },
|
data: { level: 'user', value: newStatus },
|
||||||
})
|
})
|
||||||
navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
navigate(filePath + PATHS.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
||||||
@ -132,7 +132,7 @@ export function useDismiss() {
|
|||||||
|
|
||||||
const settingsCallback = useCallback(() => {
|
const settingsCallback = useCallback(() => {
|
||||||
send({
|
send({
|
||||||
type: 'set.app.onboardingStatus',
|
type: 'set.app.onboarding_status',
|
||||||
data: { level: 'user', value: 'dismissed' },
|
data: { level: 'user', value: 'dismissed' },
|
||||||
})
|
})
|
||||||
}, [send])
|
}, [send])
|
||||||
@ -143,7 +143,7 @@ export function useDismiss() {
|
|||||||
*/
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
state.context.app.onboardingStatus.user === 'dismissed' &&
|
state.context.app.onboarding_status.user === 'dismissed' &&
|
||||||
state.matches('idle')
|
state.matches('idle')
|
||||||
) {
|
) {
|
||||||
navigate(filePath)
|
navigate(filePath)
|
||||||
|
@ -26,7 +26,9 @@ const SignIn = () => {
|
|||||||
settings: {
|
settings: {
|
||||||
state: {
|
state: {
|
||||||
context: {
|
context: {
|
||||||
app: { theme },
|
app: {
|
||||||
|
appearance: { theme },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -13,9 +13,7 @@ use tower_lsp::lsp_types::{
|
|||||||
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::execution::Sketch;
|
use crate::{execution::Sketch, std::Primitive};
|
||||||
|
|
||||||
use crate::std::Primitive;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
|
||||||
|
use super::cad_op::{OpArg, Operation};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -19,8 +20,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::cad_op::{OpArg, Operation};
|
|
||||||
|
|
||||||
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
|
const FLOAT_TO_INT_MAX_DELTA: f64 = 0.01;
|
||||||
|
|
||||||
impl BinaryPart {
|
impl BinaryPart {
|
||||||
|
@ -31,6 +31,9 @@ mod exec_ast;
|
|||||||
mod function_param;
|
mod function_param;
|
||||||
mod kcl_value;
|
mod kcl_value;
|
||||||
|
|
||||||
|
// Re-exports.
|
||||||
|
pub use cad_op::Operation;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{EngineManager, ExecutionKind},
|
engine::{EngineManager, ExecutionKind},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
@ -47,9 +50,6 @@ use crate::{
|
|||||||
ExecError, Program,
|
ExecError, Program,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Re-exports.
|
|
||||||
pub use cad_op::Operation;
|
|
||||||
|
|
||||||
/// State for executing a program.
|
/// State for executing a program.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -12,6 +12,7 @@ use winnow::{
|
|||||||
token::{any, one_of, take_till},
|
token::{any, one_of, take_till},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{ast::types::LabelledExpression, token::NumericSuffix};
|
||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::{CompilationError, Severity, Tag},
|
errors::{CompilationError, Severity, Tag},
|
||||||
@ -33,8 +34,6 @@ use crate::{
|
|||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ast::types::LabelledExpression, token::NumericSuffix};
|
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
||||||
static CTXT: RefCell<Option<ParseContext>> = const { RefCell::new(None) };
|
static CTXT: RefCell<Option<ParseContext>> = const { RefCell::new(None) };
|
||||||
|
@ -55,6 +55,11 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.settings.modeling.show_debug_panel && !settings.settings.app.show_debug_panel {
|
||||||
|
settings.settings.app.show_debug_panel = settings.settings.modeling.show_debug_panel;
|
||||||
|
settings.settings.modeling.show_debug_panel = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
settings.validate()?;
|
settings.validate()?;
|
||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
@ -121,6 +126,10 @@ pub struct AppSettings {
|
|||||||
/// When the user is idle, and this is true, the stream will be torn down.
|
/// When the user is idle, and this is true, the stream will be torn down.
|
||||||
#[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
|
||||||
stream_idle_mode: bool,
|
stream_idle_mode: bool,
|
||||||
|
/// Whether to show the debug panel, which lets you see various states
|
||||||
|
/// of the app to aid in development.
|
||||||
|
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
|
||||||
|
pub show_debug_panel: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
|
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
|
||||||
@ -264,6 +273,7 @@ pub struct ModelingSettings {
|
|||||||
pub highlight_edges: DefaultTrue,
|
pub highlight_edges: DefaultTrue,
|
||||||
/// Whether to show the debug panel, which lets you see various states
|
/// Whether to show the debug panel, which lets you see various states
|
||||||
/// of the app to aid in development.
|
/// of the app to aid in development.
|
||||||
|
/// Remove this when we remove backwards compatibility with the old settings file.
|
||||||
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
|
||||||
pub show_debug_panel: bool,
|
pub show_debug_panel: bool,
|
||||||
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||||
@ -586,13 +596,14 @@ textWrapping = true
|
|||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
stream_idle_mode: false,
|
stream_idle_mode: false,
|
||||||
|
show_debug_panel: true,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::In,
|
base_unit: UnitLength::In,
|
||||||
camera_projection: CameraProjectionType::Orthographic,
|
camera_projection: CameraProjectionType::Orthographic,
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: Default::default(),
|
||||||
enable_ssao: false.into(),
|
enable_ssao: false.into(),
|
||||||
show_scale_grid: false,
|
show_scale_grid: false,
|
||||||
},
|
},
|
||||||
@ -647,13 +658,14 @@ includeSettings = false
|
|||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
stream_idle_mode: false,
|
stream_idle_mode: false,
|
||||||
|
show_debug_panel: true,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: Default::default(),
|
||||||
enable_ssao: true.into(),
|
enable_ssao: true.into(),
|
||||||
show_scale_grid: false,
|
show_scale_grid: false,
|
||||||
},
|
},
|
||||||
@ -713,13 +725,14 @@ defaultProjectName = "projects-$nnn"
|
|||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
stream_idle_mode: false,
|
stream_idle_mode: false,
|
||||||
|
show_debug_panel: true,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: false,
|
||||||
enable_ssao: true.into(),
|
enable_ssao: true.into(),
|
||||||
show_scale_grid: false,
|
show_scale_grid: false,
|
||||||
},
|
},
|
||||||
@ -744,6 +757,7 @@ defaultProjectName = "projects-$nnn"
|
|||||||
serialized,
|
serialized,
|
||||||
r#"[settings.app]
|
r#"[settings.app]
|
||||||
onboarding_status = "dismissed"
|
onboarding_status = "dismissed"
|
||||||
|
show_debug_panel = true
|
||||||
|
|
||||||
[settings.app.appearance]
|
[settings.app.appearance]
|
||||||
theme = "dark"
|
theme = "dark"
|
||||||
@ -751,7 +765,6 @@ color = 138.0
|
|||||||
|
|
||||||
[settings.modeling]
|
[settings.modeling]
|
||||||
base_unit = "yd"
|
base_unit = "yd"
|
||||||
show_debug_panel = true
|
|
||||||
|
|
||||||
[settings.text_editor]
|
[settings.text_editor]
|
||||||
text_wrapping = false
|
text_wrapping = false
|
||||||
@ -791,13 +804,14 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
|||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
stream_idle_mode: false,
|
stream_idle_mode: false,
|
||||||
|
show_debug_panel: Default::default(),
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Mm,
|
base_unit: UnitLength::Mm,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: true.into(),
|
highlight_edges: true.into(),
|
||||||
show_debug_panel: false,
|
show_debug_panel: Default::default(),
|
||||||
enable_ssao: true.into(),
|
enable_ssao: true.into(),
|
||||||
show_scale_grid: false,
|
show_scale_grid: false,
|
||||||
},
|
},
|
||||||
|
@ -5,8 +5,11 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::settings::types::{
|
use crate::{
|
||||||
AppColor, AppSettings, AppTheme, CommandBarSettings, ModelingSettings, TextEditorSettings,
|
settings::types::{
|
||||||
|
is_default, AppColor, CommandBarSettings, DefaultTrue, FloatOrInt, OnboardingStatus, TextEditorSettings,
|
||||||
|
},
|
||||||
|
UnitLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// High level project configuration.
|
/// High level project configuration.
|
||||||
@ -24,14 +27,6 @@ impl ProjectConfiguration {
|
|||||||
// TODO: remove this when we remove backwards compatibility with the old settings file.
|
// TODO: remove this when we remove backwards compatibility with the old settings file.
|
||||||
pub fn backwards_compatible_toml_parse(toml_str: &str) -> Result<Self> {
|
pub fn backwards_compatible_toml_parse(toml_str: &str) -> Result<Self> {
|
||||||
let mut settings = toml::from_str::<Self>(toml_str)?;
|
let mut settings = toml::from_str::<Self>(toml_str)?;
|
||||||
settings.settings.app.project_directory = None;
|
|
||||||
|
|
||||||
if let Some(theme) = &settings.settings.app.theme {
|
|
||||||
if settings.settings.app.appearance.theme == AppTheme::default() {
|
|
||||||
settings.settings.app.appearance.theme = *theme;
|
|
||||||
settings.settings.app.theme = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(theme_color) = &settings.settings.app.theme_color {
|
if let Some(theme_color) = &settings.settings.app.theme_color {
|
||||||
if settings.settings.app.appearance.color == AppColor::default() {
|
if settings.settings.app.appearance.color == AppColor::default() {
|
||||||
@ -47,6 +42,11 @@ impl ProjectConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.settings.modeling.show_debug_panel && !settings.settings.app.show_debug_panel {
|
||||||
|
settings.settings.app.show_debug_panel = settings.settings.modeling.show_debug_panel;
|
||||||
|
settings.settings.modeling.show_debug_panel = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
settings.validate()?;
|
settings.validate()?;
|
||||||
|
|
||||||
Ok(settings)
|
Ok(settings)
|
||||||
@ -61,11 +61,11 @@ pub struct PerProjectSettings {
|
|||||||
/// The settings for the modeling app.
|
/// The settings for the modeling app.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[validate(nested)]
|
#[validate(nested)]
|
||||||
pub app: AppSettings,
|
pub app: ProjectAppSettings,
|
||||||
/// Settings that affect the behavior while modeling.
|
/// Settings that affect the behavior while modeling.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[validate(nested)]
|
#[validate(nested)]
|
||||||
pub modeling: ModelingSettings,
|
pub modeling: ProjectModelingSettings,
|
||||||
/// Settings that affect the behavior of the KCL text editor.
|
/// Settings that affect the behavior of the KCL text editor.
|
||||||
#[serde(default, alias = "textEditor")]
|
#[serde(default, alias = "textEditor")]
|
||||||
#[validate(nested)]
|
#[validate(nested)]
|
||||||
@ -76,15 +76,83 @@ pub struct PerProjectSettings {
|
|||||||
pub command_bar: CommandBarSettings,
|
pub command_bar: CommandBarSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Project application settings.
|
||||||
|
// TODO: When we remove backwards compatibility with the old settings file, we can remove the
|
||||||
|
// aliases to camelCase (and projects plural) from everywhere.
|
||||||
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Validate)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct ProjectAppSettings {
|
||||||
|
/// The settings for the appearance of the app.
|
||||||
|
#[serde(default, skip_serializing_if = "is_default")]
|
||||||
|
#[validate(nested)]
|
||||||
|
pub appearance: ProjectAppearanceSettings,
|
||||||
|
/// The onboarding status of the app.
|
||||||
|
#[serde(default, alias = "onboardingStatus", skip_serializing_if = "is_default")]
|
||||||
|
pub onboarding_status: OnboardingStatus,
|
||||||
|
/// The hue of the primary theme color for the app.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")]
|
||||||
|
pub theme_color: Option<FloatOrInt>,
|
||||||
|
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||||
|
#[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enable_ssao: Option<bool>,
|
||||||
|
/// Permanently dismiss the banner warning to download the desktop app.
|
||||||
|
/// This setting only applies to the web app. And is temporary until we have Linux support.
|
||||||
|
#[serde(default, alias = "dismissWebBanner", skip_serializing_if = "is_default")]
|
||||||
|
pub dismiss_web_banner: bool,
|
||||||
|
/// When the user is idle, and this is true, the stream will be torn down.
|
||||||
|
#[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
|
||||||
|
stream_idle_mode: bool,
|
||||||
|
/// Whether to show the debug panel, which lets you see various states
|
||||||
|
/// of the app to aid in development.
|
||||||
|
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
|
||||||
|
pub show_debug_panel: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per project appearance settings of the app.
|
||||||
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Validate)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub struct ProjectAppearanceSettings {
|
||||||
|
/// The hue of the primary theme color for the app.
|
||||||
|
#[serde(default, skip_serializing_if = "is_default")]
|
||||||
|
#[validate(nested)]
|
||||||
|
pub color: AppColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Per-project settings that affect the behavior while modeling.
|
||||||
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct ProjectModelingSettings {
|
||||||
|
/// The default unit to use in modeling dimensions.
|
||||||
|
#[serde(default, alias = "defaultUnit", skip_serializing_if = "is_default")]
|
||||||
|
pub base_unit: UnitLength,
|
||||||
|
/// Highlight edges of 3D objects?
|
||||||
|
#[serde(default, alias = "highlightEdges", skip_serializing_if = "is_default")]
|
||||||
|
pub highlight_edges: DefaultTrue,
|
||||||
|
/// Whether to show the debug panel, which lets you see various states
|
||||||
|
/// of the app to aid in development.
|
||||||
|
/// Remove this when we remove backwards compatibility with the old settings file.
|
||||||
|
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
|
||||||
|
pub show_debug_panel: bool,
|
||||||
|
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
|
||||||
|
#[serde(default, skip_serializing_if = "is_default")]
|
||||||
|
pub enable_ssao: DefaultTrue,
|
||||||
|
/// Whether or not to show a scale grid in the 3D modeling view
|
||||||
|
#[serde(default, alias = "showScaleGrid", skip_serializing_if = "is_default")]
|
||||||
|
pub show_scale_grid: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AppSettings, AppTheme, CommandBarSettings, ModelingSettings, PerProjectSettings, ProjectConfiguration,
|
CommandBarSettings, PerProjectSettings, ProjectAppSettings, ProjectAppearanceSettings, ProjectConfiguration,
|
||||||
TextEditorSettings,
|
ProjectModelingSettings, TextEditorSettings,
|
||||||
};
|
};
|
||||||
use crate::settings::types::{AppearanceSettings, UnitLength};
|
use crate::settings::types::UnitLength;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// Test that we can deserialize a project file from the old format.
|
// Test that we can deserialize a project file from the old format.
|
||||||
@ -112,25 +180,19 @@ includeSettings = false
|
|||||||
parsed,
|
parsed,
|
||||||
ProjectConfiguration {
|
ProjectConfiguration {
|
||||||
settings: PerProjectSettings {
|
settings: PerProjectSettings {
|
||||||
app: AppSettings {
|
app: ProjectAppSettings {
|
||||||
appearance: AppearanceSettings {
|
appearance: ProjectAppearanceSettings { color: 138.0.into() },
|
||||||
theme: AppTheme::Dark,
|
|
||||||
color: 138.0.into()
|
|
||||||
},
|
|
||||||
onboarding_status: Default::default(),
|
onboarding_status: Default::default(),
|
||||||
project_directory: None,
|
|
||||||
theme: None,
|
|
||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
stream_idle_mode: false,
|
stream_idle_mode: false,
|
||||||
},
|
|
||||||
modeling: ModelingSettings {
|
|
||||||
base_unit: UnitLength::Yd,
|
|
||||||
camera_projection: Default::default(),
|
|
||||||
mouse_controls: Default::default(),
|
|
||||||
highlight_edges: Default::default(),
|
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
|
},
|
||||||
|
modeling: ProjectModelingSettings {
|
||||||
|
base_unit: UnitLength::Yd,
|
||||||
|
highlight_edges: Default::default(),
|
||||||
|
show_debug_panel: Default::default(),
|
||||||
enable_ssao: true.into(),
|
enable_ssao: true.into(),
|
||||||
show_scale_grid: false,
|
show_scale_grid: false,
|
||||||
},
|
},
|
||||||
@ -144,6 +206,28 @@ includeSettings = false
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Write the file back out.
|
||||||
|
let serialized = toml::to_string(&parsed).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
serialized,
|
||||||
|
r#"[settings.app]
|
||||||
|
show_debug_panel = true
|
||||||
|
|
||||||
|
[settings.app.appearance]
|
||||||
|
color = 138.0
|
||||||
|
|
||||||
|
[settings.modeling]
|
||||||
|
base_unit = "yd"
|
||||||
|
|
||||||
|
[settings.text_editor]
|
||||||
|
text_wrapping = false
|
||||||
|
blinking_cursor = false
|
||||||
|
|
||||||
|
[settings.command_bar]
|
||||||
|
include_settings = false
|
||||||
|
"#
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -3,14 +3,13 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
|
|
||||||
|
use super::args::FromArgs;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue},
|
execution::{ExecState, KclValue},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::args::FromArgs;
|
|
||||||
|
|
||||||
/// Compute the remainder after dividing `num` by `div`.
|
/// Compute the remainder after dividing `num` by `div`.
|
||||||
/// If `num` is negative, the result will be too.
|
/// If `num` is negative, the result will be too.
|
||||||
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
|
@ -2223,7 +2223,10 @@ mod tests {
|
|||||||
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use crate::{execution::TagIdentifier, std::sketch::PlaneData, std::utils::calculate_circle_center};
|
use crate::{
|
||||||
|
execution::TagIdentifier,
|
||||||
|
std::{sketch::PlaneData, utils::calculate_circle_center},
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_plane_data() {
|
fn test_deserialize_plane_data() {
|
||||||
|
Reference in New Issue
Block a user