Compare commits
4 Commits
revert-580
...
v0.48.0
Author | SHA1 | Date | |
---|---|---|---|
cccb71fd30 | |||
44be072d04 | |||
86beb6ebf1 | |||
6d72104faa |
@ -22,6 +22,13 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["error", {
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"ignoreRestSiblings": true,
|
||||||
|
"vars": "all",
|
||||||
|
"args": "none"
|
||||||
|
}],
|
||||||
"jsx-a11y/click-events-have-key-events": "off",
|
"jsx-a11y/click-events-have-key-events": "off",
|
||||||
"jsx-a11y/no-autofocus": "off",
|
"jsx-a11y/no-autofocus": "off",
|
||||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||||
|
10
README.md
@ -105,7 +105,7 @@ Finally, to run the web app only, run:
|
|||||||
yarn start
|
yarn start
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're not a Zoo employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to localhost:3000 again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
|
If you're not a Zoo employee you won't be able to access the dev environment, you should copy everything from `.env.production` to `.env.development.local` to make it point to production instead, then when you navigate to `localhost:3000` the easiest way to sign in is to paste `localStorage.setItem('TOKEN_PERSIST_KEY', "your-token-from-https://zoo.dev/account/api-tokens")` replacing the with a real token from https://zoo.dev/account/api-tokens of course, then navigate to `localhost:3000` again. Note that navigating to `localhost:3000/signin` removes your token so you will need to set the token again.
|
||||||
|
|
||||||
### Development environment variables
|
### Development environment variables
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ Third-Party Cookies".
|
|||||||
|
|
||||||
## Desktop
|
## Desktop
|
||||||
|
|
||||||
To spin up the desktop app, `yarn install` and `yarn build:wasm` need to have been done before hand then
|
To spin up the desktop app, `yarn install` and `yarn build:wasm` need to have been done before hand then:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn tron:start
|
yarn tron:start
|
||||||
@ -130,13 +130,13 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
This will start the application and hot-reload on changes.
|
||||||
|
|
||||||
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
Devtools can be opened with the usual Command-Option-I (macOS) or Ctrl-Shift-I (Linux and Windows).
|
||||||
|
|
||||||
To package the app for your platform with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables)
|
To package the app for your platform with electron-builder, run `yarn tronb:package:dev` (or `yarn tronb:package:prod` to point to the .env.production variables).
|
||||||
|
|
||||||
## Checking out commits / Bisecting
|
## Checking out commits / Bisecting
|
||||||
|
|
||||||
Which commands from setup are one off vs need to be run every time?
|
Which commands from setup are one off vs. need to be run every time?
|
||||||
|
|
||||||
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
The following will need to be run when checking out a new commit and guarantees the build is not stale:
|
||||||
|
|
||||||
|
@ -100,7 +100,8 @@ test(
|
|||||||
try {
|
try {
|
||||||
const outputGltf = await fsp.readFile(firstFileFullPath)
|
const outputGltf = await fsp.readFile(firstFileFullPath)
|
||||||
return outputGltf.byteLength
|
return outputGltf.byteLength
|
||||||
} catch (e) {
|
} catch (error: unknown) {
|
||||||
|
void error
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -179,7 +180,8 @@ test(
|
|||||||
try {
|
try {
|
||||||
const outputGltf = await fsp.readFile(secondFileFullPath)
|
const outputGltf = await fsp.readFile(secondFileFullPath)
|
||||||
return outputGltf.byteLength
|
return outputGltf.byteLength
|
||||||
} catch (e) {
|
} catch (error: unknown) {
|
||||||
|
void error
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1197,7 +1197,7 @@ test.describe('Undo and redo do not keep history when navigating between files',
|
|||||||
`cloned file has an incremented name and same contents`,
|
`cloned file has an incremented name and same contents`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ page, context, homePage }, testInfo) => {
|
async ({ page, context, homePage }, testInfo) => {
|
||||||
const { panesOpen, createNewFile, cloneFile } = await getUtils(page, test)
|
const { panesOpen, cloneFile } = await getUtils(page, test)
|
||||||
|
|
||||||
const { dir } = await context.folderSetupFn(async (dir) => {
|
const { dir } = await context.folderSetupFn(async (dir) => {
|
||||||
const finalDir = join(dir, 'testDefault')
|
const finalDir = join(dir, 'testDefault')
|
||||||
|
@ -3,25 +3,15 @@
|
|||||||
import type {
|
import type {
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
ElectronApplication,
|
ElectronApplication,
|
||||||
Fixtures as PlaywrightFixtures,
|
|
||||||
TestInfo,
|
TestInfo,
|
||||||
Page,
|
Page,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import { _electron as electron } from '@playwright/test'
|
||||||
_electron as electron,
|
|
||||||
PlaywrightTestArgs,
|
|
||||||
PlaywrightWorkerArgs,
|
|
||||||
} from '@playwright/test'
|
|
||||||
|
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
import {
|
import { TEST_SETTINGS } from '../storageStates'
|
||||||
TEST_SETTINGS_KEY,
|
import { SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
TEST_SETTINGS_CORRUPTED,
|
|
||||||
TEST_SETTINGS,
|
|
||||||
TEST_SETTINGS_DEFAULT_THEME,
|
|
||||||
} from '../storageStates'
|
|
||||||
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
|
||||||
import { getUtils, setup } from '../test-utils'
|
import { getUtils, setup } from '../test-utils'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
@ -31,7 +21,6 @@ import { EditorFixture } from './editorFixture'
|
|||||||
import { ToolbarFixture } from './toolbarFixture'
|
import { ToolbarFixture } from './toolbarFixture'
|
||||||
import { SceneFixture } from './sceneFixture'
|
import { SceneFixture } from './sceneFixture'
|
||||||
import { HomePageFixture } from './homePageFixture'
|
import { HomePageFixture } from './homePageFixture'
|
||||||
import { unsafeTypedKeys } from 'lib/utils'
|
|
||||||
import { DeepPartial } from 'lib/types'
|
import { DeepPartial } from 'lib/types'
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
|
|
||||||
@ -278,13 +267,14 @@ export class ElectronZoo {
|
|||||||
if (fs.existsSync(this.projectDirName)) {
|
if (fs.existsSync(this.projectDirName)) {
|
||||||
await fsp.rm(this.projectDirName, { recursive: true })
|
await fsp.rm(this.projectDirName, { recursive: true })
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
console.error(e)
|
console.error(_e)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fsp.mkdir(this.projectDirName)
|
await fsp.mkdir(this.projectDirName)
|
||||||
} catch (e) {
|
} catch (error: unknown) {
|
||||||
|
void error
|
||||||
// Not a problem if it already exists.
|
// Not a problem if it already exists.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1906,7 +1906,6 @@ extrude001 = extrude(sketch001, length = -12)
|
|||||||
// Locators
|
// Locators
|
||||||
const firstEdgeLocation = { x: 600, y: 193 }
|
const firstEdgeLocation = { x: 600, y: 193 }
|
||||||
const secondEdgeLocation = { x: 600, y: 383 }
|
const secondEdgeLocation = { x: 600, y: 383 }
|
||||||
const bodyLocation = { x: 630, y: 290 }
|
|
||||||
const [clickOnFirstEdge] = scene.makeMouseHelpers(
|
const [clickOnFirstEdge] = scene.makeMouseHelpers(
|
||||||
firstEdgeLocation.x,
|
firstEdgeLocation.x,
|
||||||
firstEdgeLocation.y
|
firstEdgeLocation.y
|
||||||
@ -1919,7 +1918,6 @@ extrude001 = extrude(sketch001, length = -12)
|
|||||||
// Colors
|
// Colors
|
||||||
const edgeColorWhite: [number, number, number] = [248, 248, 248]
|
const edgeColorWhite: [number, number, number] = [248, 248, 248]
|
||||||
const edgeColorYellow: [number, number, number] = [251, 251, 40] // Mac:B=67 Ubuntu:B=12
|
const edgeColorYellow: [number, number, number] = [251, 251, 40] // Mac:B=67 Ubuntu:B=12
|
||||||
const bodyColor: [number, number, number] = [155, 155, 155]
|
|
||||||
const chamferColor: [number, number, number] = [168, 168, 168]
|
const chamferColor: [number, number, number] = [168, 168, 168]
|
||||||
const backgroundColor: [number, number, number] = [30, 30, 30]
|
const backgroundColor: [number, number, number] = [30, 30, 30]
|
||||||
const lowTolerance = 20
|
const lowTolerance = 20
|
||||||
|
@ -539,7 +539,8 @@ test.describe('Can export from electron app', () => {
|
|||||||
try {
|
try {
|
||||||
const outputGltf = await fsp.readFile(filepath)
|
const outputGltf = await fsp.readFile(filepath)
|
||||||
return outputGltf.byteLength
|
return outputGltf.byteLength
|
||||||
} catch (e) {
|
} catch (error: unknown) {
|
||||||
|
void error
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -14,7 +14,8 @@ try {
|
|||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (error: unknown) {
|
||||||
|
void error
|
||||||
// probably running in CI
|
// probably running in CI
|
||||||
console.warn(
|
console.warn(
|
||||||
`Error reading ${secretsPath}; environment variables will be used`
|
`Error reading ${secretsPath}; environment variables will be used`
|
||||||
|
@ -2626,11 +2626,6 @@ extrude003 = extrude(profile011, length = 2.5)
|
|||||||
{ x: 834, y: -680, z: 534 },
|
{ x: 834, y: -680, z: 534 },
|
||||||
{ x: -54, y: -476, z: 148 }
|
{ x: -54, y: -476, z: 148 }
|
||||||
)
|
)
|
||||||
const camPositionForSelectingSketchOnCapProfiles = () =>
|
|
||||||
scene.moveCameraTo(
|
|
||||||
{ x: 404, y: 690, z: 38 },
|
|
||||||
{ x: 16, y: -140, z: -10 }
|
|
||||||
)
|
|
||||||
const wallSelectionOptions = [
|
const wallSelectionOptions = [
|
||||||
{
|
{
|
||||||
title: 'select wall segment',
|
title: 'select wall segment',
|
||||||
@ -2653,29 +2648,6 @@ extrude003 = extrude(profile011, length = 2.5)
|
|||||||
selectClick: scene.makeMouseHelpers(836, 103)[0],
|
selectClick: scene.makeMouseHelpers(836, 103)[0],
|
||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
const capSelectionOptions = [
|
|
||||||
{
|
|
||||||
title: 'select cap segment',
|
|
||||||
selectClick: scene.makeMouseHelpers(688, 91)[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'select cap solid 2d',
|
|
||||||
selectClick: scene.makeMouseHelpers(733, 204)[0],
|
|
||||||
},
|
|
||||||
// TODO keeps failing
|
|
||||||
// {
|
|
||||||
// title: 'select cap circle',
|
|
||||||
// selectClick: scene.makeMouseHelpers(679, 290)[0],
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
title: 'select cap extrude wall',
|
|
||||||
selectClick: scene.makeMouseHelpers(649, 402)[0],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'select cap extrude cap',
|
|
||||||
selectClick: scene.makeMouseHelpers(693, 408)[0],
|
|
||||||
},
|
|
||||||
] as const
|
|
||||||
|
|
||||||
const verifyWallProfilesAreDrawn = async () =>
|
const verifyWallProfilesAreDrawn = async () =>
|
||||||
test.step('verify wall profiles are drawn', async () => {
|
test.step('verify wall profiles are drawn', async () => {
|
||||||
@ -2697,44 +2669,6 @@ extrude003 = extrude(profile011, length = 2.5)
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
const verifyCapProfilesAreDrawn = async () =>
|
|
||||||
test.step('verify cap profiles are drawn', async () => {
|
|
||||||
// open polygon
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
TEST_COLORS.WHITE,
|
|
||||||
// TEST_COLORS.BLUE, // When entering via the circle, it's selected and therefore blue
|
|
||||||
{ x: 620, y: 58 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
// revolved profile
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
TEST_COLORS.WHITE,
|
|
||||||
{ x: 641, y: 110 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
// closed polygon
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
TEST_COLORS.WHITE,
|
|
||||||
{ x: 632, y: 200 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
// extruded profile
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
TEST_COLORS.WHITE,
|
|
||||||
{ x: 628, y: 410 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
// circle
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
[
|
|
||||||
TEST_COLORS.WHITE,
|
|
||||||
TEST_COLORS.BLUE, // When entering via the circle, it's selected and therefore blue
|
|
||||||
],
|
|
||||||
{ x: 681, y: 303 },
|
|
||||||
15
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('select wall profiles', async () => {
|
await test.step('select wall profiles', async () => {
|
||||||
for (const { title, selectClick } of wallSelectionOptions) {
|
for (const { title, selectClick } of wallSelectionOptions) {
|
||||||
await test.step(title, async () => {
|
await test.step(title, async () => {
|
||||||
|
@ -7,12 +7,7 @@ import { spawn } from 'child_process'
|
|||||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {
|
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from './storageStates'
|
||||||
IS_PLAYWRIGHT_KEY,
|
|
||||||
TEST_SETTINGS,
|
|
||||||
TEST_SETTINGS_KEY,
|
|
||||||
} from './storageStates'
|
|
||||||
import * as TOML from '@iarna/toml'
|
|
||||||
import { SceneFixture } from './fixtures/sceneFixture'
|
import { SceneFixture } from './fixtures/sceneFixture'
|
||||||
import { CmdBarFixture } from './fixtures/cmdBarFixture'
|
import { CmdBarFixture } from './fixtures/cmdBarFixture'
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
@ -1,4 +1,3 @@
|
|||||||
import { MouseControlType } from '@rust/kcl-lib/bindings/MouseControlType'
|
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
|
@ -2,15 +2,12 @@ import {
|
|||||||
expect,
|
expect,
|
||||||
BrowserContext,
|
BrowserContext,
|
||||||
TestInfo,
|
TestInfo,
|
||||||
_electron as electron,
|
|
||||||
ElectronApplication,
|
|
||||||
Locator,
|
Locator,
|
||||||
Page,
|
Page,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { test } from './zoo-test'
|
import { test } from './zoo-test'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import pixelMatch from 'pixelmatch'
|
import pixelMatch from 'pixelmatch'
|
||||||
import { PNG } from 'pngjs'
|
import { PNG } from 'pngjs'
|
||||||
@ -24,14 +21,11 @@ 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 { 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 { DeepPartial } from 'lib/types'
|
import { DeepPartial } from 'lib/types'
|
||||||
import { Configuration } from 'lang/wasm'
|
import { Configuration } from 'lang/wasm'
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
|
||||||
|
|
||||||
const toNormalizedCode = (text: string) => {
|
const toNormalizedCode = (text: string) => {
|
||||||
return text.replace(/\s+/g, '')
|
return text.replace(/\s+/g, '')
|
||||||
@ -928,10 +922,6 @@ export async function setup(
|
|||||||
// await page.reload()
|
// await page.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
let electronApp: ElectronApplication | undefined = undefined
|
|
||||||
let context: BrowserContext | undefined = undefined
|
|
||||||
let page: Page | undefined = undefined
|
|
||||||
|
|
||||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||||
// enabled for chrome for now
|
// enabled for chrome for now
|
||||||
if (page.context().browser()?.browserType().name() === 'chromium') {
|
if (page.context().browser()?.browserType().name() === 'chromium') {
|
||||||
|
@ -4,7 +4,6 @@ import { bracket } from 'lib/exampleKcl'
|
|||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { FILE_EXT } from 'lib/constants'
|
import { FILE_EXT } from 'lib/constants'
|
||||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
|
||||||
|
|
||||||
test.describe('Testing in-app sample loading', () => {
|
test.describe('Testing in-app sample loading', () => {
|
||||||
/**
|
/**
|
||||||
@ -49,8 +48,6 @@ test.describe('Testing in-app sample loading', () => {
|
|||||||
})
|
})
|
||||||
const warningText = page.getByText('Overwrite current file and units?')
|
const warningText = page.getByText('Overwrite current file and units?')
|
||||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||||
const unitsToast = (unit: UnitLength_type) =>
|
|
||||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
|
||||||
|
|
||||||
await test.step(`Precondition: check the initial code`, async () => {
|
await test.step(`Precondition: check the initial code`, async () => {
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
@ -125,8 +122,6 @@ test.describe('Testing in-app sample loading', () => {
|
|||||||
page.getByRole('listitem').filter({
|
page.getByRole('listitem').filter({
|
||||||
has: page.getByRole('button', { name }),
|
has: page.getByRole('button', { name }),
|
||||||
})
|
})
|
||||||
const unitsToast = (unit: UnitLength_type) =>
|
|
||||||
page.getByText(`Set default unit to "${unit}" for this project`)
|
|
||||||
|
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
@ -1386,7 +1386,8 @@ profile001 = startProfileAt([56.37, 120.33], sketch001)
|
|||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
return true
|
return true
|
||||||
} catch (_) {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
} catch (_e) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -364,7 +364,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await camPosition1()
|
await camPosition1()
|
||||||
|
|
||||||
const revolve = { x: 635, y: 253 }
|
const revolve = { x: 635, y: 253 }
|
||||||
const parentExtrude = { x: 915, y: 133 }
|
|
||||||
const solid2d = { x: 770, y: 167 }
|
const solid2d = { x: 770, y: 167 }
|
||||||
const individualProfile = { x: 694, y: 432 }
|
const individualProfile = { x: 694, y: 432 }
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
createProject,
|
createProject,
|
||||||
tomlToSettings,
|
tomlToSettings,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import { SaveSettingsPayload, SettingsLevel } from 'lib/settings/settingsTypes'
|
import { SettingsLevel } from 'lib/settings/settingsTypes'
|
||||||
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
import { SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
|
||||||
import {
|
import {
|
||||||
TEST_SETTINGS_KEY,
|
TEST_SETTINGS_KEY,
|
||||||
@ -15,7 +15,6 @@ import {
|
|||||||
TEST_SETTINGS,
|
TEST_SETTINGS,
|
||||||
TEST_SETTINGS_DEFAULT_THEME,
|
TEST_SETTINGS_DEFAULT_THEME,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
|
||||||
import { DeepPartial } from 'lib/types'
|
import { DeepPartial } from 'lib/types'
|
||||||
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
import { Settings } from '@rust/kcl-lib/bindings/Settings'
|
||||||
|
|
||||||
@ -1006,11 +1005,6 @@ fn cube`
|
|||||||
page.getByText(
|
page.getByText(
|
||||||
`Set highlight edges to "${String(value)}" as a user default`
|
`Set highlight edges to "${String(value)}" as a user default`
|
||||||
)
|
)
|
||||||
const initialPath = testInfo.snapshotPath('toggle-settings-initial.png')
|
|
||||||
const initialScreenshot = await scene.streamWrapper.screenshot({
|
|
||||||
path: initialPath,
|
|
||||||
mask: [page.getByTestId('model-state-indicator')],
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Toggle highlightEdges off`, async () => {
|
await test.step(`Toggle highlightEdges off`, async () => {
|
||||||
await cmdBar.openCmdBar()
|
await cmdBar.openCmdBar()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
/* eslint-disable react-hooks/rules-of-hooks */
|
||||||
|
|
||||||
import { test as playwrightTestFn, ElectronApplication } from '@playwright/test'
|
import { test as playwrightTestFn } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fixturesBasedOnProcessEnvPlatform,
|
fixturesBasedOnProcessEnvPlatform,
|
||||||
@ -8,8 +8,6 @@ import {
|
|||||||
ElectronZoo,
|
ElectronZoo,
|
||||||
} from './fixtures/fixtureSetup'
|
} from './fixtures/fixtureSetup'
|
||||||
|
|
||||||
import { Settings } from '@rust/kcl-lib/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' {
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"install:rust": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none && source \"$HOME/.cargo/env\" && (cd rust && (rustup show active-toolchain || rustup toolchain install))",
|
"install:rust": "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none && source \"$HOME/.cargo/env\" && (cd rust && (rustup show active-toolchain || rustup toolchain install))",
|
||||||
"install:rust:windows": "winget install Microsoft.VisualStudio.2022.Community --silent --override \"--wait --quiet --add ProductLang En-us --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended\" && winget install Rustlang.Rustup",
|
"install:rust:windows": "winget install Microsoft.VisualStudio.2022.Community --silent --override \"--wait --quiet --add ProductLang En-us --add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended\" && winget install Rustlang.Rustup",
|
||||||
"install:wasm-pack:sh": ". $HOME/.cargo/env && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -y",
|
"install:wasm-pack:sh": ". $HOME/.cargo/env && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f",
|
||||||
"install:wasm-pack:cargo": "cargo install wasm-pack",
|
"install:wasm-pack:cargo": "cargo install wasm-pack",
|
||||||
"install:tools:windows": "winget install jqlang.jq MikeFarah.yq GitHub.cli",
|
"install:tools:windows": "winget install jqlang.jq MikeFarah.yq GitHub.cli",
|
||||||
"start": "vite --port=3000 --host=0.0.0.0",
|
"start": "vite --port=3000 --host=0.0.0.0",
|
||||||
|
@ -248,6 +248,7 @@ export class Ctx {
|
|||||||
this.clientSubscriptions = []
|
this.clientSubscriptions = []
|
||||||
try {
|
try {
|
||||||
await this._client?.dispose(2000)
|
await this._client?.dispose(2000)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// DO nothing.
|
// DO nothing.
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef } from 'react'
|
||||||
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
import { useHotKeyListener } from './hooks/useHotKeyListener'
|
||||||
import { Stream } from './components/Stream'
|
import { Stream } from './components/Stream'
|
||||||
import { AppHeader } from './components/AppHeader'
|
import { AppHeader } from './components/AppHeader'
|
||||||
@ -60,7 +60,6 @@ export function App() {
|
|||||||
const projectPath = project?.path || null
|
const projectPath = project?.path || null
|
||||||
|
|
||||||
const [commands] = useEngineCommands()
|
const [commands] = useEngineCommands()
|
||||||
const [capturedCanvas, setCapturedCanvas] = useState(false)
|
|
||||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||||
const lastCommandType = commands[commands.length - 1]?.type
|
const lastCommandType = commands[commands.length - 1]?.type
|
||||||
|
|
||||||
@ -109,11 +108,7 @@ export function App() {
|
|||||||
|
|
||||||
// Generate thumbnail.png when loading the app
|
// Generate thumbnail.png when loading the app
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (isDesktop() && lastCommandType === 'execution-done') {
|
||||||
isDesktop() &&
|
|
||||||
!capturedCanvas &&
|
|
||||||
lastCommandType === 'execution-done'
|
|
||||||
) {
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const projectDirectoryWithoutEndingSlash = loaderData?.project?.path
|
const projectDirectoryWithoutEndingSlash = loaderData?.project?.path
|
||||||
if (!projectDirectoryWithoutEndingSlash) {
|
if (!projectDirectoryWithoutEndingSlash) {
|
||||||
|
@ -6,7 +6,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { trap } from 'lib/trap'
|
import { trap } from 'lib/trap'
|
||||||
import { codeToIdSelections } from 'lib/selections'
|
import { codeToIdSelections } from 'lib/selections'
|
||||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
import { defaultSourceRange, SourceRange, topLevelRange } from 'lang/wasm'
|
import { defaultSourceRange, topLevelRange } from 'lang/wasm'
|
||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
|
|
||||||
export function AstExplorer() {
|
export function AstExplorer() {
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
Selections,
|
Selections,
|
||||||
canSubmitSelectionArg,
|
canSubmitSelectionArg,
|
||||||
getSelectionCountByType,
|
getSelectionCountByType,
|
||||||
getSelectionTypeDisplayText,
|
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useSearchParams } from 'react-router-dom'
|
|
||||||
import { CREATE_FILE_URL_PARAM } from 'lib/constants'
|
|
||||||
import { useSettings } from 'machines/appMachine'
|
import { useSettings } from 'machines/appMachine'
|
||||||
|
|
||||||
const DownloadAppBanner = () => {
|
const DownloadAppBanner = () => {
|
||||||
const [searchParams] = useSearchParams()
|
|
||||||
const hasCreateFileParam = searchParams.has(CREATE_FILE_URL_PARAM)
|
|
||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||||
settings.app.dismissWebBanner.current
|
settings.app.dismissWebBanner.current
|
||||||
|
@ -111,7 +111,6 @@ import { commandBarActor } from 'machines/commandBarMachine'
|
|||||||
import { useToken } from 'machines/appMachine'
|
import { useToken } from 'machines/appMachine'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { useSettings } from 'machines/appMachine'
|
import { useSettings } from 'machines/appMachine'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
|
@ -4,12 +4,11 @@ import {
|
|||||||
useLocation,
|
useLocation,
|
||||||
useNavigate,
|
useNavigate,
|
||||||
useRouteLoaderData,
|
useRouteLoaderData,
|
||||||
redirect,
|
|
||||||
} from 'react-router-dom'
|
} from 'react-router-dom'
|
||||||
import { PATHS } from 'lib/paths'
|
import { PATHS } from 'lib/paths'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
import { useAuthNavigation } from 'hooks/useAuthNavigation'
|
import { useAuthNavigation } from 'hooks/useAuthNavigation'
|
||||||
import { useAuthState, useSettings } from 'machines/appMachine'
|
import { useSettings } from 'machines/appMachine'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
import { getAppSettingsFilePath } from 'lib/desktop'
|
import { getAppSettingsFilePath } from 'lib/desktop'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
@ -17,9 +16,7 @@ import { trap } from 'lib/trap'
|
|||||||
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
||||||
import { loadAndValidateSettings } from 'lib/settings/settingsUtils'
|
import { loadAndValidateSettings } from 'lib/settings/settingsUtils'
|
||||||
import { settingsActor } from 'machines/appMachine'
|
import { settingsActor } from 'machines/appMachine'
|
||||||
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
|
||||||
import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus'
|
import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus'
|
||||||
import { SnapshotFrom } from 'xstate'
|
|
||||||
|
|
||||||
export const RouteProviderContext = createContext({})
|
export const RouteProviderContext = createContext({})
|
||||||
|
|
||||||
@ -35,7 +32,6 @@ export function RouteProvider({ children }: { children: ReactNode }) {
|
|||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const settings = useSettings()
|
const settings = useSettings()
|
||||||
|
|
||||||
const authState = useAuthState()
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// On initialization, the react-router-dom does not send a 'loading' state event.
|
// On initialization, the react-router-dom does not send a 'loading' state event.
|
||||||
// it sends an idle event first.
|
// it sends an idle event first.
|
||||||
|
@ -3,7 +3,6 @@ import { CustomIcon } from 'components/CustomIcon'
|
|||||||
import decamelize from 'decamelize'
|
import decamelize from 'decamelize'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import { interactionMap } from 'lib/settings/initialKeybindings'
|
import { interactionMap } from 'lib/settings/initialKeybindings'
|
||||||
import { Setting } from 'lib/settings/initialSettings'
|
|
||||||
import { SettingsLevel } from 'lib/settings/settingsTypes'
|
import { SettingsLevel } from 'lib/settings/settingsTypes'
|
||||||
import { useSettings } from 'machines/appMachine'
|
import { useSettings } from 'machines/appMachine'
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
@ -28,7 +28,7 @@ import { base64Decode } from 'lang/wasm'
|
|||||||
import { sendTelemetry } from 'lib/textToCad'
|
import { sendTelemetry } from 'lib/textToCad'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { commandBarActor, commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
import { EventFrom } from 'xstate'
|
import { EventFrom } from 'xstate'
|
||||||
import { fileMachine } from 'machines/fileMachine'
|
import { fileMachine } from 'machines/fileMachine'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useLayoutEffect, useEffect, useRef } from 'react'
|
import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { deferExecution } from 'lib/utils'
|
import { deferExecution } from 'lib/utils'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
@ -93,10 +93,6 @@ export function useSetupEngineManager(
|
|||||||
engineCommandManager.settings = settings
|
engineCommandManager.settings = settings
|
||||||
|
|
||||||
const handleResize = deferExecution(() => {
|
const handleResize = deferExecution(() => {
|
||||||
const { width, height } = getDimensions(
|
|
||||||
streamRef?.current?.offsetWidth ?? 0,
|
|
||||||
streamRef?.current?.offsetHeight ?? 0
|
|
||||||
)
|
|
||||||
engineCommandManager.handleResize(engineCommandManager.streamDimensions)
|
engineCommandManager.handleResize(engineCommandManager.streamDimensions)
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
|
@ -324,6 +324,7 @@ export class KclManager {
|
|||||||
if (this.wasmInitFailed) {
|
if (this.wasmInitFailed) {
|
||||||
this.wasmInitFailed = false
|
this.wasmInitFailed = false
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.wasmInitFailed = true
|
this.wasmInitFailed = true
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
defaultArtifactGraph,
|
defaultArtifactGraph,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
VariableMap,
|
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
assertParse,
|
assertParse,
|
||||||
initPromise,
|
initPromise,
|
||||||
Parameter,
|
Parameter,
|
||||||
SourceRange,
|
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
@ -38,6 +38,7 @@ afterAll(async () => {
|
|||||||
try {
|
try {
|
||||||
process.chdir('..')
|
process.chdir('..')
|
||||||
await fs.rm(DIR_KCL_SAMPLES, { recursive: true })
|
await fs.rm(DIR_KCL_SAMPLES, { recursive: true })
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
sketchFromKclValue,
|
|
||||||
isPathToNodeNumber,
|
isPathToNodeNumber,
|
||||||
parse,
|
parse,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
@ -75,7 +74,6 @@ import {
|
|||||||
import { BodyItem } from '@rust/kcl-lib/bindings/BodyItem'
|
import { BodyItem } from '@rust/kcl-lib/bindings/BodyItem'
|
||||||
import { findKwArg } from './util'
|
import { findKwArg } from './util'
|
||||||
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
||||||
import { codeManager } from 'lib/singletons'
|
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
|
@ -13,8 +13,6 @@ import {
|
|||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import {
|
import {
|
||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
createPipeSubstitution,
|
|
||||||
createObjectExpression,
|
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createPipeExpression,
|
createPipeExpression,
|
||||||
|
@ -5,9 +5,6 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
Identifier,
|
Identifier,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
PipeExpression,
|
|
||||||
CallExpression,
|
|
||||||
VariableDeclarator,
|
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import {
|
import {
|
||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
|
@ -39,7 +39,7 @@ import {
|
|||||||
import { err, Reason } from 'lib/trap'
|
import { err, Reason } from 'lib/trap'
|
||||||
import { Node } from '@rust/kcl-lib/bindings/Node'
|
import { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
import { findKwArg } from './util'
|
import { findKwArg } from './util'
|
||||||
import { codeRefFromRange, getPlaneFromArtifact } from './std/artifactGraph'
|
import { codeRefFromRange } from './std/artifactGraph'
|
||||||
import { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpression'
|
import { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpression'
|
||||||
import { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
import { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
||||||
import { KclSettingsAnnotation } from 'lib/settings/settingsTypes'
|
import { KclSettingsAnnotation } from 'lib/settings/settingsTypes'
|
||||||
@ -595,6 +595,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
artifact: artifactGraph.get(prevSegment.__geoMeta.id),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
isParallelAndConstrained: false,
|
isParallelAndConstrained: false,
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { isDesktop } from 'lib/isDesktop'
|
|
||||||
|
|
||||||
// Polyfill window.electron fs functions as needed when in a nodejs context
|
// Polyfill window.electron fs functions as needed when in a nodejs context
|
||||||
// (INTENDED FOR VITEST SHINANGANS.)
|
// (INTENDED FOR VITEST SHINANGANS.)
|
||||||
if (process.env.NODE_ENV === 'test' && process.env.VITEST) {
|
if (process.env.NODE_ENV === 'test' && process.env.VITEST) {
|
||||||
|
@ -19,7 +19,6 @@ import {
|
|||||||
ARG_INDEX_FIELD,
|
ARG_INDEX_FIELD,
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
getNodeFromPathCurry,
|
getNodeFromPathCurry,
|
||||||
getObjExprProperty,
|
|
||||||
LABELED_ARG_FIELD,
|
LABELED_ARG_FIELD,
|
||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
@ -629,7 +628,7 @@ export const lineTo: SketchLineHelperKw = {
|
|||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { to, from } = input
|
const { to } = input
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
@ -1675,7 +1674,7 @@ export const arcTo: SketchLineHelper = {
|
|||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
if (input.type !== 'circle-three-point-segment') return ARC_SEGMENT_ERR
|
if (input.type !== 'circle-three-point-segment') return ARC_SEGMENT_ERR
|
||||||
|
|
||||||
const { p1, p2, p3 } = input
|
const { p2, p3 } = input
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
const nodeMeta = getNodeFromPath<CallExpression>(_node, pathToNode)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { findKwArgAny } from 'lang/util'
|
import { findKwArgAny } from 'lang/util'
|
||||||
import { ARG_END, ARG_END_ABSOLUTE, DETERMINING_ARGS } from './sketch'
|
import { DETERMINING_ARGS } from './sketch'
|
||||||
|
|
||||||
export function getSketchSegmentFromPathToNode(
|
export function getSketchSegmentFromPathToNode(
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
|
@ -26,7 +26,6 @@ import { Discovered } from '@rust/kcl-lib/bindings/Discovered'
|
|||||||
import { KclValue } from '@rust/kcl-lib/bindings/KclValue'
|
import { KclValue } from '@rust/kcl-lib/bindings/KclValue'
|
||||||
import type { Program } from '@rust/kcl-lib/bindings/Program'
|
import type { Program } from '@rust/kcl-lib/bindings/Program'
|
||||||
import { Coords2d } from './std/sketch'
|
import { Coords2d } from './std/sketch'
|
||||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
|
||||||
import { CoreDumpInfo } from '@rust/kcl-lib/bindings/CoreDumpInfo'
|
import { CoreDumpInfo } from '@rust/kcl-lib/bindings/CoreDumpInfo'
|
||||||
import { CoreDumpManager } from 'lib/coredump'
|
import { CoreDumpManager } from 'lib/coredump'
|
||||||
import openWindow from 'lib/openWindow'
|
import openWindow from 'lib/openWindow'
|
||||||
@ -318,7 +317,7 @@ export function execStateFromRust(
|
|||||||
): ExecState {
|
): ExecState {
|
||||||
const artifactGraph = rustArtifactGraphToMap(execOutcome.artifactGraph)
|
const artifactGraph = rustArtifactGraphToMap(execOutcome.artifactGraph)
|
||||||
// We haven't ported pathToNode logic to Rust yet, so we need to fill it in.
|
// We haven't ported pathToNode logic to Rust yet, so we need to fill it in.
|
||||||
for (const [id, artifact] of artifactGraph) {
|
for (const [_id, artifact] of artifactGraph) {
|
||||||
if (!artifact) continue
|
if (!artifact) continue
|
||||||
if (!('codeRef' in artifact)) continue
|
if (!('codeRef' in artifact)) continue
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
|
@ -3,11 +3,7 @@ import { angleLengthInfo } from 'components/Toolbar/setAngleLength'
|
|||||||
import { transformAstSketchLines } from 'lang/std/sketchcombos'
|
import { transformAstSketchLines } from 'lang/std/sketchcombos'
|
||||||
import { PathToNode } from 'lang/wasm'
|
import { PathToNode } from 'lang/wasm'
|
||||||
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
||||||
import {
|
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
||||||
KCL_DEFAULT_LENGTH,
|
|
||||||
KCL_DEFAULT_DEGREE,
|
|
||||||
KCL_DEFAULT_COLOR,
|
|
||||||
} from 'lib/constants'
|
|
||||||
import { components } from 'lib/machine-api'
|
import { components } from 'lib/machine-api'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { kclManager } from 'lib/singletons'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
@ -8,9 +8,11 @@ import { MachineManager } from 'components/MachineManagerProvider'
|
|||||||
import { Node } from '@rust/kcl-lib/bindings/Node'
|
import { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
import { Artifact } from 'lang/std/artifactGraph'
|
import { Artifact } from 'lang/std/artifactGraph'
|
||||||
import { CommandBarContext } from 'machines/commandBarMachine'
|
import { CommandBarContext } from 'machines/commandBarMachine'
|
||||||
|
|
||||||
type Icon = CustomIconName
|
type Icon = CustomIconName
|
||||||
const PLATFORMS = ['both', 'web', 'desktop'] as const
|
const _PLATFORMS = ['both', 'web', 'desktop'] as const
|
||||||
const INPUT_TYPES = [
|
type PLATFORM = typeof _PLATFORMS
|
||||||
|
const _INPUT_TYPES = [
|
||||||
'options',
|
'options',
|
||||||
'string',
|
'string',
|
||||||
'text',
|
'text',
|
||||||
@ -19,6 +21,7 @@ const INPUT_TYPES = [
|
|||||||
'selectionMixed',
|
'selectionMixed',
|
||||||
'boolean',
|
'boolean',
|
||||||
] as const
|
] as const
|
||||||
|
type INPUT_TYPE = typeof _INPUT_TYPES
|
||||||
export interface KclExpression {
|
export interface KclExpression {
|
||||||
valueAst: Expr
|
valueAst: Expr
|
||||||
valueText: string
|
valueText: string
|
||||||
@ -31,7 +34,7 @@ export interface KclExpressionWithVariable extends KclExpression {
|
|||||||
insertIndex: number
|
insertIndex: number
|
||||||
}
|
}
|
||||||
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
||||||
export type CommandInputType = (typeof INPUT_TYPES)[number]
|
export type CommandInputType = INPUT_TYPE[number]
|
||||||
|
|
||||||
export type StateMachineCommandSetSchema<T extends AnyStateMachine> = Partial<{
|
export type StateMachineCommandSetSchema<T extends AnyStateMachine> = Partial<{
|
||||||
[EventType in EventFrom<T>['type']]: Record<string, any>
|
[EventType in EventFrom<T>['type']]: Record<string, any>
|
||||||
@ -86,7 +89,7 @@ export type Command<
|
|||||||
displayName?: string
|
displayName?: string
|
||||||
description?: string
|
description?: string
|
||||||
icon?: Icon
|
icon?: Icon
|
||||||
hide?: (typeof PLATFORMS)[number]
|
hide?: PLATFORM[number]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandConfig<
|
export type CommandConfig<
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ParseResult, VariableMap } from 'lang/wasm'
|
import { ParseResult } from 'lang/wasm'
|
||||||
import { getCalculatedKclExpressionValue } from './kclHelpers'
|
import { getCalculatedKclExpressionValue } from './kclHelpers'
|
||||||
|
|
||||||
describe('KCL expression calculations', () => {
|
describe('KCL expression calculations', () => {
|
||||||
|
@ -24,6 +24,7 @@ export class SafeRenderer extends Renderer {
|
|||||||
prot = decodeURIComponent(unescape(href))
|
prot = decodeURIComponent(unescape(href))
|
||||||
.replace(/[^\w:]/g, '')
|
.replace(/[^\w:]/g, '')
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@ import {
|
|||||||
import { updateCenterRectangleSketch } from './rectangleTool'
|
import { updateCenterRectangleSketch } from './rectangleTool'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { getNodeFromPath } from 'lang/queryAst'
|
import { getNodeFromPath } from 'lang/queryAst'
|
||||||
import { findUniqueName } from 'lang/modifyAst'
|
import { trap } from './trap'
|
||||||
import { err, trap } from './trap'
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
|
@ -10,12 +10,7 @@ import {
|
|||||||
createTagDeclarator,
|
createTagDeclarator,
|
||||||
createUnaryExpression,
|
createUnaryExpression,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import {
|
import { ArrayExpression, CallExpression, PipeExpression } from 'lang/wasm'
|
||||||
ArrayExpression,
|
|
||||||
CallExpression,
|
|
||||||
PipeExpression,
|
|
||||||
recast,
|
|
||||||
} from 'lang/wasm'
|
|
||||||
import { roundOff } from 'lib/utils'
|
import { roundOff } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
isCallExpression,
|
isCallExpression,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
|
import { LoaderFunction, redirect } from 'react-router-dom'
|
||||||
import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types'
|
import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types'
|
||||||
import { getProjectMetaByRouteId, PATHS } from './paths'
|
import { getProjectMetaByRouteId, PATHS } from './paths'
|
||||||
import { isDesktop } from './isDesktop'
|
import { isDesktop } from './isDesktop'
|
||||||
@ -10,13 +10,11 @@ import {
|
|||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
} from 'lib/constants'
|
} from 'lib/constants'
|
||||||
import { loadAndValidateSettings } from './settings/settingsUtils'
|
import { loadAndValidateSettings } from './settings/settingsUtils'
|
||||||
import makeUrlPathRelative from './makeUrlPathRelative'
|
|
||||||
import { codeManager } from 'lib/singletons'
|
import { codeManager } from 'lib/singletons'
|
||||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||||
import { getProjectInfo } from './desktop'
|
import { getProjectInfo } from './desktop'
|
||||||
import { normalizeLineEndings } from 'lib/codeEditor'
|
import { normalizeLineEndings } from 'lib/codeEditor'
|
||||||
import { OnboardingStatus } from '@rust/kcl-lib/bindings/OnboardingStatus'
|
import { settingsActor } from 'machines/appMachine'
|
||||||
import { getSettings, settingsActor } from 'machines/appMachine'
|
|
||||||
import { waitFor } from 'xstate'
|
import { waitFor } from 'xstate'
|
||||||
|
|
||||||
export const telemetryLoader: LoaderFunction = async ({
|
export const telemetryLoader: LoaderFunction = async ({
|
||||||
|
@ -32,6 +32,7 @@ export default class RustContext {
|
|||||||
if (this.wasmInitFailed) {
|
if (this.wasmInitFailed) {
|
||||||
this.wasmInitFailed = false
|
this.wasmInitFailed = false
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.wasmInitFailed = true
|
this.wasmInitFailed = true
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,7 @@ export function readLocalStorageAppSettingsFile():
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
return parseAppSettings(stored)
|
return parseAppSettings(stored)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const settings = defaultAppSettings()
|
const settings = defaultAppSettings()
|
||||||
if (err(settings)) return settings
|
if (err(settings)) return settings
|
||||||
@ -358,7 +359,6 @@ export function clearSettingsAtLevel(
|
|||||||
level: SettingsLevel
|
level: SettingsLevel
|
||||||
) {
|
) {
|
||||||
Object.entries(allSettings).forEach(([category, settingsCategory]) => {
|
Object.entries(allSettings).forEach(([category, settingsCategory]) => {
|
||||||
const categoryKey = category as keyof typeof settings
|
|
||||||
Object.entries(settingsCategory).forEach(
|
Object.entries(settingsCategory).forEach(
|
||||||
([_, settingValue]: [string, Setting]) => {
|
([_, settingValue]: [string, Setting]) => {
|
||||||
settingValue[level] = undefined
|
settingValue[level] = undefined
|
||||||
|
@ -12,7 +12,6 @@ import { NavigateFunction } from 'react-router-dom'
|
|||||||
import crossPlatformFetch from './crossPlatformFetch'
|
import crossPlatformFetch from './crossPlatformFetch'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { Themes } from './theme'
|
import { Themes } from './theme'
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
|
||||||
import { getNextFileName } from './desktopFS'
|
import { getNextFileName } from './desktopFS'
|
||||||
import { reportRejection } from './trap'
|
import { reportRejection } from './trap'
|
||||||
import { toSync } from './utils'
|
import { toSync } from './utils'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ActorRefFrom, assign, createActor, setup, spawnChild } from 'xstate'
|
import { ActorRefFrom, createActor, setup, spawnChild } from 'xstate'
|
||||||
import { authMachine } from './authMachine'
|
import { authMachine } from './authMachine'
|
||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { ACTOR_IDS } from './machineConstants'
|
import { ACTOR_IDS } from './machineConstants'
|
||||||
@ -11,12 +11,6 @@ const appMachineActors = {
|
|||||||
[SETTINGS]: settingsMachine,
|
[SETTINGS]: settingsMachine,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
type AppMachineActors = {
|
|
||||||
[K in keyof typeof appMachineActors]: ActorRefFrom<
|
|
||||||
(typeof appMachineActors)[K]
|
|
||||||
>
|
|
||||||
}
|
|
||||||
|
|
||||||
const appMachine = setup({
|
const appMachine = setup({
|
||||||
actors: appMachineActors,
|
actors: appMachineActors,
|
||||||
}).createMachine({
|
}).createMachine({
|
||||||
|
@ -93,9 +93,8 @@ import {
|
|||||||
} from 'lang/modifyAst/deleteSelection'
|
} from 'lang/modifyAst/deleteSelection'
|
||||||
import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph'
|
import { getPathsFromPlaneArtifact } from 'lang/std/artifactGraph'
|
||||||
import { createProfileStartHandle } from 'clientSideScene/segments'
|
import { createProfileStartHandle } from 'clientSideScene/segments'
|
||||||
import { DRAFT_POINT, SKETCH_LAYER } from 'clientSideScene/sceneInfra'
|
import { DRAFT_POINT } from 'clientSideScene/sceneInfra'
|
||||||
import { setAppearance } from 'lang/modifyAst/setAppearance'
|
import { setAppearance } from 'lang/modifyAst/setAppearance'
|
||||||
import { getThemeColorForThreeJs } from 'lib/theme'
|
|
||||||
import { DRAFT_DASHED_LINE } from 'clientSideScene/sceneEntities'
|
import { DRAFT_DASHED_LINE } from 'clientSideScene/sceneEntities'
|
||||||
|
|
||||||
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
AnyActorRef,
|
AnyActorRef,
|
||||||
assign,
|
assign,
|
||||||
enqueueActions,
|
enqueueActions,
|
||||||
EventObject,
|
|
||||||
fromCallback,
|
fromCallback,
|
||||||
fromPromise,
|
fromPromise,
|
||||||
sendTo,
|
sendTo,
|
||||||
|
25
src/main.ts
@ -10,6 +10,7 @@ import {
|
|||||||
nativeTheme,
|
nativeTheme,
|
||||||
desktopCapturer,
|
desktopCapturer,
|
||||||
systemPreferences,
|
systemPreferences,
|
||||||
|
screen,
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { Issuer } from 'openid-client'
|
import { Issuer } from 'openid-client'
|
||||||
@ -58,6 +59,7 @@ console.log('Environment vars', process.env)
|
|||||||
console.log('Parsed CLI args', args)
|
console.log('Parsed CLI args', args)
|
||||||
|
|
||||||
/// Register our application to handle all "zoo-studio:" protocols.
|
/// Register our application to handle all "zoo-studio:" protocols.
|
||||||
|
const singleInstanceLock = app.requestSingleInstanceLock()
|
||||||
if (process.defaultApp) {
|
if (process.defaultApp) {
|
||||||
if (process.argv.length >= 2) {
|
if (process.argv.length >= 2) {
|
||||||
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
|
app.setAsDefaultProtocolClient(ZOO_STUDIO_PROTOCOL, process.execPath, [
|
||||||
@ -72,11 +74,8 @@ if (process.defaultApp) {
|
|||||||
// Must be done before ready event.
|
// Must be done before ready event.
|
||||||
// Checking against this lock is needed for Windows and Linux, see
|
// Checking against this lock is needed for Windows and Linux, see
|
||||||
// https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app#windows-and-linux-code
|
// https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app#windows-and-linux-code
|
||||||
if (!IS_PLAYWRIGHT) {
|
if (!singleInstanceLock && !IS_PLAYWRIGHT) {
|
||||||
const singleInstanceLock = app.requestSingleInstanceLock()
|
app.quit()
|
||||||
if (!singleInstanceLock) {
|
|
||||||
app.quit()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
registerStartupListeners()
|
registerStartupListeners()
|
||||||
}
|
}
|
||||||
@ -88,12 +87,24 @@ const createWindow = (pathToOpen?: string, reuse?: boolean): BrowserWindow => {
|
|||||||
newWindow = mainWindow
|
newWindow = mainWindow
|
||||||
}
|
}
|
||||||
if (!newWindow) {
|
if (!newWindow) {
|
||||||
|
const primaryDisplay = screen.getPrimaryDisplay()
|
||||||
|
const { width, height } = primaryDisplay.workAreaSize
|
||||||
|
|
||||||
|
const windowWidth = Math.max(500, width - 150)
|
||||||
|
const windowHeight = Math.max(400, height - 100)
|
||||||
|
|
||||||
|
const x = primaryDisplay.workArea.x + Math.floor((width - windowWidth) / 2)
|
||||||
|
const y =
|
||||||
|
primaryDisplay.workArea.y + Math.floor((height - windowHeight) / 2)
|
||||||
|
|
||||||
newWindow = new BrowserWindow({
|
newWindow = new BrowserWindow({
|
||||||
autoHideMenuBar: false,
|
autoHideMenuBar: false,
|
||||||
show: false,
|
show: false,
|
||||||
enableLargerThanScreen: true,
|
enableLargerThanScreen: true,
|
||||||
width: 1800,
|
width: windowWidth,
|
||||||
height: 1200,
|
height: windowHeight,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: false, // do not give the application implicit system access
|
nodeIntegration: false, // do not give the application implicit system access
|
||||||
contextIsolation: true, // expose system functions in preload
|
contextIsolation: true, // expose system functions in preload
|
||||||
|
@ -9,8 +9,8 @@ import { SettingsSection } from 'components/Settings/SettingsSection'
|
|||||||
import { settingsActor, useSettings } from 'machines/appMachine'
|
import { settingsActor, useSettings } from 'machines/appMachine'
|
||||||
|
|
||||||
export default function Units() {
|
export default function Units() {
|
||||||
const dismiss = useDismiss()
|
useDismiss()
|
||||||
const next = useNextClick(onboardingPaths.STREAMING)
|
useNextClick(onboardingPaths.STREAMING)
|
||||||
const {
|
const {
|
||||||
modeling: { mouseControls },
|
modeling: { mouseControls },
|
||||||
} = useSettings()
|
} = useSettings()
|
||||||
|