Compare commits

...

26 Commits

Author SHA1 Message Date
bf36e62b97 Fmt 2024-08-20 15:41:38 +02:00
3e45d7ac4e A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest) 2024-08-20 13:40:43 +00:00
259279fd53 Merge branch 'main' into franknoirot/fix-electron-use-sep 2024-08-20 09:37:13 -04:00
0ef6eac239 Fix existing: when engine fails export we handle the failure and alert the user (#3561)
Fix existing: when engine fails export we handle the failure and alert the user #3560
2024-08-20 20:29:15 +10:00
c674feb782 Update the Web Banner (#3563)
Update the Web Banner #3503
2024-08-20 20:28:39 +10:00
fba3d7c5c1 skip failing windows tests (#3558) 2024-08-20 06:37:16 +00:00
8b8fb696d0 Fix existing: Can edit a sketch that has been extruded in the same pipe (#3552)
Fix existing: Can edit a sketch that has been extruded in the same pipe #3551
2024-08-20 14:13:56 +10:00
d05f3c00b9 test keyboard shortcuts from help menu (#3553)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-19 20:17:23 -07:00
2541e0c0ea Electron test (regression): select all in code editor does not actually select all, just what is visiable (#3540)
* select all in code editor does not actually select all, just what is visible #3175

* whops

* fix test for linux
2024-08-20 11:23:32 +10:00
5e5a204244 move the file (#3544)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-19 17:55:26 -07:00
032c2fdd24 Add GitHub action to label issues opened by ZooSpiritWolf (#3543)
* Add GitHub action to label issues opened by ZooSpiritWolf

Fixes #3541

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/KittyCAD/modeling-app/issues/3541?shareId=XXXX-XXXX-XXXX-XXXX).

* Update label-issues.yml
2024-08-19 17:54:24 -07:00
27883e7800 Electron machine api tests (#3534)
* start

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Look at this (photo)Graph *in the voice of Nickelback*

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* hide on webapp

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix machine-api

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-19 15:57:31 -07:00
1ccb810e23 Revert "electron test code with error loading" (#3533)
Revert "electron test code with error loading (#3531)"

This reverts commit c7f533b38e.
2024-08-19 14:23:01 -07:00
1c83f148d9 export on project/file pane load playwright test (#3489)
* export on project/file pane load

Signed-off-by: Jess Frazelle <github@jessfraz.com>

add desktop exxport

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Look at this (photo)Graph *in the voice of Nickelback*

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix lint

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixeds

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-19 14:21:34 -07:00
c7f533b38e electron test code with error loading (#3531)
* electron test code with error loading

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Look at this (photo)Graph *in the voice of Nickelback*

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-19 13:54:11 -07:00
2b711d216f Run eslint in CI (#3487)
* Run eslint in CI

* Add linting of e2e

* Fix formatting

* Fix new warnings in e2e

* Fix more new warnings
2024-08-19 12:36:18 -07:00
c67511f67c Electron test: (regression) you can scroll the file pane when ETOOMANYFILES to view in one view (#3520)
* can scroll files pane

* clean up
2024-08-20 05:34:26 +10:00
d9423219d1 Bump @csstools/postcss-oklab-function from 3.0.19 to 4.0.2 (#3517)
Bumps [@csstools/postcss-oklab-function](https://github.com/csstools/postcss-plugins/tree/HEAD/plugins/postcss-oklab-function) from 3.0.19 to 4.0.2.
- [Changelog](https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-oklab-function/CHANGELOG.md)
- [Commits](https://github.com/csstools/postcss-plugins/commits/HEAD/plugins/postcss-oklab-function)

---
updated-dependencies:
- dependency-name: "@csstools/postcss-oklab-function"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:29:10 -07:00
3f270d8bcf Bump react-router-dom from 6.26.0 to 6.26.1 (#3516)
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.26.0 to 6.26.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.26.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:28:59 -07:00
4c7b72329d Bump @playwright/test from 1.45.3 to 1.46.1 (#3514)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.45.3 to 1.46.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.45.3...v1.46.1)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:27:12 -07:00
4c060f3d2f Bump electron from 31.3.1 to 31.4.0 (#3515)
Bumps [electron](https://github.com/electron/electron) from 31.3.1 to 31.4.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v31.3.1...v31.4.0)

---
updated-dependencies:
- dependency-name: electron
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:26:53 -07:00
f3afbe8a7b Bump @types/node from 18.19.42 to 22.4.1 (#3513)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 18.19.42 to 22.4.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:26:36 -07:00
dad7a84798 Bump tokio from 1.39.2 to 1.39.3 in /src/wasm-lib (#3511)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.39.2 to 1.39.3.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.39.2...tokio-1.39.3)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:26:03 -07:00
1a560fdc6a Bump syn from 2.0.74 to 2.0.75 in /src/wasm-lib (#3510)
Bumps [syn](https://github.com/dtolnay/syn) from 2.0.74 to 2.0.75.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/2.0.74...2.0.75)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:25:53 -07:00
54eea741b5 Merge branch 'main' into franknoirot/fix-electron-use-sep 2024-08-19 12:24:01 -07:00
24cc00e9c5 Use sep utility instead of forward slash for path name parsing 2024-08-19 13:41:32 -04:00
44 changed files with 1124 additions and 315 deletions

View File

@ -25,6 +25,7 @@
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure "files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
"rules": { "rules": {
"@typescript-eslint/no-floating-promises": "warn", "@typescript-eslint/no-floating-promises": "warn",
"suggest-no-throw/suggest-no-throw": "off",
"testing-library/prefer-screen-queries": "off" "testing-library/prefer-screen-queries": "off"
} }
}, },

View File

@ -44,6 +44,8 @@ jobs:
- run: yarn build:wasm - run: yarn build:wasm
- run: yarn xstate:typegen - run: yarn xstate:typegen
- run: yarn tsc - run: yarn tsc
- name: Lint
run: yarn eslint --max-warnings 0 src e2e
check-typos: check-typos:

31
.github/workflows/label-issues.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Label Issues
on:
issues:
types: [opened]
permissions:
issues: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Check if issue opener is ZooSpiritWolf
id: check_opener
uses: actions/github-script@v4
with:
script: |
const issueOpener = context.payload.issue.user.login;
return issueOpener === 'ZooSpiritWolf';
- name: Add labels
if: steps.check_opener.outputs.result == 'true'
uses: actions/github-script@v4
with:
script: |
github.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: ['bug', 'regression', 'high-priority']
});

View File

@ -346,7 +346,7 @@ jobs:
run: yarn build:wasm run: yarn build:wasm
- name: build electron - name: build electron
shell: bash shell: bash
run: yarn electron:package run: yarn tron:package
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
if: ${{ !cancelled() && (success() || failure()) }} if: ${{ !cancelled() && (success() || failure()) }}
continue-on-error: true continue-on-error: true

View File

@ -101,7 +101,7 @@ This will start the application and hot-reload on changed.
Devtools can be opened with the usual Cmd/Ctrl-Shift-I. Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
To build, run `yarn electron:package`. To build, run `yarn tron:package`.
## Checking out commits / Bisecting ## Checking out commits / Bisecting

View File

@ -9,6 +9,7 @@ test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo) await tearDown(page, testInfo)
}) })
test.describe('Copilot ghost text', () => { test.describe('Copilot ghost text', () => {
// eslint-disable-next-line jest/valid-title
test.skip(true, 'Needs to get covered again') test.skip(true, 'Needs to get covered again')
test('completes code in empty file', async ({ page }) => { test('completes code in empty file', async ({ page }) => {

View File

@ -0,0 +1,188 @@
import { test, expect } from '@playwright/test'
import { getUtils, setupElectron, tearDown } from './test-utils'
import fsp from 'fs/promises'
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test(
'export works on the first try',
{ 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 Promise.all([fsp.mkdir(`${dir}/bracket`, { recursive: true })])
await Promise.all([
fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/router-template-slate.kcl',
`${dir}/bracket/other.kcl`
),
fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
`${dir}/bracket/main.kcl`
),
])
},
})
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
await test.step('on open of project', async () => {
await expect(page.getByText(`bracket`)).toBeVisible()
// open the project
await page.getByText(`bracket`).click()
// wait for the project to load
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// export the model
const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible()
const gltfOption = page.getByText('glTF')
const submitButton = page.getByText('Confirm Export')
const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// Click the export button
await exportButton.click()
await expect(gltfOption).toBeVisible()
await expect(page.getByText('STL')).toBeVisible()
await page.keyboard.press('Enter')
// Click the checkbox
await expect(submitButton).toBeVisible()
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message
await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed.
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible()
await expect(exportingToastMessage).not.toBeVisible()
await test.step('Check the export size', async () => {
await expect
.poll(
async () => {
try {
const outputGltf = await fsp.readFile('output.gltf')
return outputGltf.byteLength
} catch (e) {
return 0
}
},
{ timeout: 15_000 }
)
.toBe(477327)
// clean up output.gltf
await fsp.rm('output.gltf')
})
})
await test.step('on open of file in file pane', async () => {
const u = await getUtils(page)
await u.openFilePanel()
const otherKclButton = page.getByRole('button', { name: 'other.kcl' })
// Click the file
await otherKclButton.click()
// Close the file pane
await u.closeFilePanel()
// wait for it to finish executing (todo: make this more robust)
await page.waitForTimeout(1000)
// expect zero errors in guter
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// export the model
const exportButton = page.getByTestId('export-pane-button')
await expect(exportButton).toBeVisible()
const gltfOption = page.getByText('glTF')
const submitButton = page.getByText('Confirm Export')
const exportingToastMessage = page.getByText(`Exporting...`)
const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
// Click the export button
await exportButton.click()
await expect(gltfOption).toBeVisible()
await expect(page.getByText('STL')).toBeVisible()
await page.keyboard.press('Enter')
// Click the checkbox
await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter')
// Find the toast.
// Look out for the toast message
await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed.
await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible()
const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible()
await expect(exportingToastMessage).not.toBeVisible()
await test.step('Check the export size', async () => {
await expect
.poll(
async () => {
try {
const outputGltf = await fsp.readFile('output.gltf')
return outputGltf.byteLength
} catch (e) {
return 0
}
},
{ timeout: 15_000 }
)
.toBe(105022)
// clean up output.gltf
await fsp.rm('output.gltf')
})
await electronApp.close()
})
await electronApp.close()
}
)

View File

@ -714,17 +714,15 @@ test.describe('Editor tests', () => {
|> close(%)`) |> close(%)`)
}) })
// failing for the same reason as "Can edit a sketch that has been extruded in the same pipe" test('Can undo a sketch modification with ctrl+z', async ({ page }) => {
// please fix together
test.fixme('Can undo a sketch modification with ctrl+z', async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const sketch001 = startSketchOn('XZ') `const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %) |> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %) |> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -0.38], %)
|> close(%) |> close(%)
|> extrude(5, %)` |> extrude(5, %)`
) )
@ -759,11 +757,11 @@ test.describe('Editor tests', () => {
}) })
await page.waitForTimeout(100) await page.waitForTimeout(100)
const startPX = [665, 458] const startPX = [665, 397]
const dragPX = 40 const dragPX = 40
await page.getByText('startProfileAt([4.61, -14.01], %)').click() await page.getByText('startProfileAt([4.61, -10.01], %)').click()
await expect( await expect(
page.getByRole('button', { name: 'Edit Sketch' }) page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible() ).toBeVisible()
@ -801,7 +799,7 @@ test.describe('Editor tests', () => {
// drag tangentialArcTo handle // drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 }, sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
targetPosition: { targetPosition: {
x: tangentEnd.x + dragPX, x: tangentEnd.x + dragPX,
y: tangentEnd.y + dragPX, y: tangentEnd.y + dragPX,
@ -813,12 +811,12 @@ test.describe('Editor tests', () => {
// expect the code to have changed // expect the code to have changed
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %) |> startProfileAt([7.12, -12.68], %)
|> line([15.4, -2.74], %) |> line([15.39, -2.78], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([27.6, -3.05], %)
|> line([2.65, -2.69], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)
`)
// Hit undo // Hit undo
await page.keyboard.down('Control') await page.keyboard.down('Control')
@ -827,9 +825,9 @@ test.describe('Editor tests', () => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %) |> startProfileAt([7.12, -12.68], %)
|> line([15.4, -2.74], %) |> line([15.39, -2.78], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -0.38], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)`)
@ -840,11 +838,12 @@ test.describe('Editor tests', () => {
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %) |> startProfileAt([7.12, -12.68], %)
|> line([12.73, -0.09], %) |> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -0.38], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)
`)
// Hit undo again. // Hit undo again.
await page.keyboard.down('Control') await page.keyboard.down('Control')
@ -854,9 +853,9 @@ test.describe('Editor tests', () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %) |> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %) |> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -0.38], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)`)
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,102 @@
import { test, expect } from '@playwright/test'
import { setupElectron, tearDown } from './test-utils'
import fsp from 'fs/promises'
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test(
'When machine-api server not found butt is disabled and shows the reason',
{ 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}/bracket`, { recursive: true })
await fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
`${dir}/bracket/main.kcl`
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
const notFoundText = 'Machine API server was not discovered'
await expect(page.getByText(notFoundText).first()).not.toBeVisible()
// Find the make button
const makeButton = page.getByRole('button', { name: 'Make' })
// Make sure the button is visible but disabled
await expect(makeButton).toBeVisible()
await expect(makeButton).toBeDisabled()
// When you hover over the button, the tooltip should show
// that the machine-api server is not found
await makeButton.hover()
await expect(page.getByText(notFoundText).first()).toBeVisible()
await electronApp.close()
}
)
test(
'When machine-api server not found home screen & project status shows the reason',
{ 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}/bracket`, { recursive: true })
await fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
`${dir}/bracket/main.kcl`
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
const notFoundText = 'Machine API server was not discovered'
await expect(page.getByText(notFoundText)).not.toBeVisible()
const networkMachineToggle = page.getByTestId('network-machine-toggle')
await networkMachineToggle.hover()
await expect(page.getByText(notFoundText)).toBeVisible()
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await expect(page.getByText(notFoundText).nth(1)).not.toBeVisible()
await networkMachineToggle.hover()
await expect(page.getByText(notFoundText).nth(1)).toBeVisible()
await electronApp.close()
}
)

View File

@ -347,6 +347,10 @@ test(
'Restarting onboarding on desktop takes one attempt', 'Restarting onboarding on desktop takes one attempt',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browser: _ }, testInfo) => { async ({ browser: _ }, testInfo) => {
test.skip(
process.platform === 'win32',
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
)
const { electronApp, page } = await setupElectron({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {

View File

@ -2,6 +2,7 @@ import { test, expect } from '@playwright/test'
import { import {
doExport, doExport,
getUtils, getUtils,
isOutOfViewInScrollContainer,
Paths, Paths,
setupElectron, setupElectron,
tearDown, tearDown,
@ -14,10 +15,128 @@ test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo) await tearDown(page, testInfo)
}) })
test(
'click help/keybindings from home page',
{ tag: '@electron' },
async ({ browserName }, testInfo) => {
const { electronApp, page } = await setupElectron({
testInfo,
folderSetupFn: async () => {},
})
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
// click ? button
await page.getByTestId('help-button').click()
await expect(page.getByTestId('keybindings-button')).toBeVisible()
// Click keyboard shortcuts button.
await page.getByTestId('keybindings-button').click()
// Make sure the keyboard shortcuts modal is visible.
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
await electronApp.close()
}
)
test(
'click help/keybindings from project page',
{ 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}/bracket`, { recursive: true })
await fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl',
`${dir}/bracket/main.kcl`
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
page.on('console', console.log)
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// click ? button
await page.getByTestId('help-button').click()
await expect(page.getByTestId('keybindings-button')).toBeVisible()
// Click keyboard shortcuts button.
await page.getByTestId('keybindings-button').click()
// Make sure the keyboard shortcuts modal is visible.
await expect(page.getByText('Enter Sketch Mode')).toBeVisible()
await electronApp.close()
}
)
test(
'when code with error first loads you get errors in console',
{ 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}/broken-code`, { recursive: true })
await fsp.copyFile(
'src/wasm-lib/tests/executor/inputs/broken-code-test.kcl',
`${dir}/broken-code/main.kcl`
)
},
})
await page.setViewportSize({ width: 1200, height: 500 })
await expect(page.getByText('broken-code')).toBeVisible()
await page.getByText('broken-code').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
const crypticErrorText = `Expected a tag declarator`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
await electronApp.close()
}
)
test( test(
'Can export from electron app', 'Can export from electron app',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
@ -106,6 +225,10 @@ test(
'Rename and delete projects, also spam arrow keys when renaming', 'Rename and delete projects, also spam arrow keys when renaming',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
@ -394,6 +517,10 @@ test(
'Deleting projects, can delete individual project, can still create projects after deleting all', 'Deleting projects, can delete individual project, can still create projects after deleting all',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
}) })
@ -488,6 +615,10 @@ test(
'Can sort projects on home page', 'Can sort projects on home page',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
}) })
@ -610,6 +741,10 @@ test(
'When the project folder is empty, user can create new project and open it.', 'When the project folder is empty, user can create new project and open it.',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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 }) const { electronApp, page } = await setupElectron({ testInfo })
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
@ -694,6 +829,10 @@ test(
'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)', 'Opening a project should successfully load the stream, (regression test that this also works when switching between projects)',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
@ -907,6 +1046,10 @@ test(
'Search projects on desktop home', 'Search projects on desktop home',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName: _ }, testInfo) => { async ({ browserName: _ }, testInfo) => {
test.skip(
process.platform === 'win32',
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
)
const projectData = [ const projectData = [
['basic bracket', 'focusrite_scarlett_mounting_braket.kcl'], ['basic bracket', 'focusrite_scarlett_mounting_braket.kcl'],
['basic-cube', 'basic_fillet_cube_end.kcl'], ['basic-cube', 'basic_fillet_cube_end.kcl'],
@ -964,10 +1107,198 @@ test(
} }
) )
test(
'file pane is scrollable when there are many files',
{ 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 })
const fileNames = [
'angled_line.kcl',
'basic_fillet_cube_close_opposite.kcl',
'basic_fillet_cube_end.kcl',
'basic_fillet_cube_next_adjacent.kcl',
'basic_fillet_cube_previous_adjacent.kcl',
'basic_fillet_cube_start.kcl',
'big_number_angle_to_match_length_x.kcl',
'big_number_angle_to_match_length_y.kcl',
'close_arc.kcl',
'computed_var.kcl',
'cube-embedded.gltf',
'cube.bin',
'cube.glb',
'cube.gltf',
'cube.kcl',
'cube.mtl',
'cube.obj',
'cylinder.kcl',
'dimensions_match.kcl',
'extrude-custom-plane.kcl',
'extrude-inside-fn-with-tags.kcl',
'fillet-and-shell.kcl',
'fillet_duplicate_tags.kcl',
'focusrite_scarlett_mounting_braket.kcl',
'function_sketch.kcl',
'function_sketch_with_position.kcl',
'global-tags.kcl',
'helix_ccw.kcl',
'helix_defaults.kcl',
'helix_defaults_negative_extrude.kcl',
'helix_with_length.kcl',
'i_shape.kcl',
'kittycad_svg.kcl',
'lego.kcl',
'math.kcl',
'member_expression_sketch_group.kcl',
'mike_stress_test.kcl',
'negative_args.kcl',
'order-sketch-extrude-in-order.kcl',
'order-sketch-extrude-out-of-order.kcl',
'parametric.kcl',
'parametric_with_tan_arc.kcl',
'pattern_vase.kcl',
'pentagon_fillet_sugar.kcl',
'pipe_as_arg.kcl',
'pipes_on_pipes.kcl',
'riddle.kcl',
'riddle_small.kcl',
'router-template-slate.kcl',
'scoped-tags.kcl',
'server-rack-heavy.kcl',
'server-rack-lite.kcl',
'sketch_on_face.kcl',
'sketch_on_face_circle_tagged.kcl',
'sketch_on_face_end.kcl',
'sketch_on_face_end_negative_extrude.kcl',
'sketch_on_face_start.kcl',
'tan_arc_x_line.kcl',
'tangential_arc.kcl',
]
for (const fileName of fileNames) {
await fsp.copyFile(
`src/wasm-lib/tests/executor/inputs/${fileName}`,
`${dir}/testProject/${fileName}`
)
}
},
})
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
await test.step('setup, open file pane', async () => {
await page.getByText('testProject').click()
await expect(page.getByTestId('loading')).toBeAttached()
await expect(page.getByTestId('loading')).not.toBeAttached({
timeout: 20_000,
})
await page.getByTestId('files-pane-button').click()
})
await test.step('check the last file is out of view initially, and can be scrolled to', async () => {
const element = page.getByText('tangential_arc.kcl')
const container = page.getByTestId('file-pane-scroll-container')
await expect(await isOutOfViewInScrollContainer(element, container)).toBe(
true
)
await element.scrollIntoViewIfNeeded()
await expect(await isOutOfViewInScrollContainer(element, container)).toBe(
false
)
})
await electronApp.close()
}
)
test(
'select all in code editor does not actually select all, just what is visible (regression)',
{ 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) => {
// src/wasm-lib/tests/executor/inputs/mike_stress_test.kcl
const name = 'mike_stress_test'
await fsp.mkdir(`${dir}/${name}`, { recursive: true })
await fsp.copyFile(
`src/wasm-lib/tests/executor/inputs/${name}.kcl`,
`${dir}/${name}/main.kcl`
)
},
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
page.on('console', console.log)
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 u.codeLocator.click()
// expect u.codeLocator to have some text
await expect(u.codeLocator).toContainText('line(')
await page.keyboard.down(modifier)
await page.keyboard.press('KeyA')
await page.keyboard.up(modifier)
// check the length of the selected text
const selectedText = await page.evaluate(() => {
const selection = window.getSelection()
return selection ? selection.toString() : ''
})
// even though if the user copied the text into their clipboard they would get the full text
// it seems that the selection is limited to what is visible
// we just want to check we did select something, and later we've verify it's empty
expect(selectedText.length).toBeGreaterThan(10)
})
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.down(modifier)
await page.keyboard.press('KeyA')
await page.keyboard.up(modifier)
// check the length of the selected text
const selectedText = await page.evaluate(() => {
const selection = window.getSelection()
return selection ? selection.toString() : ''
})
expect(selectedText.length).toBe(0)
await expect(u.codeLocator).toHaveText('')
})
await electronApp.close()
}
)
test( test(
'Settings persist across restarts', 'Settings persist across restarts',
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName }, testInfo) => { async ({ browserName }, testInfo) => {
test.skip(
process.platform === 'win32',
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
)
await test.step('We can change a user setting like theme', async () => { await test.step('We can change a user setting like theme', async () => {
const { electronApp, page } = await setupElectron({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,

View File

@ -236,9 +236,13 @@ const sketch001 = startSketchAt([-0, -0])
page, page,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async (code) => { await page.addInitScript(
async ({ code }) => {
localStorage.setItem('persistCode', code) localStorage.setItem('persistCode', code)
}, TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR) ;(window as any).playwrightSkipFilePicker = true
},
{ code: TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR }
)
await page.setViewportSize({ width: 1000, height: 500 }) await page.setViewportSize({ width: 1000, height: 500 })
@ -325,7 +329,7 @@ const sketch001 = startSketchAt([-0, -0])
await expect(exportingToastMessage).toBeVisible() await expect(exportingToastMessage).toBeVisible()
// Expect it to succeed. // Expect it to succeed.
await expect(exportingToastMessage).not.toBeVisible() await expect(exportingToastMessage).not.toBeVisible({ timeout: 15_000 })
await expect(errorToastMessage).not.toBeVisible() await expect(errorToastMessage).not.toBeVisible()
await expect(engineErrorToastMessage).not.toBeVisible() await expect(engineErrorToastMessage).not.toBeVisible()
@ -337,6 +341,7 @@ const sketch001 = startSketchAt([-0, -0])
}) => { }) => {
// This is being weird on ubuntu and windows. // This is being weird on ubuntu and windows.
test.skip( test.skip(
// eslint-disable-next-line jest/valid-title
process.platform === 'linux' || process.platform === 'win32', process.platform === 'linux' || process.platform === 'win32',
'This test is being weird on ubuntu' 'This test is being weird on ubuntu'
) )
@ -420,6 +425,10 @@ const sketch001 = startSketchAt([-0, -0])
`Network health indicator only appears in modeling view`, `Network health indicator only appears in modeling view`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ browserName: _ }, testInfo) => { 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({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
@ -472,7 +481,7 @@ async function clickExportButton(page: Page) {
// Click the export button // Click the export button
await exportButton.click() await exportButton.click()
// Click the stl. // Click the gltf.
const gltfOption = page.getByRole('option', { name: 'glTF' }) const gltfOption = page.getByRole('option', { name: 'glTF' })
await expect(gltfOption).toBeVisible() await expect(gltfOption).toBeVisible()

View File

@ -344,19 +344,17 @@ test.describe('Sketch tests', () => {
}) })
}) })
// failing for the same reason as "Can undo a sketch modification with ctrl+z" test('Can edit a sketch that has been extruded in the same pipe', async ({
// please fix together page,
test.fixme( }) => {
'Can edit a sketch that has been extruded in the same pipe',
async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`const sketch001 = startSketchOn('XZ') `const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %) |> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %) |> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -0.38], %)
|> close(%) |> close(%)
|> extrude(5, %)` |> extrude(5, %)`
) )
@ -391,11 +389,11 @@ test.describe('Sketch tests', () => {
}) })
await page.waitForTimeout(100) await page.waitForTimeout(100)
const startPX = [665, 458] const startPX = [665, 397]
const dragPX = 40 const dragPX = 40
await page.getByText('startProfileAt([4.61, -14.01], %)').click() await page.getByText('startProfileAt([4.61, -10.01], %)').click()
await expect( await expect(
page.getByRole('button', { name: 'Edit Sketch' }) page.getByRole('button', { name: 'Edit Sketch' })
).toBeVisible() ).toBeVisible()
@ -429,7 +427,7 @@ test.describe('Sketch tests', () => {
// drag tangentialArcTo handle // drag tangentialArcTo handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x, y: tangentEnd.y - 5 }, sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
targetPosition: { targetPosition: {
x: tangentEnd.x + dragPX, x: tangentEnd.x + dragPX,
y: tangentEnd.y + dragPX, y: tangentEnd.y + dragPX,
@ -441,14 +439,13 @@ test.describe('Sketch tests', () => {
// expect the code to have changed // expect the code to have changed
await expect(page.locator('.cm-content')) await expect(page.locator('.cm-content'))
.toHaveText(`const sketch001 = startSketchOn('XZ') .toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -16.82], %) |> startProfileAt([7.12, -12.68], %)
|> line([15.4, -2.74], %) |> line([15.39, -2.78], %)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([27.6, -3.05], %)
|> line([2.65, -2.69], %)
|> close(%) |> close(%)
|> extrude(5, %)`) |> extrude(5, %)
} `)
) })
test('Can edit a sketch that has been revolved in the same pipe', async ({ test('Can edit a sketch that has been revolved in the same pipe', async ({
page, page,
@ -602,7 +599,7 @@ test.describe('Sketch tests', () => {
await expect(u.codeLocator).toHaveText(codeStr) await expect(u.codeLocator).toHaveText(codeStr)
// exit the sketch, reset relative clicker // exit the sketch, reset relative clicker
click00r(undefined, undefined) await click00r(undefined, undefined)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
await page.getByRole('button', { name: 'Exit Sketch' }).click() await page.getByRole('button', { name: 'Exit Sketch' }).click()
await u.expectCmdLog('[data-message-type="execution-done"]') await u.expectCmdLog('[data-message-type="execution-done"]')

View File

@ -53,6 +53,7 @@ test(
async ({ page, context }) => { async ({ page, context }) => {
// skip on macos and windows. // skip on macos and windows.
test.skip( test.skip(
// eslint-disable-next-line jest/valid-title
process.platform === 'darwin' || process.platform === 'win32', process.platform === 'darwin' || process.platform === 'win32',
'Skip on macos and windows' 'Skip on macos and windows'
) )

View File

@ -5,6 +5,7 @@ import {
TestInfo, TestInfo,
BrowserContext, BrowserContext,
_electron as electron, _electron as electron,
Locator,
} from '@playwright/test' } from '@playwright/test'
import { EngineCommand } from 'lang/std/artifactGraph' import { EngineCommand } from 'lang/std/artifactGraph'
import os from 'os' import os from 'os'
@ -94,6 +95,8 @@ async function expectCmdLog(page: Page, locatorStr: string, timeout = 5000) {
await expect(page.locator(locatorStr).last()).toBeVisible({ timeout }) await expect(page.locator(locatorStr).last()).toBeVisible({ timeout })
} }
// Ignoring the lint since I assume someone will want to use this for a test.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function waitForDefaultPlanesToBeVisible(page: Page) { async function waitForDefaultPlanesToBeVisible(page: Page) {
await page.waitForFunction( await page.waitForFunction(
() => () =>
@ -145,6 +148,27 @@ async function closeDebugPanel(page: Page) {
} }
} }
async function openFilePanel(page: Page) {
const fileLocator = page.getByTestId('files-pane-button')
await expect(fileLocator).toBeVisible()
const isOpen = (await fileLocator?.getAttribute('aria-pressed')) === 'true'
if (!isOpen) {
await fileLocator.click()
await expect(fileLocator).toHaveAttribute('aria-pressed', 'true')
}
}
async function closeFilePanel(page: Page) {
const fileLocator = page.getByTestId('files-pane-button')
await expect(fileLocator).toBeVisible()
const isOpen = (await fileLocator?.getAttribute('aria-pressed')) === 'true'
if (isOpen) {
await fileLocator.click()
await expect(fileLocator).not.toHaveAttribute('aria-pressed', 'true')
}
}
async function waitForCmdReceive(page: Page, commandType: string) { async function waitForCmdReceive(page: Page, commandType: string) {
return page return page
.locator(`[data-receive-command-type="${commandType}"]`) .locator(`[data-receive-command-type="${commandType}"]`)
@ -171,7 +195,8 @@ export const wiggleMove = async (
const isElVis = await page.locator(locator).isVisible() const isElVis = await page.locator(locator).isVisible()
if (isElVis) return if (isElVis) return
} }
const [x1, y1] = [0, Math.sin((tau / steps) * j * freq) * amplitude] // x1 is 0.
const y1 = Math.sin((tau / steps) * j * freq) * amplitude
const [x2, y2] = [ const [x2, y2] = [
Math.cos(-ang * deg) * i - Math.sin(-ang * deg) * y1, Math.cos(-ang * deg) * i - Math.sin(-ang * deg) * y1,
Math.sin(-ang * deg) * i + Math.cos(-ang * deg) * y1, Math.sin(-ang * deg) * i + Math.cos(-ang * deg) * y1,
@ -317,6 +342,8 @@ export async function getUtils(page: Page) {
closeKclCodePanel: () => closeKclCodePanel(page), closeKclCodePanel: () => closeKclCodePanel(page),
openDebugPanel: () => openDebugPanel(page), openDebugPanel: () => openDebugPanel(page),
closeDebugPanel: () => closeDebugPanel(page), closeDebugPanel: () => closeDebugPanel(page),
openFilePanel: () => openFilePanel(page),
closeFilePanel: () => closeFilePanel(page),
openAndClearDebugPanel: async () => { openAndClearDebugPanel: async () => {
await openDebugPanel(page) await openDebugPanel(page)
return clearCommandLogs(page) return clearCommandLogs(page)
@ -452,7 +479,10 @@ export async function getUtils(page: Page) {
return page.evaluate('window.tearDown()') return page.evaluate('window.tearDown()')
} }
cdpSession?.send('Network.emulateNetworkConditions', networkOptions) return cdpSession?.send(
'Network.emulateNetworkConditions',
networkOptions
)
}, },
} }
} }
@ -745,3 +775,22 @@ export async function setupElectron({
return { electronApp, page } return { electronApp, page }
} }
export async function isOutOfViewInScrollContainer(
element: Locator,
container: Locator
): Promise<boolean> {
const elementBox = await element.boundingBox({ timeout: 5_000 })
const containerBox = await container.boundingBox({ timeout: 5_000 })
let isOutOfView = false
if (elementBox && containerBox)
return (
elementBox.y + elementBox.height > containerBox.y + containerBox.height ||
elementBox.y < containerBox.y ||
elementBox.x + elementBox.width > containerBox.x + containerBox.width ||
elementBox.x < containerBox.x
)
return isOutOfView
}

View File

@ -72,7 +72,7 @@ test.describe('Testing constraints', () => {
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).not.toBeVisible() ).not.toBeVisible()
}) })
test(`Test remove constraints`, async ({ page }) => { test(`Remove constraints`, async ({ page }) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',

View File

@ -977,10 +977,6 @@ const part001 = startSketchOn('XZ')
const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y } const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
await page.mouse.move(0, 0) await page.mouse.move(0, 0)
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
let x = 0,
y = 0
x = hoverPos.x + Math.cos(ang * deg) * 32
y = hoverPos.y - Math.sin(ang * deg) * 32
await page.mouse.move(hoverPos.x, hoverPos.y) await page.mouse.move(hoverPos.x, hoverPos.y)
await wiggleMove( await wiggleMove(
page, page,

View File

@ -4,7 +4,6 @@ import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { SaveSettingsPayload } from 'lib/settings/settingsTypes' import { SaveSettingsPayload } from 'lib/settings/settingsTypes'
import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates' import { TEST_SETTINGS_KEY, TEST_SETTINGS_CORRUPTED } from './storageStates'
import * as TOML from '@iarna/toml' import * as TOML from '@iarna/toml'
import { APP_NAME } from 'lib/constants'
test.beforeEach(async ({ context, page }) => { test.beforeEach(async ({ context, page }) => {
await setup(context, page) await setup(context, page)
@ -193,6 +192,10 @@ test.describe('Testing settings', () => {
`Project settings override user settings on desktop`, `Project settings override user settings on desktop`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ browser: _ }, testInfo) => { async ({ browser: _ }, testInfo) => {
test.skip(
process.platform === 'win32',
'TODO: remove this skip https://github.com/KittyCAD/modeling-app/issues/3557'
)
const { electronApp, page } = await setupElectron({ const { electronApp, page } = await setupElectron({
testInfo, testInfo,
folderSetupFn: async (dir) => { folderSetupFn: async (dir) => {
@ -205,7 +208,6 @@ test.describe('Testing settings', () => {
}) })
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const u = await getUtils(page)
page.on('console', console.log) page.on('console', console.log)

View File

@ -1,7 +1,5 @@
import { test, expect, Page } from '@playwright/test' import { test, expect, Page } from '@playwright/test'
import * as fsp from 'fs/promises' import { getUtils, setup, tearDown } from './test-utils'
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { join } from 'path'
test.beforeEach(async ({ context, page }) => { test.beforeEach(async ({ context, page }) => {
await setup(context, page) await setup(context, page)

View File

@ -19,7 +19,7 @@
"@codemirror/search": "^6.5.6", "@codemirror/search": "^6.5.6",
"@codemirror/state": "^6.4.1", "@codemirror/state": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2", "@codemirror/theme-one-dark": "^6.1.2",
"@csstools/postcss-oklab-function": "^3.0.16", "@csstools/postcss-oklab-function": "^4.0.2",
"@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2",
@ -53,7 +53,7 @@
"react-json-view": "^1.21.3", "react-json-view": "^1.21.3",
"react-modal": "^3.16.1", "react-modal": "^3.16.1",
"react-modal-promise": "^1.0.2", "react-modal-promise": "^1.0.2",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.26.1",
"sketch-helpers": "^0.0.4", "sketch-helpers": "^0.0.4",
"three": "^0.166.1", "three": "^0.166.1",
"ua-parser-js": "^1.0.37", "ua-parser-js": "^1.0.37",
@ -86,17 +86,17 @@
"build:wasm-clean": "yarn wasm-prep && yarn build:wasm", "build:wasm-clean": "yarn wasm-prep && yarn build:wasm",
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"", "remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings", "wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src", "lint": "eslint --fix src e2e",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json", "bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
"postinstall": "yarn xstate:typegen", "postinstall": "yarn xstate:typegen",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"", "xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
"make:dev": "make dev", "make:dev": "make dev",
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts", "generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
"electron:start": "electron-forge start", "tron:start": "electron-forge start",
"electron:package": "electron-forge package", "tron:package": "electron-forge package",
"electron:make": "electron-forge make", "tron:make": "electron-forge make",
"electron:publish": "electron-forge publish", "tron:publish": "electron-forge publish",
"electron:e2e:local": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron" "tron:test": "NODE_ENV=development yarn playwright test --config=playwright.electron.config.ts --grep=@electron"
}, },
"prettier": { "prettier": {
"trailingComma": "es5", "trailingComma": "es5",
@ -130,14 +130,14 @@
"@electron/fuses": "^1.8.0", "@electron/fuses": "^1.8.0",
"@iarna/toml": "^2.2.5", "@iarna/toml": "^2.2.5",
"@lezer/generator": "^1.7.1", "@lezer/generator": "^1.7.1",
"@playwright/test": "^1.45.1", "@playwright/test": "^1.46.1",
"@testing-library/jest-dom": "^5.14.1", "@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^15.0.2", "@testing-library/react": "^15.0.2",
"@types/d3-force": "^3.0.10", "@types/d3-force": "^3.0.10",
"@types/electron": "^1.6.10", "@types/electron": "^1.6.10",
"@types/isomorphic-fetch": "^0.0.39", "@types/isomorphic-fetch": "^0.0.39",
"@types/mocha": "^10.0.6", "@types/mocha": "^10.0.6",
"@types/node": "^18.19.31", "@types/node": "^22.4.1",
"@types/pixelmatch": "^5.2.6", "@types/pixelmatch": "^5.2.6",
"@types/pngjs": "^6.0.4", "@types/pngjs": "^6.0.4",
"@types/react": "^18.3.2", "@types/react": "^18.3.2",
@ -156,7 +156,7 @@
"@xstate/cli": "^0.5.17", "@xstate/cli": "^0.5.17",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"d3-force": "^3.0.0", "d3-force": "^3.0.0",
"electron": "^31.2.1", "electron": "^31.4.0",
"eslint": "^8.0.1", "eslint": "^8.0.1",
"eslint-config-react-app": "^7.0.1", "eslint-config-react-app": "^7.0.1",
"eslint-plugin-css-modules": "^2.12.0", "eslint-plugin-css-modules": "^2.12.0",

View File

@ -185,6 +185,8 @@ export function Toolbar({
maybeIconConfig[0].disabled maybeIconConfig[0].disabled
} }
name={maybeIconConfig[0].title} name={maybeIconConfig[0].title}
// aria-description is still in ARIA 1.3 draft.
// eslint-disable-next-line jsx-a11y/aria-props
aria-description={maybeIconConfig[0].description} aria-description={maybeIconConfig[0].description}
onClick={() => onClick={() =>
maybeIconConfig[0].onClick(configCallbackProps) maybeIconConfig[0].onClick(configCallbackProps)
@ -225,6 +227,8 @@ export function Toolbar({
(!itemConfig.showTitle ? ' !px-0' : '') (!itemConfig.showTitle ? ' !px-0' : '')
} }
name={itemConfig.title} name={itemConfig.title}
// aria-description is still in ARIA 1.3 draft.
// eslint-disable-next-line jsx-a11y/aria-props
aria-description={itemConfig.description} aria-description={itemConfig.description}
aria-pressed={itemConfig.isActive} aria-pressed={itemConfig.isActive}
disabled={ disabled={

View File

@ -52,24 +52,21 @@ const DownloadAppBanner = () => {
</a>{' '} </a>{' '}
to download the app for the best experience. to download the app for the best experience.
</p> </p>
{!navigator?.userAgent.includes('Chrome') && (
<p className="mt-6"> <p className="mt-6">
If you're on Linux and the browser is your only way to use the app, If you want to stay here on the web-app, we currently only support
you can permanently dismiss this banner by{' '} Chrome. Please use{' '}
<a <a
onClick={() => { href="https://www.google.com/chrome/"
setIsBannerDismissed(true) rel="noopener noreferrer"
settings.send({ target="_blank"
type: 'set.app.dismissWebBanner',
data: { level: 'user', value: true },
})
}}
href="/"
className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline" className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline"
> >
toggling the App &gt; Dismiss Web Banner setting this link
</a> </a>{' '}
. to download it.
</p> </p>
)}
</div> </div>
</Dialog.Panel> </Dialog.Panel>
</Dialog> </Dialog>

View File

@ -464,7 +464,10 @@ export const FileTreeInner = ({
}, [documentHasFocus]) }, [documentHasFocus])
return ( return (
<div className="overflow-auto pb-12 absolute inset-0"> <div
className="overflow-auto pb-12 absolute inset-0"
data-testid="file-pane-scroll-container"
>
<ul <ul
className="m-0 p-0 text-sm" className="m-0 p-0 text-sm"
onClickCapture={(e) => { onClickCapture={(e) => {

View File

@ -23,7 +23,10 @@ export function HelpMenu(props: React.PropsWithChildren) {
return ( return (
<Popover className="relative"> <Popover className="relative">
<Popover.Button className="grid p-0 m-0 border-none rounded-full place-content-center"> <Popover.Button
className="grid p-0 m-0 border-none rounded-full place-content-center"
data-testid="help-button"
>
<CustomIcon <CustomIcon
name="questionMark" name="questionMark"
className="rounded-full w-7 h-7 bg-chalkboard-110 dark:bg-chalkboard-80 text-chalkboard-10" className="rounded-full w-7 h-7 bg-chalkboard-110 dark:bg-chalkboard-80 text-chalkboard-10"
@ -95,6 +98,7 @@ export function HelpMenu(props: React.PropsWithChildren) {
: PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS : PATHS.HOME + PATHS.SETTINGS_KEYBINDINGS
navigate(targetPath) navigate(targetPath)
}} }}
data-testid="keybindings-button"
> >
Keyboard shortcuts Keyboard shortcuts
</HelpMenuItem> </HelpMenuItem>

View File

@ -63,8 +63,10 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
data: { name: 'Make', groupId: 'modeling' }, data: { name: 'Make', groupId: 'modeling' },
}) })
}, },
hide: () => machineManager.machineCount() === 0, hide: () => !isDesktop(),
hideOnPlatform: 'web', disable: () => {
return machineManager.noMachinesReason()
},
}, },
] ]
const filteredActions: SidebarAction[] = sidebarActions.filter( const filteredActions: SidebarAction[] = sidebarActions.filter(
@ -186,6 +188,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
iconSize: 'md', iconSize: 'md',
}} }}
onClick={action.action} onClick={action.action}
disabledText={action.disable?.()}
/> />
))} ))}
</ul> </ul>
@ -238,6 +241,7 @@ interface ModelingPaneButtonProps
onClick: () => void onClick: () => void
paneIsOpen?: boolean paneIsOpen?: boolean
showBadge?: BadgeInfoComputed showBadge?: BadgeInfoComputed
disabledText?: string
} }
function ModelingPaneButton({ function ModelingPaneButton({
@ -245,6 +249,7 @@ function ModelingPaneButton({
onClick, onClick,
paneIsOpen, paneIsOpen,
showBadge, showBadge,
disabledText,
...props ...props
}: ModelingPaneButtonProps) { }: ModelingPaneButtonProps) {
useHotkeys(paneConfig.keybinding, onClick, { useHotkeys(paneConfig.keybinding, onClick, {
@ -258,6 +263,8 @@ function ModelingPaneButton({
onClick={onClick} onClick={onClick}
name={paneConfig.title} name={paneConfig.title}
data-testid={paneConfig.id + '-pane-button'} data-testid={paneConfig.id + '-pane-button'}
disabled={disabledText !== undefined}
aria-disabled={disabledText !== undefined}
{...props} {...props}
> >
<ActionIcon <ActionIcon
@ -284,6 +291,7 @@ function ModelingPaneButton({
> >
<span className="flex-1"> <span className="flex-1">
{paneConfig.title} {paneConfig.title}
{disabledText !== undefined ? ` (${disabledText})` : ''}
{paneIsOpen !== undefined ? ` pane` : ''} {paneIsOpen !== undefined ? ` pane` : ''}
</span> </span>
<kbd className="hotkey text-xs capitalize"> <kbd className="hotkey text-xs capitalize">
@ -326,4 +334,5 @@ export type SidebarAction = {
action: () => void action: () => void
hideOnPlatform?: 'desktop' | 'web' hideOnPlatform?: 'desktop' | 'web'
hide?: boolean | (() => boolean) hide?: boolean | (() => boolean)
disable?: () => string | undefined
} }

View File

@ -9,7 +9,9 @@ export const NetworkMachineIndicator = ({
}: { }: {
className?: string className?: string
}) => { }) => {
const machineCount = Object.keys(machineManager.machines).length const machineCount = machineManager.machineCount()
const reason = machineManager.noMachinesReason()
return isDesktop() ? ( return isDesktop() ? (
<Popover className="relative"> <Popover className="relative">
<Popover.Button <Popover.Button
@ -26,7 +28,7 @@ export const NetworkMachineIndicator = ({
</p> </p>
)} )}
<Tooltip position="top-right" wrapperClassName="ui-open:hidden"> <Tooltip position="top-right" wrapperClassName="ui-open:hidden">
Network machines ({machineCount}) Network machines ({machineCount}) {reason && `: ${reason}`}
</Tooltip> </Tooltip>
</Popover.Button> </Popover.Button>
<Popover.Panel <Popover.Panel

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -154,10 +154,6 @@ export function buildCommandArgument<
} satisfies Omit<CommandArgument<O, T>, 'inputType'> } satisfies Omit<CommandArgument<O, T>, 'inputType'>
if (arg.inputType === 'options') { if (arg.inputType === 'options') {
if (!(arg.options || arg.optionsFromContext)) {
throw new Error('Options must be provided for options input type')
}
return { return {
inputType: arg.inputType, inputType: arg.inputType,
...baseCommandArgument, ...baseCommandArgument,

View File

@ -38,7 +38,10 @@ export async function renameProjectDirectory(
// Make sure the new name does not exist. // Make sure the new name does not exist.
const newPath = window.electron.path.join( const newPath = window.electron.path.join(
projectPath.split('/').slice(0, -1).join('/'), projectPath
.split(window.electron.sep)
.slice(0, -1)
.join(window.electron.sep),
newName newName
) )
try { try {
@ -179,7 +182,7 @@ const collectAllFilesRecursiveFrom = async (path: string) => {
return Promise.reject(new Error(`Path ${path} is not a directory`)) return Promise.reject(new Error(`Path ${path} is not a directory`))
} }
const pathParts = path.split('/') const pathParts = path.split(window.electron.sep)
let entry: FileEntry = { let entry: FileEntry = {
name: pathParts.slice(-1)[0], name: pathParts.slice(-1)[0],
path, path,

View File

@ -38,6 +38,9 @@ const bracket = startSketchOn('XY')
tags: [getPreviousAdjacentEdge(outerEdge)] tags: [getPreviousAdjacentEdge(outerEdge)]
}, %)` }, %)`
/**
* @throws Error if the search text is not found in the example code.
*/
function findLineInExampleCode({ function findLineInExampleCode({
searchText, searchText,
example = bracket, example = bracket,
@ -48,6 +51,8 @@ function findLineInExampleCode({
const lines = example.split('\n') const lines = example.split('\n')
const lineNumber = lines.findIndex((l) => l.includes(searchText)) + 1 const lineNumber = lines.findIndex((l) => l.includes(searchText)) + 1
if (lineNumber === 0) { if (lineNumber === 0) {
// We are exporting a constant, so we don't want to return an Error.
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
throw new Error( throw new Error(
`Could not find the line with search text "${searchText}" in the example code. Was it removed?` `Could not find the line with search text "${searchText}" in the example code. Was it removed?`
) )

View File

@ -51,6 +51,19 @@ export class MachineManager {
return this._machineApiIp return this._machineApiIp
} }
// Get the reason message for why there are no machines.
noMachinesReason(): string | undefined {
if (this.machineCount() > 0) {
return undefined
}
if (this.machineApiIp === null) {
return 'Machine API server was not discovered'
}
return 'Machine API server was discovered, but no machines are available'
}
get currentMachine(): components['schemas']['Machine'] | null { get currentMachine(): components['schemas']['Machine'] | null {
return this._currentMachine return this._currentMachine
} }

View File

@ -131,8 +131,6 @@ ipcMain.handle('kittycad', (event, data) => {
)(data.args) )(data.args)
}) })
const SERVICE_NAME = '_machine-api._tcp.local.'
ipcMain.handle('find_machine_api', () => { ipcMain.handle('find_machine_api', () => {
const timeoutAfterMs = 5000 const timeoutAfterMs = 5000
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -144,8 +142,19 @@ ipcMain.handle('find_machine_api', () => {
resolve(null) resolve(null)
}) })
console.log('Looking for machine API...') console.log('Looking for machine API...')
bonjourEt.find({ type: SERVICE_NAME }, (service: Service) => { bonjourEt.find(
resolve(service.fqdn) { protocol: 'tcp', type: 'machine-api' },
}) (service: Service) => {
console.log('Found machine API!', JSON.stringify(service))
if (!service.addresses || service.addresses?.length === 0) {
console.log('No addresses found for machine API!')
return resolve(null)
}
const ip = service.addresses[0]
const port = service.port
// We want to return the ip address of the machine API.
resolve(`${ip}:${port}`)
}
)
}) })
}) })

View File

@ -169,7 +169,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -180,7 +180,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -191,7 +191,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -443,7 +443,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -643,7 +643,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim 0.10.0", "strsim 0.10.0",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -654,7 +654,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
dependencies = [ dependencies = [
"darling_core", "darling_core",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -709,7 +709,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
"synstructure", "synstructure",
] ]
@ -738,7 +738,7 @@ dependencies = [
"rustfmt-wrapper", "rustfmt-wrapper",
"serde", "serde",
"serde_tokenstream", "serde_tokenstream",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -749,7 +749,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -776,7 +776,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -948,7 +948,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -1038,7 +1038,7 @@ dependencies = [
"inflections", "inflections",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -1462,7 +1462,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -1841,7 +1841,7 @@ dependencies = [
"regex", "regex",
"regex-syntax 0.8.3", "regex-syntax 0.8.3",
"structmeta", "structmeta",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -1894,7 +1894,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2058,7 +2058,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-macros-backend", "pyo3-macros-backend",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2071,7 +2071,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-build-config", "pyo3-build-config",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2533,7 +2533,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2607,7 +2607,7 @@ checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2618,7 +2618,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2642,7 +2642,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2663,7 +2663,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde", "serde",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2800,7 +2800,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive", "structmeta-derive",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2811,7 +2811,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2855,9 +2855,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.74" version = "2.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7" checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2878,7 +2878,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -2985,7 +2985,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -3056,9 +3056,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.39.2" version = "1.39.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -3080,7 +3080,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -3233,7 +3233,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -3261,7 +3261,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -3338,7 +3338,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
"termcolor", "termcolor",
] ]
@ -3502,7 +3502,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]
@ -3563,7 +3563,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3598,7 +3598,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3924,7 +3924,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.74", "syn 2.0.75",
] ]
[[package]] [[package]]

View File

@ -17,7 +17,7 @@ gloo-utils = "0.2.0"
kcl-lib = { path = "kcl" } kcl-lib = { path = "kcl" }
kittycad.workspace = true kittycad.workspace = true
serde_json = "1.0.125" serde_json = "1.0.125"
tokio = { version = "1.39.2", features = ["sync"] } tokio = { version = "1.39.3", features = ["sync"] }
toml = "0.8.19" toml = "0.8.19"
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] } uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
wasm-bindgen = "0.2.91" wasm-bindgen = "0.2.91"
@ -30,7 +30,7 @@ image = { version = "0.25.1", default-features = false, features = ["png"] }
kittycad = { workspace = true, default-features = true } kittycad = { workspace = true, default-features = true }
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"
reqwest = { version = "0.11.26", default-features = false } reqwest = { version = "0.11.26", default-features = false }
tokio = { version = "1.39.2", features = ["rt-multi-thread", "macros", "time"] } tokio = { version = "1.39.3", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.8" twenty-twenty = "0.8"
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] } uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }

View File

@ -20,7 +20,7 @@ quote = "1"
regex = "1.10" regex = "1.10"
serde = { version = "1.0.208", features = ["derive"] } serde = { version = "1.0.208", features = ["derive"] }
serde_tokenstream = "0.2" serde_tokenstream = "0.2"
syn = { version = "2.0.74", features = ["full"] } syn = { version = "2.0.75", features = ["full"] }
[dev-dependencies] [dev-dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"

View File

@ -15,7 +15,7 @@ databake = "0.1.8"
kcl-lib = { path = "../kcl" } kcl-lib = { path = "../kcl" }
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = { version = "2.0.74", features = ["full"] } syn = { version = "2.0.75", features = ["full"] }
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.0" pretty_assertions = "1.4.0"

View File

@ -12,4 +12,4 @@ kcl-lib = { version = "0.2", path = "../kcl" }
pico-args = "0.5.0" pico-args = "0.5.0"
serde = { version = "1.0.208", features = ["derive"] } serde = { version = "1.0.208", features = ["derive"] }
serde_json = "1.0.125" serde_json = "1.0.125"
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.39.3", features = ["macros", "rt-multi-thread"] }

View File

@ -50,7 +50,7 @@ zip = { version = "2.0.0", default-features = false }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3.69" } js-sys = { version = "0.3.69" }
tokio = { version = "1.39.2", features = ["sync", "time"] } tokio = { version = "1.39.3", features = ["sync", "time"] }
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] } tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
wasm-bindgen = "0.2.91" wasm-bindgen = "0.2.91"
wasm-bindgen-futures = "0.4.42" wasm-bindgen-futures = "0.4.42"
@ -59,7 +59,7 @@ web-sys = { version = "0.3.69", features = ["console"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
approx = "0.5" approx = "0.5"
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] } bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
tokio = { version = "1.39.2", features = ["full"] } tokio = { version = "1.39.3", features = ["full"] }
tokio-tungstenite = { version = "0.23.1", features = ["rustls-tls-native-roots"] } tokio-tungstenite = { version = "0.23.1", features = ["rustls-tls-native-roots"] }
tower-lsp = { version = "0.20.0", features = ["proposed"] } tower-lsp = { version = "0.20.0", features = ["proposed"] }

View File

@ -0,0 +1,60 @@
// Shelf Bracket
// This is a shelf bracket made out of 6061-T6 aluminum sheet metal. The required thickness is calculated based on a point load of 300 lbs applied to the end of the shelf. There are two brackets holding up the shelf, so the moment experienced is divided by 2. The shelf is 1 foot long from the wall.
// Define our bracket feet lengths
const shelfMountL = 8 // The length of the bracket holding up the shelf is 6 inches
const wallMountL = 6 // the length of the bracket
// Define constants required to calculate the thickness needed to support 300 lbs
const sigmaAllow = 35000 // psi
const width = 6 // inch
const p = 300 // Force on shelf - lbs
const L = 12 // inches
const M = L * p / 2 // Moment experienced at fixed end of bracket
const FOS = 2 // Factor of safety of 2 to be conservative
// Calculate the thickness off the bending stress and factor of safety
const thickness = sqrt(6 * M * FOS / (width * sigmaAllow))
// 0.25 inch fillet radius
const filletR = 0.25
// Sketch the bracket and extrude with fillets
const bracket = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, wallMountL], %, 'outerEdge')
|> line([-shelfMountL, 0], %)
|> line([0, -thickness], %)
|> line([shelfMountL - thickness, 0], %, 'innerEdge')
|> line([0, -wallMountL + thickness], %)
|> close(%)
|> extrude(width, %)
|> fillet({
radius: filletR,
tags: [
getPreviousAdjacentEdge('innerEdge', %)
]
}, %)
|> fillet({
radius: filletR + thickness,
tags: [
getPreviousAdjacentEdge('outerEdge', %)
]
}, %)

169
yarn.lock
View File

@ -1228,56 +1228,56 @@
dependencies: dependencies:
"@jridgewell/trace-mapping" "0.3.9" "@jridgewell/trace-mapping" "0.3.9"
"@csstools/color-helpers@^4.2.1": "@csstools/color-helpers@^5.0.1":
version "4.2.1" version "5.0.1"
resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-4.2.1.tgz#da573554220ccb59757f12de62bf70c6b15645d4" resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.1.tgz#829f1c76f5800b79c51c709e2f36821b728e0e10"
integrity sha512-CEypeeykO9AN7JWkr1OEOQb0HRzZlPWGwV0Ya6DuVgFdDi6g3ma/cPZ5ZPZM4AWQikDpq/0llnGGlIL+j8afzw== integrity sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==
"@csstools/css-calc@^1.2.4": "@csstools/css-calc@^2.0.1":
version "1.2.4" version "2.0.1"
resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-1.2.4.tgz#9d9fb0dca33666cf97659f8f2c343ed0210e0e73" resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.0.1.tgz#1675297b19f0933c729fdd7f4f5279b855ae724f"
integrity sha512-tfOuvUQeo7Hz+FcuOd3LfXVp+342pnWUJ7D2y8NUpu1Ww6xnTbHLpz018/y6rtbHifJ3iIEf9ttxXd8KG7nL0Q== integrity sha512-e59V+sNp6e5m+9WnTUydA1DQO70WuKUdseflRpWmXxocF/h5wWGIxUjxfvLtajcmwstH0vm6l0reKMzcyI757Q==
"@csstools/css-color-parser@^2.0.4": "@csstools/css-color-parser@^3.0.2":
version "2.0.5" version "3.0.2"
resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-2.0.5.tgz#ce1fe52f23f35f37bea2cf61ac865115aa17880a" resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.2.tgz#710abb97142d58bcefc3a5e032a55a246895351c"
integrity sha512-lRZSmtl+DSjok3u9hTWpmkxFZnz7stkbZxzKc08aDUsdrWwhSgWo8yq9rq9DaFUtbAyAq2xnH92fj01S+pwIww== integrity sha512-mNg7A6HnNjlm0we/pDS9dUafOuBxcanN0TBhEGeIk6zZincuk0+mAbnBqfVs29NlvWHZ8diwTG6g5FeU8246sA==
dependencies: dependencies:
"@csstools/color-helpers" "^4.2.1" "@csstools/color-helpers" "^5.0.1"
"@csstools/css-calc" "^1.2.4" "@csstools/css-calc" "^2.0.1"
"@csstools/css-parser-algorithms@^2.7.1": "@csstools/css-parser-algorithms@^3.0.1":
version "2.7.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz#6d93a8f7d8aeb7cd9ed0868f946e46f021b6aa70" resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz#f14ade63bae5f6025ac85c7d03fe47a7ca0e58af"
integrity sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw== integrity sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==
"@csstools/css-tokenizer@^2.4.1": "@csstools/css-tokenizer@^3.0.1":
version "2.4.1" version "3.0.1"
resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz#1d8b2e200197cf5f35ceb07ca2dade31f3a00ae8" resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz#9dd9b10084f3011290f96789598091e5bcb3c29a"
integrity sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg== integrity sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==
"@csstools/postcss-oklab-function@^3.0.16": "@csstools/postcss-oklab-function@^4.0.2":
version "3.0.19" version "4.0.2"
resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-3.0.19.tgz#3bd0719914780fb53558af11958d0f4e6d2f952e" resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.2.tgz#3d36974fbb7c3a589d52756e4eb029eaa29e4735"
integrity sha512-e3JxXmxjU3jpU7TzZrsNqSX4OHByRC3XjItV3Ieo/JEQmLg5rdOL4lkv/1vp27gXemzfNt44F42k/pn0FpE21Q== integrity sha512-2iSK/T77PHMeorakBAk/WLxSodfIJ/lmi6nxEkuruXfhGH7fByZim4Fw6ZJf4B73SVieRSH2ep8zvYkA2ZfRtA==
dependencies: dependencies:
"@csstools/css-color-parser" "^2.0.4" "@csstools/css-color-parser" "^3.0.2"
"@csstools/css-parser-algorithms" "^2.7.1" "@csstools/css-parser-algorithms" "^3.0.1"
"@csstools/css-tokenizer" "^2.4.1" "@csstools/css-tokenizer" "^3.0.1"
"@csstools/postcss-progressive-custom-properties" "^3.3.0" "@csstools/postcss-progressive-custom-properties" "^4.0.0"
"@csstools/utilities" "^1.0.0" "@csstools/utilities" "^2.0.0"
"@csstools/postcss-progressive-custom-properties@^3.3.0": "@csstools/postcss-progressive-custom-properties@^4.0.0":
version "3.3.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-3.3.0.tgz#20177d3fc61d8f170c4ee1686f3d2ab6eec27bbb" resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz#ecdb85bcdb1852d73970a214a376684a91f82bdc"
integrity sha512-W2oV01phnILaRGYPmGFlL2MT/OgYjQDrL9sFlbdikMFi6oQkFki9B86XqEWR7HCsTZFVq7dbzr/o71B75TKkGg== integrity sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==
dependencies: dependencies:
postcss-value-parser "^4.2.0" postcss-value-parser "^4.2.0"
"@csstools/utilities@^1.0.0": "@csstools/utilities@^2.0.0":
version "1.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/@csstools/utilities/-/utilities-1.0.0.tgz#42f3c213f2fb929324d465684ab9f46a0febd4bb" resolved "https://registry.yarnpkg.com/@csstools/utilities/-/utilities-2.0.0.tgz#f7ff0fee38c9ffb5646d47b6906e0bc8868bde60"
integrity sha512-tAgvZQe/t2mlvpNosA4+CkMiZ2azISW5WPAcdSalZlEjQvUfghHxfQcrCiK/7/CrfAWVxyM88kGFYO82heIGDg== integrity sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==
"@electron-forge/cli@^7.4.0": "@electron-forge/cli@^7.4.0":
version "7.4.0" version "7.4.0"
@ -2075,12 +2075,12 @@
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@playwright/test@^1.45.1": "@playwright/test@^1.46.1":
version "1.45.3" version "1.46.1"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.3.tgz#22e9c38b3081d6674b28c6e22f784087776c72e5" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.1.tgz#a8dfdcd623c4c23bb1b7ea588058aad41055c188"
integrity sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA== integrity sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==
dependencies: dependencies:
playwright "1.45.3" playwright "1.46.1"
"@react-hook/latest@^1.0.2": "@react-hook/latest@^1.0.2":
version "1.0.3" version "1.0.3"
@ -2100,10 +2100,10 @@
"@react-hook/latest" "^1.0.2" "@react-hook/latest" "^1.0.2"
"@react-hook/passive-layout-effect" "^1.2.0" "@react-hook/passive-layout-effect" "^1.2.0"
"@remix-run/router@1.19.0": "@remix-run/router@1.19.1":
version "1.19.0" version "1.19.1"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.19.0.tgz#745dbffbce67f05386d57ca22c51dfd85c979593" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.19.1.tgz#984771bfd1de2715f42394c87fb716c1349e014f"
integrity sha512-zDICCLKEwbVYTS6TjYaWtHXxkdoUvD/QXvyVZjGCsWz5vyH7aFeONlPffPdW+Y/t6KT0MgXb2Mfjun9YpWN1dA== integrity sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==
"@replit/codemirror-interact@^6.3.1": "@replit/codemirror-interact@^6.3.1":
version "6.3.1" version "6.3.1"
@ -2478,19 +2478,12 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f"
integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==
"@types/node@*": "@types/node@*", "@types/node@^22.4.1":
version "22.0.2" version "22.4.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.2.tgz#9fb1a2b31970871e8bf696f0e8a40d2e6d2bd04e" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.1.tgz#9b595d292c65b94c20923159e2ce947731b6fdce"
integrity sha512-yPL6DyFwY5PiMVEwymNeqUTKsDczQBJ/5T7W/46RwLU/VH+AA8aT5TZkvBviLKLbbm0hlfftEkGrNzfRk/fofQ== integrity sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==
dependencies: dependencies:
undici-types "~6.11.1" undici-types "~6.19.2"
"@types/node@^18.19.31":
version "18.19.42"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.42.tgz#b54ed4752c85427906aab40917b0f7f3d724bf72"
integrity sha512-d2ZFc/3lnK2YCYhos8iaNIYu9Vfhr92nHiyJHRltXWjXUBjEE+A4I58Tdbnw4VhggSW+2j5y5gTrLs4biNnubg==
dependencies:
undici-types "~5.26.4"
"@types/node@^20.9.0": "@types/node@^20.9.0":
version "20.14.13" version "20.14.13"
@ -4203,10 +4196,10 @@ electron-winstaller@^5.3.0:
optionalDependencies: optionalDependencies:
"@electron/windows-sign" "^1.1.2" "@electron/windows-sign" "^1.1.2"
electron@*, electron@^31.2.1: electron@*, electron@^31.4.0:
version "31.3.1" version "31.4.0"
resolved "https://registry.yarnpkg.com/electron/-/electron-31.3.1.tgz#de5f21f10db1ba0568e0cdd7ae76ec40a4b800c3" resolved "https://registry.yarnpkg.com/electron/-/electron-31.4.0.tgz#fd55fd882117b09fd68b2d182c8fda2dbb7ef454"
integrity sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA== integrity sha512-YTwKoAA+nrJMlI1TTHnIXLYWoQLKnhbkz0qxZcI7Hadcy0UaFMFs9xzwvH2MnrRpVJy7RKo49kVGuvSdRl8zMA==
dependencies: dependencies:
"@electron/get" "^2.0.0" "@electron/get" "^2.0.0"
"@types/node" "^20.9.0" "@types/node" "^20.9.0"
@ -7226,17 +7219,17 @@ pkg-types@^1.0.3, pkg-types@^1.1.1:
mlly "^1.7.1" mlly "^1.7.1"
pathe "^1.1.2" pathe "^1.1.2"
playwright-core@1.45.3: playwright-core@1.46.1:
version "1.45.3" version "1.46.1"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.3.tgz#e77bc4c78a621b96c3e629027534ee1d25faac93" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.1.tgz#28f3ab35312135dda75b0c92a3e5c0e7edb9cc8b"
integrity sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA== integrity sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==
playwright@1.45.3: playwright@1.46.1:
version "1.45.3" version "1.46.1"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.3.tgz#75143f73093a6e1467f7097083d2f0846fb8dd2f" resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.1.tgz#ea562bc48373648e10420a10c16842f0b227c218"
integrity sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww== integrity sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==
dependencies: dependencies:
playwright-core "1.45.3" playwright-core "1.46.1"
optionalDependencies: optionalDependencies:
fsevents "2.3.2" fsevents "2.3.2"
@ -7560,20 +7553,20 @@ react-refresh@^0.14.2:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
react-router-dom@^6.23.1: react-router-dom@^6.26.1:
version "6.26.0" version "6.26.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.26.0.tgz#8debe13295c58605c04f93018d659a763245e58c" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.26.1.tgz#a408892b41767a49dc94b3564b0e7d8e3959f623"
integrity sha512-RRGUIiDtLrkX3uYcFiCIxKFWMcWQGMojpYZfcstc63A1+sSnVgILGIm9gNUA6na3Fm1QuPGSBQH2EMbAZOnMsQ== integrity sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==
dependencies: dependencies:
"@remix-run/router" "1.19.0" "@remix-run/router" "1.19.1"
react-router "6.26.0" react-router "6.26.1"
react-router@6.26.0: react-router@6.26.1:
version "6.26.0" version "6.26.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.26.0.tgz#d5af4c46835b202348ef2b7ddacd32a2db539fde" resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.26.1.tgz#88c64837e05ffab6899a49df2a1484a22471e4ce"
integrity sha512-wVQq0/iFYd3iZ9H2l3N3k4PL8EEHcb0XlU2Na8nEwmiXgIUElEH6gaJDtUQxJ+JFzmIXaQjfdpcGWaM6IoQGxg== integrity sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==
dependencies: dependencies:
"@remix-run/router" "1.19.0" "@remix-run/router" "1.19.1"
react-textarea-autosize@^8.3.2: react-textarea-autosize@^8.3.2:
version "8.5.3" version "8.5.3"
@ -8765,10 +8758,10 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
undici-types@~6.11.1: undici-types@~6.19.2:
version "6.11.1" version "6.19.6"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.6.tgz#e218c3df0987f4c0e0008ca00d6b6472d9b89b36"
integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== integrity sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==
unicode-canonical-property-names-ecmascript@^2.0.0: unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0" version "2.0.0"