Compare commits
7 Commits
franknoiro
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
e1da72a0ae | |||
ec2d1999a7 | |||
95683f1cc1 | |||
f48f1c21c1 | |||
5cdf2de89a | |||
543e809739 | |||
61b669cf4e |
61
.github/workflows/playwright.yml
vendored
@ -13,7 +13,7 @@ permissions:
|
|||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
actions: read
|
actions: read
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@ -34,8 +34,13 @@ jobs:
|
|||||||
- 'src/wasm-lib/**'
|
- 'src/wasm-lib/**'
|
||||||
|
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 30
|
||||||
runs-on: ubuntu-latest-8-cores
|
runs-on: ubuntu-latest-8-cores
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shardIndex: [1, 2, 3, 4]
|
||||||
|
shardTotal: [4]
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
- name: Tune GitHub-hosted runner network
|
- name: Tune GitHub-hosted runner network
|
||||||
@ -107,7 +112,7 @@ jobs:
|
|||||||
run: yarn build:local
|
run: yarn build:local
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
run: |
|
run: |
|
||||||
yarn playwright test --project="Google Chrome" --retries="3" --update-snapshots e2e/playwright/snapshot-tests.spec.ts
|
yarn playwright test --project="Google Chrome" --retries="3" --update-snapshots --grep=@snapshot --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||||
@ -115,7 +120,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-snapshot-${{ github.sha }}
|
name: playwright-report-ubuntu-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
@ -149,7 +154,7 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: steps.git-check.outputs.modified == 'true'
|
if: steps.git-check.outputs.modified == 'true'
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
# if have previous run results, use them
|
# if have previous run results, use them
|
||||||
@ -157,7 +162,7 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-ubuntu-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run ubuntu/chrome flow (with retries)
|
- name: Run ubuntu/chrome flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -166,14 +171,14 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="Google Chrome" e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="Google Chrome" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=4
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
@ -181,7 +186,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="Google Chrome" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="Google Chrome" --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
@ -194,9 +199,9 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "retried=false" >>$GITHUB_OUTPUT
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
@ -216,21 +221,26 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-ubuntu-${{ github.sha }}
|
name: test-results-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-${{ github.sha }}
|
name: playwright-report-ubuntu-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
playwright-macos:
|
playwright-macos:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 30
|
||||||
runs-on: macos-14-large
|
runs-on: macos-14
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
shardIndex: [1, 2, 3, 4]
|
||||||
|
shardTotal: [4]
|
||||||
needs: check-rust-changes
|
needs: check-rust-changes
|
||||||
steps:
|
steps:
|
||||||
- name: Tune GitHub-hosted runner network
|
- name: Tune GitHub-hosted runner network
|
||||||
@ -306,7 +316,7 @@ jobs:
|
|||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-macos-${{ github.sha }}
|
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
- name: Run macos/safari flow (with retries)
|
- name: Run macos/safari flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
@ -315,14 +325,14 @@ jobs:
|
|||||||
if [[ ! -f "test-results/.last-run.json" ]]; then
|
if [[ ! -f "test-results/.last-run.json" ]]; then
|
||||||
# if no last run artifact, than run plawright normally
|
# if no last run artifact, than run plawright normally
|
||||||
echo "run playwright normally"
|
echo "run playwright normally"
|
||||||
yarn playwright test --project="webkit" e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="webkit" --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} --grep-invert=@snapshot || true
|
||||||
# # send to axiom
|
# # send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=4
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
@ -330,7 +340,7 @@ jobs:
|
|||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
echo "retried=true" >>$GITHUB_OUTPUT
|
echo "retried=true" >>$GITHUB_OUTPUT
|
||||||
echo "run playwright with last failed tests and retry $retry"
|
echo "run playwright with last failed tests and retry $retry"
|
||||||
yarn playwright test --project="webkit" --last-failed e2e/playwright/flow-tests.spec.ts || true
|
yarn playwright test --project="webkit" --last-failed --grep-invert=@snapshot || true
|
||||||
# send to axiom
|
# send to axiom
|
||||||
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
node playwrightProcess.mjs | tee /tmp/github-actions.log > /dev/null 2>&1
|
||||||
retry=$((retry + 1))
|
retry=$((retry + 1))
|
||||||
@ -343,9 +353,9 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "retried=false" >>$GITHUB_OUTPUT
|
echo "retried=false" >>$GITHUB_OUTPUT
|
||||||
|
|
||||||
if [[ -f "test-results/.last-run.json" ]]; then
|
if [[ -f "test-results/.last-run.json" ]]; then
|
||||||
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
failed_tests=$(jq '.failedTests | length' test-results/.last-run.json)
|
||||||
if [[ $failed_tests -gt 0 ]]; then
|
if [[ $failed_tests -gt 0 ]]; then
|
||||||
@ -360,15 +370,14 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: test-results-macos-${{ github.sha }}
|
name: test-results-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-macos-${{ github.sha }}
|
name: playwright-report-macos-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ document.addEventListener('mousemove', (e) =>
|
|||||||
const deg = (Math.PI * 2) / 360
|
const deg = (Math.PI * 2) / 360
|
||||||
|
|
||||||
const commonPoints = {
|
const commonPoints = {
|
||||||
startAt: '[7.19, -9.7]',
|
startAt: '[0.75, -1.01]',
|
||||||
num1: 7.25,
|
num1: 0.75,
|
||||||
num2: 14.44,
|
num2: 1.5,
|
||||||
}
|
}
|
||||||
|
|
||||||
test.afterEach(async ({ context, page }, testInfo) => {
|
test.afterEach(async ({ context, page }, testInfo) => {
|
||||||
@ -2535,18 +2535,29 @@ test.describe('Onboarding tests', () => {
|
|||||||
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
// Test that the text in this step is correct
|
// Test that the text in this step is correct
|
||||||
const avatarLocator = await page
|
const sidebar = page.getByTestId('user-sidebar-toggle')
|
||||||
.getByTestId('user-sidebar-toggle')
|
const avatar = sidebar.locator('img')
|
||||||
.locator('img')
|
const onboardingOverlayLocator = page
|
||||||
const onboardingOverlayLocator = await page
|
|
||||||
.getByTestId('onboarding-content')
|
.getByTestId('onboarding-content')
|
||||||
.locator('div')
|
.locator('div')
|
||||||
.nth(1)
|
.nth(1)
|
||||||
|
|
||||||
// Expect the avatar to be visible and for the text to reference it
|
// Expect the avatar to be visible and for the text to reference it
|
||||||
await expect(avatarLocator).not.toBeVisible()
|
await expect(avatar).not.toBeVisible()
|
||||||
await expect(onboardingOverlayLocator).toBeVisible()
|
await expect(onboardingOverlayLocator).toBeVisible()
|
||||||
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
await expect(onboardingOverlayLocator).toContainText('the menu button')
|
||||||
|
|
||||||
|
// Test we mention what else is in this menu for https://github.com/KittyCAD/modeling-app/issues/2939
|
||||||
|
// which doesn't deserver its own full test spun up
|
||||||
|
const userMenuFeatures = [
|
||||||
|
'manage your account',
|
||||||
|
'report a bug',
|
||||||
|
'request a feature',
|
||||||
|
'sign out',
|
||||||
|
]
|
||||||
|
for (const feature of userMenuFeatures) {
|
||||||
|
await expect(onboardingOverlayLocator).toContainText(feature)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 29 KiB |
@ -15,6 +15,23 @@ export const TEST_COLORS = {
|
|||||||
BLUE: [0, 0, 255] as TestColor,
|
BLUE: [0, 0, 255] as TestColor,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
async function waitForPageLoadWithRetry(page: Page) {
|
||||||
|
await expect(async () => {
|
||||||
|
await page.goto('/')
|
||||||
|
const errorMessage = 'App failed to load - 🔃 Retrying ...'
|
||||||
|
await expect(page.getByTestId('loading'), errorMessage).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' }),
|
||||||
|
errorMessage
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
}).toPass({ timeout: 70_000, intervals: [1_000] })
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForPageLoad(page: Page) {
|
async function waitForPageLoad(page: Page) {
|
||||||
// wait for all spinners to be gone
|
// wait for all spinners to be gone
|
||||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
@ -218,9 +235,12 @@ async function waitForAuthAndLsp(page: Page) {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
if (process.env.CI) {
|
||||||
await page.goto('/')
|
await waitForPageLoadWithRetry(page)
|
||||||
await waitForPageLoad(page)
|
} else {
|
||||||
|
await page.goto('/')
|
||||||
|
await waitForPageLoad(page)
|
||||||
|
}
|
||||||
|
|
||||||
return waitForLspPromise
|
return waitForLspPromise
|
||||||
}
|
}
|
||||||
@ -234,6 +254,7 @@ export async function getUtils(page: Page) {
|
|||||||
return {
|
return {
|
||||||
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
|
waitForAuthSkipAppStart: () => waitForAuthAndLsp(page),
|
||||||
waitForPageLoad: () => waitForPageLoad(page),
|
waitForPageLoad: () => waitForPageLoad(page),
|
||||||
|
waitForPageLoadWithRetry: () => waitForPageLoadWithRetry(page),
|
||||||
removeCurrentCode: () => removeCurrentCode(page),
|
removeCurrentCode: () => removeCurrentCode(page),
|
||||||
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
||||||
updateCamPosition: async (xyz: [number, number, number]) => {
|
updateCamPosition: async (xyz: [number, number, number]) => {
|
||||||
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||||||
/* Do not retry */
|
/* Do not retry */
|
||||||
retries: process.env.CI ? 0 : 0,
|
retries: process.env.CI ? 0 : 0,
|
||||||
/* Different amount of parallelism on CI and local. */
|
/* Different amount of parallelism on CI and local. */
|
||||||
workers: process.env.CI ? 4 : 4,
|
workers: process.env.CI ? 1 : 4,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: [
|
reporter: [
|
||||||
[process.env.CI ? 'dot' : 'list'],
|
[process.env.CI ? 'dot' : 'list'],
|
||||||
|
@ -98,7 +98,6 @@ import {
|
|||||||
import { getThemeColorForThreeJs } from 'lib/theme'
|
import { getThemeColorForThreeJs } from 'lib/theme'
|
||||||
import { err, trap } from 'lib/trap'
|
import { err, trap } from 'lib/trap'
|
||||||
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||||
import { addCircleToSketchAst } from 'lib/circleTool'
|
|
||||||
|
|
||||||
type DraftSegment = 'line' | 'tangentialArcTo'
|
type DraftSegment = 'line' | 'tangentialArcTo'
|
||||||
|
|
||||||
@ -715,8 +714,11 @@ export class SceneEntities {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
setupDraftRectangle = async (
|
setupDraftRectangle = async (
|
||||||
rectangleOrigin: [number, number],
|
sketchPathToNode: PathToNode,
|
||||||
{ sketchPathToNode, origin, zAxis, yAxis }: SketchDetails
|
forward: [number, number, number],
|
||||||
|
up: [number, number, number],
|
||||||
|
sketchOrigin: [number, number, number],
|
||||||
|
rectangleOrigin: [x: number, y: number]
|
||||||
) => {
|
) => {
|
||||||
let _ast = structuredClone(kclManager.ast)
|
let _ast = structuredClone(kclManager.ast)
|
||||||
|
|
||||||
@ -748,9 +750,9 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
forward: yAxis,
|
forward,
|
||||||
up: zAxis,
|
up,
|
||||||
position: origin,
|
position: sketchOrigin,
|
||||||
maybeModdedAst: _ast,
|
maybeModdedAst: _ast,
|
||||||
draftExpressionsIndices: { start: 0, end: 3 },
|
draftExpressionsIndices: { start: 0, end: 3 },
|
||||||
})
|
})
|
||||||
@ -860,52 +862,6 @@ export class SceneEntities {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setupCircleOriginListener = () => {
|
|
||||||
sceneInfra.setCallbacks({
|
|
||||||
onClick: (args) => {
|
|
||||||
const twoD = args.intersectionPoint?.twoD
|
|
||||||
if (!twoD) {
|
|
||||||
console.warn(`This click didn't have a 2D intersection`, args)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sceneInfra.modelingSend({
|
|
||||||
type: 'Add circle origin',
|
|
||||||
data: [twoD.x, twoD.y],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setupDraftCircle = async (
|
|
||||||
circleOrigin: [number, number],
|
|
||||||
{ sketchPathToNode, origin, zAxis, yAxis }: SketchDetails
|
|
||||||
) => {
|
|
||||||
const astWithCircle = await addCircleToSketchAst({
|
|
||||||
sourceAst: kclManager.ast,
|
|
||||||
sketchPathToNode,
|
|
||||||
circleOrigin,
|
|
||||||
})
|
|
||||||
|
|
||||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
|
||||||
sketchPathToNode,
|
|
||||||
forward: yAxis,
|
|
||||||
up: zAxis,
|
|
||||||
position: origin,
|
|
||||||
maybeModdedAst: astWithCircle,
|
|
||||||
draftExpressionsIndices: { start: 0, end: 3 },
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: Move this into the onclick handler to get the real shit
|
|
||||||
// Update the primary AST and unequip the rectangle tool
|
|
||||||
await kclManager.executeAstMock(astWithCircle)
|
|
||||||
sceneInfra.modelingSend({ type: 'CancelSketch' })
|
|
||||||
|
|
||||||
const { programMemory } = await executeAst({
|
|
||||||
ast: astWithCircle,
|
|
||||||
useFakeExecutor: true,
|
|
||||||
engineCommandManager: this.engineCommandManager,
|
|
||||||
programMemoryOverride,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setupSketchIdleCallbacks = ({
|
setupSketchIdleCallbacks = ({
|
||||||
pathToNode,
|
pathToNode,
|
||||||
up,
|
up,
|
||||||
@ -1220,9 +1176,6 @@ export class SceneEntities {
|
|||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, group)) /
|
: perspScale(sceneInfra.camControls.camera, group)) /
|
||||||
sceneInfra._baseUnitMultiplier
|
sceneInfra._baseUnitMultiplier
|
||||||
|
|
||||||
console.log('segment type', type)
|
|
||||||
|
|
||||||
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
return this.updateTangentialArcToSegment({
|
return this.updateTangentialArcToSegment({
|
||||||
prevSegment: sgPaths[index - 1],
|
prevSegment: sgPaths[index - 1],
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
import {
|
|
||||||
createArrayExpression,
|
|
||||||
createCallExpressionStdLib,
|
|
||||||
createLiteral,
|
|
||||||
createPipeExpression,
|
|
||||||
createPipeSubstitution,
|
|
||||||
createTagDeclarator,
|
|
||||||
findUniqueName,
|
|
||||||
} from 'lang/modifyAst'
|
|
||||||
import { roundOff } from './utils'
|
|
||||||
import {
|
|
||||||
PathToNode,
|
|
||||||
Program,
|
|
||||||
VariableDeclaration,
|
|
||||||
parse,
|
|
||||||
recast,
|
|
||||||
} from 'lang/wasm'
|
|
||||||
import { getNodeFromPath } from 'lang/queryAst'
|
|
||||||
import { trap } from './trap'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide away the working with the AST
|
|
||||||
*/
|
|
||||||
export async function addCircleToSketchAst({
|
|
||||||
sourceAst,
|
|
||||||
sketchPathToNode,
|
|
||||||
circleOrigin,
|
|
||||||
}: {
|
|
||||||
sourceAst: Program
|
|
||||||
sketchPathToNode?: PathToNode
|
|
||||||
circleOrigin?: [number, number]
|
|
||||||
}) {
|
|
||||||
let _ast = JSON.parse(JSON.stringify(sourceAst))
|
|
||||||
|
|
||||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
|
||||||
_ast,
|
|
||||||
sketchPathToNode || [],
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (trap(_node1)) return Promise.reject(_node1)
|
|
||||||
|
|
||||||
const tag = findUniqueName(_ast, 'circle')
|
|
||||||
|
|
||||||
const _node2 = getNodeFromPath<VariableDeclaration>(
|
|
||||||
_ast,
|
|
||||||
sketchPathToNode || [],
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (trap(_node2)) return Promise.reject(_node2)
|
|
||||||
const startSketchOn = _node2.node?.declarations
|
|
||||||
|
|
||||||
const startSketchOnInit = startSketchOn?.[0]?.init
|
|
||||||
startSketchOn[0].init = createPipeExpression([
|
|
||||||
startSketchOnInit,
|
|
||||||
...getCircleCallExpressions({
|
|
||||||
center: circleOrigin,
|
|
||||||
tag,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
|
|
||||||
const maybeModdedAst = parse(recast(_ast))
|
|
||||||
if (trap(maybeModdedAst)) return Promise.reject(maybeModdedAst)
|
|
||||||
return Promise.resolve(maybeModdedAst)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns AST expressions for this KCL code:
|
|
||||||
* const yo = startSketchOn('XY')
|
|
||||||
* |> startProfileAt([0, 0], %)
|
|
||||||
* |> circle([0, 0], 0, %) <- this line
|
|
||||||
*/
|
|
||||||
export function getCircleCallExpressions({
|
|
||||||
center = [0, 0],
|
|
||||||
radius = 10,
|
|
||||||
tag,
|
|
||||||
}: {
|
|
||||||
center?: [number, number]
|
|
||||||
radius?: number
|
|
||||||
tag: string
|
|
||||||
}) {
|
|
||||||
return [
|
|
||||||
createCallExpressionStdLib('circle', [
|
|
||||||
createArrayExpression([
|
|
||||||
createLiteral(roundOff(center[0])),
|
|
||||||
createLiteral(roundOff(center[1])),
|
|
||||||
]),
|
|
||||||
createLiteral(roundOff(radius)),
|
|
||||||
createPipeSubstitution(),
|
|
||||||
createTagDeclarator(tag),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
}
|
|
@ -293,8 +293,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
status: 'available',
|
status: 'available',
|
||||||
disabled: (state) =>
|
disabled: (state) =>
|
||||||
state.matches('Sketch no face') ||
|
state.matches('Sketch no face') ||
|
||||||
state.matches('Sketch.Rectangle tool.Awaiting second corner') ||
|
state.matches('Sketch.Rectangle tool.Awaiting second corner'),
|
||||||
state.matches('Sketch.Circle tool.Awaiting perimeter click'),
|
|
||||||
title: 'Line',
|
title: 'Line',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches('Sketch.Line tool') ? ['Esc', 'L'] : 'L',
|
state.matches('Sketch.Line tool') ? ['Esc', 'L'] : 'L',
|
||||||
@ -356,20 +355,9 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: 'circle-center',
|
id: 'circle-center',
|
||||||
onClick: ({ modelingStateMatches, modelingSend }) =>
|
onClick: () => console.error('Center circle not yet implemented'),
|
||||||
modelingSend({
|
|
||||||
type: 'change tool',
|
|
||||||
data: {
|
|
||||||
tool: !modelingStateMatches('Sketch.Circle tool')
|
|
||||||
? 'circle'
|
|
||||||
: 'none',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
status: 'available',
|
status: 'unavailable',
|
||||||
disabled: (state) =>
|
|
||||||
!canRectangleTool(state.context) &&
|
|
||||||
!state.matches('Sketch.Circle tool'),
|
|
||||||
title: 'Center circle',
|
title: 'Center circle',
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
description: 'Start drawing a circle from its center',
|
description: 'Start drawing a circle from its center',
|
||||||
@ -379,9 +367,6 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
url: 'https://github.com/KittyCAD/modeling-app/issues/1501',
|
url: 'https://github.com/KittyCAD/modeling-app/issues/1501',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
hotkey: (state) =>
|
|
||||||
state.matches('Sketch.Circle tool') ? ['Esc', 'C'] : 'C',
|
|
||||||
isActive: (state) => state.matches('Sketch.Circle tool'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'circle-three-points',
|
id: 'circle-three-points',
|
||||||
|
@ -141,12 +141,7 @@ interface Store {
|
|||||||
openPanes: SidebarType[]
|
openPanes: SidebarType[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SketchTool =
|
export type SketchTool = 'line' | 'tangentialArc' | 'rectangle' | 'none'
|
||||||
| 'line'
|
|
||||||
| 'tangentialArc'
|
|
||||||
| 'rectangle'
|
|
||||||
| 'circle'
|
|
||||||
| 'none'
|
|
||||||
|
|
||||||
export type ModelingMachineEvent =
|
export type ModelingMachineEvent =
|
||||||
| {
|
| {
|
||||||
@ -214,10 +209,6 @@ export type ModelingMachineEvent =
|
|||||||
type: 'Add rectangle origin'
|
type: 'Add rectangle origin'
|
||||||
data: [x: number, y: number]
|
data: [x: number, y: number]
|
||||||
}
|
}
|
||||||
| {
|
|
||||||
type: 'Add circle origin'
|
|
||||||
data: [x: number, y: number]
|
|
||||||
}
|
|
||||||
| {
|
| {
|
||||||
type: 'done.invoke.animate-to-face' | 'done.invoke.animate-to-sketch'
|
type: 'done.invoke.animate-to-face' | 'done.invoke.animate-to-sketch'
|
||||||
data: SketchDetails
|
data: SketchDetails
|
||||||
@ -375,7 +366,7 @@ export const modelingMachine = createMachine(
|
|||||||
'Artifact graph emptied': 'hidePlanes',
|
'Artifact graph emptied': 'hidePlanes',
|
||||||
},
|
},
|
||||||
|
|
||||||
entry: 'show default planes',
|
entry: ['show default planes', 'reset camera position'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -600,8 +591,6 @@ export const modelingMachine = createMachine(
|
|||||||
Cancel: '#Modeling.Sketch.undo startSketchOn',
|
Cancel: '#Modeling.Sketch.undo startSketchOn',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'new state 1': {},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initial: 'Init',
|
initial: 'Init',
|
||||||
@ -807,31 +796,8 @@ export const modelingMachine = createMachine(
|
|||||||
target: 'Tangential arc to',
|
target: 'Tangential arc to',
|
||||||
cond: 'next is tangential arc',
|
cond: 'next is tangential arc',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
target: 'Circle tool',
|
|
||||||
cond: 'next is circle',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
'Circle tool': {
|
|
||||||
entry: 'listen for circle origin',
|
|
||||||
|
|
||||||
states: {
|
|
||||||
'Awaiting origin': {
|
|
||||||
on: {
|
|
||||||
'Add circle origin': {
|
|
||||||
target: 'Awaiting perimeter click',
|
|
||||||
actions: 'set up draft circle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
'Awaiting perimeter click': {},
|
|
||||||
},
|
|
||||||
|
|
||||||
initial: 'Awaiting origin',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
initial: 'Init',
|
initial: 'Init',
|
||||||
@ -1075,14 +1041,6 @@ export const modelingMachine = createMachine(
|
|||||||
if ((state?.event as any).data.tool !== 'rectangle') return false
|
if ((state?.event as any).data.tool !== 'rectangle') return false
|
||||||
return canRectangleTool({ sketchDetails })
|
return canRectangleTool({ sketchDetails })
|
||||||
},
|
},
|
||||||
'next is circle': ({ sketchDetails }, _, { state }) => {
|
|
||||||
if ((state?.event as any).data.tool !== 'circle') return false
|
|
||||||
// TODO: Both this an rectangle can currently only be used
|
|
||||||
// if the sketch is empty. They share limited implementations,
|
|
||||||
// so I'm using the same cond until we have
|
|
||||||
// multi-profile sketch support in.
|
|
||||||
return canRectangleTool({ sketchDetails })
|
|
||||||
},
|
|
||||||
'next is line': (_, __, { state }) =>
|
'next is line': (_, __, { state }) =>
|
||||||
(state?.event as any).data.tool === 'line',
|
(state?.event as any).data.tool === 'line',
|
||||||
'next is none': (_, __, { state }) =>
|
'next is none': (_, __, { state }) =>
|
||||||
@ -1105,6 +1063,8 @@ export const modelingMachine = createMachine(
|
|||||||
sketchEnginePathId: '',
|
sketchEnginePathId: '',
|
||||||
sketchPlaneId: '',
|
sketchPlaneId: '',
|
||||||
}),
|
}),
|
||||||
|
'reset camera position': () =>
|
||||||
|
sceneInfra.camControls.resetCameraPosition(),
|
||||||
'set new sketch metadata': assign((_, { data }) => ({
|
'set new sketch metadata': assign((_, { data }) => ({
|
||||||
sketchDetails: data,
|
sketchDetails: data,
|
||||||
})),
|
})),
|
||||||
@ -1323,18 +1283,13 @@ export const modelingMachine = createMachine(
|
|||||||
},
|
},
|
||||||
'set up draft rectangle': ({ sketchDetails }, { data }) => {
|
'set up draft rectangle': ({ sketchDetails }, { data }) => {
|
||||||
if (!sketchDetails || !data) return
|
if (!sketchDetails || !data) return
|
||||||
|
sceneEntitiesManager.setupDraftRectangle(
|
||||||
sceneEntitiesManager.setupDraftRectangle(data, sketchDetails)
|
sketchDetails.sketchPathToNode,
|
||||||
},
|
sketchDetails.zAxis,
|
||||||
'listen for circle origin': ({ sketchDetails }) => {
|
sketchDetails.yAxis,
|
||||||
if (!sketchDetails) return
|
sketchDetails.origin,
|
||||||
sceneEntitiesManager.setupCircleOriginListener()
|
data
|
||||||
},
|
)
|
||||||
'set up draft circle': ({ sketchDetails }, { data }) => {
|
|
||||||
if (!sketchDetails || !data) return
|
|
||||||
console.log('setting up draft circle', data)
|
|
||||||
|
|
||||||
sceneEntitiesManager.setupDraftCircle(data, sketchDetails)
|
|
||||||
},
|
},
|
||||||
'set up draft line without teardown': ({ sketchDetails }) => {
|
'set up draft line without teardown': ({ sketchDetails }) => {
|
||||||
if (!sketchDetails) return
|
if (!sketchDetails) return
|
||||||
@ -1705,10 +1660,7 @@ export function isEditingExistingSketch({
|
|||||||
(item) =>
|
(item) =>
|
||||||
item.type === 'CallExpression' && item.callee.name === 'startProfileAt'
|
item.type === 'CallExpression' && item.callee.name === 'startProfileAt'
|
||||||
)
|
)
|
||||||
const hasCircle = pipeExpression.body.some(
|
return hasStartProfileAt && pipeExpression.body.length > 2
|
||||||
(item) => item.type === 'CallExpression' && item.callee.name === 'circle'
|
|
||||||
)
|
|
||||||
return (hasStartProfileAt && pipeExpression.body.length > 2) || hasCircle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canRectangleTool({
|
export function canRectangleTool({
|
||||||
|
@ -43,8 +43,8 @@ export default function UserMenu() {
|
|||||||
<h2 className="text-2xl font-bold">User Menu</h2>
|
<h2 className="text-2xl font-bold">User Menu</h2>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
Click {buttonDescription} in the upper right to open the user menu.
|
Click {buttonDescription} in the upper right to open the user menu.
|
||||||
You can change your user-level settings, sign out, or request a
|
You can change your user-level settings, sign out, report a bug,
|
||||||
feature.
|
manage your account, request a feature, and more.
|
||||||
</p>
|
</p>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
Many settings can be set either a user or per-project level. User
|
Many settings can be set either a user or per-project level. User
|
||||||
|