Compare commits

...

10 Commits

Author SHA1 Message Date
e1da72a0ae Update common points to reflect empty zoom-to-fit 2024-08-05 17:40:06 +02:00
ec2d1999a7 fmt 2024-08-05 16:31:07 +02:00
95683f1cc1 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) 2024-08-05 14:17:29 +00:00
f48f1c21c1 A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) 2024-08-05 14:16:05 +00:00
5cdf2de89a Reset camera position when artifact graph is cleared 2024-08-05 16:08:28 +02:00
543e809739 Add "report a bug" mention to user menu onboarding step (#3278)
* Mention "report a bug" in user menu onboarding step

* Add to a test so we match QAWolf a bit more

* Re-run CI
2024-08-05 08:24:19 -04:00
61b669cf4e github actions Playwright shard execution (#3199)
* add @snapshot tag to all snapshot-tests

* set workers to 1 on CI

* try sharding on google chrome only

* add retry when navigating to the app (on CI)

* reduce ubuntu-cores to 2

* revert runner back to 8 cores

* re-add retry + enable macos

* Reduce timeouts to 30 minutes

* ensure retry is triggered

* removed sharding when retrying failures

* added --pass-with-no-tests

* ensure failure occurs

* revert failure

* use smaller sized runners

* revert back to supported runner size

* revert to og version

* minimize changes

* yarn fmt

* ensure failure

* undo failure

---------

Co-authored-by: ryanrosello-og <ry@zoo.dev>
2024-08-05 21:30:16 +10:00
75f1aaa824 camera breaks on extrude zoom to fit (#3276) 2024-08-05 16:08:30 +10:00
f4848d7dea Jump to error not lint (#3271)
* dont jump to lints

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

* updates

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

* fix

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-08-05 02:44:33 +00:00
a0167f6ba6 Coplanar sketch should have diagnostic error. (#3269)
* artifactMapTweak

* typo
2024-08-05 02:04:53 +00:00
19 changed files with 830 additions and 606 deletions

View File

@ -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

View File

@ -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) => {
@ -1020,7 +1020,7 @@ test.describe('Editor tests', () => {
|> line([0, -10], %, $revolveAxis) |> line([0, -10], %, $revolveAxis)
|> close(%) |> close(%)
|> extrude(10, %) |> extrude(10, %)
const sketch001 = startSketchOn(box, revolveAxis) const sketch001 = startSketchOn(box, revolveAxis)
|> startProfileAt([5, 10], %) |> startProfileAt([5, 10], %)
|> line([0, -10], %) |> line([0, -10], %)
@ -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)
}
}) })
}) })
@ -3894,6 +3905,39 @@ const extrude001 = extrude(distance001, sketch001)`.replace(
test.describe('Regression tests', () => { test.describe('Regression tests', () => {
// bugs we found that don't fit neatly into other categories // bugs we found that don't fit neatly into other categories
test('bad model has inline error #3251', async ({ page }) => {
// because the model has `line([0,0]..` it is valid code, but the model is invalid
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
const u = await getUtils(page)
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch2 = startSketchOn("XY")
const sketch001 = startSketchAt([-0, -0])
|> line([0, 0], %)
|> line([-4.84, -5.29], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)`
)
})
await page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
// error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
// error text on hover
await page.hover('.cm-lint-marker-error')
// this is a cryptic error message, fact that all the lines are co-linear from the `line([0,0])` is the issue why
// the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating
const crypticErrorText = `ApiError`
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
})
test('executes on load', async ({ page }) => { test('executes on load', async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -8333,6 +8377,83 @@ test.describe('Code pane and errors', () => {
await badge.click() await badge.click()
// Ensure we have an error diagnostic. // Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'sketch profile must lie entirely on one side of the revolution axis'
)
.first()
).toBeVisible()
})
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
page,
}) => {
const u = await getUtils(page)
// Load the app with the working starter code
await page.addInitScript((code) => {
localStorage.setItem('persistCode', code)
}, TEST_CODE_LONG_WITH_ERROR_OUT_OF_VIEW)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page.waitForTimeout(1000)
// Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification')
// Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
// click in the editor to focus it
await page.locator('.cm-content').click()
await page.waitForTimeout(500)
// go to the start of the editor and enter more text which will trigger
// a lint error.
// GO to the start of the editor.
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('ArrowUp')
await page.keyboard.press('Home')
await page.keyboard.type('const foo_bar = 1')
await page.waitForTimeout(500)
await page.keyboard.press('Enter')
// ensure we have a lint error
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
// Click the badge.
const badge = page.locator('#code-badge')
await expect(badge).toBeVisible()
await badge.click()
// Ensure we have an error diagnostic.
await expect(page.locator('.cm-lint-marker-error').first()).toBeVisible()
// Hover over the error to see the error message
await page.hover('.cm-lint-marker-error')
await expect(
page
.getByText(
'sketch profile must lie entirely on one side of the revolution axis'
)
.first()
).toBeVisible()
}) })
}) })

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -337,7 +337,24 @@ fn svg = (surface, origin, depth) => {
|> close(%) |> close(%)
|> extrude(depth, %) |> extrude(depth, %)
"thing";kajsnd;akjsnd const box = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> line([10, 0], %)
|> line([0, -10], %, $revolveAxis)
|> close(%)
|> extrude(10, %)
const sketch001 = startSketchOn(box, revolveAxis)
|> startProfileAt([5, 10], %)
|> line([0, -10], %)
|> line([2, 0], %)
|> line([0, -10], %)
|> close(%)
|> revolve({
axis: revolveAxis,
angle: 90
}, %)
return 0 return 0
} }

View File

@ -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]) => {

View File

@ -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'],

View File

@ -22,7 +22,7 @@ import {
historyKeymap, historyKeymap,
history, history,
} from '@codemirror/commands' } from '@codemirror/commands'
import { lintGutter, lintKeymap } from '@codemirror/lint' import { diagnosticCount, lintGutter, lintKeymap } from '@codemirror/lint'
import { import {
foldGutter, foldGutter,
foldKeymap, foldKeymap,
@ -196,7 +196,10 @@ export const KclEditorPane = () => {
// On first load of this component, ensure we show the current errors // On first load of this component, ensure we show the current errors
// in the editor. // in the editor.
kclManager.setDiagnosticsForCurrentErrors() // Make sure we don't add them twice.
if (diagnosticCount(_editorView.state) === 0) {
kclManager.setDiagnosticsForCurrentErrors()
}
}} }}
/> />
</div> </div>

View File

@ -63,7 +63,7 @@ export const sidebarPanes: SidebarPane[] = [
}, },
onClick: (e) => { onClick: (e) => {
e.preventDefault() e.preventDefault()
editorManager.scrollToFirstDiagnosticIfExists() editorManager.scrollToFirstErrorDiagnosticIfExists()
}, },
}, },
}, },

View File

@ -141,14 +141,14 @@ export default class EditorManager {
}) })
} }
scrollToFirstDiagnosticIfExists() { scrollToFirstErrorDiagnosticIfExists() {
if (!this._editorView) return if (!this._editorView) return
let firstDiagnosticPos: [number, number] | null = null let firstDiagnosticPos: [number, number] | null = null
forEachDiagnostic( forEachDiagnostic(
this._editorView.state, this._editorView.state,
(d: Diagnostic, from: number, to: number) => { (d: Diagnostic, from: number, to: number) => {
if (!firstDiagnosticPos) { if (!firstDiagnosticPos && d.severity === 'error') {
firstDiagnosticPos = [from, to] firstDiagnosticPos = [from, to]
} }
} }
@ -161,7 +161,11 @@ export default class EditorManager {
selection: EditorSelection.create([ selection: EditorSelection.create([
EditorSelection.cursor(firstDiagnosticPos[0]), EditorSelection.cursor(firstDiagnosticPos[0]),
]), ]),
effects: [EditorView.scrollIntoView(firstDiagnosticPos[0])], effects: [
EditorView.scrollIntoView(
EditorSelection.range(firstDiagnosticPos[0], firstDiagnosticPos[1])
),
],
annotations: [ annotations: [
updateOutsideEditorEvent, updateOutsideEditorEvent,
Transaction.addToHistory.of(false), Transaction.addToHistory.of(false),

View File

@ -240,6 +240,7 @@ export function getArtifactsToUpdate({
const response = responseMap[id] const response = responseMap[id]
const cmd = command.cmd const cmd = command.cmd
const returnArr: ReturnType<typeof getArtifactsToUpdate> = [] const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
if (!response) return returnArr
if (cmd.type === 'enable_sketch_mode') { if (cmd.type === 'enable_sketch_mode') {
const plane = getArtifact(currentPlaneId) const plane = getArtifact(currentPlaneId)
const pathIds = plane?.type === 'plane' ? plane?.pathIds : [] const pathIds = plane?.type === 'plane' ? plane?.pathIds : []
@ -316,7 +317,7 @@ export function getArtifactsToUpdate({
artifact: { ...path, segIds: [id] }, artifact: { ...path, segIds: [id] },
}) })
if ( if (
response.type === 'modeling' && response?.type === 'modeling' &&
response.data.modeling_response.type === 'close_path' response.data.modeling_response.type === 'close_path'
) { ) {
returnArr.push({ returnArr.push({

View File

@ -2,7 +2,7 @@ import { Program, SourceRange } from 'lang/wasm'
import { VITE_KC_API_WS_MODELING_URL } from 'env' import { VITE_KC_API_WS_MODELING_URL } from 'env'
import { Models } from '@kittycad/lib' import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave' import { exportSave } from 'lib/exportSave'
import { deferExecution, uuidv4 } from 'lib/utils' import { deferExecution, isOverlap, uuidv4 } from 'lib/utils'
import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme' import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
import { import {
@ -1899,15 +1899,10 @@ export class EngineCommandManager extends EventTarget {
range: SourceRange, range: SourceRange,
commandTypeToTarget: string commandTypeToTarget: string
): string | undefined { ): string | undefined {
const values = Object.entries(this.artifactGraph) for (const [artifactId, artifact] of this.artifactGraph) {
for (const [id, data] of values) { if ('codeRef' in artifact && isOverlap(range, artifact.codeRef.range)) {
// // Our range selection seems to just select the cursor position, so either if (commandTypeToTarget === artifact.type) return artifactId
// // of these can be right... }
if (
(data.range[0] === range[0] || data.range[1] === range[1]) &&
data.type === commandTypeToTarget
)
return id
} }
return undefined return undefined
} }

View File

@ -4,6 +4,7 @@ import EditorManager from 'editor/manager'
import { KclManager } from 'lang/KclSingleton' import { KclManager } from 'lang/KclSingleton'
import CodeManager from 'lang/codeManager' import CodeManager from 'lang/codeManager'
import { EngineCommandManager } from 'lang/std/engineConnection' import { EngineCommandManager } from 'lang/std/engineConnection'
import { uuidv4 } from './utils'
export const codeManager = new CodeManager() export const codeManager = new CodeManager()
@ -40,4 +41,14 @@ if (typeof window !== 'undefined') {
;(window as any).enableFillet = () => { ;(window as any).enableFillet = () => {
;(window as any)._enableFillet = true ;(window as any)._enableFillet = true
} }
;(window as any).zoomToFit = () =>
engineCommandManager.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'zoom_to_fit',
object_ids: [], // leave empty to zoom to all objects
padding: 0.2, // padding around the objects
},
})
} }

View File

@ -366,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'],
}, },
}, },
@ -1063,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,
})), })),
@ -1101,11 +1103,13 @@ export const modelingMachine = createMachine(
store.videoElement?.pause() store.videoElement?.pause()
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: pathToExtrudeArg, focusPath: pathToExtrudeArg,
zoomToFit: true, // commented out as a part of https://github.com/KittyCAD/modeling-app/issues/3270
zoomOnRangeAndType: { // looking to add back in the future
range: selection.codeBasedSelections[0].range, // zoomToFit: true,
type: 'start_path', // zoomOnRangeAndType: {
}, // range: selection.codeBasedSelections[0].range,
// type: 'path',
// },
}) })
if (!engineCommandManager.engineConnection?.idleMode) { if (!engineCommandManager.engineConnection?.idleMode) {
store.videoElement?.play().catch((e) => { store.videoElement?.play().catch((e) => {

View File

@ -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