Merge branch 'main' into pierremtb/issue3528-Add-electron-updater
This commit is contained in:
@ -124,7 +124,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await expect(cmdSearchBar).not.toBeVisible()
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
await expect(cmdSearchBar).toBeFocused()
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
@ -250,7 +250,7 @@ const extrude001 = extrude(-10, sketch001)`
|
|||||||
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
await page.getByRole('button', { name: 'Extrude' }).isEnabled()
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('ControlOrMeta+K')
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
|
|
||||||
// Search for extrude command and choose it
|
// Search for extrude command and choose it
|
||||||
|
|||||||
@ -332,7 +332,6 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -349,10 +348,10 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Going elsewhere in the code should hide the ghost text.
|
// Going elsewhere in the code should hide the ghost text.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
@ -368,8 +367,6 @@ test.describe('Copilot ghost text', () => {
|
|||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
@ -382,17 +379,17 @@ test.describe('Copilot ghost text', () => {
|
|||||||
await page.waitForTimeout(800)
|
await page.waitForTimeout(800)
|
||||||
|
|
||||||
// Ctrl+z
|
// Ctrl+z
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|
||||||
// Ctrl+shift+z
|
// Ctrl+shift+z
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
await expect(page.locator('.cm-content')).toHaveText(`{thing: "blah"}`)
|
||||||
@ -411,14 +408,14 @@ test.describe('Copilot ghost text', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Once for the enter.
|
// Once for the enter.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Once for the text.
|
// Once for the text.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
await expect(page.locator('.cm-ghostText').first()).not.toBeVisible()
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ test.describe('Editor tests', () => {
|
|||||||
await page.setViewportSize({ width: 1000, height: 500 })
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
// check no error to begin with
|
// check no error to begin with
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
@ -29,9 +28,9 @@ test.describe('Editor tests', () => {
|
|||||||
|> line([-20, 0], %)
|
|> line([-20, 0], %)
|
||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
|
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -42,9 +41,9 @@ test.describe('Editor tests', () => {
|
|||||||
// |> close(%)`)
|
// |> close(%)`)
|
||||||
|
|
||||||
// uncomment the code
|
// uncomment the code
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('/')
|
await page.keyboard.press('/')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const sketch001 = startSketchOn('XY')
|
.toHaveText(`const sketch001 = startSketchOn('XY')
|
||||||
@ -148,9 +147,7 @@ test.describe('Editor tests', () => {
|
|||||||
// Delete all the code.
|
// Delete all the code.
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
// Select all
|
// Select all
|
||||||
await page.keyboard.press('Control+A')
|
await page.keyboard.press('ControlOrMeta+A')
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await page.keyboard.press('Meta+A')
|
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(``)
|
await expect(page.locator('.cm-content')).toHaveText(``)
|
||||||
|
|||||||
@ -1233,18 +1233,13 @@ test(
|
|||||||
|
|
||||||
await page.getByText('mike_stress_test').click()
|
await page.getByText('mike_stress_test').click()
|
||||||
|
|
||||||
const modifier =
|
|
||||||
process.platform === 'win32' || process.platform === 'linux'
|
|
||||||
? 'Control'
|
|
||||||
: 'Meta'
|
|
||||||
|
|
||||||
await test.step('select all in code editor, check its length', async () => {
|
await test.step('select all in code editor, check its length', async () => {
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
// expect u.codeLocator to have some text
|
// expect u.codeLocator to have some text
|
||||||
await expect(u.codeLocator).toContainText('line(')
|
await expect(u.codeLocator).toContainText('line(')
|
||||||
await page.keyboard.down(modifier)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(modifier)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// check the length of the selected text
|
// check the length of the selected text
|
||||||
const selectedText = await page.evaluate(() => {
|
const selectedText = await page.evaluate(() => {
|
||||||
@ -1260,9 +1255,9 @@ test(
|
|||||||
await test.step('delete all the text, select again and verify there are no characters left', async () => {
|
await test.step('delete all the text, select again and verify there are no characters left', async () => {
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await page.keyboard.down(modifier)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(modifier)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// check the length of the selected text
|
// check the length of the selected text
|
||||||
const selectedText = await page.evaluate(() => {
|
const selectedText = await page.evaluate(() => {
|
||||||
@ -1329,7 +1324,7 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
process.platform === 'win32',
|
process.platform === 'win32',
|
||||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
)
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page, dir } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
||||||
@ -1357,6 +1352,16 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
// Constants and locators
|
// Constants and locators
|
||||||
const projectLink = page.getByText('Test Project')
|
const projectLink = page.getByText('Test Project')
|
||||||
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||||
|
const checkUnRenamedFS = () => {
|
||||||
|
const filePath = join(dir, 'Test Project', 'fileToRename.kcl')
|
||||||
|
return fs.existsSync(filePath)
|
||||||
|
}
|
||||||
|
const newFileName = 'newFileName'
|
||||||
|
const checkRenamedFS = () => {
|
||||||
|
const filePath = join(dir, 'Test Project', `${newFileName}.kcl`)
|
||||||
|
return fs.existsSync(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
const fileToRename = page
|
const fileToRename = page
|
||||||
.getByRole('listitem')
|
.getByRole('listitem')
|
||||||
.filter({ has: page.getByRole('button', { name: 'fileToRename.kcl' }) })
|
.filter({ has: page.getByRole('button', { name: 'fileToRename.kcl' }) })
|
||||||
@ -1365,7 +1370,6 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
.filter({ has: page.getByRole('button', { name: 'newFileName.kcl' }) })
|
.filter({ has: page.getByRole('button', { name: 'newFileName.kcl' }) })
|
||||||
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
||||||
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
const renameInput = page.getByPlaceholder('fileToRename.kcl')
|
||||||
const newFileName = 'newFileName'
|
|
||||||
const codeLocator = page.locator('.cm-content')
|
const codeLocator = page.locator('.cm-content')
|
||||||
|
|
||||||
await test.step('Open project and file pane', async () => {
|
await test.step('Open project and file pane', async () => {
|
||||||
@ -1376,6 +1380,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(fileToRename).toBeVisible()
|
await expect(fileToRename).toBeVisible()
|
||||||
|
expect(checkUnRenamedFS()).toBeTruthy()
|
||||||
|
expect(checkRenamedFS()).toBeFalsy()
|
||||||
await fileToRename.click()
|
await fileToRename.click()
|
||||||
await expect(projectMenuButton).toContainText('fileToRename.kcl')
|
await expect(projectMenuButton).toContainText('fileToRename.kcl')
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
@ -1394,6 +1400,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
await test.step('Verify the file is renamed', async () => {
|
await test.step('Verify the file is renamed', async () => {
|
||||||
await expect(fileToRename).not.toBeAttached()
|
await expect(fileToRename).not.toBeAttached()
|
||||||
await expect(renamedFile).toBeVisible()
|
await expect(renamedFile).toBeVisible()
|
||||||
|
expect(checkUnRenamedFS()).toBeFalsy()
|
||||||
|
expect(checkRenamedFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify we navigated', async () => {
|
await test.step('Verify we navigated', async () => {
|
||||||
@ -1421,7 +1429,7 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
process.platform === 'win32',
|
process.platform === 'win32',
|
||||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
)
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page, dir } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
||||||
@ -1448,6 +1456,14 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
const newFileName = 'newFileName'
|
const newFileName = 'newFileName'
|
||||||
|
const checkUnRenamedFS = () => {
|
||||||
|
const filePath = join(dir, 'Test Project', 'fileToRename.kcl')
|
||||||
|
return fs.existsSync(filePath)
|
||||||
|
}
|
||||||
|
const checkRenamedFS = () => {
|
||||||
|
const filePath = join(dir, 'Test Project', `${newFileName}.kcl`)
|
||||||
|
return fs.existsSync(filePath)
|
||||||
|
}
|
||||||
const projectLink = page.getByText('Test Project')
|
const projectLink = page.getByText('Test Project')
|
||||||
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||||
const fileToRename = page
|
const fileToRename = page
|
||||||
@ -1468,6 +1484,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(fileToRename).toBeVisible()
|
await expect(fileToRename).toBeVisible()
|
||||||
|
expect(checkUnRenamedFS()).toBeTruthy()
|
||||||
|
expect(checkRenamedFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the file', async () => {
|
await test.step('Rename the file', async () => {
|
||||||
@ -1481,6 +1499,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
await test.step('Verify the file is renamed', async () => {
|
await test.step('Verify the file is renamed', async () => {
|
||||||
await expect(fileToRename).not.toBeAttached()
|
await expect(fileToRename).not.toBeAttached()
|
||||||
await expect(renamedFile).toBeVisible()
|
await expect(renamedFile).toBeVisible()
|
||||||
|
expect(checkUnRenamedFS()).toBeFalsy()
|
||||||
|
expect(checkRenamedFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Verify we have not navigated', async () => {
|
await test.step('Verify we have not navigated', async () => {
|
||||||
@ -1511,7 +1531,7 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
process.platform === 'win32',
|
process.platform === 'win32',
|
||||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
)
|
)
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page, dir } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
||||||
@ -1548,8 +1568,17 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
})
|
})
|
||||||
const renamedFolder = page.getByRole('button', { name: 'newFolderName' })
|
const renamedFolder = page.getByRole('button', { name: 'newFolderName' })
|
||||||
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
||||||
const renameInput = page.getByPlaceholder('folderToRename')
|
const originalFolderName = 'folderToRename'
|
||||||
|
const renameInput = page.getByPlaceholder(originalFolderName)
|
||||||
const newFolderName = 'newFolderName'
|
const newFolderName = 'newFolderName'
|
||||||
|
const checkUnRenamedFolderFS = () => {
|
||||||
|
const folderPath = join(dir, 'Test Project', originalFolderName)
|
||||||
|
return fs.existsSync(folderPath)
|
||||||
|
}
|
||||||
|
const checkRenamedFolderFS = () => {
|
||||||
|
const folderPath = join(dir, 'Test Project', newFolderName)
|
||||||
|
return fs.existsSync(folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
await test.step('Open project and file pane', async () => {
|
await test.step('Open project and file pane', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await expect(projectLink).toBeVisible()
|
||||||
@ -1563,6 +1592,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
|
|
||||||
await u.openFilePanel()
|
await u.openFilePanel()
|
||||||
await expect(folderToRename).toBeVisible()
|
await expect(folderToRename).toBeVisible()
|
||||||
|
expect(checkUnRenamedFolderFS()).toBeTruthy()
|
||||||
|
expect(checkRenamedFolderFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the folder', async () => {
|
await test.step('Rename the folder', async () => {
|
||||||
@ -1582,6 +1613,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
await expect(projectMenuButton).toContainText('main.kcl')
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
await expect(renamedFolder).toBeVisible()
|
await expect(renamedFolder).toBeVisible()
|
||||||
await expect(folderToRename).not.toBeAttached()
|
await expect(folderToRename).not.toBeAttached()
|
||||||
|
expect(checkUnRenamedFolderFS()).toBeFalsy()
|
||||||
|
expect(checkRenamedFolderFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
@ -1597,7 +1630,7 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
)
|
)
|
||||||
const exampleDir = join('src', 'wasm-lib', 'tests', 'executor', 'inputs')
|
const exampleDir = join('src', 'wasm-lib', 'tests', 'executor', 'inputs')
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page, dir } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async (dir) => {
|
folderSetupFn: async (dir) => {
|
||||||
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
await fsp.mkdir(join(dir, 'Test Project'), { recursive: true })
|
||||||
@ -1630,8 +1663,17 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
|
has: page.getByRole('button', { name: 'someFileWithin.kcl' }),
|
||||||
})
|
})
|
||||||
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
const renameMenuItem = page.getByRole('button', { name: 'Rename' })
|
||||||
const renameInput = page.getByPlaceholder('folderToRename')
|
const originalFolderName = 'folderToRename'
|
||||||
|
const renameInput = page.getByPlaceholder(originalFolderName)
|
||||||
const newFolderName = 'newFolderName'
|
const newFolderName = 'newFolderName'
|
||||||
|
const checkUnRenamedFolderFS = () => {
|
||||||
|
const folderPath = join(dir, 'Test Project', originalFolderName)
|
||||||
|
return fs.existsSync(folderPath)
|
||||||
|
}
|
||||||
|
const checkRenamedFolderFS = () => {
|
||||||
|
const folderPath = join(dir, 'Test Project', newFolderName)
|
||||||
|
return fs.existsSync(folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
await test.step('Open project and navigate into folder', async () => {
|
await test.step('Open project and navigate into folder', async () => {
|
||||||
await expect(projectLink).toBeVisible()
|
await expect(projectLink).toBeVisible()
|
||||||
@ -1654,6 +1696,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
expect(newUrl).toContain('folderToRename')
|
expect(newUrl).toContain('folderToRename')
|
||||||
expect(newUrl).toContain('someFileWithin.kcl')
|
expect(newUrl).toContain('someFileWithin.kcl')
|
||||||
expect(newUrl).not.toContain('main.kcl')
|
expect(newUrl).not.toContain('main.kcl')
|
||||||
|
expect(checkUnRenamedFolderFS()).toBeTruthy()
|
||||||
|
expect(checkRenamedFolderFS()).toBeFalsy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Rename the folder', async () => {
|
await test.step('Rename the folder', async () => {
|
||||||
@ -1680,6 +1724,8 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
expect(url).not.toContain('main.kcl')
|
expect(url).not.toContain('main.kcl')
|
||||||
expect(url).toContain(newFolderName)
|
expect(url).toContain(newFolderName)
|
||||||
expect(url).toContain('someFileWithin.kcl')
|
expect(url).toContain('someFileWithin.kcl')
|
||||||
|
expect(checkUnRenamedFolderFS()).toBeFalsy()
|
||||||
|
expect(checkRenamedFolderFS()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
@ -1687,6 +1733,78 @@ test.describe('Renaming in the file tree', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('Deleting files from the file pane', () => {
|
||||||
|
test(
|
||||||
|
`when main.kcl exists, navigate to main.kcl`,
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
test.skip(
|
||||||
|
process.platform === 'win32',
|
||||||
|
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
|
||||||
|
)
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
await fsp.mkdir(`${dir}/testProject`, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
||||||
|
`${dir}/testProject/main.kcl`
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
'src/wasm-lib/tests/executor/inputs/basic_fillet_cube_end.kcl',
|
||||||
|
`${dir}/testProject/fileToDelete.kcl`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
const projectCard = page.getByText('testProject')
|
||||||
|
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||||
|
const fileToDelete = page
|
||||||
|
.getByRole('listitem')
|
||||||
|
.filter({ has: page.getByRole('button', { name: 'fileToDelete.kcl' }) })
|
||||||
|
const deleteMenuItem = page.getByRole('button', { name: 'Delete' })
|
||||||
|
const deleteConfirmation = page.getByTestId('delete-confirmation')
|
||||||
|
|
||||||
|
await test.step('Open project and navigate to fileToDelete.kcl', async () => {
|
||||||
|
await projectCard.click()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
await u.openFilePanel()
|
||||||
|
|
||||||
|
await fileToDelete.click()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await expect(u.codeLocator).toContainText('getOppositeEdge(thing)')
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete fileToDelete.kcl', async () => {
|
||||||
|
await fileToDelete.click({ button: 'right' })
|
||||||
|
await expect(deleteMenuItem).toBeVisible()
|
||||||
|
await deleteMenuItem.click()
|
||||||
|
await expect(deleteConfirmation).toBeVisible()
|
||||||
|
await deleteConfirmation.click()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Check deletion and navigation', async () => {
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
await expect(fileToDelete).not.toBeVisible()
|
||||||
|
await u.closeFilePanel()
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await expect(u.codeLocator).toContainText('circle(')
|
||||||
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test.fixme('TODO - when main.kcl does not exist', async () => {})
|
||||||
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Original project name persist after onboarding',
|
'Original project name persist after onboarding',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import {
|
|||||||
test,
|
test,
|
||||||
} from '@playwright/test'
|
} from '@playwright/test'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
import os from 'os'
|
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import fsSync from 'fs'
|
import fsSync from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
@ -78,11 +77,10 @@ async function waitForPageLoad(page: Page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function removeCurrentCode(page: Page) {
|
async function removeCurrentCode(page: Page) {
|
||||||
const hotkey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
await page.keyboard.down(hotkey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('a')
|
await page.keyboard.press('a')
|
||||||
await page.keyboard.up(hotkey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
await expect(page.locator('.cm-content')).toHaveText('')
|
await expect(page.locator('.cm-content')).toHaveText('')
|
||||||
}
|
}
|
||||||
@ -745,11 +743,6 @@ export const doExport = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the appropriate modifier key for the platform.
|
|
||||||
*/
|
|
||||||
export const metaModifier = os.platform() === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
export async function tearDown(page: Page, testInfo: TestInfo) {
|
export async function tearDown(page: Page, testInfo: TestInfo) {
|
||||||
if (testInfo.status === 'skipped') return
|
if (testInfo.status === 'skipped') return
|
||||||
if (testInfo.status === 'failed') return
|
if (testInfo.status === 'failed') return
|
||||||
@ -872,7 +865,7 @@ export async function setupElectron({
|
|||||||
|
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
|
|
||||||
return { electronApp, page }
|
return { electronApp, page, dir: projectDirName }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isOutOfViewInScrollContainer(
|
export async function isOutOfViewInScrollContainer(
|
||||||
|
|||||||
@ -72,7 +72,7 @@ test.describe('Testing settings', () => {
|
|||||||
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
|
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
|
||||||
|
|
||||||
// Open the settings modal with the browser keyboard shortcut
|
// Open the settings modal with the browser keyboard shortcut
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||||
|
|
||||||
await expect(headingLocator).toBeVisible()
|
await expect(headingLocator).toBeVisible()
|
||||||
await page.locator('#showDebugPanel').getByText('OffOn').click()
|
await page.locator('#showDebugPanel').getByText('OffOn').click()
|
||||||
@ -82,7 +82,7 @@ test.describe('Testing settings', () => {
|
|||||||
await test.step('Open settings with keyboard shortcut', async () => {
|
await test.step('Open settings with keyboard shortcut', async () => {
|
||||||
await page.getByTestId('settings-close-button').click()
|
await page.getByTestId('settings-close-button').click()
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
await page.keyboard.press('Meta+Shift+,')
|
await page.keyboard.press('ControlOrMeta+Shift+,')
|
||||||
await expect(headingLocator).toBeVisible()
|
await expect(headingLocator).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,6 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
test.describe('Text-to-CAD tests', () => {
|
test.describe('Text-to-CAD tests', () => {
|
||||||
test('basic lego happy case', async ({ page }) => {
|
test('basic lego happy case', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -298,9 +296,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await expect(page.locator('textarea')).toContainText(badPrompt)
|
await expect(page.locator('textarea')).toContainText(badPrompt)
|
||||||
|
|
||||||
// Select all and start a new prompt.
|
// Select all and start a new prompt.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.type('a 2x4 lego')
|
await page.keyboard.type('a 2x4 lego')
|
||||||
|
|
||||||
// Submit the new prompt.
|
// Submit the new prompt.
|
||||||
@ -520,9 +518,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
await expect(page.locator('.cm-content')).toContainText(`2x8`)
|
||||||
@ -549,13 +547,13 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyA')
|
await page.keyboard.press('KeyA')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||||
@ -636,9 +634,9 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
await page.locator('.cm-content').click({ position: { x: 10, y: 10 } })
|
||||||
|
|
||||||
// Paste the code.
|
// Paste the code.
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyV')
|
await page.keyboard.press('KeyV')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
// Expect the code to be pasted.
|
// Expect the code to be pasted.
|
||||||
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
await expect(page.locator('.cm-content')).toContainText(`2x4`)
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
import { test, expect } from '@playwright/test'
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
import {
|
import { doExport, getUtils, makeTemplate, setup, tearDown } from './test-utils'
|
||||||
doExport,
|
|
||||||
getUtils,
|
|
||||||
makeTemplate,
|
|
||||||
metaModifier,
|
|
||||||
setup,
|
|
||||||
tearDown,
|
|
||||||
} from './test-utils'
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
await setup(context, page)
|
await setup(context, page)
|
||||||
@ -17,8 +10,6 @@ test.afterEach(async ({ page }, testInfo) => {
|
|||||||
await tearDown(page, testInfo)
|
await tearDown(page, testInfo)
|
||||||
})
|
})
|
||||||
|
|
||||||
const CtrlKey = process.platform === 'darwin' ? 'Meta' : 'Control'
|
|
||||||
|
|
||||||
test('Units menu', async ({ page }) => {
|
test('Units menu', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
@ -157,7 +148,7 @@ test('Paste should not work unless an input is focused', async ({
|
|||||||
|
|
||||||
// Paste without the code pane focused
|
// Paste without the code pane focused
|
||||||
await codeEditorText.blur()
|
await codeEditorText.blur()
|
||||||
await page.keyboard.press(`${metaModifier}+KeyV`)
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
|
|
||||||
// Show that the paste didn't work but typing did
|
// Show that the paste didn't work but typing did
|
||||||
await expect(codeEditorText).not.toContainText(pasteContent)
|
await expect(codeEditorText).not.toContainText(pasteContent)
|
||||||
@ -166,7 +157,7 @@ test('Paste should not work unless an input is focused', async ({
|
|||||||
// Paste with the code editor focused
|
// Paste with the code editor focused
|
||||||
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
||||||
await codeEditorText.focus()
|
await codeEditorText.focus()
|
||||||
await page.keyboard.press(`${metaModifier}+KeyV`)
|
await page.keyboard.press('ControlOrMeta+KeyV')
|
||||||
await expect(
|
await expect(
|
||||||
await page.evaluate(
|
await page.evaluate(
|
||||||
() => document.querySelector('.cm-content')?.textContent
|
() => document.querySelector('.cm-content')?.textContent
|
||||||
@ -380,9 +371,9 @@ test('Basic default modeling and sketch hotkeys work', async ({ page }) => {
|
|||||||
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
await test.step(`Type code with sketch hotkeys, shouldn't fire`, async () => {
|
||||||
// Since there's code now, we have to get to the end of the line
|
// Since there's code now, we have to get to the end of the line
|
||||||
await page.locator('.cm-line').last().click()
|
await page.locator('.cm-line').last().click()
|
||||||
await page.keyboard.down(CtrlKey)
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('ArrowRight')
|
await page.keyboard.press('ArrowRight')
|
||||||
await page.keyboard.up(CtrlKey)
|
await page.keyboard.up('ControlOrMeta')
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type('//')
|
await page.keyboard.type('//')
|
||||||
|
|||||||
@ -190,7 +190,7 @@ function CoreDump() {
|
|||||||
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
() => new CoreDumpManager(engineCommandManager, codeManager, token),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
useHotkeyWrapper(['meta + shift + .'], () => {
|
useHotkeyWrapper(['mod + shift + .'], () => {
|
||||||
toast.promise(
|
toast.promise(
|
||||||
coreDump(coreDumpManager, true),
|
coreDump(coreDumpManager, true),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -396,8 +396,8 @@ export const FileTreeMenu = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
useHotkeyWrapper(['meta + n'], createFile)
|
useHotkeyWrapper(['mod + n'], createFile)
|
||||||
useHotkeyWrapper(['meta + shift + n'], createFolder)
|
useHotkeyWrapper(['mod + shift + n'], createFolder)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -90,12 +90,12 @@ export const processMemory = (programMemory: ProgramMemory) => {
|
|||||||
for (const [key, val] of programMemory?.visibleEntries()) {
|
for (const [key, val] of programMemory?.visibleEntries()) {
|
||||||
if (typeof val.value !== 'function') {
|
if (typeof val.value !== 'function') {
|
||||||
const sg = sketchGroupFromKclValue(val, null)
|
const sg = sketchGroupFromKclValue(val, null)
|
||||||
if (!err(sg)) {
|
if (val.type === 'ExtrudeGroup') {
|
||||||
processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => {
|
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
|
||||||
return rest
|
return rest
|
||||||
})
|
})
|
||||||
} else if (val.type === 'ExtrudeGroup') {
|
} else if (!err(sg)) {
|
||||||
processedMemory[key] = val.value.map(({ ...rest }: ExtrudeSurface) => {
|
processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => {
|
||||||
return rest
|
return rest
|
||||||
})
|
})
|
||||||
} else if ((val.type as any) === 'Function') {
|
} else if ((val.type as any) === 'Function') {
|
||||||
|
|||||||
@ -1,44 +1,11 @@
|
|||||||
import { isDesktop } from 'lib/isDesktop'
|
import { Platform, platform } from 'lib/utils'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
export type Platform = 'macos' | 'windows' | 'linux' | ''
|
|
||||||
|
|
||||||
export default function usePlatform() {
|
export default function usePlatform() {
|
||||||
const [platformName, setPlatformName] = useState<Platform>('')
|
const [platformName, setPlatformName] = useState<Platform>('')
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function getPlatform(): Platform {
|
setPlatformName(platform())
|
||||||
const platform = window.electron.platform ?? ''
|
|
||||||
// https://nodejs.org/api/process.html#processplatform
|
|
||||||
switch (platform) {
|
|
||||||
case 'darwin':
|
|
||||||
return 'macos'
|
|
||||||
case 'win32':
|
|
||||||
return 'windows'
|
|
||||||
// We don't currently care to distinguish between these.
|
|
||||||
case 'android':
|
|
||||||
case 'freebsd':
|
|
||||||
case 'linux':
|
|
||||||
case 'openbsd':
|
|
||||||
case 'sunos':
|
|
||||||
return 'linux'
|
|
||||||
default:
|
|
||||||
console.error('Unknown platform:', platform)
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDesktop()) {
|
|
||||||
setPlatformName(getPlatform())
|
|
||||||
} else {
|
|
||||||
if (navigator.userAgent.indexOf('Mac') !== -1) {
|
|
||||||
setPlatformName('macos')
|
|
||||||
} else if (navigator.userAgent.indexOf('Win') !== -1) {
|
|
||||||
setPlatformName('windows')
|
|
||||||
} else if (navigator.userAgent.indexOf('Linux') !== -1) {
|
|
||||||
setPlatformName('linux')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [setPlatformName])
|
}, [setPlatformName])
|
||||||
|
|
||||||
return platformName
|
return platformName
|
||||||
|
|||||||
@ -338,13 +338,16 @@ export function sketchGroupFromKclValue(
|
|||||||
varName: string | null
|
varName: string | null
|
||||||
): SketchGroup | Error {
|
): SketchGroup | Error {
|
||||||
if (obj?.value?.type === 'SketchGroup') return obj.value
|
if (obj?.value?.type === 'SketchGroup') return obj.value
|
||||||
|
if (obj?.value?.type === 'ExtrudeGroup') return obj.value.sketchGroup
|
||||||
|
if (obj?.type === 'ExtrudeGroup') return obj.sketchGroup
|
||||||
if (!varName) {
|
if (!varName) {
|
||||||
varName = 'a KCL value'
|
varName = 'a KCL value'
|
||||||
}
|
}
|
||||||
const actualType = obj?.value?.type ?? obj?.type
|
const actualType = obj?.value?.type ?? obj?.type
|
||||||
if (actualType) {
|
if (actualType) {
|
||||||
|
console.log(obj)
|
||||||
return new Error(
|
return new Error(
|
||||||
`Expected ${varName} to be a sketchGroup, but it was ${actualType} instead.`
|
`Expected ${varName} to be a sketchGroup or extrudeGroup, but it was ${actualType} instead.`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return new Error(`Expected ${varName} to be a sketchGroup, but it wasn't.`)
|
return new Error(`Expected ${varName} to be a sketchGroup, but it wasn't.`)
|
||||||
|
|||||||
@ -109,11 +109,13 @@ export class CoreDumpManager {
|
|||||||
|
|
||||||
getWebrtcStats(): Promise<string> {
|
getWebrtcStats(): Promise<string> {
|
||||||
if (!this.engineCommandManager.engineConnection) {
|
if (!this.engineCommandManager.engineConnection) {
|
||||||
throw new Error('Engine connection not initialized')
|
// when the engine connection is not available, return an empty object.
|
||||||
|
return Promise.resolve(JSON.stringify({}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.engineCommandManager.engineConnection.webrtcStatsCollector) {
|
if (!this.engineCommandManager.engineConnection.webrtcStatsCollector) {
|
||||||
throw new Error('Engine webrtcStatsCollector not initialized')
|
// when the engine connection is not available, return an empty object.
|
||||||
|
return Promise.resolve(JSON.stringify({}))
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.engineCommandManager.engineConnection
|
return this.engineCommandManager.engineConnection
|
||||||
|
|||||||
@ -441,28 +441,34 @@ export const readProjectSettingsFile = async (
|
|||||||
return configObj
|
return configObj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the app settings file, or creates an initial one if it doesn't exist.
|
||||||
|
*/
|
||||||
export const readAppSettingsFile = async () => {
|
export const readAppSettingsFile = async () => {
|
||||||
let settingsPath = await getAppSettingsFilePath()
|
let settingsPath = await getAppSettingsFilePath()
|
||||||
try {
|
|
||||||
await window.electron.stat(settingsPath)
|
|
||||||
} catch (e) {
|
|
||||||
if (e === 'ENOENT') {
|
|
||||||
const config = defaultAppSettings()
|
|
||||||
if (err(config)) return Promise.reject(config)
|
|
||||||
if (!config.settings?.app)
|
|
||||||
return Promise.reject(new Error('config.app is falsey'))
|
|
||||||
|
|
||||||
config.settings.app.project_directory = await getInitialDefaultDir()
|
// The file exists, read it and parse it.
|
||||||
return config
|
if (window.electron.exists(settingsPath)) {
|
||||||
|
const configToml = await window.electron.readFile(settingsPath)
|
||||||
|
const configObj = parseAppSettings(configToml)
|
||||||
|
if (err(configObj)) {
|
||||||
|
return Promise.reject(configObj)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const configToml = await window.electron.readFile(settingsPath)
|
return configObj
|
||||||
const configObj = parseAppSettings(configToml)
|
|
||||||
if (err(configObj)) {
|
|
||||||
return Promise.reject(configObj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return configObj
|
// The file doesn't exist, create a new one.
|
||||||
|
// This defaultAppConfig is truly an empty object every time.
|
||||||
|
const defaultAppConfig = defaultAppSettings()
|
||||||
|
if (err(defaultAppConfig)) {
|
||||||
|
return Promise.reject(defaultAppConfig)
|
||||||
|
}
|
||||||
|
const initialDirConfig: DeepPartial<Configuration> = {
|
||||||
|
settings: { project: { directory: await getInitialDefaultDir() } },
|
||||||
|
}
|
||||||
|
const config = Object.assign(defaultAppConfig, initialDirConfig)
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
export const writeAppSettingsFile = async (tomlStr: string) => {
|
export const writeAppSettingsFile = async (tomlStr: string) => {
|
||||||
|
|||||||
@ -31,7 +31,7 @@ function mapHotkeyToCodeMirrorHotkey(hotkey: string): string {
|
|||||||
return hotkey
|
return hotkey
|
||||||
.replaceAll('+', '-')
|
.replaceAll('+', '-')
|
||||||
.replaceAll(' ', '')
|
.replaceAll(' ', '')
|
||||||
.replaceAll('mod', 'Meta')
|
.replaceAll('mod', 'Mod')
|
||||||
.replaceAll('meta', 'Meta')
|
.replaceAll('meta', 'Meta')
|
||||||
.replaceAll('ctrl', 'Ctrl')
|
.replaceAll('ctrl', 'Ctrl')
|
||||||
.replaceAll('shift', 'Shift')
|
.replaceAll('shift', 'Shift')
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
|
import { platform } from 'lib/utils'
|
||||||
|
|
||||||
export type InteractionMapItem = {
|
export type InteractionMapItem = {
|
||||||
name: string
|
name: string
|
||||||
@ -24,6 +25,11 @@ export const interactionMapCategories = [
|
|||||||
|
|
||||||
type InteractionMapCategory = (typeof interactionMapCategories)[number]
|
type InteractionMapCategory = (typeof interactionMapCategories)[number]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary modifier key for the current platform.
|
||||||
|
*/
|
||||||
|
const PRIMARY = platform() === 'macos' ? 'Command' : 'Control'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A temporary implementation of the interaction map for
|
* A temporary implementation of the interaction map for
|
||||||
* display purposes only.
|
* display purposes only.
|
||||||
@ -38,7 +44,7 @@ export const interactionMap: Record<
|
|||||||
Settings: [
|
Settings: [
|
||||||
{
|
{
|
||||||
name: 'toggle-settings',
|
name: 'toggle-settings',
|
||||||
sequence: isDesktop() ? 'Meta+,' : 'Shift+Meta+,',
|
sequence: isDesktop() ? `${PRIMARY}+,` : `Shift+${PRIMARY}+,`,
|
||||||
title: 'Toggle Settings',
|
title: 'Toggle Settings',
|
||||||
description: 'Opens the settings dialog. Always available.',
|
description: 'Opens the settings dialog. Always available.',
|
||||||
},
|
},
|
||||||
@ -53,7 +59,7 @@ export const interactionMap: Record<
|
|||||||
'Command Palette': [
|
'Command Palette': [
|
||||||
{
|
{
|
||||||
name: 'toggle-command-palette',
|
name: 'toggle-command-palette',
|
||||||
sequence: 'Meta+K',
|
sequence: `${PRIMARY}+K`,
|
||||||
title: 'Toggle Command Palette',
|
title: 'Toggle Command Palette',
|
||||||
description: 'Always available. Use Ctrl+/ on Windows/Linux.',
|
description: 'Always available. Use Ctrl+/ on Windows/Linux.',
|
||||||
},
|
},
|
||||||
@ -159,7 +165,7 @@ export const interactionMap: Record<
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'delete-file',
|
name: 'delete-file',
|
||||||
sequence: 'Meta+Backspace',
|
sequence: `${PRIMARY}+Backspace`,
|
||||||
title: 'Delete File/Folder',
|
title: 'Delete File/Folder',
|
||||||
description:
|
description:
|
||||||
'Available when a file or folder is selected in the file tree.',
|
'Available when a file or folder is selected in the file tree.',
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { SourceRange } from '../lang/wasm'
|
import { SourceRange } from '../lang/wasm'
|
||||||
|
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
|
import { isDesktop } from './isDesktop'
|
||||||
|
|
||||||
export const uuidv4 = v4
|
export const uuidv4 = v4
|
||||||
|
|
||||||
@ -126,6 +127,41 @@ export function getNormalisedCoordinates({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove the empty platform type.
|
||||||
|
export type Platform = 'macos' | 'windows' | 'linux' | ''
|
||||||
|
|
||||||
|
export function platform(): Platform {
|
||||||
|
if (isDesktop()) {
|
||||||
|
const platform = window.electron.platform ?? ''
|
||||||
|
// https://nodejs.org/api/process.html#processplatform
|
||||||
|
switch (platform) {
|
||||||
|
case 'darwin':
|
||||||
|
return 'macos'
|
||||||
|
case 'win32':
|
||||||
|
return 'windows'
|
||||||
|
// We don't currently care to distinguish between these.
|
||||||
|
case 'android':
|
||||||
|
case 'freebsd':
|
||||||
|
case 'linux':
|
||||||
|
case 'openbsd':
|
||||||
|
case 'sunos':
|
||||||
|
return 'linux'
|
||||||
|
default:
|
||||||
|
console.error('Unknown platform:', platform)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (navigator.userAgent.indexOf('Mac') !== -1) {
|
||||||
|
return 'macos'
|
||||||
|
} else if (navigator.userAgent.indexOf('Win') !== -1) {
|
||||||
|
return 'windows'
|
||||||
|
} else if (navigator.userAgent.indexOf('Linux') !== -1) {
|
||||||
|
return 'linux'
|
||||||
|
}
|
||||||
|
console.error('Unknown platform userAgent:', navigator.userAgent)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
export function isReducedMotion(): boolean {
|
export function isReducedMotion(): boolean {
|
||||||
return (
|
return (
|
||||||
typeof window !== 'undefined' &&
|
typeof window !== 'undefined' &&
|
||||||
|
|||||||
@ -230,29 +230,42 @@ pub struct OsInfo {
|
|||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub struct WebrtcStats {
|
pub struct WebrtcStats {
|
||||||
/// The packets lost.
|
/// The packets lost.
|
||||||
pub packets_lost: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub packets_lost: Option<u32>,
|
||||||
/// The frames received.
|
/// The frames received.
|
||||||
pub frames_received: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub frames_received: Option<u32>,
|
||||||
/// The frame width.
|
/// The frame width.
|
||||||
pub frame_width: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub frame_width: Option<f32>,
|
||||||
/// The frame height.
|
/// The frame height.
|
||||||
pub frame_height: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub frame_height: Option<f32>,
|
||||||
/// The frame rate.
|
/// The frame rate.
|
||||||
pub frame_rate: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub frame_rate: Option<f32>,
|
||||||
/// The number of key frames decoded.
|
/// The number of key frames decoded.
|
||||||
pub key_frames_decoded: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub key_frames_decoded: Option<u32>,
|
||||||
/// The number of frames dropped.
|
/// The number of frames dropped.
|
||||||
pub frames_dropped: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub frames_dropped: Option<u32>,
|
||||||
/// The pause count.
|
/// The pause count.
|
||||||
pub pause_count: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub pause_count: Option<u32>,
|
||||||
/// The total pauses duration.
|
/// The total pauses duration.
|
||||||
pub total_pauses_duration: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub total_pauses_duration: Option<f32>,
|
||||||
/// The freeze count.
|
/// The freeze count.
|
||||||
pub freeze_count: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub freeze_count: Option<u32>,
|
||||||
/// The total freezes duration.
|
/// The total freezes duration.
|
||||||
pub total_freezes_duration: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub total_freezes_duration: Option<f32>,
|
||||||
/// The pli count.
|
/// The pli count.
|
||||||
pub pli_count: u32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub pli_count: Option<u32>,
|
||||||
/// Packet jitter for this synchronizing source, measured in seconds.
|
/// Packet jitter for this synchronizing source, measured in seconds.
|
||||||
pub jitter: f32,
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub jitter: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user